tests: amend for PHPUnit 7
[racktables] / wwwroot / inc / interface.php
CommitLineData
b325120a 1<?php
cddbb9fd
DO
2
3# This file is a part of RackTables, a datacenter and server room management
4# framework. See accompanying file "COPYING" for the full copyright and
5# licensing information.
6
e673ee24
DO
7/*
8*
9* This file contains frontend functions for RackTables.
10*
11*/
12
e82deb48 13require_once 'ajax-interface.php';
defd92d8 14require_once 'slb-interface.php';
65e557dd 15
9c0b0016
DO
16// Interface function's special.
17$nextorder['odd'] = 'even';
18$nextorder['even'] = 'odd';
19
52c836b1
DO
20// address allocation type
21$aat = array
22(
23 'regular' => 'Connected',
24 'virtual' => 'Loopback',
25 'shared' => 'Shared',
26 'router' => 'Router',
2f96a91a 27 'point2point' => 'Point-to-point',
52c836b1 28);
beb9e88a 29// address allocation code, IPv4 addresses and objects view
9d5f6a80 30$aac_right = array
52c836b1
DO
31(
32 'regular' => '',
9d5f6a80
AA
33 'virtual' => '<span class="aac-right" title="' . $aat['virtual'] . '">L</span>',
34 'shared' => '<span class="aac-right" title="' . $aat['shared'] . '">S</span>',
35 'router' => '<span class="aac-right" title="' . $aat['router'] . '">R</span>',
2f96a91a 36 'point2point' => '<span class="aac-right" title="' . $aat['point2point'] . '">P</span>',
52c836b1 37);
beb9e88a 38// address allocation code, IPv4 networks view
9d5f6a80 39$aac_left = array
beb9e88a
DO
40(
41 'regular' => '',
9d5f6a80
AA
42 'virtual' => '<span class="aac-left" title="' . $aat['virtual'] . '">L:</span>',
43 'shared' => '<span class="aac-left" title="' . $aat['shared'] . '">S:</span>',
44 'router' => '<span class="aac-left" title="' . $aat['router'] . '">R:</span>',
2f96a91a 45 'point2point' => '<span class="aac-left" title="' . $aat['point2point'] . '">P:</span>',
beb9e88a 46);
52c836b1 47
0dabdc53
DO
48$vtdecoder = array
49(
50 'ondemand' => '',
54b7ced3
DO
51 'compulsory' => 'P',
52# 'alien' => 'NT',
0dabdc53
DO
53);
54
55$vtoptions = array
56(
54b7ced3
DO
57 'ondemand' => 'auto',
58 'compulsory' => 'permanent',
59# 'alien' => 'never touch',
0dabdc53
DO
60);
61
e5c4506d
DO
62// This may be populated later onsite, report rendering function will use it.
63// See the $systemreport for structure.
64$localreports = array();
d6adad3e 65
8d4f7d18
DO
66$attrtypes = array
67(
68 'uint' => '[U] unsigned integer',
69 'float' => '[F] floating point',
70 'string' => '[S] string',
86ff26ae
DO
71 'dict' => '[D] dictionary record',
72 'date' => '[T] date'
8d4f7d18
DO
73);
74
5eb81cf8
AD
75function showLogoutURL ()
76{
55eefced 77 $https = (isset ($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 's' : '';
5eb81cf8 78 $port = (! in_array ($_SERVER['SERVER_PORT'], array (80, 443))) ? ':' . $_SERVER['SERVER_PORT'] : '';
73d1e589 79 printf ('http%s://logout@%s%s%s?logout', $https, $_SERVER['SERVER_NAME'], $port, $_SERVER['PHP_SELF']);
5eb81cf8
AD
80}
81
a1d72d2e
AA
82$quick_links = NULL; // you can override this in your local.php, but first initialize it with getConfiguredQuickLinks()
83
84function renderQuickLinks()
85{
86 global $quick_links;
87 if (! isset ($quick_links))
88 $quick_links = getConfiguredQuickLinks();
89 echo '<ul class="qlinks">';
90 foreach ($quick_links as $link)
91 echo '<li><a href="' . $link['href'] . '">' . str_replace (' ', '&nbsp;', $link['title']) . '</a></li>';
92 echo '</ul>';
93}
94
f97df464
DO
95function renderInterfaceHTML ($pageno, $tabno, $payload)
96{
1cfcac1b
DO
97 global $remote_displayname;
98 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
99 echo '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">';
100 echo '<head><title>' . getTitle ($pageno) . '</title>';
101 printPageHeaders();
102 echo '</head>';
103 echo '<body>';
104 echo '<div class=maintable>';
105 echo '<div class=mainheader>';
106 echo '<div style="float: right" class=greeting>';
107 echo mkA ($remote_displayname, 'myaccount', NULL, 'default');
108 echo ' [ <a href="';
109 showLogoutURL();
110 echo '">logout</a> ]</div>'; // greeting
111 echo getConfigVar ('enterprise') . ' RackTables ';
112 echo '<a href="http://racktables.org" title="Visit RackTables site">' . CODE_VERSION . '</a>';
113 renderQuickLinks();
114 echo '</div>'; // mainheader
115 echo '<div class=menubar>';
116 showPathAndSearch ($pageno, $tabno);
117 echo '</div>';
118 echo '<div class=tabbar>';
119 showTabs ($pageno, $tabno);
120 echo '</div>';
121 echo '<div class=msgbar>';
122 showMessageOrError();
123 echo '</div>';
124 echo "<div class=pagebar>${payload}</div>";
125 echo '</div>'; // maintable
126 echo '</body>';
127 echo '</html>';
f97df464
DO
128}
129
e673ee24 130// Main menu.
f2168c4b
DO
131function renderIndexItem ($ypageno)
132{
133 echo (! permitted ($ypageno)) ? " <td>&nbsp;</td>\n" :
134 " <td>\n" .
135 " <h1><a href='" . makeHref (array ('page' => $ypageno)) . "'>" .
136 getPageName ($ypageno) . "<br>\n" . getImageHREF ($ypageno) .
137 "</a></h1>\n" .
138 " </td>\n";
4b680876
DO
139}
140
e673ee24
DO
141function renderIndex ()
142{
67c5c049 143 global $indexlayout;
1cfcac1b
DO
144 echo '<table border=0 cellpadding=0 cellspacing=0 width="100%">';
145 echo '<tr><td><div style="text-align: center; margin: 10px; ">';
146 echo '<table width="100%" cellspacing=0 cellpadding=20 class=mainmenu border=0>';
147 foreach ($indexlayout as $row)
148 {
149 echo '<tr>';
150 foreach ($row as $column)
151 if ($column === NULL)
152 echo '<td>&nbsp;</td>';
153 else
154 renderIndexItem ($column);
155 echo '</tr>';
156 }
157 echo '</table>';
158 echo '</div></td></tr>';
159 echo '</table>';
e673ee24
DO
160}
161
f3505b35
AA
162function getRenderedAlloc ($object_id, $alloc)
163{
164 $ret = array
165 (
166 'tr_class' => '',
40c9fc94 167 'td_name_suffix' => '',
f3505b35
AA
168 'td_ip' => '',
169 'td_network' => '',
170 'td_routed_by' => '',
171 'td_peers' => '',
172 );
173 $dottedquad = $alloc['addrinfo']['ip'];
4318ced5 174 $ip_bin = $alloc['addrinfo']['ip_bin'];
f3505b35 175
4318ced5
AA
176 $hl_ip_bin = NULL;
177 if (isset ($_REQUEST['hl_ip']))
f3505b35 178 {
4318ced5
AA
179 $hl_ip_bin = ip_parse ($_REQUEST['hl_ip']);
180 addAutoScrollScript ("ip-" . $_REQUEST['hl_ip']);
f3505b35
AA
181 }
182
183 $ret['tr_class'] = $alloc['addrinfo']['class'];
4318ced5 184 if ($hl_ip_bin === $ip_bin)
5c717ce5 185 $ret['tr_class'] .= ' highlight';
dec748f6 186
68c91b9c
AA
187 // render IP change history
188 $ip_title = '';
189 $ip_class = '';
190 if (isset ($alloc['addrinfo']['last_log']))
191 {
192 $log = $alloc['addrinfo']['last_log'];
193 $ip_title = "title='" .
194 htmlspecialchars
195 (
196 $log['user'] . ', ' . formatAge ($log['time']),
197 ENT_QUOTES
198 ) . "'";
199 $ip_class = 'hover-history underline';
200 }
201
f3505b35 202 // render IP address td
9d5f6a80 203 global $aac_right;
4318ced5
AA
204 $netinfo = spotNetworkByIP ($ip_bin);
205 $ret['td_ip'] = "<td class='tdleft'>";
206 if (isset ($netinfo))
207 {
208 $title = $dottedquad;
209 if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
210 $title .= '/' . $netinfo['mask'];
68c91b9c 211 $ret['td_ip'] .= "<a name='ip-$dottedquad' class='$ip_class' $ip_title href='" .
f3505b35
AA
212 makeHref (
213 array
214 (
4318ced5 215 'page' => 'ipaddress',
f3505b35
AA
216 'hl_object_id' => $object_id,
217 'ip' => $dottedquad,
218 )
4318ced5
AA
219 ) . "'>$title</a>";
220 }
f3505b35 221 else
68c91b9c 222 $ret['td_ip'] .= "<span class='$ip_class' $ip_title>$dottedquad</span>";
9d5f6a80 223 $ret['td_ip'] .= $aac_right[$alloc['type']];
33973968 224 if ($alloc['addrinfo']['name'] != '')
a0bdf6dc 225 $ret['td_ip'] .= ' (' . stringForLabel ($alloc['addrinfo']['name']) . ')';
f3505b35
AA
226 $ret['td_ip'] .= '</td>';
227
228 // render network and routed_by tds
4318ced5
AA
229 $td_class = 'tdleft';
230 if (! isset ($netinfo))
f3505b35
AA
231 {
232 $ret['td_network'] = "<td class='$td_class sparenetwork'>N/A</td>";
233 $ret['td_routed_by'] = $ret['td_network'];
234 }
235 else
236 {
237 $ret['td_network'] = "<td class='$td_class'>" .
238 getOutputOf ('renderCell', $netinfo) . '</td>';
239
98350e52
AA
240 // render "routed by" td
241 if ($display_routers = (getConfigVar ('IPV4_TREE_RTR_AS_CELL') == 'none'))
9d5f6a80 242 $ret['td_routed_by'] = '<td>&nbsp;</td>';
f3505b35 243 else
98350e52 244 {
98350e52 245 $other_routers = array();
6063fab7 246 foreach (findNetRouters ($netinfo) as $router)
98350e52
AA
247 if ($router['id'] != $object_id)
248 $other_routers[] = $router;
249 if (count ($other_routers))
250 $ret['td_routed_by'] = getOutputOf ('printRoutersTD', $other_routers, $display_routers);
251 else
252 $ret['td_routed_by'] = "<td class='$td_class'>&nbsp;</td>";
253 }
f3505b35 254 }
dec748f6 255
f3505b35
AA
256 // render peers td
257 $ret['td_peers'] = "<td class='$td_class'>";
258 $prefix = '';
e749a34f 259 $separator = '; ';
f3505b35
AA
260 if ($alloc['addrinfo']['reserved'] == 'yes')
261 {
262 $ret['td_peers'] .= $prefix . '<strong>RESERVED</strong>';
e749a34f 263 $prefix = $separator;
f3505b35
AA
264 }
265 foreach ($alloc['addrinfo']['allocs'] as $allocpeer)
2f96a91a
AA
266 if ($allocpeer['object_id'] != $object_id)
267 {
268 $ret['td_peers'] .= $prefix . makeIPAllocLink ($ip_bin, $allocpeer);
269 $prefix = $separator;
270 }
271 elseif ($allocpeer['type'] == 'point2point' && isset ($netinfo))
272 {
273 // show PtP peers in the IP network
6063fab7
AA
274 $addrlist = isset ($netinfo['own_addrlist']) ? $netinfo['own_addrlist'] : getIPAddrList ($netinfo, IPSCAN_DO_ALLOCS);
275 foreach (getPtPNeighbors ($ip_bin, $addrlist) as $p_ip_bin => $p_alloc_list)
2f96a91a
AA
276 foreach ($p_alloc_list as $p_alloc)
277 {
278 $ret['td_peers'] .= $prefix . '&harr;&nbsp;' . makeIPAllocLink ($p_ip_bin, $p_alloc);
279 $prefix = $separator;
280 }
281 }
282
f3505b35
AA
283 $ret['td_peers'] .= '</td>';
284
285 return $ret;
286}
287
e1add254
AD
288function renderLocationFilterPortlet ()
289{
e1add254 290 // Recursive function used to build the location tree
2056822d 291 function renderLocationCheckbox ($subtree, $level = 0)
e1add254
AD
292 {
293 $self = __FUNCTION__;
e1add254 294
2056822d 295 foreach ($subtree as $location_id => $location)
e1add254 296 {
9f68b666 297 echo "<div class=tagbox style='text-align:left; padding-left:" . ($level * 16) . "px;'>";
950dfa1f 298 $checked = (! isset ($_SESSION['locationFilter']) || in_array ($location['id'], $_SESSION['locationFilter'])) ? 'checked' : '';
9c88bba9 299 echo "<label><input type=checkbox name='location_id[]' class=${level} value='${location['id']}'${checked} onClick=checkAll(this)>${location['name']}";
9f68b666 300 echo '</label>';
2056822d 301 if ($location['kidc'])
9f68b666
AD
302 {
303 echo "<a id='lfa" . $location['id'] . "' onclick=\"expand('${location['id']}')\" href\"#\" > - </a>";
304 echo "<div id='lfd" . $location['id'] . "'>";
2056822d 305 $self ($location['kids'], $level + 1);
9f68b666
AD
306 echo '</div>';
307 }
308 echo '</div>';
e1add254
AD
309 }
310 }
311
a6e090f0 312 addJS(<<<'END'
9c88bba9
AD
313function checkAll(bx) {
314 for (var tbls=document.getElementsByTagName("table"), i=tbls.length; i--;)
4e9e1b7a 315 if (tbls[i].id == "locationFilter") {
9c88bba9
AD
316 var bxs=tbls[i].getElementsByTagName("input");
317 var in_tree = false;
9f68b666 318 for (var j=0; j<bxs.length; j++) {
9c88bba9
AD
319 if(in_tree == false && bxs[j].value == bx.value)
320 in_tree = true;
321 else if(parseInt(bxs[j].className) <= parseInt(bx.className))
322 in_tree = false;
323 if (bxs[j].type=="checkbox" && in_tree == true)
324 bxs[j].checked = bx.checked;
325 }
326 }
327}
9f68b666
AD
328
329function collapseAll(bx) {
330 for (var tbls=document.getElementsByTagName("table"), i=tbls.length; i--;)
331 if (tbls[i].id == "locationFilter") {
332 var bxs=tbls[i].getElementsByTagName("div");
333 //loop through divs to hide unchecked
334 for (var j=0; j<bxs.length; j++) {
335 var is_checked = -1;
336 var in_div=bxs[j].getElementsByTagName("input");
337 //loop through input to find if any is checked
338 for (var k=0; k<in_div.length; k++) {
339 if(in_div[k].type="checkbox") {
340 if (in_div[k].checked == true) {
341 is_checked = true;
342 break;
343 }
344 else
345 is_checked = false;
346 }
347 }
348 // nothing selected and element id is lfd, collapse it
349 if (is_checked == false && !bxs[j].id.indexOf("lfd"))
350 expand(bxs[j].id.substr(3));
351 }
352 }
353}
354
355function expand(id) {
356 var divid = document.getElementById("lfd" + id);
357 var iconid = document.getElementById("lfa" + id);
358 if (divid.style.display == 'none') {
359 divid.style.display = 'block';
360 iconid.innerHTML = ' - ';
361 } else {
362 divid.style.display = 'none';
363 iconid.innerHTML = ' + ';
364 }
365}
9c88bba9
AD
366END
367 ,TRUE);
e1add254 368 startPortlet ('Location filter');
a6e090f0 369 echo <<<'END'
9c88bba9 370<table border=0 align=center cellspacing=0 class="tagtree" id="locationFilter">
6b816bde 371 <form method=post>
e1add254
AD
372 <input type=hidden name=page value=rackspace>
373 <input type=hidden name=tab value=default>
374 <input type=hidden name=changeLocationFilter value=true>
375END;
2056822d
AA
376
377 $locationlist = listCells ('location');
e1add254
AD
378 if (count ($locationlist))
379 {
9c88bba9
AD
380 echo "<tr><td class=tagbox style='padding-left: 0px'><label>";
381 echo "<input type=checkbox name='location' onClick=checkAll(this)> Toggle all";
1341dc93 382 echo "<img src=?module=chrome&uri=pix/1x1t.gif onLoad=collapseAll(this)>"; // dirty hack to collapse all when page is displayed
9c88bba9 383 echo "</label></td></tr>\n";
9f68b666 384 echo "<tr><td class=tagbox><hr>\n";
d6409c53 385 renderLocationCheckbox (treeFromList (addTraceToNodes ($locationlist)));
9f68b666
AD
386 echo "<hr></td></tr>\n";
387 echo '<tr><td>';
e1add254
AD
388 printImageHREF ('setfilter', 'set filter', TRUE);
389 echo "</td></tr>\n";
390 }
391 else
392 {
393 echo "<tr><td class='tagbox sparenetwork'>(no locations exist)</td></tr>\n";
394 echo "<tr><td>";
395 printImageHREF ('setfilter gray');
396 echo "</td></tr>\n";
397 }
398
399 echo "</form></table>\n";
400 finishPortlet ();
401}
402
d9bad31a
AD
403function rackspaceCmp ($a, $b)
404{
405 $ret = strnatcasecmp ($a['location_tree'], $b['location_tree']);
406 if (!$ret)
407 $ret = strnatcasecmp ($a['row_name'], $b['row_name']);
408 return $ret;
409}
410
150effa7 411function getRackThumbLink ($rack, $scale = 1, $object_id = NULL)
e10d9597
DO
412{
413 if (! is_int ($scale) || $scale <= 0)
414 throw new InvalidArgException ('scale', $scale, 'must be a natural number');
415 $width = getRackImageWidth() * $scale;
416 $height = getRackImageHeight ($rack['height']) * $scale;
417 $title = "${rack['height']} units";
418 $src = '?module=image' .
150effa7
DO
419 ($scale == 1 && $object_id === NULL ? '&img=minirack' : "&img=midirack&scale=${scale}") .
420 "&rack_id=${rack['id']}" .
421 ($object_id === NULL ? '' : "&object_id=${object_id}");
e10d9597 422 $img = "<img border=0 width=${width} height=${height} title='${title}' src='${src}'>";
168c2acb 423 return mkA ($img, 'rack', $rack['id']);
e10d9597
DO
424}
425
e673ee24
DO
426function renderRackspace ()
427{
d4e27fe5 428 global $pageno;
e1add254 429 // Handle the location filter
0a00b344 430 startSession();
e1add254
AD
431 if (isset ($_REQUEST['changeLocationFilter']))
432 unset ($_SESSION['locationFilter']);
433 if (isset ($_REQUEST['location_id']))
434 $_SESSION['locationFilter'] = $_REQUEST['location_id'];
5d6352df 435 session_commit();
e1add254 436
950dfa1f
AA
437 echo "<table class=objview border=0 width='100%'><tr><td class=pcleft>";
438
95857b5c 439 $found_racks = array();
a8efc03e 440 $cellfilter = getCellFilter();
950dfa1f 441 if (! ($cellfilter['is_empty'] && !isset ($_SESSION['locationFilter']) && renderEmptyResults ($cellfilter, 'racks', getEntitiesCount ('rack'))))
e1add254 442 {
950dfa1f
AA
443 $rows = array();
444 $rackCount = 0;
524f8a7b 445 foreach (listCells ('row') as $row_id => $rowInfo)
950dfa1f 446 {
2b16c179 447 $rackList = applyCellFilter ('rack', $cellfilter, $row_id);
950dfa1f 448 $found_racks = array_merge ($found_racks, $rackList);
d9bad31a
AD
449 $location_id = $rowInfo['location_id'];
450 $locationIdx = 0;
451 // contains location names in the form of 'grandparent parent child', used for sorting
452 $locationTree = '';
453 // contains location names as well as links
454 $hrefLocationTree = '';
455 while ($location_id)
456 {
457 if ($locationIdx == 20)
458 {
459 showWarning ("Warning: There is likely a circular reference in the location tree. Investigate location ${location_id}.");
460 break;
461 }
462 $parentLocation = spotEntity ('location', $location_id);
463 $locationTree = sprintf ('%s %s', $parentLocation['name'], $locationTree);
464 $hrefLocationTree = "&raquo; <a href='" .
465 makeHref(array('page'=>'location', 'location_id'=>$parentLocation['id'])) .
466 "${cellfilter['urlextra']}'>${parentLocation['name']}</a> " .
467 $hrefLocationTree;
468 $location_id = $parentLocation['parent_id'];
469 $locationIdx++;
470 }
471 $hrefLocationTree = substr ($hrefLocationTree, 8);
950dfa1f
AA
472 $rows[] = array (
473 'location_id' => $rowInfo['location_id'],
d9bad31a
AD
474 'location_tree' => $locationTree,
475 'href_location_tree' => $hrefLocationTree,
950dfa1f
AA
476 'row_id' => $row_id,
477 'row_name' => $rowInfo['name'],
478 'racks' => $rackList
479 );
480 $rackCount += count($rackList);
481 }
1ebbf889 482
d9bad31a
AD
483 // sort by location, then by row
484 usort ($rows, 'rackspaceCmp');
485
950dfa1f 486 if (! renderEmptyResults($cellfilter, 'racks', $rackCount))
1ebbf889 487 {
950dfa1f
AA
488 // generate thumb gallery
489 global $nextorder;
950dfa1f
AA
490 // Zero value effectively disables the limit.
491 $maxPerRow = getConfigVar ('RACKS_PER_ROW');
492 $order = 'odd';
18733c4a
AD
493 if (! count ($rows))
494 echo "<h2>No rows found</h2>\n";
495 else
e1add254 496 {
950dfa1f
AA
497 echo '<table border=0 cellpadding=10 class=cooltable>';
498 echo '<tr><th class=tdleft>Location</th><th class=tdleft>Row</th><th class=tdleft>Racks</th></tr>';
499 foreach ($rows as $row)
c08915c7 500 {
950dfa1f 501 $rackList = $row['racks'];
d1d19f79 502 $location_id = $row['location_id'];
950dfa1f
AA
503
504 if (
55eefced
DO
505 $location_id != '' && isset ($_SESSION['locationFilter']) && ! in_array ($location_id, $_SESSION['locationFilter']) ||
506 empty ($rackList) && ! $cellfilter['is_empty']
950dfa1f
AA
507 )
508 continue;
509 $rackListIdx = 0;
d9bad31a
AD
510 echo "<tr class=row_${order}><th class=tdleft>${row['href_location_tree']}</th>";
511 echo "<th class=tdleft><a href='".makeHref(array('page'=>'row', 'row_id'=>$row['row_id']))."${cellfilter['urlextra']}'>${row['row_name']}</a></th>";
950dfa1f 512 echo "<th class=tdleft><table border=0 cellspacing=5><tr>";
18733c4a
AD
513 if (! count ($rackList))
514 echo '<td>(empty row)</td>';
950dfa1f
AA
515 else
516 foreach ($rackList as $rack)
e1add254 517 {
55eefced 518 if ($rackListIdx > 0 && $maxPerRow > 0 && $rackListIdx % $maxPerRow == 0)
950dfa1f
AA
519 {
520 echo '</tr></table></th></tr>';
d9bad31a 521 echo "<tr class=row_${order}><th class=tdleft></th><th class=tdleft>${row['row_name']} (continued)";
950dfa1f
AA
522 echo "</th><th class=tdleft><table border=0 cellspacing=5><tr>";
523 }
168c2acb
DO
524 echo '<td align=center valign=bottom>' . getRackThumbLink ($rack);
525 echo '<br>' . mkA (stringForLabel ($rack['name']), 'rack', $rack['id']) . '</td>';
950dfa1f 526 $rackListIdx++;
e1add254 527 }
950dfa1f
AA
528 $order = $nextorder[$order];
529 echo "</tr></table></th></tr>\n";
530 }
531 echo "</table>\n";
e1add254 532 }
1ebbf889 533 }
e673ee24 534 }
d4e27fe5 535 echo "</td><td class='pcright ${pageno}' width='25%'>";
14ad4f4a 536 renderCellFilterPortlet ($cellfilter, 'rack', $found_racks);
e1add254
AD
537 echo "<br>\n";
538 renderLocationFilterPortlet ();
489a7502 539 echo "</td></tr></table>\n";
e673ee24
DO
540}
541
2056822d 542function renderLocationRowForEditor ($subtree, $level = 0)
e1add254
AD
543{
544 $self = __FUNCTION__;
2056822d
AA
545 foreach ($subtree as $locationinfo)
546 {
547 echo "<tr><td align=left style='padding-left: " . ($locationinfo['kidc'] ? $level : ($level + 1) * 16) . "px;'>";
548 if ($locationinfo['kidc'])
549 printImageHREF ('node-expanded-static');
550 if ($locationinfo['refcnt'] > 0 || $locationinfo['kidc'] > 0)
551 printImageHREF ('nodestroy');
552 else
10f5133a 553 echo getOpLink (array ('op' => 'deleteLocation', 'location_id' => $locationinfo['id']), '', 'destroy', 'Delete location');
2056822d
AA
554 echo '</td><td class=tdleft>';
555 printOpFormIntro ('updateLocation', array ('location_id' => $locationinfo['id']));
556 $parent = isset ($locationinfo['parent_id']) ? $locationinfo['parent_id'] : 0;
557 echo getSelect
558 (
559 array ( $parent => $parent ? htmlspecialchars ($locationinfo['parent_name']) : '-- NONE --'),
560 array ('name' => 'parent_id', 'id' => 'locationid_' . $locationinfo['id'], 'class' => 'locationlist-popup'),
561 $parent,
562 FALSE
563 );
564 echo "</td><td class=tdleft>";
565 echo "<input type=text size=48 name=name value='${locationinfo['name']}'>";
566 echo '</td><td>' . getImageHREF ('save', 'Save changes', TRUE) . "</form></td></tr>\n";
567 if ($locationinfo['kidc'])
568 $self ($locationinfo['kids'], $level + 1);
569 }
e1add254
AD
570}
571
82fa45a9 572function renderLocationSelectTree ($select_name, $selected_id = NULL)
eda41675 573{
82fa45a9 574 echo "<select name='${select_name}'>";
eda41675 575 echo '<option value=0>-- NONE --</option>';
359ea9f9 576 $locationlist = listCells ('location');
d6409c53 577 foreach (treeFromList (addTraceToNodes ($locationlist)) as $location)
eda41675
AD
578 {
579 echo "<option value=${location['id']} style='font-weight: bold' ";
580 if ($location['id'] == $selected_id )
581 echo ' selected';
582 echo ">${location['name']}</option>";
e3648fa6 583 printLocationChildrenSelectOptions ($location, $selected_id);
eda41675
AD
584 }
585 echo '</select>';
586}
587
e1add254
AD
588function renderRackspaceLocationEditor ()
589{
a6e090f0 590 $js = <<<'JSTXT'
b090b2da
RQ
591 function locationeditor_showselectbox(e) {
592 $(this).load('index.php', {module: 'ajax', ac: 'get-location-select', locationid: this.id});
593 $(this).unbind('mousedown', locationeditor_showselectbox);
594 }
595 $(document).ready(function () {
596 $('select.locationlist-popup').bind('mousedown', locationeditor_showselectbox);
597 });
598JSTXT;
599
600 addJS($js, TRUE );
e1add254
AD
601 function printNewItemTR ()
602 {
e1add254 603 printOpFormIntro ('addLocation');
eda41675 604 echo '<tr><td>';
e1add254 605 printImageHREF ('create', 'Add new location', TRUE);
82fa45a9
DO
606 echo '</td><td>';
607 renderLocationSelectTree ('parent_id');
04c61e7e
AB
608 echo '</td><td><input type=text size=48 name=name></td><td>';
609 printImageHREF ('create', 'Add new location', TRUE);
e1add254
AD
610 echo "</td></tr></form>\n";
611 }
e1add254
AD
612
613 startPortlet ('Locations');
614 echo "<table border=0 cellspacing=0 cellpadding=5 align=center class=widetable>\n";
615 echo "<tr><th>&nbsp;</th><th>Parent</th><th>Name</th><th>&nbsp;</th></tr>\n";
616 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
617 printNewItemTR();
2056822d 618
1e0f13f3 619 $locations = listCells ('location');
d6409c53 620 renderLocationRowForEditor (treeFromList (addTraceToNodes ($locations)));
2056822d 621
e1add254
AD
622 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
623 printNewItemTR();
624 echo "</table><br>\n";
625 finishPortlet();
626}
627
345fd640
AD
628function renderRackspaceRowEditor ()
629{
eda41675 630 function printNewItemTR ()
345fd640
AD
631 {
632 printOpFormIntro ('addRow');
eda41675 633 echo '<tr><td>';
7be51fea 634 printImageHREF ('create', 'Add new row', TRUE);
04eee8d2
VS
635 echo '</td><td>&nbsp;';
636 echo '</td><td>&nbsp;';
82fa45a9
DO
637 echo '</td><td>';
638 renderLocationSelectTree ('location_id');
04c61e7e
AB
639 echo '</td><td><input type=text name=name></td><td>';
640 printImageHREF ('create', 'Add new row', TRUE);
aca7e169 641 echo '</td><td>&nbsp;';
34f5d4eb 642 echo '</td><td>&nbsp;</td></tr></form>';
345fd640 643 }
d732e47e 644 startPortlet ('Rows');
345fd640 645 echo "<table border=0 cellspacing=0 cellpadding=5 align=center class=widetable>\n";
34f5d4eb 646 echo "<tr><th>&nbsp;</th><th># Racks</th><th># Devices</th><th>Location</th><th>Name</th><th>&nbsp;</th><th>&nbsp;</th><th>Row link</th></tr>\n";
345fd640 647 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
eda41675 648 printNewItemTR ();
524f8a7b 649 foreach (listCells ('row') as $row_id => $rowInfo)
345fd640 650 {
eda41675 651 echo '<tr><td>';
04eee8d2
VS
652 $rc = $rowInfo['rackc'];
653 $delete_racks_str = $rc ? " and $rc rack(s)" : '';
654 echo getOpLink (array ('op'=>'deleteRow', 'row_id'=>$row_id), '', 'destroy', 'Delete row'.$delete_racks_str, 'need-confirmation');
c40666f5 655 printOpFormIntro ('updateRow', array ('row_id' => $row_id));
34f5d4eb 656 echo '</td><td class=tdright>';
04eee8d2 657 echo $rc;
34f5d4eb 658 echo '</td><td class=tdright>';
04eee8d2
VS
659 echo getRowMountsCount ($row_id);
660 echo '</td><td>';
82fa45a9 661 renderLocationSelectTree ('location_id', $rowInfo['location_id']);
04c61e7e 662 echo "</td><td><input type=text name=name value='${rowInfo['name']}'></td><td>";
7be51fea 663 printImageHREF ('save', 'Save changes', TRUE);
524f8a7b 664 echo "</form></td>";
34f5d4eb
DO
665 echo '<td>&nbsp;</td>';
666 echo '<td class=tdleft>' . mkCellA ($rowInfo) . '</td>';
524f8a7b 667 echo "</tr>\n";
345fd640
AD
668 }
669 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
eda41675 670 printNewItemTR ();
345fd640
AD
671 echo "</table><br>\n";
672 finishPortlet();
673}
674
d10401bf 675function renderRow ($row_id)
e673ee24 676{
e16b666c 677 $rowInfo = getRowInfo ($row_id);
a8efc03e 678 $cellfilter = getCellFilter();
2b16c179 679 $rackList = applyCellFilter ('rack', $cellfilter, $row_id);
138655f5
AD
680
681 $summary = array ();
682 $summary['Name'] = $rowInfo['name'];
683 if ($rowInfo['location_id'])
684 $summary['Location'] = mkA ($rowInfo['location'], 'location', $rowInfo['location_id']);
685 $summary['Racks'] = $rowInfo['count'];
686 $summary['Units'] = $rowInfo['sum'];
687 $summary['% used'] = getProgressBar (getRSUforRow ($rackList));
249c78b2 688 foreach (getAttrValuesSorted ($row_id) as $record)
138655f5
AD
689 if
690 (
55eefced 691 $record['value'] != '' &&
138655f5
AD
692 permitted (NULL, NULL, NULL, array (array ('tag' => '$attr_' . $record['id'])))
693 )
5b908204 694 $summary['{sticker}' . $record['name']] = formatAttributeValue ($record, 1561);
138655f5 695
e673ee24
DO
696 // Main layout starts.
697 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
698
699 // Left portlet with row information.
700 echo "<tr><td class=pcleft>";
138655f5 701 renderEntitySummary ($rowInfo, 'Summary', $summary);
7f297114 702 renderCellFilterPortlet ($cellfilter, 'rack', $rackList, array ('row_id' => $row_id));
6ccfd4bd 703 renderFilesPortlet ('row',$row_id);
48325652 704 echo "</td><td class=pcright>";
e673ee24 705
4fbb5a00 706 global $nextorder;
f5883ec1
DO
707 // Maximum number of racks per row is proportionally less, but at least 1.
708 $maxPerRow = max (floor (getConfigVar ('RACKS_PER_ROW') / getConfigVar ('ROW_SCALE')), 1);
709 $rackListIdx = 0;
e673ee24
DO
710 $order = 'odd';
711 startPortlet ('Racks');
712 echo "<table border=0 cellspacing=5 align='center'><tr>";
64b95774 713 foreach ($rackList as $rack)
e673ee24 714 {
f5883ec1
DO
715 if ($rackListIdx % $maxPerRow == 0)
716 {
717 if ($rackListIdx > 0)
718 echo '</tr>';
719 echo '<tr>';
720 }
9b8174d7 721 $class = ($rack['has_problems'] == 'yes') ? 'error' : $order;
168c2acb
DO
722 echo "<td align=center valign=bottom class=row_${class}>" .
723 getRackThumbLink ($rack, getConfigVar ('ROW_SCALE')) .
724 '<br>' . mkA (stringForLabel ($rack['name']), 'rack', $rack['id']) . '</td>';
e673ee24 725 $order = $nextorder[$order];
f5883ec1 726 $rackListIdx++;
e673ee24
DO
727 }
728 echo "</tr></table>\n";
729 finishPortlet();
e673ee24
DO
730 echo "</td></tr></table>";
731}
732
5ff78aff
DO
733function renderEditAttributeTRs ($update_op, $values, $objtype_id, $skip_ids = array())
734{
735 $datehint = ' (' . datetimeFormatHint (getConfigVar ('DATETIME_FORMAT')) . ')';
736 $i = 0;
737 foreach ($values as $record)
738 {
739 $annex = array (array ('tag' => '$attr_' . $record['id']));
740 $can_view = permitted (NULL, NULL, NULL, $annex);
741 if (in_array ($record['id'], $skip_ids) || ! $can_view)
742 continue;
743 $can_update = permitted (NULL, NULL, $update_op, $annex);
744 $can_clear = permitted (NULL, NULL, 'clearSticker', $annex);
745 // Ability to update ultimately includes ability to set to an empty value,
746 // i.e. to clear, but making the check this way in the ophandler is complicated,
747 // so let's keep it consistently imperfect for the time being and maybe
748 // fix it later.
749 $clear_html = ($record['value'] != '' && ($can_clear /* || $can_update*/)) ?
750 getOpLink (array ('op' => 'clearSticker', 'attr_id' => $record['id']), '', 'clear', 'Clear value', 'need-confirmation') :
751 '&nbsp;';
752 echo "<tr><td>${clear_html}</td>";
753 echo '<th class=sticker>' . $record['name'] . ($record['type'] == 'date' ? $datehint : '') . ':</th>';
754 echo '<td class=tdleft>';
755 switch ($record['type'])
756 {
757 case 'uint':
758 case 'float':
759 case 'string':
760 $ro_or_rw = $can_update ? "name=${i}_value" : 'disabled';
761 echo "<input type=text ${ro_or_rw} value='${record['value']}'>";
762 break;
763 case 'date':
764 $ro_or_rw = $can_update ? "name=${i}_value" : 'disabled';
765 $date_value = $record['value'] ? datetimestrFromTimestamp ($record['value']) : '';
766 echo "<input type=text ${ro_or_rw} value='${date_value}'>";
767 break;
768 case 'dict':
769 $ro_or_rw = $can_update ? array ('name' => "${i}_value") : array ('name' => "${i}_value", 'disabled' => 1);
770 $chapter = readChapter ($record['chapter_id'], 'o');
771 $chapter[0] = '-- NOT SET --';
772 $chapter = cookOptgroups ($chapter, $objtype_id, $record['key']);
773 printNiftySelect ($chapter, $ro_or_rw, $record['key']);
774 break;
775 default:
776 throw new InvalidArgException ('record[type]', $record['type']);
777 } // switch
778 if ($can_update)
779 {
780 echo "<input type=hidden name=${i}_attr_id value=${record['id']}>";
781 $i++;
782 }
783 echo '</td></tr>';
784 } // foreach
785 echo "<input type=hidden name=num_attrs value=${i}>";
786}
787
d10401bf 788function renderEditRowForm ($row_id)
138655f5
AD
789{
790 $row = getRowInfo ($row_id);
791
792 startPortlet ('Attributes');
793 printOpFormIntro ('updateRow');
794 echo '<table border=0 align=center>';
795 echo '<tr><td>&nbsp;</td><th class=tdright>Location:</th><td class=tdleft>';
796 $locations = array ();
797 $locations[0] = '-- NOT SET --';
798 foreach (listCells ('location') as $id => $locationInfo)
799 $locations[$id] = $locationInfo['name'];
800 natcasesort ($locations);
801 printSelect ($locations, array ('name' => 'location_id'), $row['location_id']);
802 echo "</td></tr>\n";
803 echo "<tr><td>&nbsp;</td><th class=tdright>Name (required):</th><td class=tdleft><input type=text name=name value='${row['name']}'></td></tr>\n";
5ff78aff 804 renderEditAttributeTRs ('updateRow', getAttrValuesSorted ($row_id), 1561);
138655f5
AD
805 if ($row['count'] == 0)
806 {
807 echo '<tr><td>&nbsp;</td><th class=tdright>Actions:</th><td class=tdleft>';
808 echo getOpLink (array ('op'=>'deleteRow'), '', 'destroy', 'Delete row', 'need-confirmation');
809 echo "&nbsp;</td></tr>\n";
810 }
811 echo "<tr><td class=submit colspan=3>";
812 printImageHREF ('SAVE', 'Save changes', TRUE);
813 echo "</td></tr>\n";
814 echo '</form></table><br>';
815 finishPortlet();
816
817 startPortlet ('History');
818 renderObjectHistory ($row_id);
819 finishPortlet();
820}
821
0a7feebb 822// Used by renderRack()
84feb87b 823function printObjectDetailsForRenderRack ($object_id, $hl_obj_id = 0)
0a7feebb
AD
824{
825 $objectData = spotEntity ('object', $object_id);
33973968 826 if ($objectData['asset_no'] != '')
0a7feebb
AD
827 $prefix = "<div title='${objectData['asset_no']}";
828 else
829 $prefix = "<div title='no asset tag";
830 // Don't tell about label, if it matches common name.
831 $body = '';
55eefced 832 if ($objectData['name'] != $objectData['label'] && $objectData['label'] != '')
0a7feebb
AD
833 $body = ", visible label is \"${objectData['label']}\"";
834 // Display list of child objects, if any
c17a36f0 835 $objectChildren = getChildren ($objectData, 'object');
5a7bf292 836 $slotRows = $slotCols = $slotInfo = $slotData = $slotTitle = $slotClass = array ();
0a7feebb
AD
837 if (count($objectChildren) > 0)
838 {
c17a36f0 839 foreach ($objectChildren as $childData)
84feb87b 840 {
c17a36f0
AA
841 $childNames[] = $childData['name'];
842 $attrData = getAttrValues ($childData['id']);
be28528c
M
843 $numRows = $numCols = 1;
844 if (isset ($attrData[2])) // HW type
845 {
846 extractLayout ($attrData[2]);
847 if (isset ($attrData[2]['rows']))
848 {
849 $numRows = $attrData[2]['rows'];
850 $numCols = $attrData[2]['cols'];
851 }
852 }
05abd358 853 if (isset ($attrData['28'])) // slot number
84feb87b
AD
854 {
855 $slot = $attrData['28']['value'];
be28528c
M
856 if (preg_match ('/\d+/', $slot, $matches))
857 $slot = $matches[0];
858 $slotRows[$slot] = $numRows;
859 $slotCols[$slot] = $numCols;
c17a36f0
AA
860 $slotInfo[$slot] = $childData['dname'];
861 $slotData[$slot] = $childData['id'];
33973968 862 if ($childData['asset_no'] != '')
84feb87b
AD
863 $slotTitle[$slot] = "<div title='${childData['asset_no']}";
864 else
865 $slotTitle[$slot] = "<div title='no asset tag";
55eefced 866 if ($childData['label'] != '' && $childData['label'] != $childData['dname'])
84feb87b
AD
867 $slotTitle[$slot] .= ", visible label is \"${childData['label']}\"";
868 $slotTitle[$slot] .= "'>";
5a7bf292 869 $slotClass[$slot] = 'state_T';
c17a36f0 870 if ($childData['id'] == $hl_obj_id)
0cdfc55c
DO
871 $slotClass[$slot] .= 'h';
872 if ($childData['has_problems'] == 'yes')
873 $slotClass[$slot] .= 'w';
25526a97
ME
874
875 $child = spotEntity ('object', $childData['id']);
876 setEntityColors ($child);
5abb804e 877 $slotClass[$slot] .= getObjectClass ($child, 'background:white;');
25526a97 878
84feb87b
AD
879 }
880 }
0a7feebb
AD
881 natsort($childNames);
882 $suffix = sprintf(", contains %s'>", implode(', ', $childNames));
883 }
884 else
885 $suffix = "'>";
6d8424f8 886 echo "${prefix}${body}${suffix}" . mkCellA ($objectData) . '</div>';
84feb87b
AD
887 if (in_array ($objectData['objtype_id'], array (1502,1503))) // server chassis, network chassis
888 {
889 $objAttr = getAttrValues ($objectData['id']);
890 if (isset ($objAttr[2])) // HW type
891 {
892 extractLayout ($objAttr[2]);
893 if (isset ($objAttr[2]['rows']))
894 {
895 $rows = $objAttr[2]['rows'];
896 $cols = $objAttr[2]['cols'];
897 $layout = $objAttr[2]['layout'];
898 echo "<table width='100%' border='1'>";
899 for ($r = 0; $r < $rows; $r++)
900 {
901 echo '<tr>';
902 for ($c = 0; $c < $cols; $c++)
903 {
904 $s = ($r * $cols) + $c + 1;
905 if (isset ($slotData[$s]))
906 {
be28528c 907 if ($slotData[$s] >= 0)
84feb87b 908 {
be28528c
M
909 for ($lr = 0; $lr < $slotRows[$s]; $lr++)
910 for ($lc = 0; $lc < $slotCols[$s]; $lc++)
911 {
912 $skip = ($lr * $cols) + $lc;
913 if ($skip > 0)
914 $slotData[$s + $skip] = -1;
915 }
916 echo '<td';
917 if ($slotRows[$s] > 1)
918 echo " rowspan=$slotRows[$s]";
919 if ($slotCols[$s] > 1)
920 echo " colspan=$slotCols[$s]";
5a7bf292 921 echo " class='${slotClass[$s]}'>${slotTitle[$s]}";
be28528c
M
922 if ($layout == 'V')
923 {
3e393bcc
AY
924 $tmp = mb_substr($slotInfo[$s], 0, 1);
925 for($i = 1; $i < mb_strlen($slotInfo[$s]); $i++)
926 {
927 $tmp .= '<br>' . mb_substr($slotInfo[$s], $i, 1);
928 }
be28528c
M
929 $slotInfo[$s] = $tmp;
930 }
931 echo mkA ($slotInfo[$s], 'object', $slotData[$s]);
932 echo '</div></td>';
84feb87b 933 }
84feb87b
AD
934 }
935 else
936 echo "<td class='state_F'><div title=\"Free slot\">&nbsp;</div></td>";
937 }
938 echo '</tr>';
939 }
940 echo '</table>';
941 }
942 }
943 }
0a7feebb
AD
944}
945
e673ee24 946// This function renders rack as HTML table.
7e1db771 947function renderRack ($rack_id, $hl_obj_id = 0)
e673ee24 948{
61a1d996
DO
949 $rackData = spotEntity ('rack', $rack_id);
950 amplifyCell ($rackData);
e673ee24 951 markAllSpans ($rackData);
25526a97 952 setEntityColors ($rackData);
e673ee24
DO
953 if ($hl_obj_id > 0)
954 highlightObject ($rackData, $hl_obj_id);
736acd8d 955 $neighbors = getRackNeighbors ($rackData['row_id'], $rack_id);
eda8b3ce
DO
956 $prev_id = $neighbors['prev'];
957 $next_id = $neighbors['next'];
d48d8820 958 echo "<center><table border=0><tr valign=middle>";
ad3e5e7e 959 echo '<td><h2>' . mkA ($rackData['row_name'], 'row', $rackData['row_id']) . ' :</h2></td>';
e673ee24 960 if ($prev_id != NULL)
ad3e5e7e
DO
961 echo '<td>' . mkA (getImageHREF ('prev', 'previous rack'), 'rack', $prev_id) . '</td>';
962 echo '<td><h2>' . mkA ($rackData['name'], 'rack', $rackData['id']) . '</h2></td>';
e673ee24 963 if ($next_id != NULL)
ad3e5e7e 964 echo '<td>' . mkA (getImageHREF ('next', 'next rack'), 'rack', $next_id) . '</td>';
d48d8820 965 echo "</h2></td></tr></table>\n";
e673ee24 966 echo "<table class=rack border=0 cellspacing=0 cellpadding=1>\n";
d08147d6
DO
967 echo "<tr><th width='10%'>&nbsp;</th><th width='20%'>Front</th>";
968 echo "<th width='50%'>Interior</th><th width='20%'>Back</th></tr>\n";
e673ee24
DO
969 for ($i = $rackData['height']; $i > 0; $i--)
970 {
4dcd770e 971 echo "<tr><th>" . inverseRackUnit ($i, $rackData) . "</th>";
e673ee24
DO
972 for ($locidx = 0; $locidx < 3; $locidx++)
973 {
974 if (isset ($rackData[$i][$locidx]['skipped']))
975 continue;
976 $state = $rackData[$i][$locidx]['state'];
25526a97
ME
977
978 $class = "atom state_${state}";
979
e673ee24 980 if (isset ($rackData[$i][$locidx]['hl']))
25526a97
ME
981 $class .= $rackData[$i][$locidx]['hl'];
982
983 if($state == 'T')
984 {
985 $objectData = spotEntity ('object', $rackData[$i][$locidx]['object_id']);
986 setEntityColors ($objectData);
5abb804e 987 $class .= getObjectClass ($objectData, (isset ($rackData[$i][$locidx]['hl']) && $rackData[$i][$locidx]['hl'] != "" ? "border:3px solid #80ffff !important;" : "")."background:white;");
25526a97
ME
988 }
989
990 echo "<td class='${class}'";
991
e673ee24
DO
992 if (isset ($rackData[$i][$locidx]['colspan']))
993 echo ' colspan=' . $rackData[$i][$locidx]['colspan'];
994 if (isset ($rackData[$i][$locidx]['rowspan']))
995 echo ' rowspan=' . $rackData[$i][$locidx]['rowspan'];
996 echo ">";
997 switch ($state)
998 {
999 case 'T':
84feb87b 1000 printObjectDetailsForRenderRack ($rackData[$i][$locidx]['object_id'], $hl_obj_id);
e673ee24
DO
1001 break;
1002 case 'A':
1003 echo '<div title="This rackspace does not exist">&nbsp;</div>';
1004 break;
1005 case 'F':
1006 echo '<div title="Free rackspace">&nbsp;</div>';
1007 break;
1008 case 'U':
1009 echo '<div title="Problematic rackspace, you CAN\'T mount here">&nbsp;</div>';
1010 break;
1011 default:
1012 echo '<div title="No data">&nbsp;</div>';
1013 break;
1014 }
1015 echo '</td>';
1016 }
1017 echo "</tr>\n";
1018 }
0a7feebb
AD
1019 echo "</table>\n";
1020 // Get a list of all of objects Zero-U mounted to this rack
c17a36f0
AA
1021 $zeroUObjects = getChildren ($rackData, 'object');
1022 uasort ($zeroUObjects, 'compare_name');
a20a4e3c
DO
1023 if (count ($zeroUObjects) > 0)
1024 {
0a7feebb
AD
1025 echo "<br><table width='75%' class=rack border=0 cellspacing=0 cellpadding=1>\n";
1026 echo "<tr><th>Zero-U:</th></tr>\n";
1027 foreach ($zeroUObjects as $zeroUObject)
1028 {
c17a36f0 1029 $state = ($zeroUObject['id'] == $hl_obj_id) ? 'Th' : 'T';
a2037cdd
DO
1030 if ($zeroUObject['has_problems'] == 'yes')
1031 $state .= 'w';
25526a97
ME
1032
1033 $class = "atom state_${state}";
1034 setEntityColors ($zeroUObject);
5abb804e 1035 $class .= getObjectClass ($zeroUObject, 'background:white;');
25526a97
ME
1036
1037 echo "<tr><td class='${class}'>";
1038 printObjectDetailsForRenderRack ($zeroUObject['id']);
0a7feebb
AD
1039 echo "</td></tr>\n";
1040 }
1041 echo "</table>\n";
1042 }
1043 echo "</center>\n";
e673ee24
DO
1044}
1045
d10401bf 1046function renderRackSortForm ($row_id)
42504426 1047{
b090b2da 1048 includeJQueryUI (false);
a6e090f0
DO
1049 // Heredoc, not nowdoc!
1050 $js = <<<"JSTXT"
b090b2da
RQ
1051 $(document).ready(
1052 function () {
1053 $("#sortRacks").sortable({
1054 update : function () {
1055 serial = $('#sortRacks').sortable('serialize');
1056 $.ajax({
824886b2 1057 url: 'index.php?module=ajax&ac=upd-rack-sort-order&row_id=${row_id}',
b090b2da
RQ
1058 type: 'post',
1059 data: serial,
1060 });
1061 }
1062 });
1063 }
42504426 1064 );
b090b2da 1065JSTXT;
1d44a1e1 1066 addJS ($js, TRUE);
42504426
AD
1067
1068 startPortlet ('Racks');
1069 echo "<table border=0 cellspacing=0 cellpadding=5 align=center class=widetable>\n";
1070 echo "<tr><th>Drag to change order</th></tr>\n";
34f5d4eb 1071 echo "<tr><td class=tdleft><ul class='uflist' id='sortRacks'>\n";
42504426
AD
1072 foreach (getRacks($row_id) as $rack_id => $rackInfo)
1073 echo "<li id=racks_${rack_id}>${rackInfo['name']}</li>\n";
1074 echo "</ul></td></tr></table>\n";
1075 finishPortlet();
1076}
1077
78ba6f56 1078function renderNewRackForm()
e673ee24 1079{
d3e838cd 1080 $default_height = emptyStrIfZero (getConfigVar ('DEFAULT_RACK_HEIGHT'));
50b4ba85 1081 startPortlet ('Add one');
6b552311 1082 printOpFormIntro ('addRack', array ('mode' => 'one'));
e673ee24 1083 echo '<table border=0 align=center>';
04c61e7e
AB
1084 echo "<tr><th class=tdright>Name (required):</th><td class=tdleft><input type=text name=name></td>";
1085 echo "<tr><th class=tdright>Height in units (required):</th><td class=tdleft><input type=text name=height1 value='${default_height}'></td></tr>\n";
1086 echo "<tr><th class=tdright>Asset tag:</th><td class=tdleft><input type=text name=asset_no></td></tr>\n";
1087 echo "<tr><th class=tdright>Tags:</td><td class=tdleft>";
e7b84015 1088 printTagsPicker ();
72d52730 1089 echo "</td></tr>\n";
f19c75d6
DO
1090 echo "<tr><td class=submit colspan=2>";
1091 printImageHREF ('CREATE', 'Add', TRUE);
1092 echo "</td></tr></table></form>";
406b54bd 1093 finishPortlet();
406b54bd 1094
50b4ba85 1095 startPortlet ('Add many');
6b552311 1096 printOpFormIntro ('addRack', array ('mode' => 'many'));
50b4ba85 1097 echo '<table border=0 align=center>';
9b8174d7 1098 echo "<tr><th class=tdright>Height in units (*):</th><td class=tdleft><input type=text name=height2 value='${default_height}'></td>";
04c61e7e 1099 echo "<tr><th class=tdright>Assign tags:</td><td class=tdleft>";
e7b84015 1100 printTagsPicker ();
72d52730 1101 echo "</td></tr>\n";
9b8174d7 1102 echo "<tr><th class=tdright>Rack names (required):</th><td class=tdleft><textarea name=names cols=40 rows=25></textarea></td></tr>\n";
f19c75d6
DO
1103 echo "<tr><td class=submit colspan=2>";
1104 printImageHREF ('CREATE', 'Add', TRUE);
e673ee24
DO
1105 echo '</form></table>';
1106 finishPortlet();
1107}
1108
3825c4db 1109function renderEditObjectForm()
e673ee24 1110{
42422859 1111 global $pageno;
3825c4db 1112 $object_id = getBypassValue();
fb6e2f57 1113 $object = spotEntity ('object', $object_id);
cdd3efe9 1114 startPortlet ();
43fb6eaf 1115 printOpFormIntro ('update');
cdd3efe9
AD
1116
1117 // static attributes
e10f583a
DO
1118 echo '<table border=0 cellspacing=0 cellpadding=3 align=center>';
1119 echo "<tr><td>&nbsp;</td><th colspan=2><h2>Attributes</h2></th></tr>";
3825c4db
DO
1120 echo '<tr><td>&nbsp;</td><th class=tdright>Type:</th><td class=tdleft>';
1121 printSelect (getObjectTypeChangeOptions ($object['id']), array ('name' => 'object_type_id'), $object['objtype_id']);
1122 echo '</td></tr>';
22bb04da 1123 // baseline info
e10f583a 1124 echo "<tr><td>&nbsp;</td><th class=tdright>Common name:</th><td class=tdleft><input type=text name=object_name value='${object['name']}'></td></tr>\n";
42422859
AD
1125 echo "<tr><td>&nbsp;</td><th class=tdright>Visible label:</th><td class=tdleft><input type=text name=object_label value='${object['label']}'></td></tr>\n";
1126 echo "<tr><td>&nbsp;</td><th class=tdright>Asset tag:</th><td class=tdleft><input type=text name=object_asset_no value='${object['asset_no']}'></td></tr>\n";
e7b84015
AB
1127 echo "<tr><td>&nbsp;</td><th class=tdright>Tags:</th><td class=tdleft>";
1128 printTagsPicker ();
1129 echo "</td></tr>\n";
0682218d 1130 // parent selection
9b8174d7 1131 if (objectTypeMayHaveParent ($object['objtype_id']))
0682218d 1132 {
9563f56f 1133 $parents = getParents ($object, 'object');
af8916e9
AD
1134 // lookup the human-readable object type, sort by it
1135 foreach ($parents as $parent_id => $parent)
1136 $parents[$parent_id]['object_type'] = decodeObjectType ($parent['objtype_id']);
1137 $grouped_parents = groupBy ($parents, 'object_type');
1138 ksort ($grouped_parents);
1139 foreach ($grouped_parents as $parents_group)
0682218d 1140 {
9563f56f 1141 uasort ($parents_group, 'compare_name');
af8916e9 1142 $label = $parents_group[key ($parents_group)]['object_type'] . (count($parents_group) > 1 ? ' containers:' : ' container:');
9563f56f 1143 foreach ($parents_group as $link_id => $parent_cell)
8526b4cc 1144 {
8526b4cc
J
1145 echo "<tr><td>&nbsp;</td>";
1146 echo "<th class=tdright>${label}</th><td class=tdleft>";
9563f56f 1147 echo mkCellA ($parent_cell);
8526b4cc
J
1148 echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
1149 echo getOpLink (array('op'=>'unlinkObjects', 'link_id'=>$link_id), '', 'cut', 'Unlink container');
1150 echo "</td></tr>\n";
1151 $label = '&nbsp;';
1152 }
0682218d
AD
1153 }
1154 echo "<tr><td>&nbsp;</td>";
1155 echo "<th class=tdright>Select container:</th><td class=tdleft>";
5d4ca0f3 1156 echo getPopupLink ('objlist', array(), 'findlink', 'attach', 'Select a container');
de55c069 1157 echo "</td></tr>\n";
0682218d 1158 }
5ff78aff 1159 renderEditAttributeTRs ('update', getAttrValuesSorted ($object_id), $object['objtype_id']);
f5b9c21a
DO
1160 echo '<tr><td>&nbsp;</td><th class=tdright><label for=object_has_problems>Has problems:</label></th>';
1161 echo '<td class=tdleft><input type=checkbox name=object_has_problems id=object_has_problems';
e10f583a
DO
1162 if ($object['has_problems'] == 'yes')
1163 echo ' checked';
1164 echo "></td></tr>\n";
dec748f6 1165 echo "<tr><td>&nbsp;</td><th class=tdright>Actions:</th><td class=tdleft>";
10f5133a 1166 echo getOpLink (array ('op'=>'deleteObject', 'page'=>'depot', 'tab'=>'addmore', 'object_id'=>$object_id), '' ,'destroy', 'Delete object', 'need-confirmation');
abd1e9ac 1167 echo "&nbsp;";
10f5133a 1168 echo getOpLink (array ('op'=>'resetObject'), '' ,'clear', 'Reset (cleanup) object', 'need-confirmation');
abd1e9ac 1169 echo "</td></tr>\n";
e10f583a 1170 echo "<tr><td colspan=3><b>Comment:</b><br><textarea name=object_comment rows=10 cols=80>${object['comment']}</textarea></td></tr>";
cdd3efe9 1171
e10f583a 1172 echo "<tr><th class=submit colspan=3>";
5bbbeaa2 1173 printImageHREF ('SAVE', 'Save changes', TRUE);
cdd3efe9 1174 echo "</form></th></tr></table>\n";
e673ee24 1175 finishPortlet();
e673ee24 1176
cdd3efe9 1177 echo '<table border=0 width=100%><tr><td>';
e673ee24 1178 startPortlet ('history');
9b8174d7 1179 renderObjectHistory ($object_id);
e673ee24 1180 finishPortlet();
cdd3efe9 1181 echo '</td></tr></table>';
e673ee24
DO
1182}
1183
d10401bf 1184function renderEditRackForm ($rack_id)
e673ee24 1185{
0c2b7c4a 1186 global $pageno;
61a1d996
DO
1187 $rack = spotEntity ('rack', $rack_id);
1188 amplifyCell ($rack);
e673ee24 1189
9b8174d7 1190 startPortlet ('Attributes');
7056988c 1191 printOpFormIntro ('updateRack');
e673ee24 1192 echo '<table border=0 align=center>';
9b8174d7 1193 echo "<tr><td>&nbsp;</td><th class=tdright>Rack row:</th><td class=tdleft>";
524f8a7b 1194 foreach (listCells ('row') as $row_id => $rowInfo)
64a973a7
AD
1195 {
1196 $trail = getLocationTrail ($rowInfo['location_id'], FALSE);
1197 $rows[$row_id] = empty ($trail) ? $rowInfo['name'] : $rowInfo['name'] . ' [' . $trail . ']';
1198 }
1199 natcasesort ($rows);
e1add254 1200 printSelect ($rows, array ('name' => 'row_id'), $rack['row_id']);
e673ee24 1201 echo "</td></tr>\n";
9b8174d7
AD
1202 echo "<tr><td>&nbsp;</td><th class=tdright>Name (required):</th><td class=tdleft><input type=text name=name value='${rack['name']}'></td></tr>\n";
1203 echo "<tr><td>&nbsp;</td><th class=tdright>Height (required):</th><td class=tdleft><input type=text name=height value='${rack['height']}'></td></tr>\n";
9b8174d7 1204 echo "<tr><td>&nbsp;</td><th class=tdright>Asset tag:</th><td class=tdleft><input type=text name=asset_no value='${rack['asset_no']}'></td></tr>\n";
e7b84015
AB
1205 echo "<tr><td>&nbsp;</td><th class=tdright>Tags:</th><td class=tdleft>";
1206 printTagsPicker ();
1207 echo "</td></tr>\n";
5ff78aff 1208 renderEditAttributeTRs ('updateRack', getAttrValuesSorted ($rack_id), 1560, array (27, 29));
f0fe0da7
DO
1209 echo '<tr><td>&nbsp;</td><th class=tdright><label for=has_problems>Has problems:</label></th>';
1210 echo '<td class=tdleft><input type=checkbox name=has_problems id=has_problems';
9b8174d7
AD
1211 if ($rack['has_problems'] == 'yes')
1212 echo ' checked';
1213 echo "></td></tr>\n";
04eee8d2 1214 echo "<tr><td>&nbsp;</td><th class=tdright>Actions:</th><td class=tdleft>";
c495997f 1215 if ($rack['isDeletable'])
c8187437 1216 {
10f5133a 1217 echo getOpLink (array ('op'=>'deleteRack'), '', 'destroy', 'Delete rack', 'need-confirmation');
04eee8d2 1218 echo "&nbsp;";
c8187437 1219 }
04eee8d2
VS
1220 else
1221 {
1222 echo getOpLink (array ('op'=>'cleanRack'), '' ,'clear', 'Reset (cleanup) rack mounts', 'need-confirmation');
1223 echo "&nbsp;";
1224 }
1225 echo "</td></tr>\n";
9b8174d7
AD
1226 echo "<tr><td colspan=3><b>Comment:</b><br><textarea name=comment rows=10 cols=80>${rack['comment']}</textarea></td></tr>";
1227 echo "<tr><td class=submit colspan=3>";
7056988c
DO
1228 printImageHREF ('SAVE', 'Save changes', TRUE);
1229 echo "</td></tr>\n";
e673ee24
DO
1230 echo '</form></table><br>';
1231 finishPortlet();
dec748f6 1232
e673ee24 1233 startPortlet ('History');
9b8174d7 1234 renderObjectHistory ($rack_id);
e673ee24
DO
1235 finishPortlet();
1236}
1237
6b9bdb08
AA
1238// populates the $summary array with the sum of power attributes of the objects mounted into the rack
1239function populateRackPower ($rackData, &$summary)
1240{
1241 $power_attrs = array(
1242 7, // 'float','max. current, Ampers'
1243 13, // 'float','max power, Watts'
1244 );
1245 $sum = array();
1246 if (! isset ($rackData['mountedObjects']))
1247 amplifyCell ($rackData);
1248 foreach ($rackData['mountedObjects'] as $object_id)
1249 {
1250 $attrs = getAttrValues ($object_id);
1251 foreach ($power_attrs as $attr_id)
1252 if (isset ($attrs[$attr_id]) && $attrs[$attr_id]['type'] == 'float')
1253 {
1254 if (! isset ($sum[$attr_id]))
1255 {
1256 $sum[$attr_id]['sum'] = 0.0;
1257 $sum[$attr_id]['name'] = $attrs[$attr_id]['name'];
1258 }
1259 $sum[$attr_id]['sum'] += $attrs[$attr_id]['value'];
1260 }
1261 }
1262 foreach ($sum as $attr)
1263 if ($attr['sum'] > 0.0)
1264 $summary[$attr['name']] = $attr['sum'];
1265}
1266
254673dd
DO
1267// used by renderGridForm() and renderRackPage()
1268function renderRackInfoPortlet ($rackData)
1269{
646272be 1270 $summary = array();
67cfe546 1271 $summary['Rack row'] = mkA ($rackData['row_name'], 'row', $rackData['row_id']);
646272be
AA
1272 $summary['Name'] = $rackData['name'];
1273 $summary['Height'] = $rackData['height'];
33973968 1274 if ($rackData['asset_no'] != '')
646272be 1275 $summary['Asset tag'] = $rackData['asset_no'];
9b8174d7 1276 if ($rackData['has_problems'] == 'yes')
646272be 1277 $summary[] = array ('<tr><td colspan=2 class=msg_error>Has problems</td></tr>');
6b9bdb08 1278 populateRackPower ($rackData, $summary);
42504426
AD
1279 // Display populated attributes, but skip 'height' since it's already displayed above
1280 // and skip 'sort_order' because it's modified using AJAX
249c78b2 1281 foreach (getAttrValuesSorted ($rackData['id']) as $record)
5b908204
DO
1282 if
1283 (
1284 $record['id'] != 27 && $record['id'] != 29 &&
1285 $record['value'] != '' &&
1286 permitted (NULL, NULL, NULL, array (array ('tag' => '$attr_' . $record['id'])))
1287 )
1288 $summary['{sticker}' . $record['name']] = formatAttributeValue ($record, 1560);
646272be
AA
1289 $summary['% used'] = getProgressBar (getRSUforRack ($rackData));
1290 $summary['Objects'] = count ($rackData['mountedObjects']);
1291 $summary['tags'] = '';
646272be 1292 renderEntitySummary ($rackData, 'summary', $summary);
0602fc27
DO
1293 if ($rackData['comment'] != '')
1294 {
1295 startPortlet ('Comment');
1296 echo '<div class=commentblock>' . string_insert_hrefs ($rackData['comment']) . '</div>';
1297 finishPortlet ();
1298 }
254673dd
DO
1299}
1300
e673ee24 1301// This is a universal editor of rack design/waste.
7056988c 1302// FIXME: switch to using printOpFormIntro()
7e1db771 1303function renderGridForm ($rack_id, $filter, $header, $submit, $state1, $state2)
e673ee24 1304{
61a1d996
DO
1305 $rackData = spotEntity ('rack', $rack_id);
1306 amplifyCell ($rackData);
254673dd 1307 $filter ($rackData);
e673ee24 1308
e673ee24
DO
1309 // Render the result whatever it is.
1310 // Main layout.
1311 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
1312 echo "<tr><td colspan=2 align=center><h1>${rackData['name']}</h1></td></tr>\n";
1313
1314 // Left column with information portlet.
1315 echo "<tr><td class=pcleft height='1%' width='50%'>";
254673dd 1316 renderRackInfoPortlet ($rackData);
e673ee24 1317 echo "</td>\n";
bb09cca0 1318 echo "<td class=pcright>";
e673ee24
DO
1319
1320 // Grid form.
c26d97b6 1321 $is_ro = !rackModificationPermitted ($rackData, 'updateRack');
e673ee24 1322 startPortlet ($header);
65e557dd 1323 addJS ('js/racktables.js');
e673ee24 1324 echo "<center>\n";
c26d97b6
VS
1325 $read_only_text = $is_ro ? '(read-only)' : '&nbsp;';
1326 echo "<p style='color: red; margin-top:0px'>${read_only_text}</p>\n";
e673ee24 1327 echo "<table class=rack border=0 cellspacing=0 cellpadding=1>\n";
57c69e9c 1328 echo "<tr><th width='10%'>&nbsp;</th>";
bd0bfb76
AD
1329 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
1330 echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
1331 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
4fbb5a00 1332 printOpFormIntro ('updateRack');
e673ee24 1333 markupAtomGrid ($rackData, $state2);
c26d97b6 1334 renderAtomGrid ($rackData, $is_ro);
e673ee24
DO
1335 echo "</table></center>\n";
1336 echo "<br><input type=submit name=do_update value='${submit}'></form><br><br>\n";
1337 finishPortlet();
bb09cca0 1338 echo "</td></tr></table>\n";
e673ee24
DO
1339}
1340
1341function renderRackDesign ($rack_id)
1342{
bb09cca0 1343 renderGridForm ($rack_id, 'applyRackDesignMask', 'Rack design', 'Set rack design', 'A', 'F');
e673ee24
DO
1344}
1345
7e1db771 1346function renderRackProblems ($rack_id)
e673ee24 1347{
bb09cca0 1348 renderGridForm ($rack_id, 'applyRackProblemMask', 'Rack problems', 'Mark unusable atoms', 'F', 'U');
e673ee24
DO
1349}
1350
40c9fc94
AA
1351function renderObjectPortRow ($port, $is_highlighted)
1352{
acb01e71 1353 // highlight port name with yellow if its name is not canonical
8579b51b
AA
1354 $canon_pn = shortenPortName ($port['name'], $port['object_id']);
1355 $name_class = $canon_pn == $port['name'] ? '' : 'trwarning';
1356
90bf1755
DO
1357 echo '<tr';
1358 if ($is_highlighted)
1359 echo ' class=highlight';
40c9fc94 1360 $a_class = isEthernetPort ($port) ? 'port-menu' : '';
8579b51b 1361 echo "><td class='tdleft $name_class' NOWRAP><a name='port-${port['id']}' class='interactive-portname nolink $a_class'>${port['name']}</a></td>";
40c9fc94 1362 echo "<td class=tdleft>${port['label']}</td>";
bba495fc 1363 echo "<td class=tdleft>" . formatPortIIFOIF ($port) . "</td><td class='tdleft l2address'>${port['l2address']}</td>";
0b09238d 1364 if ($port['remote_object_id'])
90bf1755 1365 {
f4d00faf 1366 $dname = formatObjectDisplayedName ($port['remote_object_name'], $port['remote_object_tid']);
0b09238d 1367 echo "<td class=tdleft>" .
f4d00faf 1368 formatPortLink ($port['remote_object_id'], $dname, $port['remote_id'], NULL) .
0b09238d
DO
1369 "</td>";
1370 echo "<td class=tdleft>" . formatLoggedSpan ($port['last_log'], $port['remote_name'], 'underline') . "</td>";
1371 $editable = permitted ('object', 'ports', 'editPort')
1372 ? 'editable'
1373 : '';
1374 echo "<td class=tdleft><span class='rsvtext $editable id-${port['id']} op-upd-reservation-cable'>${port['cableid']}</span></td>";
40c9fc94 1375 }
90bf1755
DO
1376 else
1377 echo implode ('', formatPortReservation ($port)) . '<td></td>';
0b09238d 1378 echo "</tr>";
40c9fc94
AA
1379}
1380
d10401bf 1381function renderObject ($object_id)
e673ee24 1382{
8dc2a6c8 1383 global $nextorder, $virtual_obj_types;
fb6e2f57 1384 $info = spotEntity ('object', $object_id);
94fe6370 1385 amplifyCell ($info);
e673ee24
DO
1386 // Main layout starts.
1387 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
1388 echo "<tr><td colspan=2 align=center><h1>${info['dname']}</h1></td></tr>\n";
3a243872 1389 // A mandatory left column with varying number of portlets.
e673ee24 1390 echo "<tr><td class=pcleft>";
646272be
AA
1391
1392 // display summary portlet
1393 $summary = array();
33973968 1394 if ($info['name'] != '')
646272be 1395 $summary['Common name'] = $info['name'];
94fe6370 1396 elseif (considerConfiguredConstraint ($info, 'NAMEWARN_LISTSRC'))
646272be
AA
1397 $summary[] = array ('<tr><td colspan=2 class=msg_error>Common name is missing.</td></tr>');
1398 $summary['Object type'] = '<a href="' . makeHref (array (
341fd895
DO
1399 'page' => 'depot',
1400 'tab' => 'default',
1401 'cfe' => '{$typeid_' . $info['objtype_id'] . '}'
eb11ba46 1402 )) . '">' . decodeObjectType ($info['objtype_id']) . '</a>';
33973968 1403 if ($info['label'] != '')
646272be 1404 $summary['Visible label'] = $info['label'];
33973968 1405 if ($info['asset_no'] != '')
646272be 1406 $summary['Asset tag'] = $info['asset_no'];
d4e8ddeb 1407 elseif (considerConfiguredConstraint ($info, 'ASSETWARN_LISTSRC'))
646272be 1408 $summary[] = array ('<tr><td colspan=2 class=msg_error>Asset tag is missing.</td></tr>');
9563f56f 1409 $parents = getParents ($info, 'object');
af8916e9
AD
1410 // lookup the human-readable object type, sort by it
1411 foreach ($parents as $parent_id => $parent)
1412 $parents[$parent_id]['object_type'] = decodeObjectType ($parent['objtype_id']);
1413 $grouped_parents = groupBy ($parents, 'object_type');
1414 ksort ($grouped_parents);
1415 foreach ($grouped_parents as $parents_group)
0682218d 1416 {
9563f56f 1417 uasort ($parents_group, 'compare_name');
af8916e9 1418 $label = $parents_group[key ($parents_group)]['object_type'] . (count($parents_group) > 1 ? ' containers' : ' container');
9563f56f
AA
1419 $fmt_parents = array();
1420 foreach ($parents_group as $parent)
1421 $fmt_parents[] = mkCellA ($parent);
af8916e9 1422 $summary[$label] = implode ('<br>', $fmt_parents);
0682218d 1423 }
9563f56f
AA
1424 $children = getChildren ($info, 'object');
1425 foreach (groupBy ($children, 'objtype_id') as $objtype_id => $children_group)
42fb3aa2 1426 {
9563f56f
AA
1427 uasort ($children_group, 'compare_name');
1428 $fmt_children = array();
1429 foreach ($children_group as $child)
1430 $fmt_children[] = mkCellA ($child);
c49fddcc 1431 $summary["Contains " . mb_strtolower(decodeObjectType ($objtype_id))] = implode ('<br>', $fmt_children);
42fb3aa2 1432 }
e673ee24 1433 if ($info['has_problems'] == 'yes')
646272be 1434 $summary[] = array ('<tr><td colspan=2 class=msg_error>Has problems</td></tr>');
249c78b2 1435 foreach (getAttrValuesSorted ($object_id) as $record)
4969aa31
DO
1436 if
1437 (
55eefced 1438 $record['value'] != '' &&
4969aa31
DO
1439 permitted (NULL, NULL, NULL, array (array ('tag' => '$attr_' . $record['id'])))
1440 )
5b908204 1441 $summary['{sticker}' . $record['name']] = formatAttributeValue ($record, $info['objtype_id']);
a2c597ea
AA
1442 $summary[] = array (getOutputOf ('printTagTRs',
1443 $info,
1444 makeHref
1445 (
1446 array
1447 (
1448 'page'=>'depot',
1449 'tab'=>'default',
1450 'andor' => 'and',
1451 'cfe' => '{$typeid_' . $info['objtype_id'] . '}',
1452 )
1453 )."&"
1454 ));
0b09238d 1455 renderEntitySummary ($info, 'summary', $summary);
e673ee24 1456
33973968 1457 if ($info['comment'] != '')
e673ee24
DO
1458 {
1459 startPortlet ('Comment');
6cc5d6cd 1460 echo '<div class=commentblock>' . string_insert_hrefs ($info['comment']) . '</div>';
e673ee24
DO
1461 finishPortlet ();
1462 }
1463
b5700329
AA
1464 $logrecords = getLogRecordsForObject ($_REQUEST['object_id']);
1465 if (count ($logrecords))
9a90adc4
DO
1466 {
1467 startPortlet ('log records');
1468 echo "<table cellspacing=0 cellpadding=5 align=center class=widetable width='100%'>";
1469 $order = 'odd';
b5700329 1470 foreach ($logrecords as $row)
9a90adc4
DO
1471 {
1472 echo "<tr class=row_${order} valign=top>";
1473 echo '<td class=tdleft>' . $row['date'] . '<br>' . $row['user'] . '</td>';
0b8d8309 1474 echo '<td class="logentry">' . string_insert_hrefs (htmlspecialchars ($row['content'], ENT_NOQUOTES)) . '</td>';
9a90adc4
DO
1475 echo '</tr>';
1476 $order = $nextorder[$order];
1477 }
1478 echo '</table>';
1479 finishPortlet();
1480 }
1481
65e557dd 1482 switchportInfoJS ($object_id); // load JS code to make portnames interactive
e1ae3fb4
AD
1483 renderFilesPortlet ('object', $object_id);
1484
9f75e165 1485 if (count ($info['ports']))
e673ee24 1486 {
0b09238d 1487 startPortlet ('ports and links');
9f75e165
DO
1488 $hl_port_id = 0;
1489 if (isset ($_REQUEST['hl_port_id']))
e673ee24 1490 {
0cc24e9a 1491 assertUIntArg ('hl_port_id');
9f75e165 1492 $hl_port_id = $_REQUEST['hl_port_id'];
65e557dd 1493 addAutoScrollScript ("port-$hl_port_id");
9f75e165 1494 }
0b09238d 1495 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>";
9f75e165 1496 echo '<tr><th class=tdleft>Local name</th><th class=tdleft>Visible label</th>';
0d59a51c 1497 echo '<th class=tdleft>Interface</th><th class=tdleft>L2 address</th>';
0c7c9f8b 1498 echo '<th class=tdcenter colspan=2>Remote object and port</th>';
0b09238d 1499 echo '<th class=tdleft>Cable ID</th></tr>';
9f75e165 1500 foreach ($info['ports'] as $port)
40c9fc94 1501 callHook ('renderObjectPortRow', $port, ($hl_port_id == $port['id']));
fd5edda8 1502 if (permitted (NULL, 'ports', 'set_reserve_comment'))
a8dc2982 1503 addJS ('js/inplace-edit.js');
9f75e165 1504 echo "</table><br>";
e673ee24
DO
1505 finishPortlet();
1506 }
85970da2 1507
21ee3351 1508 if (count ($info['ipv4']) + count ($info['ipv6']))
e673ee24 1509 {
21ee3351 1510 startPortlet ('IP addresses');
e673ee24 1511 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
e9446e3b 1512 if ('yes' == $ext_ipv4_view = getConfigVar ('EXT_IPV4_VIEW'))
9d5f6a80 1513 echo "<tr class=tdleft><th>OS interface</th><th>IP address</th><th>network</th><th>routed by</th><th>peers</th></tr>\n";
83fdf85f 1514 else
9d5f6a80 1515 echo "<tr class=tdleft><th>OS interface</th><th>IP address</th><th>peers</th></tr>\n";
21ee3351
AA
1516
1517 // group IP allocations by interface name instead of address family
1518 $allocs_by_iface = array();
1519 foreach (array ('ipv4', 'ipv6') as $ip_v)
4318ced5
AA
1520 foreach ($info[$ip_v] as $ip_bin => $alloc)
1521 $allocs_by_iface[$alloc['osif']][$ip_bin] = $alloc;
dec748f6 1522
21ee3351
AA
1523 // sort allocs array by portnames
1524 foreach (sortPortList ($allocs_by_iface) as $iface_name => $alloclist)
1525 {
1526 $is_first_row = TRUE;
f3505b35 1527 foreach ($alloclist as $alloc)
b90c3907 1528 {
40c9fc94 1529 $rendered_alloc = callHook ('getRenderedAlloc', $object_id, $alloc);
f3505b35 1530 echo "<tr class='${rendered_alloc['tr_class']}' valign=top>";
21ee3351 1531
f3505b35 1532 // display iface name, same values are grouped into single cell
21ee3351
AA
1533 if ($is_first_row)
1534 {
1535 $rowspan = count ($alloclist) > 1 ? 'rowspan="' . count ($alloclist) . '"' : '';
40c9fc94 1536 echo "<td class=tdleft $rowspan>" . $iface_name . $rendered_alloc['td_name_suffix'] . "</td>";
21ee3351
AA
1537 $is_first_row = FALSE;
1538 }
f3505b35 1539 echo $rendered_alloc['td_ip'];
e9446e3b 1540 if ($ext_ipv4_view == 'yes')
83fdf85f 1541 {
f3505b35
AA
1542 echo $rendered_alloc['td_network'];
1543 echo $rendered_alloc['td_routed_by'];
21ee3351 1544 }
f3505b35
AA
1545 echo $rendered_alloc['td_peers'];
1546
1547 echo "</tr>\n";
b90c3907 1548 }
e673ee24
DO
1549 }
1550 echo "</table><br>\n";
1551 finishPortlet();
1552 }
1553
94fe6370 1554 $forwards = $info['nat4'];
55eefced 1555 if (count ($forwards['in']) || count ($forwards['out']))
e673ee24 1556 {
f28fbe8b 1557 startPortlet('NATv4');
e673ee24
DO
1558
1559 if (count($forwards['out']))
1560 {
1561
f28fbe8b 1562 echo "<h3>locally performed NAT</h3>";
e673ee24 1563
105a3bc8 1564 echo "<table class='widetable' cellpadding=5 cellspacing=0 border=0 align='center'>\n";
f28fbe8b 1565 echo "<tr><th>Proto</th><th>Match endpoint</th><th>Translate to</th><th>Target object</th><th>Rule comment</th></tr>\n";
e673ee24
DO
1566
1567 foreach ($forwards['out'] as $pf)
1568 {
2c817354
DO
1569 $class = 'trerror';
1570 $osif = '';
6a3c2dc5
AA
1571 $localip_bin = ip_parse ($pf['localip']);
1572 if (array_key_exists ($localip_bin, $info['ipv4']))
2c817354 1573 {
6a3c2dc5
AA
1574 $class = $info['ipv4'][$localip_bin]['addrinfo']['class'];
1575 $osif = $info['ipv4'][$localip_bin]['osif'] . ': ';
2c817354 1576 }
e673ee24 1577 echo "<tr class='$class'>";
7419401e
AA
1578 echo "<td>${pf['proto']}</td><td class=tdleft>${osif}" . getRenderedIPPortPair ($pf['localip'], $pf['localport']) . "</td>";
1579 echo "<td class=tdleft>" . getRenderedIPPortPair ($pf['remoteip'], $pf['remoteport']) . "</td>";
4318ced5 1580 $address = getIPAddress (ip4_parse ($pf['remoteip']));
e673ee24 1581 echo "<td class='description'>";
53ef3908
DO
1582 if (count ($address['allocs']))
1583 foreach($address['allocs'] as $bond)
ad3e5e7e 1584 echo mkA ("${bond['object_name']}(${bond['name']})", 'object', $bond['object_id']) . ' ';
33973968 1585 elseif ($pf['remote_addr_name'] != '')
f28fbe8b 1586 echo '(' . $pf['remote_addr_name'] . ')';
53ef3908 1587 echo "</td><td class='description'>${pf['description']}</td></tr>";
e673ee24
DO
1588 }
1589 echo "</table><br><br>";
1590 }
1591 if (count($forwards['in']))
1592 {
f28fbe8b 1593 echo "<h3>arriving NAT connections</h3>";
105a3bc8 1594 echo "<table class='widetable' cellpadding=5 cellspacing=0 border=0 align='center'>\n";
f28fbe8b 1595 echo "<tr><th>Matched endpoint</th><th>Source object</th><th>Translated to</th><th>Rule comment</th></tr>\n";
e673ee24
DO
1596 foreach ($forwards['in'] as $pf)
1597 {
1598 echo "<tr>";
7419401e 1599 echo "<td>${pf['proto']}/" . getRenderedIPPortPair ($pf['localip'], $pf['localport']) . "</td>";
ad3e5e7e 1600 echo '<td class="description">' . mkA ($pf['object_name'], 'object', $pf['object_id']);
7419401e 1601 echo "</td><td>" . getRenderedIPPortPair ($pf['remoteip'], $pf['remoteport']) . "</td>";
e673ee24
DO
1602 echo "<td class='description'>${pf['description']}</td></tr>";
1603 }
e673ee24
DO
1604 echo "</table><br><br>";
1605 }
1606 finishPortlet();
1607 }
1608
eacc0983 1609 renderSLBTriplets2 ($info);
defd92d8 1610 renderSLBTriplets ($info);
e673ee24
DO
1611 echo "</td>\n";
1612
3a243872
DO
1613 // A conditional right column with the rackspace portlet only.
1614 if
1615 (
1616 ! in_array ($info['objtype_id'], $virtual_obj_types) &&
950d4117 1617 count ($rack_ids = getResidentRackIDs ($object_id))
3a243872 1618 )
0682218d 1619 {
3a243872 1620 echo '<td class=pcright>';
0682218d 1621 startPortlet ('rackspace allocation');
3a243872 1622 foreach ($rack_ids as $rack_id)
0682218d
AD
1623 renderRack ($rack_id, $object_id);
1624 echo '<br>';
1625 finishPortlet();
3a243872 1626 echo '</td>';
0682218d 1627 }
3a243872 1628 echo "<tr></table>\n";
e673ee24
DO
1629}
1630
1631function renderRackMultiSelect ($sname, $racks, $selected)
1632{
04571888
DO
1633 // Transform the given flat list into a list of groups, each representing a rack row.
1634 $rdata = array();
e673ee24 1635 foreach ($racks as $rack)
d016010a 1636 {
b956dd24 1637 if ('' != $trail = getLocationTrail ($rack['location_id'], FALSE))
7b6ae79a
AD
1638 $row_name = $trail . ' : ' . $rack['row_name'];
1639 else
1640 $row_name = $rack['row_name'];
d016010a
AD
1641 $rdata[$row_name][$rack['id']] = $rack['name'];
1642 }
4fbb5a00 1643 echo "<select name=${sname} multiple size=" . getConfigVar ('MAXSELSIZE') . " onchange='getElementsByName(\"updateObjectAllocation\")[0].submit()'>\n";
acfc0d89
AA
1644 $row_names = array_keys ($rdata);
1645 natsort ($row_names);
1646 foreach ($row_names as $optgroup)
e673ee24 1647 {
04571888 1648 echo "<optgroup label='${optgroup}'>";
acfc0d89 1649 foreach ($rdata[$optgroup] as $rack_id => $rack_name)
04571888
DO
1650 {
1651 echo "<option value=${rack_id}";
1652 if (!(array_search ($rack_id, $selected) === FALSE))
1653 echo ' selected';
1654 echo">${rack_name}</option>\n";
1655 }
e673ee24
DO
1656 }
1657 echo "</select>\n";
1658}
1659
e673ee24 1660// This function renders a form for port edition.
d10401bf 1661function renderPortsForObject ($object_id)
e673ee24 1662{
3153a326
DO
1663 $prefs = getPortListPrefs();
1664 function printNewItemTR ($prefs)
39b4abd0
DO
1665 {
1666 printOpFormIntro ('addPort');
1667 echo "<tr><td>";
f5ff50f5 1668 printImageHREF ('add', 'add a port', TRUE);
7b30e187 1669 echo "</td><td class='tdleft'><input type=text size=16 name=port_name></td>\n";
04c61e7e 1670 echo "<td><input type=text name=port_label></td><td>";
759b21d2 1671 printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type_id'), $prefs['selected']);
97792a8a 1672 echo "<td><input type=text name=port_l2address size=17 maxlength=59></td>\n";
318c48a8 1673 echo "<td colspan=4>&nbsp;</td><td>";
04c61e7e 1674 printImageHREF ('add', 'add a port', TRUE);
9318d2ef 1675 echo "</td></tr></form>";
39b4abd0 1676 }
70c0cb08
DO
1677
1678 function printBulkForm ($prefs)
1679 {
a1fc539a
RF
1680 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
1681 echo "<tr><th>&nbsp;</th><th class=tdleft>Local name</th><th class=tdleft>Visible label</th><th class=tdleft>Interface</th><th class=tdleft>Start Number</th>";
1682 echo "<th class=tdleft>Count</th><th>&nbsp;</th></tr>\n";
1683 printOpFormIntro ('addBulkPorts');
1684 echo "<tr><td>";
1685 printImageHREF ('add', 'add ports', TRUE);
04c61e7e
AB
1686 echo "</td><td><input type=text size=8 name=port_name></td>\n";
1687 echo "<td><input type=text name=port_label></td><td>";
759b21d2 1688 printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type_id'), $prefs['selected']);
04c61e7e
AB
1689 echo "<td><input type=text name=port_numbering_start size=3 maxlength=3></td>\n";
1690 echo "<td><input type=text name=port_numbering_count size=3 maxlength=3></td>\n";
0b09238d 1691 echo "<td>&nbsp;</td><td>";
04c61e7e 1692 printImageHREF ('add', 'add ports', TRUE);
a1fc539a
RF
1693 echo "</td></tr></form>";
1694 echo "</table><br>\n";
1695 }
dec748f6 1696
70c0cb08
DO
1697 startPortlet ('Ports and interfaces');
1698 $object = spotEntity ('object', $object_id);
1699 amplifyCell ($object);
1700 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes' && getConfigVar ('ENABLE_BULKPORT_FORM') == 'yes')
1701 printBulkForm ($prefs);
1702
0b09238d 1703 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
0d59a51c 1704 echo "<tr><th>&nbsp;</th><th class=tdleft>Local name</th><th class=tdleft>Visible label</th><th class=tdleft>Interface</th><th class=tdleft>L2 address</th>";
0b09238d 1705 echo "<th class=tdcenter colspan=2>Remote object and port</th><th>Cable ID</th><th class=tdcenter>(Un)link or (un)reserve</th><th>&nbsp;</th></tr>\n";
39b4abd0 1706 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
3153a326 1707 printNewItemTR ($prefs);
45ff9012 1708
0b09238d
DO
1709 // clear ports link
1710 echo getOpLink (array ('op'=>'deleteAll'), 'Clear port list', 'clear', '', 'need-confirmation');
1711
8579b51b
AA
1712 // rename ports link
1713 $n_ports_to_rename = 0;
1714 foreach ($object['ports'] as $port)
1715 if ($port['name'] != shortenPortName ($port['name'], $object['id']))
1716 $n_ports_to_rename++;
1717 if ($n_ports_to_rename)
bd2dcd32 1718 echo '<p>' . getOpLink (array ('op'=>'renameAll'), "Auto-rename $n_ports_to_rename port(s)", 'recalc', 'Use RackTables naming convention for this device type') . '</p>';
8579b51b 1719
00276508
AA
1720 if (isset ($_REQUEST['hl_port_id']))
1721 {
1722 assertUIntArg ('hl_port_id');
1723 $hl_port_id = intval ($_REQUEST['hl_port_id']);
65e557dd 1724 addAutoScrollScript ("port-$hl_port_id");
00276508 1725 }
d10401bf 1726 switchportInfoJS ($object_id); // load JS code to make portnames interactive
9f75e165 1727 foreach ($object['ports'] as $port)
e673ee24 1728 {
acb01e71 1729 // highlight port name with yellow if its name is not canonical
8579b51b
AA
1730 $canon_pn = shortenPortName ($port['name'], $port['object_id']);
1731 $name_class = $canon_pn == $port['name'] ? '' : 'trwarning';
1732
5c717ce5 1733 $tr_class = isset ($hl_port_id) && $hl_port_id == $port['id'] ? 'class="highlight"' : '';
7056988c 1734 printOpFormIntro ('editPort', array ('port_id' => $port['id']));
d0d40671 1735 echo "<tr $tr_class><td><a name='port-${port['id']}' href='".makeHrefProcess(array('op'=>'delPort', 'port_id'=>$port['id']))."'>";
e673ee24
DO
1736 printImageHREF ('delete', 'Unlink and Delete this port');
1737 echo "</a></td>\n";
65e557dd 1738 $a_class = isEthernetPort ($port) ? 'port-menu' : '';
7b30e187 1739 echo "<td class='tdleft $name_class' NOWRAP><input type=text name=name class='interactive-portname $a_class' value='${port['name']}' size=16></td>";
cd3775e9 1740 echo "<td><input type=text name=label value='${port['label']}'></td>";
70c0cb08 1741 echo '<td class=tdleft>';
8e021b86
AA
1742 if ($port['iif_id'] != 1)
1743 echo '<label>' . $port['iif_name'] . ' ';
4f968fa8 1744 $port_type_opts = array();
78b59ab3 1745 if (! $port['linked'])
759b21d2 1746 $port_type_opts = getUnlinkedPortTypeOptions ($port['iif_id']);
78b59ab3 1747 else
4f968fa8
AA
1748 foreach (getExistingPortTypeOptions ($port) as $oif_id => $opt_str)
1749 $port_type_opts[$port['iif_name']][$port['iif_id'] . '-' . $oif_id] = $opt_str;
4f968fa8 1750 printNiftySelect ($port_type_opts, array ('name' => 'port_type_id'), $port['iif_id'] . '-' . $port['oif_id']);
8e021b86
AA
1751 if ($port['iif_id'] != 1)
1752 echo '</label>';
1753 echo '</td>';
1754
97792a8a
DO
1755 // 17 is the full notation length of a MAC address, 23 -- of a WWN address and 59 -- of an IPoIB address.
1756 echo "<td><input type=text name=l2address value='${port['l2address']}' size=17 maxlength=59></td>\n";
0b09238d 1757 if ($port['remote_object_id'])
90bf1755 1758 {
f4d00faf
DO
1759 $dname = formatObjectDisplayedName ($port['remote_object_name'], $port['remote_object_tid']);
1760 echo "<td class=tdleft>" .
1761 formatLoggedSpan ($port['last_log'], formatPortLink ($port['remote_object_id'], $dname, $port['remote_id'], NULL)) .
0b09238d 1762 "</td>";
f4d00faf 1763 echo "<td class=tdleft> " . formatLoggedSpan ($port['last_log'], $port['remote_name'], 'underline') .
0b09238d
DO
1764 "<input type=hidden name=reservation_comment value=''></td>";
1765 echo "<td><input type=text name=cable value='${port['cableid']}'></td>";
1766 echo "<td class=tdcenter>";
1767 echo getOpLink (array('op'=>'unlinkPort', 'port_id'=>$port['id'], ), '', 'cut', 'Unlink this port');
1768 echo "</td>";
e673ee24 1769 }
33973968 1770 elseif ($port['reservation_comment'] != '')
e673ee24 1771 {
0b09238d 1772 echo "<td>" . formatLoggedSpan ($port['last_log'], 'Reserved:', 'strong underline') . "</td>";
e673ee24 1773 echo "<td><input type=text name=reservation_comment value='${port['reservation_comment']}'></td>";
318c48a8 1774 echo "<td></td>";
10f5133a
AA
1775 echo "<td class=tdcenter>";
1776 echo getOpLink (array('op'=>'useup', 'port_id'=>$port['id']), '', 'clear', 'Use up this port');
1777 echo "</td>";
e673ee24
DO
1778 }
1779 else
1780 {
6b1ca530 1781 $in_rack = getConfigVar ('NEAREST_RACKS_CHECKBOX');
de55c069
AA
1782 echo "<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td class=tdcenter>";
1783 echo getPopupLink ('portlist', array ('port' => $port['id'], 'in_rack' => ($in_rack == "yes" ? "on" : "")), 'findlink', 'plug', '', 'Link this port');
0b09238d 1784 echo " <input type=text name=reservation_comment></td>\n";
90bf1755 1785 }
0b09238d
DO
1786 echo "<td>";
1787 printImageHREF ('save', 'Save changes', TRUE);
1788 echo "</td></form></tr>\n";
e673ee24 1789 }
39b4abd0 1790 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
3153a326 1791 printNewItemTR ($prefs);
e673ee24 1792 echo "</table><br>\n";
70c0cb08
DO
1793 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes' && getConfigVar ('ENABLE_BULKPORT_FORM') == 'yes')
1794 printBulkForm ($prefs);
e673ee24 1795 finishPortlet();
70c0cb08
DO
1796
1797 if (getConfigVar ('ENABLE_MULTIPORT_FORM') == 'yes' && permitted (NULL, NULL, 'addMultiPorts'))
1798 {
1799 startPortlet ('Add/update multiple ports');
1800 printOpFormIntro ('addMultiPorts');
1801 $formats = array
1802 (
1803 'ssv1' => 'SSV:<interface name> [<MAC address>]',
1804 );
1805 echo 'Format: ' . getSelect ($formats, array ('name' => 'format'), 'ssv1') . ' ';
1806 echo 'Default port type: ';
1807 printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type'), $prefs['selected']);
1808 echo "<input type=submit value='Parse output'><br>\n";
1809 echo "<textarea name=input cols=100 rows=50></textarea><br>\n";
1810 echo '</form>';
1811 finishPortlet();
1812 }
e673ee24
DO
1813}
1814
d10401bf 1815function renderIPForObject ($object_id)
e673ee24 1816{
15e27821 1817 function printNewItemTR ($default_type, $object_id)
21ee3351
AA
1818 {
1819 global $aat;
15e27821 1820
1821 includeJQueryUI (TRUE);
1822
a6e090f0
DO
1823 // Heredoc, not nowdoc!
1824 addJS (<<<"JSEND"
15e27821 1825 $(document).ready( function() {
1826 $('[name="bond_name"]').autocomplete({
1827 source: "?module=ajax&ac=autocomplete&realm=bond_name&object_id=$object_id",
1828 //minLength: 3,
1829 focus: function(event, ui) {
1830 if( ui.item.value == '' )
1831 event.preventDefault();
1832 },
1833 select: function(event, ui) {
1834 if( ui.item.value == '' )
1835 event.preventDefault();
1836 }
1837 });
1838 });
1839JSEND
1840 , TRUE);
4318ced5
AA
1841 printOpFormIntro ('add');
1842 echo "<tr><td>"; // left btn
dec748f6 1843 printImageHREF ('add', 'allocate', TRUE);
21ee3351 1844 echo "</td>";
04c61e7e
AB
1845 echo "<td class=tdleft><input type='text' size='10' name='bond_name'></td>\n"; // if-name
1846 echo "<td class=tdleft><input type=text name='ip'></td>\n"; // IP
4318ced5
AA
1847 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
1848 echo "<td colspan=2>&nbsp;</td>"; // network, routed by
1849 echo '<td>';
04c61e7e 1850 printSelect ($aat, array ('name' => 'bond_type'), $default_type); // type
4318ced5 1851 echo "</td><td>&nbsp;</td><td>"; // misc
04c61e7e 1852 printImageHREF ('add', 'allocate', TRUE); // right btn
21ee3351
AA
1853 echo "</td></tr></form>";
1854 }
21ee3351 1855 global $aat;
0d5ef6f0 1856
21ee3351 1857 startPortlet ('Allocations');
4318ced5
AA
1858 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'><tr>\n";
1859 echo '<th>&nbsp;</th>';
1860 echo '<th>OS interface</th>';
1861 echo '<th>IP address</th>';
21ee3351 1862 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
4318ced5
AA
1863 {
1864 echo '<th>network</th>';
1865 echo '<th>routed by</th>';
1866 }
1867 echo '<th>type</th>';
1868 echo '<th>misc</th>';
b1b07891 1869 echo '<th>&nbsp;</th>';
4318ced5 1870 echo '</tr>';
21ee3351 1871
710ead7e
AA
1872 $alloc_list = ''; // most of the output is stored here
1873 $used_alloc_types = array();
e9446e3b 1874 $ext_ipv4_view = getConfigVar ('EXT_IPV4_VIEW');
4318ced5 1875 foreach (getObjectIPAllocations ($object_id) as $alloc)
21ee3351 1876 {
710ead7e
AA
1877 if (! isset ($used_alloc_types[$alloc['type']]))
1878 $used_alloc_types[$alloc['type']] = 0;
1879 $used_alloc_types[$alloc['type']]++;
1880
40c9fc94 1881 $rendered_alloc = callHook ('getRenderedAlloc', $object_id, $alloc);
4318ced5 1882 $alloc_list .= getOutputOf ('printOpFormIntro', 'upd', array ('ip' => $alloc['addrinfo']['ip']));
710ead7e 1883 $alloc_list .= "<tr class='${rendered_alloc['tr_class']}' valign=top>";
f3505b35 1884
10f5133a 1885 $alloc_list .= "<td>" . getOpLink (array ('op' => 'del', 'ip' => $alloc['addrinfo']['ip']), '', 'delete', 'Delete this IP address') . "</td>";
40c9fc94 1886 $alloc_list .= "<td class=tdleft><input type='text' name='bond_name' value='${alloc['osif']}' size=10>" . $rendered_alloc['td_name_suffix'] . "</td>";
710ead7e 1887 $alloc_list .= $rendered_alloc['td_ip'];
e9446e3b 1888 if ($ext_ipv4_view == 'yes')
21ee3351 1889 {
710ead7e
AA
1890 $alloc_list .= $rendered_alloc['td_network'];
1891 $alloc_list .= $rendered_alloc['td_routed_by'];
21ee3351 1892 }
710ead7e
AA
1893 $alloc_list .= '<td>' . getSelect ($aat, array ('name' => 'bond_type'), $alloc['type']) . "</td>";
1894 $alloc_list .= $rendered_alloc['td_peers'];
1895 $alloc_list .= "<td>" .getImageHREF ('save', 'Save changes', TRUE) . "</td>";
f3505b35 1896
710ead7e 1897 $alloc_list .= "</form></tr>\n";
21ee3351 1898 }
710ead7e 1899 asort ($used_alloc_types, SORT_NUMERIC);
589c027b 1900 $most_popular_type = ! count ($used_alloc_types) ? 'regular' : array_last (array_keys ($used_alloc_types));
710ead7e
AA
1901
1902 if ($list_on_top = (getConfigVar ('ADDNEW_AT_TOP') != 'yes'))
1903 echo $alloc_list;
15e27821 1904 printNewItemTR ($most_popular_type, $object_id);
710ead7e
AA
1905 if (! $list_on_top)
1906 echo $alloc_list;
21ee3351
AA
1907
1908 echo "</table><br>\n";
1909 finishPortlet();
f3505b35
AA
1910}
1911
08408472
AA
1912// This function is deprecated. Do not rely on its internals,
1913// it will probably be removed in the next major relese.
1914// Use new showError, showWarning, showSuccess functions.
dec748f6 1915// Log array is stored in global $log_messages. Its format is simple: plain ordered array
8ab645cf 1916// with values having keys 'c' (both message code and severity) and 'a' (sprintf arguments array)
1cb6adf5 1917function showMessageOrError ()
e673ee24 1918{
a2b16202
AA
1919 global $log_messages;
1920
0a00b344 1921 startSession();
a2b16202
AA
1922 if (isset ($_SESSION['log']))
1923 {
1924 $log_messages = array_merge ($_SESSION['log'], $log_messages);
1925 unset ($_SESSION['log']);
1926 }
1927 session_commit();
1928
1929 if (empty ($log_messages))
1cb6adf5 1930 return;
8ab645cf
AA
1931 $msginfo = array
1932 (
dec748f6 1933// records 0~99 with success messages
8ab645cf
AA
1934 0 => array ('code' => 'success', 'format' => '%s'),
1935 5 => array ('code' => 'success', 'format' => 'added record "%s" successfully'),
1936 6 => array ('code' => 'success', 'format' => 'updated record "%s" successfully'),
1937 7 => array ('code' => 'success', 'format' => 'deleted record "%s" successfully'),
1938 8 => array ('code' => 'success', 'format' => 'Port %s successfully linked with %s'),
493e13dd 1939 10 => array ('code' => 'success', 'format' => 'Added %u port(s), updated %u port(s), encountered %u error(s).'),
8ab645cf 1940 21 => array ('code' => 'success', 'format' => 'Generation complete'),
493e13dd
DO
1941 26 => array ('code' => 'success', 'format' => 'updated %u record(s) successfully'),
1942 37 => array ('code' => 'success', 'format' => 'added %u record(s) successfully'),
1943 38 => array ('code' => 'success', 'format' => 'removed %u record(s) successfully'),
8ab645cf 1944 43 => array ('code' => 'success', 'format' => 'Saved successfully.'),
493e13dd 1945 44 => array ('code' => 'success', 'format' => '%s failure(s) and %s successfull change(s).'),
8ab645cf
AA
1946 48 => array ('code' => 'success', 'format' => 'added a record successfully'),
1947 49 => array ('code' => 'success', 'format' => 'deleted a record successfully'),
1948 51 => array ('code' => 'success', 'format' => 'updated a record successfully'),
1949 57 => array ('code' => 'success', 'format' => 'Reset complete'),
04eee8d2 1950 58 => array ('code' => 'success', 'format' => '%u device(s) unmounted successfully'),
8ab645cf 1951 63 => array ('code' => 'success', 'format' => '%u change request(s) have been processed'),
493e13dd 1952 67 => array ('code' => 'success', 'format' => "Tag rolling done, %u object(s) involved"),
8ab645cf
AA
1953 71 => array ('code' => 'success', 'format' => 'File "%s" was linked successfully'),
1954 72 => array ('code' => 'success', 'format' => 'File was unlinked successfully'),
493e13dd
DO
1955 82 => array ('code' => 'success', 'format' => "Bulk port creation was successful. %u port(s) created, %u failed"),
1956 87 => array ('code' => 'success', 'format' => '802.1Q recalculate: %d port(s) changed on %d switch(es)'),
8628ae44 1957// records 100~199 with fatal error messages
8ab645cf
AA
1958 100 => array ('code' => 'error', 'format' => '%s'),
1959 109 => array ('code' => 'error', 'format' => 'failed updating a record'),
8ab645cf 1960 131 => array ('code' => 'error', 'format' => 'invalid format requested'),
493e13dd 1961 141 => array ('code' => 'error', 'format' => 'Encountered %u error(s), updated %u record(s)'),
8ab645cf
AA
1962 149 => array ('code' => 'error', 'format' => 'Turing test failed'),
1963 150 => array ('code' => 'error', 'format' => 'Can only change password under DB authentication.'),
1964 151 => array ('code' => 'error', 'format' => 'Old password doesn\'t match.'),
1965 152 => array ('code' => 'error', 'format' => 'New passwords don\'t match.'),
1966 154 => array ('code' => 'error', 'format' => "Verification error: %s"),
1967 155 => array ('code' => 'error', 'format' => 'Save failed.'),
1968 159 => array ('code' => 'error', 'format' => 'Permission denied moving port %s from VLAN%u to VLAN%u'),
8ab645cf
AA
1969 170 => array ('code' => 'error', 'format' => 'There is no network for IP address "%s"'),
1970 172 => array ('code' => 'error', 'format' => 'Malformed request'),
1971 179 => array ('code' => 'error', 'format' => 'Expired form has been declined.'),
8ab645cf 1972 191 => array ('code' => 'error', 'format' => "deploy was blocked due to conflicting configuration versions"),
f0ff4930 1973
8628ae44 1974// records 200~299 with warnings
8ab645cf
AA
1975 200 => array ('code' => 'warning', 'format' => '%s'),
1976 201 => array ('code' => 'warning', 'format' => 'nothing happened...'),
e1add254 1977 206 => array ('code' => 'warning', 'format' => '%s is not empty'),
7587edca 1978 207 => array ('code' => 'warning', 'format' => 'File upload failed, error: %s'),
57777e4a 1979
24d3182a 1980// records 300~399 with notices
8ab645cf 1981 300 => array ('code' => 'neutral', 'format' => '%s'),
24d3182a 1982
8ab645cf
AA
1983 );
1984 // Handle the arguments. Is there any better way to do it?
a2b16202 1985 foreach ($log_messages as $record)
8ab645cf 1986 {
55eefced 1987 if (! isset ($record['c']) || ! isset ($msginfo[$record['c']]))
8ab645cf 1988 {
41152018
AA
1989 $prefix = isset ($record['c']) ? $record['c'] . ': ' : '';
1990 echo "<div class=msg_neutral>(${prefix}this message was lost)</div>";
8ab645cf
AA
1991 continue;
1992 }
1993 if (isset ($record['a']))
1994 switch (count ($record['a']))
baf37d01 1995 {
8ab645cf
AA
1996 case 1:
1997 $msgtext = sprintf
1998 (
1999 $msginfo[$record['c']]['format'],
2000 $record['a'][0]
2001 );
2002 break;
2003 case 2:
2004 $msgtext = sprintf
2005 (
2006 $msginfo[$record['c']]['format'],
2007 $record['a'][0],
2008 $record['a'][1]
2009 );
2010 break;
2011 case 3:
2012 $msgtext = sprintf
2013 (
2014 $msginfo[$record['c']]['format'],
2015 $record['a'][0],
2016 $record['a'][1],
2017 $record['a'][2]
2018 );
2019 break;
2020 case 4:
2021 default:
2022 $msgtext = sprintf
2023 (
2024 $msginfo[$record['c']]['format'],
2025 $record['a'][0],
2026 $record['a'][1],
2027 $record['a'][2],
2028 $record['a'][3]
2029 );
2030 break;
baf37d01 2031 }
8ab645cf
AA
2032 else
2033 $msgtext = $msginfo[$record['c']]['format'];
2034 echo '<div class=msg_' . $msginfo[$record['c']]['code'] . ">${msgtext}</div>";
baf37d01 2035 }
a2b16202 2036 $log_messages = array();
e673ee24
DO
2037}
2038
65e557dd 2039// renders two tables: port link status and learned MAC list
d10401bf 2040function renderPortsInfo($object_id)
65e557dd 2041{
2d33a804 2042 $breed = detectDeviceBreed ($object_id);
03522bd8
AA
2043 try
2044 {
2045 if (permitted (NULL, NULL, 'get_link_status'))
2d33a804 2046 {
2047 if (validBreedFunction ($breed, 'getportstatus'))
2048 $linkStatus = queryDevice ($object_id, 'getportstatus');
2049 }
03522bd8
AA
2050 else
2051 showWarning ("You don't have permission to view ports link status");
2052
2053 if (permitted (NULL, NULL, 'get_mac_list'))
2d33a804 2054 {
2055 if (validBreedFunction ($breed, 'getmaclist'))
2056 $macList = sortPortList (queryDevice ($object_id, 'getmaclist'));
2057 }
03522bd8
AA
2058 else
2059 showWarning ("You don't have permission to view learned MAC list");
2060 }
2061 catch (RTGatewayError $e)
2062 {
2063 showError ($e->getMessage());
2064 return;
2065 }
2066
65e557dd
AA
2067 global $nextorder;
2068 echo "<table width='100%'><tr>";
03522bd8 2069 if (! empty ($linkStatus))
65e557dd 2070 {
03522bd8
AA
2071 echo "<td valign='top' width='50%'>";
2072 startPortlet('Link status');
c51492f5
DO
2073 echo "<table width='80%' class='widetable' cellspacing=0 cellpadding='5px' align='center'>";
2074 echo '<tr><th>Port</th><th>&nbsp;</th><th>Link status</th><th>Link info</th></tr>';
72c3bebc
DO
2075 $statusmap = array
2076 (
2077 'up' => 'link up',
2078 'down' => 'link down',
2079 'disabled' => 'link disabled',
2080 );
03522bd8
AA
2081 $order = 'even';
2082 foreach ($linkStatus as $pn => $link)
65e557dd 2083 {
03522bd8
AA
2084 echo "<tr class='row_$order'>";
2085 $order = $nextorder[$order];
c51492f5
DO
2086 echo '<td>' . $pn . '</td>';
2087 echo '<td>' . getImageHREF (array_fetch ($statusmap, $link['status'], '16x16t')) . '</td>';
2088 echo '<td>' . $link['status'] . '</td>';
03522bd8
AA
2089 $info = '';
2090 if (isset ($link['speed']))
2091 $info .= $link['speed'];
2092 if (isset ($link['duplex']))
2093 {
cfb42424 2094 if ($info != '')
03522bd8
AA
2095 $info .= ', ';
2096 $info .= $link['duplex'];
65e557dd 2097 }
c51492f5 2098 echo '<td>' . $info . '</td>';
03522bd8 2099 echo '</tr>';
65e557dd 2100 }
03522bd8
AA
2101 echo "</table></td>";
2102 finishPortlet();
65e557dd
AA
2103 }
2104
65e557dd
AA
2105 if (! empty ($macList))
2106 {
2107 echo "<td valign='top' width='50%'>";
2108 $rendered_macs = '';
2109 $mac_count = 0;
c51492f5
DO
2110 $rendered_macs .= "<table width='80%' class='widetable' cellspacing=0 cellpadding='5px' align='center'>";
2111 $rendered_macs .= '<tr><th>MAC address</th><th>VLAN</th><th>Port</th></tr>';
65e557dd
AA
2112 $order = 'even';
2113 foreach ($macList as $pn => $list)
2114 {
2115 $order = $nextorder[$order];
2116 foreach ($list as $item)
2117 {
2118 ++$mac_count;
2119 $rendered_macs .= "<tr class='row_$order'>";
c51492f5
DO
2120 $rendered_macs .= '<td style="font-family: monospace">' . $item['mac'] . '</td>';
2121 $rendered_macs .= '<td>' . $item['vid'] . '</td>';
2122 $rendered_macs .= '<td>' . $pn . '</td>';
65e557dd
AA
2123 $rendered_macs .= '</tr>';
2124 }
2125 }
2126 $rendered_macs .= "</table></td>";
dec748f6 2127
65e557dd
AA
2128 startPortlet("Learned MACs ($mac_count)");
2129 echo $rendered_macs;
2130 finishPortlet();
2131 }
2132
2133 echo "</td></tr></table>";
2134}
2135
e673ee24 2136/*
51c18782 2137The following conditions must be met:
e673ee24
DO
21381. We can mount onto free atoms only. This means: if any record for an atom
2139already exists in RackSpace, it can't be used for mounting.
21402. We can't unmount from 'W' atoms. Operator should review appropriate comments
2141and either delete them before unmounting or refuse to unmount the object.
2142*/
d10401bf 2143function renderRackSpaceForObject ($object_id)
e673ee24 2144{
e673ee24
DO
2145 // Always process occupied racks plus racks chosen by user. First get racks with
2146 // already allocated rackspace...
950d4117
DO
2147 $workingRacksData = array();
2148 foreach (getResidentRackIDs ($object_id) as $rack_id)
2149 {
2150 $rackData = spotEntity ('rack', $rack_id);
2151 amplifyCell ($rackData);
2152 $workingRacksData[$rack_id] = $rackData;
2153 }
e673ee24 2154 // ...and then add those chosen by user (if any).
2135fd83 2155 if (isset($_REQUEST['rackmulti']))
e673ee24 2156 foreach ($_REQUEST['rackmulti'] as $cand_id)
e673ee24
DO
2157 if (!isset ($workingRacksData[$cand_id]))
2158 {
0cc24e9a 2159 $rackData = spotEntity ('rack', $cand_id);
61a1d996 2160 amplifyCell ($rackData);
e673ee24
DO
2161 $workingRacksData[$cand_id] = $rackData;
2162 }
51690ad4 2163
c17a36f0
AA
2164 // Get a list of rack ids that are parents of this object.
2165 $object = spotEntity ('object', $object_id);
2166 $parentRacks = reduceSubarraysToColumn (getParents ($object, 'rack'), 'id');
0a7feebb 2167
e673ee24
DO
2168 // Main layout starts.
2169 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0><tr>";
2170
2171 // Left portlet with rack list.
2172 echo "<td class=pcleft height='1%'>";
2173 startPortlet ('Racks');
a8efc03e 2174 $allRacksData = listCells ('rack');
6d42599c
AA
2175
2176 // filter rack list to match only racks having common tags with the object (reducing $allRacksData)
55eefced 2177 if (! isset ($_REQUEST['show_all_racks']) && getConfigVar ('FILTER_RACKLIST_BY_TAGS') == 'yes')
6d42599c
AA
2178 {
2179 $matching_racks = array();
6d42599c
AA
2180 $matched_tags = array();
2181 foreach ($allRacksData as $rack)
01205bb6 2182 {
b51c1007 2183 $tag_chain = array_merge ($rack['etags'], $rack['itags']);
6d42599c 2184 foreach ($object['etags'] as $tag)
01205bb6 2185 if (tagOnChain ($tag, $tag_chain))
6d42599c
AA
2186 {
2187 $matching_racks[$rack['id']] = $rack;
2188 $matched_tags[$tag['id']] = $tag;
2189 break;
2190 }
01205bb6 2191 }
6d42599c
AA
2192 // add current object's racks even if they dont match filter
2193 foreach ($workingRacksData as $rack_id => $rack)
2194 if (! isset ($matching_racks[$rack_id]))
2195 $matching_racks[$rack_id] = $rack;
2196 // if matching racks found, and rack list is reduced, show 'show all' link
55eefced 2197 if (count ($matching_racks) && count ($matching_racks) != count ($allRacksData))
6d42599c 2198 {
1e9266ad 2199 $tmp = array();
6d42599c 2200 foreach ($matched_tags as $tag)
1e9266ad
DO
2201 $tmp[] = '{' . $tag['tag'] . '}';
2202 $filter_text = implode (' or ', $tmp);
6d42599c
AA
2203 $href_show_all = trim($_SERVER['REQUEST_URI'], '&');
2204 $href_show_all .= htmlspecialchars('&show_all_racks=1');
2205 echo "(filtered by <span class='filter-text'>$filter_text</span>, <a href='$href_show_all'>show all</a>)<p>";
2206 $allRacksData = $matching_racks;
2207 }
2208 }
2209
4b8d413e 2210 if (count ($allRacksData) <= getConfigVar ('RACK_PRESELECT_THRESHOLD'))
61a1d996 2211 foreach ($allRacksData as $rack)
51c18782
DO
2212 if (!array_key_exists ($rack['id'], $workingRacksData))
2213 {
2214 amplifyCell ($rack);
2215 $workingRacksData[$rack['id']] = $rack;
2216 }
2217 foreach (array_keys ($workingRacksData) as $rackId)
2218 applyObjectMountMask ($workingRacksData[$rackId], $object_id);
2219 printOpFormIntro ('updateObjectAllocation');
4b8d413e 2220 renderRackMultiSelect ('rackmulti[]', $allRacksData, array_keys ($workingRacksData));
51c18782 2221 echo "<br><br>";
e673ee24
DO
2222 finishPortlet();
2223 echo "</td>";
2224
2225 // Middle portlet with comment and submit.
2226 echo "<td class=pcleft>";
5c8bf2ba 2227 startPortlet ('Comment (for Rackspace History)');
e673ee24
DO
2228 echo "<textarea name=comment rows=10 cols=40></textarea><br>\n";
2229 echo "<input type=submit value='Save' name=got_atoms>\n";
51c18782 2230 echo "<br><br>";
e673ee24
DO
2231 finishPortlet();
2232 echo "</td>";
2233
2234 // Right portlet with rendered racks. If this form submit is not final, we have to
2235 // reflect the former state of the grid in current form.
2236 echo "<td class=pcright rowspan=2 height='1%'>";
2237 startPortlet ('Working copy');
e8b94513 2238 includeJQueryUI (false);
65e557dd 2239 addJS ('js/racktables.js');
c82023ad 2240 addJS ('js/bulkselector.js');
e673ee24
DO
2241 echo '<table border=0 cellspacing=10 align=center><tr>';
2242 foreach ($workingRacksData as $rack_id => $rackData)
2243 {
c26d97b6 2244 $is_ro = !rackModificationPermitted ($rackData, 'updateObjectAllocation');
e673ee24
DO
2245 // Order is important here: only original allocation is highlighted.
2246 highlightObject ($rackData, $object_id);
2247 markupAtomGrid ($rackData, 'T');
2248 // If we have a form processed, discard user input and show new database
2249 // contents.
c26d97b6 2250 if (!$is_ro && isset ($_REQUEST['rackmulti'][0])) // is an update
e673ee24
DO
2251 mergeGridFormToRack ($rackData);
2252 echo "<td valign=top>";
c26d97b6
VS
2253 echo "<center>\n<h2 style='margin:0px'>${rackData['name']}</h2>\n";
2254 $read_only_text = $is_ro ? '(read-only)' : '&nbsp;';
2255 echo "<p style='color: red; margin-top:0px'>${read_only_text}</p>\n";
e8b94513 2256 echo "<table class=rack id=selectableRack border=0 cellspacing=0 cellpadding=1>\n";
57c69e9c 2257 echo "<tr><th width='10%'>&nbsp;</th>";
bd0bfb76
AD
2258 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
2259 echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
2260 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
c26d97b6 2261 renderAtomGrid ($rackData, $is_ro);
57c69e9c 2262 echo "<tr><th width='10%'>&nbsp;</th>";
bd0bfb76
AD
2263 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
2264 echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
2265 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
0a7feebb
AD
2266 echo "</table>\n<br>\n";
2267 // Determine zero-u checkbox status.
2268 // If form has been submitted, use form data, otherwise use DB data.
c26d97b6 2269 if (!$is_ro && isset($_REQUEST['op']))
0a7feebb
AD
2270 $checked = isset($_REQUEST['zerou_'.$rack_id]) ? 'checked' : '';
2271 else
2272 $checked = in_array($rack_id, $parentRacks) ? 'checked' : '';
c26d97b6
VS
2273 $disabled_text = $is_ro ? ' disabled' : '';
2274 echo "<label for=zerou_${rack_id}>Zero-U:</label> <input type=checkbox ${checked} name=zerou_${rack_id} id=zerou_${rack_id}${disabled_text}>\n<br><br>\n";
bd0bfb76 2275 echo "<input type='button' onclick='uncheckAll();' value='Uncheck all'>\n";
0a7feebb 2276 echo '</center></td>';
e673ee24
DO
2277 }
2278 echo "</tr></table>";
2279 finishPortlet();
2280 echo "</td>\n";
2281
2282 echo "</form>\n";
2283 echo "</tr></table>\n";
2284}
2285
2286function renderMolecule ($mdata, $object_id)
2287{
2288 // sort data out
2289 $rackpack = array();
2290 global $loclist;
64b95774 2291 foreach ($mdata as $rua)
e673ee24
DO
2292 {
2293 $rack_id = $rua['rack_id'];
2294 $unit_no = $rua['unit_no'];
2295 $atom = $rua['atom'];
2296 if (!isset ($rackpack[$rack_id]))
2297 {
61a1d996
DO
2298 $rackData = spotEntity ('rack', $rack_id);
2299 amplifyCell ($rackData);
e673ee24
DO
2300 for ($i = $rackData['height']; $i > 0; $i--)
2301 for ($locidx = 0; $locidx < 3; $locidx++)
2302 $rackData[$i][$locidx]['state'] = 'F';
2303 $rackpack[$rack_id] = $rackData;
2304 }
2305 $rackpack[$rack_id][$unit_no][$loclist[$atom]]['state'] = 'T';
2306 $rackpack[$rack_id][$unit_no][$loclist[$atom]]['object_id'] = $object_id;
2307 }
2308 // now we have some racks to render
64b95774 2309 foreach ($rackpack as $rackData)
e673ee24
DO
2310 {
2311 markAllSpans ($rackData);
2312 echo "<table class=molecule cellspacing=0>\n";
2313 echo "<caption>${rackData['name']}</caption>\n";
2314 echo "<tr><th width='10%'>&nbsp;</th><th width='20%'>Front</th><th width='50%'>Interior</th><th width='20%'>Back</th></tr>\n";
2315 for ($i = $rackData['height']; $i > 0; $i--)
2316 {
4dcd770e 2317 echo "<tr><th>" . inverseRackUnit ($i, $rackData) . "</th>";
e673ee24
DO
2318 for ($locidx = 0; $locidx < 3; $locidx++)
2319 {
2320 $state = $rackData[$i][$locidx]['state'];
65e557dd 2321 echo "<td class='atom state_${state}'>&nbsp;</td>\n";
e673ee24
DO
2322 }
2323 echo "</tr>\n";
2324 }
2325 echo "</table>\n";
2326 }
2327}
2328
23cdc7e9 2329function renderDepot ()
e673ee24 2330{
23cdc7e9 2331 global $pageno, $nextorder;
23cdc7e9 2332 $cellfilter = getCellFilter();
950dfa1f
AA
2333 $objects = array();
2334 $objects_count = getEntitiesCount ('object');
db8255ac 2335 $showobjecttype = (getConfigVar ('SHOW_OBJECTTYPE') == 'yes');
23cdc7e9
DO
2336
2337 echo "<table border=0 class=objectview>\n";
2338 echo "<tr><td class=pcleft>";
2339
950dfa1f
AA
2340 if ($objects_count == 0)
2341 echo '<h2>No objects exist</h2>';
2342 // 1st attempt: do not fetch all objects if cellfilter is empty and rendering empty result is enabled
2343 elseif (! ($cellfilter['is_empty'] && renderEmptyResults ($cellfilter, 'objects', $objects_count)))
e673ee24 2344 {
2b16c179 2345 $objects = applyCellFilter ('object', $cellfilter);
20506de3 2346 // 2nd attempt: do not render all fetched objects if rendering empty result is enabled
950dfa1f 2347 if (! renderEmptyResults ($cellfilter, 'objects', count($objects)))
1ebbf889 2348 {
4899096d
AD
2349 startPortlet ('Objects (' . count ($objects) . ')');
2350 echo '<br><br><table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
db8255ac
J
2351 echo '<tr><th>Common name</th>';
2352 if ($showobjecttype)
2353 echo '<th>Type</th>';
2354 echo '<th>Visible label</th><th>Asset tag</th><th>Row/Rack or Container</th></tr>';
4899096d 2355 $order = 'odd';
3f8a25d6
DO
2356 # gather IDs of all objects and fetch rackspace info in one pass
2357 $idlist = array();
2358 foreach ($objects as $obj)
2359 $idlist[] = $obj['id'];
2360 $mountinfo = getMountInfo ($idlist);
cae81598 2361 $containerinfo = getContainerInfo ($idlist);
4899096d
AD
2362 foreach ($objects as $obj)
2363 {
ec788aba 2364 $problem = ($obj['has_problems'] == 'yes') ? 'has_problems' : '';
25526a97
ME
2365
2366 setEntityColors ($obj);
5abb804e 2367 $class = getObjectClass ($obj, ($order == 'even' ? 'background:white;' : ''));
25526a97
ME
2368
2369 echo "<tr class='row_${order} tdleft ${problem}${class}' valign=top><td>" . mkA ("<strong>${obj['dname']}</strong>", 'object', $obj['id']);
4899096d
AD
2370 if (count ($obj['etags']))
2371 echo '<br><small>' . serializeTags ($obj['etags'], makeHref(array('page'=>$pageno, 'tab'=>'default')) . '&') . '</small>';
db8255ac
J
2372 echo '</td>';
2373 if ($showobjecttype)
9563f56f 2374 echo "<td>" . decodeObjectType ($obj['objtype_id']) . "</td>";
8526b4cc 2375 echo "<td>${obj['label']}</td>";
4318ced5 2376 echo "<td>${obj['asset_no']}</td>";
3f8a25d6 2377 $places = array();
cae81598
AD
2378 if (array_key_exists ($obj['id'], $containerinfo))
2379 foreach ($containerinfo[$obj['id']] as $ci)
20506de3 2380 $places[] = mkA ($ci['container_dname'], 'object', $ci['container_id']);
cae81598 2381 if (array_key_exists ($obj['id'], $mountinfo))
3f8a25d6
DO
2382 foreach ($mountinfo[$obj['id']] as $mi)
2383 $places[] = mkA ($mi['row_name'], 'row', $mi['row_id']) . '/' . mkA ($mi['rack_name'], 'rack', $mi['rack_id']);
cae81598
AD
2384 if (! count ($places))
2385 $places[] = 'Unmounted';
4318ced5 2386 echo "<td>" . implode (', ', $places) . '</td>';
4899096d
AD
2387 echo '</tr>';
2388 $order = $nextorder[$order];
2389 }
2390 echo '</table>';
2391 finishPortlet();
1ebbf889 2392 }
e673ee24 2393 }
23cdc7e9 2394
d4e27fe5 2395 echo "</td><td class='pcright ${pageno}' width='25%'>";
23cdc7e9 2396
95857b5c 2397 renderCellFilterPortlet ($cellfilter, 'object', $objects);
23cdc7e9 2398 echo "</td></tr></table>\n";
e673ee24
DO
2399}
2400
1ebbf889
AA
2401// This function returns TRUE if the result set is too big to be rendered, and no filter is set.
2402// In this case it renders the describing message instead.
2403function renderEmptyResults($cellfilter, $entities_name, $count = NULL)
2404{
2405 if (!$cellfilter['is_empty'])
2406 return FALSE;
2407 if (isset ($_REQUEST['show_all_objects']))
2408 return FALSE;
7c91a821 2409 $max = getConfigVar('MAX_UNFILTERED_ENTITIES');
1ebbf889
AA
2410 if (0 == $max || $count <= $max)
2411 return FALSE;
2412
2413 $href_show_all = trim($_SERVER['REQUEST_URI'], '&');
2414 $href_show_all .= htmlspecialchars('&show_all_objects=1');
2415 $suffix = isset ($count) ? " ($count)" : '';
a6e090f0
DO
2416 // Heredoc, not nowdoc!
2417 echo <<<"END"
1ebbf889
AA
2418<p>Please set a filter to display the corresponging $entities_name.
2419<br><a href="$href_show_all">Show all $entities_name$suffix</a>
2420END;
2421 return TRUE;
2422}
2423
e673ee24 2424// History viewer for history-enabled simple dictionaries.
9b8174d7 2425function renderObjectHistory ($object_id)
e673ee24 2426{
e673ee24
DO
2427 $order = 'odd';
2428 global $nextorder;
9b8174d7
AD
2429 echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
2430 echo '<tr><th>change time</th><th>author</th><th>name</th><th>visible label</th><th>asset no</th><th>has problems?</th><th>comment</th></tr>';
2431 $result = usePreparedSelectBlade
2432 (
2433 'SELECT ctime, user_name, name, label, asset_no, has_problems, comment FROM ObjectHistory WHERE id=? ORDER BY ctime',
2434 array ($object_id)
2435 );
e673ee24
DO
2436 while ($row = $result->fetch (PDO::FETCH_NUM))
2437 {
2438 echo "<tr class=row_${order}><td>${row[0]}</td>";
9b8174d7 2439 for ($i = 1; $i <= 6; $i++)
e673ee24
DO
2440 echo "<td>" . $row[$i] . "</td>";
2441 echo "</tr>\n";
2442 $order = $nextorder[$order];
2443 }
2444 echo "</table><br>\n";
2445}
2446
2447function renderRackspaceHistory ()
2448{
4fbb5a00 2449 global $nextorder, $pageno, $tabno;
e673ee24
DO
2450 $order = 'odd';
2451 $history = getRackspaceHistory();
2452 // Show the last operation by default.
2453 if (isset ($_REQUEST['op_id']))
2454 $op_id = $_REQUEST['op_id'];
2455 elseif (isset ($history[0]['mo_id']))
2456 $op_id = $history[0]['mo_id'];
2457 else $op_id = NULL;
dec748f6 2458
e673ee24
DO
2459 $omid = NULL;
2460 $nmid = NULL;
2461 $object_id = 1;
2462 if ($op_id)
2463 list ($omid, $nmid) = getOperationMolecules ($op_id);
2464
2465 // Main layout starts.
2466 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
2467
2468 // Left top portlet with old allocation.
2469 echo "<tr><td class=pcleft>";
2470 startPortlet ('Old allocation');
2471 if ($omid)
2472 {
2473 $oldMolecule = getMolecule ($omid);
2474 renderMolecule ($oldMolecule, $object_id);
2475 }
2476 else
2477 echo "nothing";
2478 finishPortlet();
2479
2480 echo '</td><td class=pcright>';
2481
2482 // Right top portlet with new allocation
2483 startPortlet ('New allocation');
2484 if ($nmid)
2485 {
2486 $newMolecule = getMolecule ($nmid);
2487 renderMolecule ($newMolecule, $object_id);
2488 }
2489 else
2490 echo "nothing";
2491 finishPortlet();
737a3f72 2492
e673ee24 2493 echo '</td></tr><tr><td colspan=2>';
737a3f72 2494
e673ee24
DO
2495 // Bottom portlet with list
2496
2497 startPortlet ('Rackspace allocation history');
2498 echo "<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>\n";
7fa7047a 2499 echo "<tr><th>timestamp</th><th>author</th><th>object</th><th>comment</th></tr>\n";
e673ee24
DO
2500 foreach ($history as $row)
2501 {
2502 if ($row['mo_id'] == $op_id)
2503 $class = 'hl';
2504 else
2505 $class = "row_${order}";
4fbb5a00 2506 echo "<tr class=${class}><td><a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'op_id'=>$row['mo_id']))."'>${row['ctime']}</a></td>";
7fa7047a
DO
2507 echo "<td>${row['user_name']}</td><td>";
2508 renderCell (spotEntity ('object', $row['ro_id']));
a0bdf6dc 2509 echo '</td><td>' . stringForTD ($row['comment'], 0) . '</td></tr>';
e673ee24
DO
2510 $order = $nextorder[$order];
2511 }
2512 echo "</table>\n";
2513 finishPortlet();
737a3f72 2514
e673ee24 2515 echo '</td></tr></table>';
e673ee24
DO
2516}
2517
4318ced5 2518function renderIPSpaceRecords ($tree, $baseurl, $target = 0, $level = 1)
d65353ad
DO
2519{
2520 $self = __FUNCTION__;
4318ced5 2521 $knight = (getConfigVar ('IPV4_ENABLE_KNIGHT') == 'yes');
774968ea 2522 $display_routers = (getConfigVar ('IPV4_TREE_RTR_AS_CELL') != 'none');
00276508
AA
2523
2524 // scroll page to the highlighted item
2525 if ($target && isset ($_REQUEST['hl_net']))
65e557dd 2526 addAutoScrollScript ("net-$target");
00276508 2527
d65353ad
DO
2528 foreach ($tree as $item)
2529 {
0137d53c
DO
2530 if (isset ($item['id']))
2531 {
1f54e1ba 2532 $decor = array ('indent' => $level);
fec0c8da 2533 if ($item['symbol'] == 'node-collapsed')
4318ced5 2534 $decor['symbolurl'] = "${baseurl}&eid=${item['id']}&hl_net=1";
fec0c8da 2535 elseif ($item['symbol'] == 'node-expanded')
4318ced5
AA
2536 $decor['symbolurl'] = $baseurl . ($item['parent_id'] ? "&eid=${item['parent_id']}&hl_net=1" : '');
2537 $tr_class = '';
25526a97 2538 $extrastyle = '';
21ee3351 2539 if ($target == $item['id'] && isset ($_REQUEST['hl_net']))
64347dcf 2540 {
25526a97
ME
2541 if (isset ($item['colors'][0]))
2542 $extrastyle = 'outline: 3px solid #0aff0a;';
2543 else
2544 {
2545 $decor['tdclass'] = ' highlight';
2546 $tr_class = $decor['tdclass'];
2547 }
2548
64347dcf 2549 }
25526a97
ME
2550
2551 setEntityColors ($item);
5abb804e 2552 $tr_class .= getObjectClass ($item, $extrastyle);
25526a97 2553
4318ced5
AA
2554 echo "<tr valign=top class=\"$tr_class\">";
2555 printIPNetInfoTDs ($item, $decor);
2556
2557 // capacity and usage
2558 echo "<td class=tdcenter>";
2559 echo getRenderedIPNetCapacity ($item);
64347dcf 2560 echo "</td>";
4318ced5
AA
2561
2562 if ($display_routers)
6063fab7 2563 printRoutersTD (findNetRouters ($item), getConfigVar ('IPV4_TREE_RTR_AS_CELL'));
0137d53c 2564 echo "</tr>";
55eefced 2565 if ($item['symbol'] == 'node-expanded' || $item['symbol'] == 'node-expanded-static')
4318ced5 2566 $self ($item['kids'], $baseurl, $target, $level + 1);
0137d53c 2567 }
26fc6e7c 2568 elseif (getConfigVar ('IPV4_TREE_SHOW_UNALLOCATED') == 'yes')
0137d53c 2569 {
4318ced5 2570 // non-allocated (spare) IP range
0137d53c 2571 echo "<tr valign=top>";
21ee3351 2572 printIPNetInfoTDs ($item, array ('indent' => $level, 'knight' => $knight, 'tdclass' => 'sparenetwork'));
00276508 2573
4318ced5 2574 // capacity and usage
21ee3351 2575 echo "<td class=tdcenter>";
4318ced5 2576 echo getRenderedIPNetCapacity ($item);
21ee3351 2577 echo "</td>";
4318ced5
AA
2578 if ($display_routers)
2579 echo "<td></td>";
21ee3351 2580 echo "</tr>";
21ee3351 2581 }
21ee3351
AA
2582 }
2583}
2584
4318ced5 2585function renderIPSpace()
e673ee24 2586{
4fbb5a00 2587 global $pageno, $tabno;
4318ced5 2588 $realm = ($pageno == 'ipv4space' ? 'ipv4net' : 'ipv6net');
31c941ec 2589 $cellfilter = getCellFilter();
21ee3351 2590
b99b4167
AA
2591 // expand request can take either natural values or "ALL". Zero means no expanding.
2592 $eid = isset ($_REQUEST['eid']) ? $_REQUEST['eid'] : 0;
2593
21ee3351
AA
2594 echo "<table border=0 class=objectview>\n";
2595 echo "<tr><td class=pcleft>";
950dfa1f
AA
2596
2597 $netlist = array();
b99b4167 2598 if (! ($cellfilter['is_empty'] && ! $eid && renderEmptyResults($cellfilter, 'IP nets', getEntitiesCount ($realm))))
21ee3351 2599 {
950dfa1f
AA
2600 $top = NULL;
2601 foreach (listCells ($realm) as $net)
21ee3351 2602 {
55eefced 2603 if (isset ($top) && IPNetContains ($top, $net))
950dfa1f 2604 ;
55eefced 2605 elseif (! count ($cellfilter['expression']) || judgeCell ($net, $cellfilter['expression']))
950dfa1f
AA
2606 $top = $net;
2607 else
2608 continue;
2609 $netlist[$net['id']] = $net;
2610 }
2611 $netcount = count ($netlist);
950dfa1f
AA
2612 $tree = prepareIPTree ($netlist, $eid);
2613
2614 if (! renderEmptyResults($cellfilter, 'IP nets', count($tree)))
2615 {
2616 startPortlet ("networks (${netcount})");
2617 echo '<h4>';
2618 $all = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'ALL')) .
2619 $cellfilter['urlextra'] . "'>expand&nbsp;all</a>";
2620 $none = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'NONE')) .
2621 $cellfilter['urlextra'] . "'>collapse&nbsp;all</a>";
2622 $auto = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno)) .
2623 $cellfilter['urlextra'] . "'>auto-collapse</a>";
2624
2625 if ($eid === 0)
2626 echo 'auto-collapsing at threshold ' . getConfigVar ('TREE_THRESHOLD') . " ($all / $none)";
2627 elseif ($eid === 'ALL')
2628 echo "expanding all ($auto / $none)";
2629 elseif ($eid === 'NONE')
2630 echo "collapsing all ($all / $auto)";
2631 else
2632 {
a0f0c0c7
AA
2633 try
2634 {
2635 $netinfo = spotEntity ($realm, $eid);
2636 echo "expanding ${netinfo['ip']}/${netinfo['mask']} ($auto / $all / $none)";
2637 }
2638 catch (EntityNotFoundException $e)
2639 {
2640 // ignore invalid eid error
2641 }
950dfa1f 2642 }
6edc799c 2643 echo "</h4><table class='widetable zebra' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
950dfa1f
AA
2644 echo "<tr><th>prefix</th><th>name/tags</th><th>capacity</th>";
2645 if (getConfigVar ('IPV4_TREE_RTR_AS_CELL') != 'none')
2646 echo "<th>routed by</th>";
2647 echo "</tr>\n";
2648 $baseurl = makeHref(array('page'=>$pageno, 'tab'=>$tabno)) . $cellfilter['urlextra'];
2649 renderIPSpaceRecords ($tree, $baseurl, $eid);
2650 echo "</table>\n";
2651 finishPortlet();
21ee3351 2652 }
21ee3351
AA
2653 }
2654
d4e27fe5 2655 echo "</td><td class='pcright ${pageno}'>";
950dfa1f 2656 renderCellFilterPortlet ($cellfilter, $realm, $netlist);
21ee3351
AA
2657 echo "</td></tr></table>\n";
2658}
2659
4318ced5 2660function renderIPSpaceEditor()
21ee3351 2661{
4318ced5
AA
2662 global $pageno;
2663 $realm = ($pageno == 'ipv4space' ? 'ipv4net' : 'ipv6net');
4318ced5 2664 $addrspaceList = listCells ($realm);
93a49e5b
AA
2665 startPortlet ('Manage existing (' . count ($addrspaceList) . ')');
2666 if (count ($addrspaceList))
21ee3351 2667 {
21ee3351
AA
2668 echo "<table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
2669 echo "<tr><th>&nbsp;</th><th>prefix</th><th>name</th><th>capacity</th></tr>";
21ee3351
AA
2670 foreach ($addrspaceList as $netinfo)
2671 {
21ee3351 2672 echo "<tr valign=top><td>";
93a49e5b 2673 if (! isIPNetworkEmpty ($netinfo))
21ee3351
AA
2674 printImageHREF ('nodestroy', 'There are ' . count ($netinfo['addrlist']) . ' allocations inside');
2675 else
93a49e5b 2676 echo getOpLink (array ('op' => 'del', 'id' => $netinfo['id']), '', 'destroy', 'Delete this prefix');
6d8424f8 2677 echo '</td><td class=tdleft>' . mkCellA ($netinfo) . '</td>';
a0bdf6dc 2678 echo '<td class=tdleft>' . stringForTD ($netinfo['name']);
1f54e1ba
DO
2679 if (count ($netinfo['etags']))
2680 echo '<br><small>' . serializeTags ($netinfo['etags']) . '</small>';
2681 echo '</td><td>';
4318ced5 2682 echo getRenderedIPNetCapacity ($netinfo);
b1e39212 2683 echo '</tr>';
e673ee24 2684 }
04d619d0
DO
2685 echo "</table>";
2686 finishPortlet();
e673ee24 2687 }
93a49e5b
AA
2688}
2689
2690function renderIPNewNetForm ()
2691{
2692 global $pageno;
2693 if ($pageno == 'ipv6space')
2694 {
2695 $realm = 'ipv6net';
3495ec6e 2696 $regexp = '^[a-fA-F0-9:]*:[a-fA-F0-9:\.]*/\d{1,3}$';
93a49e5b
AA
2697 }
2698 else
2699 {
2700 $realm = 'ipv4net';
3495ec6e 2701 $regexp = '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$';
93a49e5b
AA
2702 }
2703
2704 // IP prefix validator
10f5133a 2705 addJS ('js/live_validation.js');
93a49e5b 2706 $regexp = addslashes ($regexp);
a6e090f0
DO
2707 // Heredoc, not nowdoc!
2708 addJS (<<<"END"
93a49e5b 2709$(document).ready(function () {
b895e6b4 2710 $('form#add input[name="range"]').attr('match', '$regexp');
93a49e5b
AA
2711 Validate.init();
2712});
2713END
2714 , TRUE);
dec748f6 2715
93a49e5b 2716 startPortlet ('Add new');
10f5133a 2717 printOpFormIntro ('add');
e7b84015
AB
2718 echo '<table border=0 cellpadding=5 cellspacing=0 align=center>';
2719
93a49e5b
AA
2720 // inputs column
2721 $prefix_value = empty ($_REQUEST['set-prefix']) ? '' : $_REQUEST['set-prefix'];
04c61e7e 2722 echo "<th class=tdright>Prefix:</th><td class=tdleft><input type=text name='range' size=36 class='live-validate' value='${prefix_value}'></td>";
e7b84015 2723 echo '<tr><th class=tdright>VLAN:</th><td class=tdleft>';
04c61e7e
AB
2724 echo getOptionTree ('vlan_ck', getAllVLANOptions(), array ('select_class' => 'vertical')) . '</td></tr>';
2725 echo "<tr><th class=tdright>Name:</th><td class=tdleft><input type=text name='name' size='20'></td></tr>";
e7b84015
AB
2726 echo '<tr><th class=tdright>Tags:</th><td class="tdleft">';
2727 printTagsPicker ();
2728 echo '</td></tr>';
38707670
DO
2729 echo '<tr><td class=tdright><input type=checkbox name="is_connected" id="is_connected"></td>';
2730 echo '<th class=tdleft><label for="is_connected">reserve subnet-router anycast address</label></th></tr>';
93a49e5b 2731 echo "<tr><td colspan=2>";
04c61e7e 2732 printImageHREF ('CREATE', 'Add a new network', TRUE);
93a49e5b 2733 echo '</td></tr>';
e7b84015 2734 echo "</table></form><br><br>\n";
93a49e5b 2735 finishPortlet();
e673ee24
DO
2736}
2737
4318ced5 2738function getRenderedIPNetBacktrace ($range)
e673ee24 2739{
4318ced5
AA
2740 if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
2741 return array();
e673ee24 2742
4318ced5
AA
2743 $v = ($range['realm'] == 'ipv4net') ? 4 : 6;
2744 $space = "ipv${v}space"; // ipv4space, ipv6space
2745 $tag = "\$ip${v}netid_"; // $ip4netid_, $ip6netid_
dec748f6 2746
4318ced5
AA
2747 $ret = array();
2748 // Build a backtrace from all parent networks.
2749 $clen = $range['mask'];
2750 $backtrace = array();
2751 $backtrace['&rarr;'] = $range;
2752 $key = '';
2753 while (NULL !== ($upperid = getIPAddressNetworkId ($range['ip_bin'], $clen)))
2754 {
2755 $upperinfo = spotEntity ($range['realm'], $upperid);
2756 $clen = $upperinfo['mask'];
2757 $key .= '&uarr;';
2758 $backtrace[$key] = $upperinfo;
2759 }
2760 foreach (array_reverse ($backtrace) as $arrow => $ainfo)
2761 {
2762 $link = '<a href="' . makeHref (array (
2763 'page' => $space,
2764 'tab' => 'default',
2765 'clear-cf' => '',
2766 'cfe' => '{' . $tag . $ainfo['id'] . '}',
2767 'hl_net' => 1,
2768 'eid' => $range['id'],
2769 )) . '" title="View IP tree with this net as root">' . $arrow . '</a>';
2770 $ret[] = array ($link, getOutputOf ('renderCell', $ainfo));
2771 }
2772 return $ret;
2773}
2774
d10401bf 2775function renderIPNetwork ($id)
4318ced5
AA
2776{
2777 global $pageno;
2778 $realm = $pageno; // 'ipv4net', 'ipv6net'
d10401bf 2779 $range = spotEntity ($realm, $id);
4318ced5 2780 loadIPAddrList ($range);
25526a97 2781 setEntityColors ($range);
2034d968 2782 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
99ab184f
DO
2783 echo "<tr><td colspan=2 align=center><h1>${range['ip']}/${range['mask']}</h1><h2>";
2784 echo htmlspecialchars ($range['name'], ENT_QUOTES, 'UTF-8') . "</h2></td></tr>\n";
2034d968
DO
2785
2786 echo "<tr><td class=pcleft width='50%'>";
646272be
AA
2787
2788 // render summary portlet
2789 $summary = array();
967bd0b1 2790 $summary['% used'] = getRenderedIPNetCapacity ($range);
2b6b0038 2791 $summary = getRenderedIPNetBacktrace ($range) + $summary;
4318ced5 2792 if ($realm == 'ipv4net')
001e61fa 2793 {
4318ced5
AA
2794 $summary[] = array ('Netmask:', ip4_format ($range['mask_bin']));
2795 $summary[] = array ('Netmask:', "0x" . strtoupper (implode ('', unpack ('H*', $range['mask_bin']))));
2796 $summary['Wildcard bits'] = ip4_format ( ~ $range['mask_bin']);
001e61fa 2797 }
dec748f6 2798
11e3af31
DO
2799 $reuse_domain = considerConfiguredConstraint ($range, '8021Q_MULTILINK_LISTSRC');
2800 $domainclass = array();
2801 foreach (array_count_values (reduceSubarraysToColumn ($range['8021q'], 'domain_id')) as $domain_id => $vlan_count)
2802 $domainclass[$domain_id] = $vlan_count == 1 ? '' : ($reuse_domain ? '{trwarning}' : '{trerror}');
a5f9d1ca 2803 foreach ($range['8021q'] as $item)
3f642516 2804 $summary[] = array ($domainclass[$item['domain_id']] . 'VLAN:', formatVLANAsHyperlink (getVlanRow ($item['domain_id'] . '-' . $item['vlan_id'])));
55eefced 2805 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes' && count ($routers = findNetRouters ($range)))
04d619d0 2806 {
646272be
AA
2807 $summary['Routed by'] = '';
2808 foreach ($routers as $rtr)
4318ced5 2809 $summary['Routed by'] .= getOutputOf ('renderRouterCell', $rtr['ip_bin'], $rtr['iface'], spotEntity ('object', $rtr['id']));
04d619d0 2810 }
646272be
AA
2811 $summary['tags'] = '';
2812 renderEntitySummary ($range, 'summary', $summary);
e1ae3fb4 2813
33973968 2814 if ($range['comment'] != '')
99ab184f
DO
2815 {
2816 startPortlet ('Comment');
2817 echo '<div class=commentblock>' . string_insert_hrefs (htmlspecialchars ($range['comment'], ENT_QUOTES, 'UTF-8')) . '</div>';
2818 finishPortlet ();
2819 }
2820
d10401bf 2821 renderFilesPortlet ($realm, $id);
2034d968 2822 echo "</td>\n";
e673ee24 2823
2034d968
DO
2824 echo "<td class=pcright>";
2825 startPortlet ('details');
4318ced5
AA
2826 renderIPNetworkAddresses ($range);
2827 finishPortlet();
2828 echo "</td></tr></table>\n";
2829}
e673ee24 2830
4318ced5
AA
2831// Used solely by renderSeparator
2832function renderEmptyIPv6 ($ip_bin, $hl_ip)
2833{
2834 $class = 'tdleft';
2835 if ($ip_bin === $hl_ip)
5c717ce5 2836 $class .= ' highlight';
4318ced5 2837 $fmt = ip6_format ($ip_bin);
235124a6 2838 echo "<tr class='$class'><td><a name='ip-$fmt' href='" . makeHref (array ('page' => 'ipaddress', 'ip' => $fmt)) . "'>" . $fmt;
0b8d8309
AA
2839 $editable = permitted ('ipaddress', 'properties', 'editAddress')
2840 ? 'editable'
2841 : '';
cc2fa820
AD
2842 echo "</a></td><td><span class='rsvtext $editable id-$fmt op-upd-ip-name'></span></td>";
2843 echo "<td><span class='rsvtext $editable id-$fmt op-upd-ip-comment'></span></td><td>&nbsp;</td></tr>\n";
4318ced5
AA
2844}
2845
2846// Renders empty table line to shrink empty IPv6 address ranges.
2847// If the range consists of single address, renders the address instead of empty line.
2848// Renders address $hl_ip inside the range.
2849// Used solely by renderIPv6NetworkAddresses
2850function renderSeparator ($first, $last, $hl_ip)
2851{
2852 $self = __FUNCTION__;
2853 if (strcmp ($first, $last) > 0)
2854 return;
2855 if ($first == $last)
2856 renderEmptyIPv6 ($first, $hl_ip);
2857 elseif (isset ($hl_ip) && strcmp ($hl_ip, $first) >= 0 && strcmp ($hl_ip, $last) <= 0)
2858 { // $hl_ip is inside the range $first - $last
2859 $self ($first, ip_prev ($hl_ip), $hl_ip);
2860 renderEmptyIPv6 ($hl_ip, $hl_ip);
2861 $self (ip_next ($hl_ip), $last, $hl_ip);
2862 }
2863 else
cc2fa820 2864 echo "<tr><td colspan=4 class=tdleft></td></tr>\n";
4318ced5
AA
2865}
2866
75e7c0c6 2867// calculates page number that contains given $ip (used by renderIPv6NetworkAddresses)
4318ced5
AA
2868function getPageNumOfIPv6 ($list, $ip_bin, $maxperpage)
2869{
7c91a821 2870 if ($maxperpage <= 0 || count ($list) <= $maxperpage)
4318ced5
AA
2871 return 0;
2872 $keys = array_keys ($list);
2873 for ($i = 1; $i <= count ($keys); $i++)
2874 if (strcmp ($keys[$i-1], $ip_bin) >= 0)
2875 return intval ($i / $maxperpage);
2876 return intval (count ($list) / $maxperpage);
2877}
2878
2879function renderIPNetworkAddresses ($range)
2880{
2881 switch (strlen ($range['ip_bin']))
2882 {
2883 case 4: return renderIPv4NetworkAddresses ($range);
2884 case 16: return renderIPv6NetworkAddresses ($range);
2885 default: throw new InvalidArgException ("range['ip_bin']", $range['ip_bin']);
2886 }
2887}
2888
247acd67
KZ
2889function renderIPv4NetworkPageLink ($rangeid, $page, $title)
2890{
2891 global $pageno, $tabno;
2892 return "<a href='".makeHref (array ('page' => $pageno, 'tab' => $tabno, 'id' => $rangeid, 'pg' => $page)) . "' title='".$title."'>".$page."</a> ";
2893}
2894
2895function renderIPv4NetworkPagination ($range, $page, $numpages)
2896{
2897 $rendered_pager = '';
2898 $startip = ip4_bin2int ($range['ip_bin']);
2899 $endip = ip4_bin2int (ip_last ($range));
2900 $rangeid = $range['id'];
2901 // Should make this configurable perhaps
2902 // How many pages before/after current page to show
2903 $prepostpagecount = 8;
2904 // Minimum pages where pagination does not happen
2905 $paginationat = 16; // 16 pages is a /20, 32 is a /19
2906 $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
2907 if ($numpages <= $paginationat)
2908 {
2909 // create original pagination
2910 for ($i = 0; $i < $numpages; $i++)
2911 if ($i == $page)
2912 $rendered_pager .= "<b>".$i."</b> ";
2913 else
2914 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2915 }
2916 else // number of pages > page range, create ranged pagination
2917 {
2918 // page is within first subset
2919 if ($page - $prepostpagecount <= 1)
2920 {
2921 for ($i = 0; $i < $page; $i++)
2922 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2923 }
2924 // render 0 ... [page - prepostpagecount] [page - prepostpagecount + 1] ... [page - 1]
2925 else
2926 {
2927 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, 0, ip4_format(ip4_int2bin($startip)));
2928 $rendered_pager .= "... ";
2929 for ($i = $page - $prepostpagecount; $i < $page; $i++)
2930 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2931 }
2932 // render current page
2933 $rendered_pager .= "<b>".$page."</b> ";
2934 // page is within last subset
2935 if ($page + $prepostpagecount >= $numpages-2)
2936 {
2937 for ($i = $page+1; $i < $numpages; $i++)
2938 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2939 }
2940 // render [page + 1] [page + 2] ... [page + postpagecount] ... [end page]
2941 else
2942 {
2943 for ($i = $page+1; $i <= $page+$prepostpagecount; $i++)
2944 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2945 $rendered_pager .= "... ";
2946 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, ($numpages-1), ip4_format(ip4_int2bin($endip)));
2947 }
2948 }
2949 return $rendered_pager;
2950}
2951
4318ced5
AA
2952function renderIPv4NetworkAddresses ($range)
2953{
9d5f6a80 2954 global $pageno, $tabno, $aac_left;
4318ced5
AA
2955 $startip = ip4_bin2int ($range['ip_bin']);
2956 $endip = ip4_bin2int (ip_last ($range));
2957
2958 if (isset ($_REQUEST['hl_ip']))
00276508 2959 {
4318ced5
AA
2960 $hl_ip = ip4_bin2int (ip4_parse ($_REQUEST['hl_ip']));
2961 addAutoScrollScript ('ip-' . $_REQUEST['hl_ip']); // scroll page to highlighted ip
00276508
AA
2962 }
2963
79b57b18
AA
2964 // pager
2965 $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
2966 $address_count = $endip - $startip + 1;
2967 $page = 0;
446c25c3 2968 $rendered_pager = '';
79b57b18
AA
2969 if ($address_count > $maxperpage && $maxperpage > 0)
2970 {
2971 $page = isset ($_REQUEST['pg']) ? $_REQUEST['pg'] : (isset ($hl_ip) ? intval (($hl_ip - $startip) / $maxperpage) : 0);
2972 if ($numpages = ceil ($address_count / $maxperpage))
2973 {
4318ced5 2974 echo '<h3>' . ip4_format (ip4_int2bin ($startip)) . ' ~ ' . ip4_format (ip4_int2bin ($endip)) . '</h3>';
247acd67 2975 $rendered_pager = renderIPv4NetworkPagination ($range, $page, $numpages);
79b57b18
AA
2976 }
2977 $startip = $startip + $page * $maxperpage;
2978 $endip = min ($startip + $maxperpage - 1, $endip);
2979 }
2980
446c25c3 2981 echo $rendered_pager;
1ce89bdd 2982 echo "<table class='widetable' border=0 cellspacing=0 cellpadding=5 align='center' width='100%'>\n";
5731b2cb
JV
2983
2984 ob_start ();
cc2fa820 2985 echo "<tr><th>Address</th><th>Name</th><th>Comment</th><th>Allocation</th></tr>\n";
5731b2cb
JV
2986 $row_html = ob_get_clean ();
2987 $override = callHook ('renderIPv4NetworkAddressesHeaderRow_hook', $row_html);
2988 echo is_string ($override) ? $override : $row_html;
e673ee24 2989
4318ced5
AA
2990 markupIPAddrList ($range['addrlist']);
2991 for ($ip = $startip; $ip <= $endip; $ip++)
2992 {
2993 $ip_bin = ip4_int2bin ($ip);
2994 $dottedquad = ip4_format ($ip_bin);
5c717ce5 2995 $tr_class = (isset ($hl_ip) && $hl_ip == $ip ? 'highlight' : '');
0cd7c0f6
AA
2996 if (isset ($range['addrlist'][$ip_bin]))
2997 $addr = $range['addrlist'][$ip_bin];
2998 else
e673ee24 2999 {
5731b2cb 3000 ob_start ();
235124a6 3001 echo "<tr class='tdleft $tr_class'><td class=tdleft><a name='ip-$dottedquad' href='" . makeHref(array('page'=>'ipaddress', 'ip' => $dottedquad)) . "'>$dottedquad</a></td>";
0b8d8309
AA
3002 $editable = permitted ('ipaddress', 'properties', 'editAddress')
3003 ? 'editable'
3004 : '';
cc2fa820
AD
3005 echo "<td><span class='rsvtext $editable id-$dottedquad op-upd-ip-name'></span></td>";
3006 echo "<td><span class='rsvtext $editable id-$dottedquad op-upd-ip-comment'></span></td><td></td></tr>\n";
5731b2cb 3007 $row_html = ob_get_clean ();
97d8533e 3008 $override = callHook ('renderIPv4NetworkAddressesRow_hook', $row_html, $ip_bin, null);
5731b2cb 3009 echo is_string ($override) ? $override : $row_html;
b2035dca
DO
3010 continue;
3011 }
68c91b9c
AA
3012 // render IP change history
3013 $title = '';
3014 $history_class = '';
3015 if (isset ($addr['last_log']))
3016 {
3017 $title = ' title="' . htmlspecialchars ($addr['last_log']['user'] . ', ' . formatAge ($addr['last_log']['time']) , ENT_QUOTES) . '"';
3018 $history_class = 'hover-history underline';
3019 }
4318ced5 3020 $tr_class .= ' ' . $addr['class'];
5731b2cb 3021 ob_start ();
4318ced5 3022 echo "<tr class='tdleft $tr_class'>";
235124a6 3023 echo "<td><a class='$history_class' $title name='ip-$dottedquad' href='".makeHref(array('page'=>'ipaddress', 'ip'=>$addr['ip']))."'>${addr['ip']}</a></td>";
0b8d8309 3024 $editable =
cc2fa820 3025 (empty ($addr['allocs']) || !empty ($addr['name']) || !empty ($addr['comment']))
0b8d8309
AA
3026 && permitted ('ipaddress', 'properties', 'editAddress')
3027 ? 'editable'
3028 : '';
cc2fa820
AD
3029 echo "<td><span class='rsvtext $editable id-$dottedquad op-upd-ip-name'>${addr['name']}</span></td>";
3030 echo "<td><span class='rsvtext $editable id-$dottedquad op-upd-ip-comment'>${addr['comment']}</span></td>";
0b8d8309 3031 echo "<td>";
b2035dca 3032 $delim = '';
b2035dca
DO
3033 if ( $addr['reserved'] == 'yes')
3034 {
2d318652 3035 echo "<strong>RESERVED</strong> ";
b2035dca
DO
3036 $delim = '; ';
3037 }
212b565a 3038 foreach ($addr['allocs'] as $ref)
b2035dca 3039 {
9d5f6a80 3040 echo $delim . $aac_left[$ref['type']];
e749a34f 3041 echo makeIPAllocLink ($ip_bin, $ref, TRUE);
b2035dca
DO
3042 $delim = '; ';
3043 }
3044 if ($delim != '')
e0d6e38e 3045 $delim = '<br>';
212b565a
AA
3046 foreach ($addr['vslist'] as $vs_id)
3047 {
3048 $vs = spotEntity ('ipv4vs', $vs_id);
ad3e5e7e 3049 echo $delim . mkA ("${vs['name']}:${vs['vport']}/${vs['proto']}", 'ipv4vs', $vs['id']) . '&rarr;';
212b565a
AA
3050 $delim = '<br>';
3051 }
eacc0983
AA
3052 foreach ($addr['vsglist'] as $vs_id)
3053 {
3054 $vs = spotEntity ('ipvs', $vs_id);
3055 echo $delim . mkA ($vs['name'], 'ipvs', $vs['id']) . '&rarr;';
3056 $delim = '<br>';
3057 }
212b565a
AA
3058 foreach ($addr['rsplist'] as $rsp_id)
3059 {
3060 $rsp = spotEntity ('ipv4rspool', $rsp_id);
ad3e5e7e 3061 echo "${delim}&rarr;" . mkA ($rsp['name'], 'ipv4rspool', $rsp['id']);
212b565a
AA
3062 $delim = '<br>';
3063 }
b2035dca 3064 echo "</td></tr>\n";
5731b2cb 3065 $row_html = ob_get_clean ();
97d8533e 3066 $override = callHook ('renderIPv4NetworkAddressesRow_hook', $row_html, $ip_bin, $addr);
5731b2cb 3067 echo is_string ($override) ? $override : $row_html;
4318ced5 3068 }
2727c7df 3069 // end of iteration
a8dc2982
AA
3070 if (permitted (NULL, NULL, 'set_reserve_comment'))
3071 addJS ('js/inplace-edit.js');
e673ee24
DO
3072
3073 echo "</table>";
b956dd24 3074 if ($rendered_pager != '')
446c25c3 3075 echo '<p>' . $rendered_pager . '</p>';
21ee3351
AA
3076}
3077
3078function renderIPv6NetworkAddresses ($netinfo)
3079{
9d5f6a80 3080 global $pageno, $tabno, $aac_left;
1ce89bdd 3081 echo "<table class='widetable' border=0 cellspacing=0 cellpadding=5 align='center' width='100%'>\n";
5731b2cb 3082 ob_start ();
cc2fa820 3083 echo "<tr><th>Address</th><th>Name</th><th>Comment</th><th>Allocation</th></tr>\n";
5731b2cb
JV
3084 $row_html = ob_get_clean ();
3085 $override = callHook ('renderIPv6NetworkAddressesHeaderRow_hook', $row_html);
3086 echo is_string ($override) ? $override : $row_html;
21ee3351 3087
4318ced5
AA
3088 $hl_ip = NULL;
3089 if (isset ($_REQUEST['hl_ip']))
3090 {
3091 $hl_ip = ip6_parse ($_REQUEST['hl_ip']);
3092 addAutoScrollScript ('ip-' . ip6_format ($hl_ip));
3093 }
21ee3351 3094
21ee3351
AA
3095 $addresses = $netinfo['addrlist'];
3096 ksort ($addresses);
4318ced5 3097 markupIPAddrList ($addresses);
21ee3351
AA
3098
3099 // pager
3100 $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
3101 if (count ($addresses) > $maxperpage && $maxperpage > 0)
3102 {
3103 $page = isset ($_REQUEST['pg']) ? $_REQUEST['pg'] : (isset ($hl_ip) ? getPageNumOfIPv6 ($addresses, $hl_ip, $maxperpage) : 0);
3104 $numpages = ceil (count ($addresses) / $maxperpage);
3105 echo "<center><h3>$numpages pages:</h3>";
3106 for ($i=0; $i<$numpages; $i++)
3107 {
3108 if ($i == $page)
3109 echo "<b>$i</b> ";
3110 else
3111 echo "<a href='" . makeHref (array ('page' => $pageno, 'tab' => $tabno, 'id' => $netinfo['id'], 'pg' => $i)) . "'>$i</a> ";
3112 }
3113 echo "</center>";
3114 }
3115
3116 $i = 0;
dec748f6 3117 $interruped = FALSE;
4318ced5
AA
3118 $prev_ip = ip_prev ($netinfo['ip_bin']);
3119 foreach ($addresses as $ip_bin => $addr)
21ee3351
AA
3120 {
3121 if (isset ($page))
3122 {
3123 ++$i;