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