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