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