reformat installation instructions
[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";
d6409c53 395 renderLocationCheckbox (treeFromList (addTraceToNodes ($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
82fa45a9 579function renderLocationSelectTree ($select_name, $selected_id = NULL)
eda41675 580{
82fa45a9 581 echo "<select name='${select_name}'>";
eda41675 582 echo '<option value=0>-- NONE --</option>';
359ea9f9 583 $locationlist = listCells ('location');
d6409c53 584 foreach (treeFromList (addTraceToNodes ($locationlist)) as $location)
eda41675
AD
585 {
586 echo "<option value=${location['id']} style='font-weight: bold' ";
587 if ($location['id'] == $selected_id )
588 echo ' selected';
589 echo ">${location['name']}</option>";
e3648fa6 590 printLocationChildrenSelectOptions ($location, $selected_id);
eda41675
AD
591 }
592 echo '</select>';
593}
594
e1add254
AD
595function renderRackspaceLocationEditor ()
596{
b090b2da
RQ
597 $js = <<<JSTXT
598 function locationeditor_showselectbox(e) {
599 $(this).load('index.php', {module: 'ajax', ac: 'get-location-select', locationid: this.id});
600 $(this).unbind('mousedown', locationeditor_showselectbox);
601 }
602 $(document).ready(function () {
603 $('select.locationlist-popup').bind('mousedown', locationeditor_showselectbox);
604 });
605JSTXT;
606
607 addJS($js, TRUE );
e1add254
AD
608 function printNewItemTR ()
609 {
e1add254 610 printOpFormIntro ('addLocation');
eda41675 611 echo '<tr><td>';
e1add254 612 printImageHREF ('create', 'Add new location', TRUE);
82fa45a9
DO
613 echo '</td><td>';
614 renderLocationSelectTree ('parent_id');
04c61e7e
AB
615 echo '</td><td><input type=text size=48 name=name></td><td>';
616 printImageHREF ('create', 'Add new location', TRUE);
e1add254
AD
617 echo "</td></tr></form>\n";
618 }
e1add254
AD
619
620 startPortlet ('Locations');
621 echo "<table border=0 cellspacing=0 cellpadding=5 align=center class=widetable>\n";
622 echo "<tr><th>&nbsp;</th><th>Parent</th><th>Name</th><th>&nbsp;</th></tr>\n";
623 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
624 printNewItemTR();
2056822d 625
1e0f13f3 626 $locations = listCells ('location');
d6409c53 627 renderLocationRowForEditor (treeFromList (addTraceToNodes ($locations)));
2056822d 628
e1add254
AD
629 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
630 printNewItemTR();
631 echo "</table><br>\n";
632 finishPortlet();
633}
634
345fd640
AD
635function renderRackspaceRowEditor ()
636{
eda41675 637 function printNewItemTR ()
345fd640
AD
638 {
639 printOpFormIntro ('addRow');
eda41675 640 echo '<tr><td>';
7be51fea 641 printImageHREF ('create', 'Add new row', TRUE);
04eee8d2
VS
642 echo '</td><td>&nbsp;';
643 echo '</td><td>&nbsp;';
82fa45a9
DO
644 echo '</td><td>';
645 renderLocationSelectTree ('location_id');
04c61e7e
AB
646 echo '</td><td><input type=text name=name></td><td>';
647 printImageHREF ('create', 'Add new row', TRUE);
eda41675 648 echo '</td></tr></form>';
345fd640 649 }
d732e47e 650 startPortlet ('Rows');
345fd640 651 echo "<table border=0 cellspacing=0 cellpadding=5 align=center class=widetable>\n";
04eee8d2 652 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 653 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
eda41675 654 printNewItemTR ();
524f8a7b 655 foreach (listCells ('row') as $row_id => $rowInfo)
345fd640 656 {
eda41675 657 echo '<tr><td>';
04eee8d2
VS
658 $rc = $rowInfo['rackc'];
659 $delete_racks_str = $rc ? " and $rc rack(s)" : '';
660 echo getOpLink (array ('op'=>'deleteRow', 'row_id'=>$row_id), '', 'destroy', 'Delete row'.$delete_racks_str, 'need-confirmation');
c40666f5 661 printOpFormIntro ('updateRow', array ('row_id' => $row_id));
eda41675 662 echo '</td><td>';
04eee8d2
VS
663 echo $rc;
664 echo '</td><td>';
665 echo getRowMountsCount ($row_id);
666 echo '</td><td>';
82fa45a9 667 renderLocationSelectTree ('location_id', $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
d10401bf 680function renderRow ($row_id)
e673ee24 681{
e16b666c 682 $rowInfo = getRowInfo ($row_id);
a8efc03e 683 $cellfilter = getCellFilter();
2b16c179 684 $rackList = applyCellFilter ('rack', $cellfilter, $row_id);
138655f5
AD
685
686 $summary = array ();
687 $summary['Name'] = $rowInfo['name'];
688 if ($rowInfo['location_id'])
689 $summary['Location'] = mkA ($rowInfo['location'], 'location', $rowInfo['location_id']);
690 $summary['Racks'] = $rowInfo['count'];
691 $summary['Units'] = $rowInfo['sum'];
692 $summary['% used'] = getProgressBar (getRSUforRow ($rackList));
249c78b2 693 foreach (getAttrValuesSorted ($row_id) as $record)
138655f5
AD
694 if
695 (
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
d10401bf 738function renderEditRowForm ($row_id)
138655f5
AD
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 851 $slotClass[$slot] = 'state_T';
c17a36f0 852 if ($childData['id'] == $hl_obj_id)
0cdfc55c
DO
853 $slotClass[$slot] .= 'h';
854 if ($childData['has_problems'] == 'yes')
855 $slotClass[$slot] .= 'w';
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
d10401bf 1004function renderRackSortForm ($row_id)
42504426 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({
824886b2 1014 url: 'index.php?module=ajax&ac=upd-rack-sort-order&row_id=${row_id}',
b090b2da
RQ
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
78ba6f56 1035function renderNewRackForm()
e673ee24 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
d10401bf 1187function renderEditRackForm ($rack_id)
e673ee24 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
d10401bf 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.
d10401bf 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>';
d10401bf 1711 $object = spotEntity ('object', $object_id);
9f75e165 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 }
d10401bf 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
d10401bf 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 2066// renders two tables: port link status and learned MAC list
d10401bf 2067function renderPortsInfo($object_id)
65e557dd 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*/
d10401bf 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();
6d42599c
AA
2208 $matched_tags = array();
2209 foreach ($allRacksData as $rack)
01205bb6 2210 {
b51c1007 2211 $tag_chain = array_merge ($rack['etags'], $rack['itags']);
6d42599c 2212 foreach ($object['etags'] as $tag)
01205bb6 2213 if (tagOnChain ($tag, $tag_chain))
6d42599c
AA
2214 {
2215 $matching_racks[$rack['id']] = $rack;
2216 $matched_tags[$tag['id']] = $tag;
2217 break;
2218 }
01205bb6 2219 }
6d42599c
AA
2220 // add current object's racks even if they dont match filter
2221 foreach ($workingRacksData as $rack_id => $rack)
2222 if (! isset ($matching_racks[$rack_id]))
2223 $matching_racks[$rack_id] = $rack;
2224 // if matching racks found, and rack list is reduced, show 'show all' link
2225 if (count ($matching_racks) and count ($matching_racks) != count ($allRacksData))
2226 {
2227 $filter_text = '';
2228 foreach ($matched_tags as $tag)
2229 $filter_text .= (empty ($filter_text) ? '' : ' or ') . '{' . $tag['tag'] . '}';
2230 $href_show_all = trim($_SERVER['REQUEST_URI'], '&');
2231 $href_show_all .= htmlspecialchars('&show_all_racks=1');
2232 echo "(filtered by <span class='filter-text'>$filter_text</span>, <a href='$href_show_all'>show all</a>)<p>";
2233 $allRacksData = $matching_racks;
2234 }
2235 }
2236
4b8d413e 2237 if (count ($allRacksData) <= getConfigVar ('RACK_PRESELECT_THRESHOLD'))
61a1d996 2238 foreach ($allRacksData as $rack)
51c18782
DO
2239 if (!array_key_exists ($rack['id'], $workingRacksData))
2240 {
2241 amplifyCell ($rack);
2242 $workingRacksData[$rack['id']] = $rack;
2243 }
2244 foreach (array_keys ($workingRacksData) as $rackId)
2245 applyObjectMountMask ($workingRacksData[$rackId], $object_id);
2246 printOpFormIntro ('updateObjectAllocation');
4b8d413e 2247 renderRackMultiSelect ('rackmulti[]', $allRacksData, array_keys ($workingRacksData));
51c18782 2248 echo "<br><br>";
e673ee24
DO
2249 finishPortlet();
2250 echo "</td>";
2251
2252 // Middle portlet with comment and submit.
2253 echo "<td class=pcleft>";
5c8bf2ba 2254 startPortlet ('Comment (for Rackspace History)');
e673ee24
DO
2255 echo "<textarea name=comment rows=10 cols=40></textarea><br>\n";
2256 echo "<input type=submit value='Save' name=got_atoms>\n";
51c18782 2257 echo "<br><br>";
e673ee24
DO
2258 finishPortlet();
2259 echo "</td>";
2260
2261 // Right portlet with rendered racks. If this form submit is not final, we have to
2262 // reflect the former state of the grid in current form.
2263 echo "<td class=pcright rowspan=2 height='1%'>";
2264 startPortlet ('Working copy');
e8b94513 2265 includeJQueryUI (false);
65e557dd 2266 addJS ('js/racktables.js');
c82023ad 2267 addJS ('js/bulkselector.js');
e673ee24
DO
2268 echo '<table border=0 cellspacing=10 align=center><tr>';
2269 foreach ($workingRacksData as $rack_id => $rackData)
2270 {
c26d97b6 2271 $is_ro = !rackModificationPermitted ($rackData, 'updateObjectAllocation');
e673ee24
DO
2272 // Order is important here: only original allocation is highlighted.
2273 highlightObject ($rackData, $object_id);
2274 markupAtomGrid ($rackData, 'T');
2275 // If we have a form processed, discard user input and show new database
2276 // contents.
c26d97b6 2277 if (!$is_ro && isset ($_REQUEST['rackmulti'][0])) // is an update
e673ee24
DO
2278 mergeGridFormToRack ($rackData);
2279 echo "<td valign=top>";
c26d97b6
VS
2280 echo "<center>\n<h2 style='margin:0px'>${rackData['name']}</h2>\n";
2281 $read_only_text = $is_ro ? '(read-only)' : '&nbsp;';
2282 echo "<p style='color: red; margin-top:0px'>${read_only_text}</p>\n";
e8b94513 2283 echo "<table class=rack id=selectableRack border=0 cellspacing=0 cellpadding=1>\n";
57c69e9c 2284 echo "<tr><th width='10%'>&nbsp;</th>";
bd0bfb76
AD
2285 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
2286 echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
2287 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
c26d97b6 2288 renderAtomGrid ($rackData, $is_ro);
57c69e9c 2289 echo "<tr><th width='10%'>&nbsp;</th>";
bd0bfb76
AD
2290 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
2291 echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
2292 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
0a7feebb
AD
2293 echo "</table>\n<br>\n";
2294 // Determine zero-u checkbox status.
2295 // If form has been submitted, use form data, otherwise use DB data.
c26d97b6 2296 if (!$is_ro && isset($_REQUEST['op']))
0a7feebb
AD
2297 $checked = isset($_REQUEST['zerou_'.$rack_id]) ? 'checked' : '';
2298 else
2299 $checked = in_array($rack_id, $parentRacks) ? 'checked' : '';
c26d97b6
VS
2300 $disabled_text = $is_ro ? ' disabled' : '';
2301 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 2302 echo "<input type='button' onclick='uncheckAll();' value='Uncheck all'>\n";
0a7feebb 2303 echo '</center></td>';
e673ee24
DO
2304 }
2305 echo "</tr></table>";
2306 finishPortlet();
2307 echo "</td>\n";
2308
2309 echo "</form>\n";
2310 echo "</tr></table>\n";
2311}
2312
2313function renderMolecule ($mdata, $object_id)
2314{
2315 // sort data out
2316 $rackpack = array();
2317 global $loclist;
64b95774 2318 foreach ($mdata as $rua)
e673ee24
DO
2319 {
2320 $rack_id = $rua['rack_id'];
2321 $unit_no = $rua['unit_no'];
2322 $atom = $rua['atom'];
2323 if (!isset ($rackpack[$rack_id]))
2324 {
61a1d996
DO
2325 $rackData = spotEntity ('rack', $rack_id);
2326 amplifyCell ($rackData);
e673ee24
DO
2327 for ($i = $rackData['height']; $i > 0; $i--)
2328 for ($locidx = 0; $locidx < 3; $locidx++)
2329 $rackData[$i][$locidx]['state'] = 'F';
2330 $rackpack[$rack_id] = $rackData;
2331 }
2332 $rackpack[$rack_id][$unit_no][$loclist[$atom]]['state'] = 'T';
2333 $rackpack[$rack_id][$unit_no][$loclist[$atom]]['object_id'] = $object_id;
2334 }
2335 // now we have some racks to render
64b95774 2336 foreach ($rackpack as $rackData)
e673ee24
DO
2337 {
2338 markAllSpans ($rackData);
2339 echo "<table class=molecule cellspacing=0>\n";
2340 echo "<caption>${rackData['name']}</caption>\n";
2341 echo "<tr><th width='10%'>&nbsp;</th><th width='20%'>Front</th><th width='50%'>Interior</th><th width='20%'>Back</th></tr>\n";
2342 for ($i = $rackData['height']; $i > 0; $i--)
2343 {
4dcd770e 2344 echo "<tr><th>" . inverseRackUnit ($i, $rackData) . "</th>";
e673ee24
DO
2345 for ($locidx = 0; $locidx < 3; $locidx++)
2346 {
2347 $state = $rackData[$i][$locidx]['state'];
65e557dd 2348 echo "<td class='atom state_${state}'>&nbsp;</td>\n";
e673ee24
DO
2349 }
2350 echo "</tr>\n";
2351 }
2352 echo "</table>\n";
2353 }
2354}
2355
23cdc7e9 2356function renderDepot ()
e673ee24 2357{
23cdc7e9 2358 global $pageno, $nextorder;
23cdc7e9 2359 $cellfilter = getCellFilter();
950dfa1f
AA
2360 $objects = array();
2361 $objects_count = getEntitiesCount ('object');
db8255ac 2362 $showobjecttype = (getConfigVar ('SHOW_OBJECTTYPE') == 'yes');
23cdc7e9
DO
2363
2364 echo "<table border=0 class=objectview>\n";
2365 echo "<tr><td class=pcleft>";
2366
950dfa1f
AA
2367 if ($objects_count == 0)
2368 echo '<h2>No objects exist</h2>';
2369 // 1st attempt: do not fetch all objects if cellfilter is empty and rendering empty result is enabled
2370 elseif (! ($cellfilter['is_empty'] && renderEmptyResults ($cellfilter, 'objects', $objects_count)))
e673ee24 2371 {
2b16c179 2372 $objects = applyCellFilter ('object', $cellfilter);
20506de3 2373 // 2nd attempt: do not render all fetched objects if rendering empty result is enabled
950dfa1f 2374 if (! renderEmptyResults ($cellfilter, 'objects', count($objects)))
1ebbf889 2375 {
4899096d
AD
2376 startPortlet ('Objects (' . count ($objects) . ')');
2377 echo '<br><br><table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
db8255ac
J
2378 echo '<tr><th>Common name</th>';
2379 if ($showobjecttype)
2380 echo '<th>Type</th>';
2381 echo '<th>Visible label</th><th>Asset tag</th><th>Row/Rack or Container</th></tr>';
4899096d 2382 $order = 'odd';
3f8a25d6
DO
2383 # gather IDs of all objects and fetch rackspace info in one pass
2384 $idlist = array();
2385 foreach ($objects as $obj)
2386 $idlist[] = $obj['id'];
2387 $mountinfo = getMountInfo ($idlist);
cae81598 2388 $containerinfo = getContainerInfo ($idlist);
4899096d
AD
2389 foreach ($objects as $obj)
2390 {
ec788aba
AD
2391 $problem = ($obj['has_problems'] == 'yes') ? 'has_problems' : '';
2392 echo "<tr class='row_${order} tdleft ${problem}' valign=top><td>" . mkA ("<strong>${obj['dname']}</strong>", 'object', $obj['id']);
4899096d
AD
2393 if (count ($obj['etags']))
2394 echo '<br><small>' . serializeTags ($obj['etags'], makeHref(array('page'=>$pageno, 'tab'=>'default')) . '&') . '</small>';
db8255ac
J
2395 echo '</td>';
2396 if ($showobjecttype)
9563f56f 2397 echo "<td>" . decodeObjectType ($obj['objtype_id']) . "</td>";
8526b4cc 2398 echo "<td>${obj['label']}</td>";
4318ced5 2399 echo "<td>${obj['asset_no']}</td>";
3f8a25d6 2400 $places = array();
cae81598
AD
2401 if (array_key_exists ($obj['id'], $containerinfo))
2402 foreach ($containerinfo[$obj['id']] as $ci)
20506de3 2403 $places[] = mkA ($ci['container_dname'], 'object', $ci['container_id']);
cae81598 2404 if (array_key_exists ($obj['id'], $mountinfo))
3f8a25d6
DO
2405 foreach ($mountinfo[$obj['id']] as $mi)
2406 $places[] = mkA ($mi['row_name'], 'row', $mi['row_id']) . '/' . mkA ($mi['rack_name'], 'rack', $mi['rack_id']);
cae81598
AD
2407 if (! count ($places))
2408 $places[] = 'Unmounted';
4318ced5 2409 echo "<td>" . implode (', ', $places) . '</td>';
4899096d
AD
2410 echo '</tr>';
2411 $order = $nextorder[$order];
2412 }
2413 echo '</table>';
2414 finishPortlet();
1ebbf889 2415 }
e673ee24 2416 }
23cdc7e9
DO
2417
2418 echo "</td><td class=pcright width='25%'>";
2419
95857b5c 2420 renderCellFilterPortlet ($cellfilter, 'object', $objects);
23cdc7e9 2421 echo "</td></tr></table>\n";
e673ee24
DO
2422}
2423
1ebbf889
AA
2424// This function returns TRUE if the result set is too big to be rendered, and no filter is set.
2425// In this case it renders the describing message instead.
2426function renderEmptyResults($cellfilter, $entities_name, $count = NULL)
2427{
2428 if (!$cellfilter['is_empty'])
2429 return FALSE;
2430 if (isset ($_REQUEST['show_all_objects']))
2431 return FALSE;
2432 $max = intval(getConfigVar('MAX_UNFILTERED_ENTITIES'));
2433 if (0 == $max || $count <= $max)
2434 return FALSE;
2435
2436 $href_show_all = trim($_SERVER['REQUEST_URI'], '&');
2437 $href_show_all .= htmlspecialchars('&show_all_objects=1');
2438 $suffix = isset ($count) ? " ($count)" : '';
2439 echo <<<END
2440<p>Please set a filter to display the corresponging $entities_name.
2441<br><a href="$href_show_all">Show all $entities_name$suffix</a>
2442END;
2443 return TRUE;
2444}
2445
e673ee24 2446// History viewer for history-enabled simple dictionaries.
9b8174d7 2447function renderObjectHistory ($object_id)
e673ee24 2448{
e673ee24
DO
2449 $order = 'odd';
2450 global $nextorder;
9b8174d7
AD
2451 echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
2452 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>';
2453 $result = usePreparedSelectBlade
2454 (
2455 'SELECT ctime, user_name, name, label, asset_no, has_problems, comment FROM ObjectHistory WHERE id=? ORDER BY ctime',
2456 array ($object_id)
2457 );
e673ee24
DO
2458 while ($row = $result->fetch (PDO::FETCH_NUM))
2459 {
2460 echo "<tr class=row_${order}><td>${row[0]}</td>";
9b8174d7 2461 for ($i = 1; $i <= 6; $i++)
e673ee24
DO
2462 echo "<td>" . $row[$i] . "</td>";
2463 echo "</tr>\n";
2464 $order = $nextorder[$order];
2465 }
2466 echo "</table><br>\n";
2467}
2468
2469function renderRackspaceHistory ()
2470{
4fbb5a00 2471 global $nextorder, $pageno, $tabno;
e673ee24
DO
2472 $order = 'odd';
2473 $history = getRackspaceHistory();
2474 // Show the last operation by default.
2475 if (isset ($_REQUEST['op_id']))
2476 $op_id = $_REQUEST['op_id'];
2477 elseif (isset ($history[0]['mo_id']))
2478 $op_id = $history[0]['mo_id'];
2479 else $op_id = NULL;
dec748f6 2480
e673ee24
DO
2481 $omid = NULL;
2482 $nmid = NULL;
2483 $object_id = 1;
2484 if ($op_id)
2485 list ($omid, $nmid) = getOperationMolecules ($op_id);
2486
2487 // Main layout starts.
2488 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
2489
2490 // Left top portlet with old allocation.
2491 echo "<tr><td class=pcleft>";
2492 startPortlet ('Old allocation');
2493 if ($omid)
2494 {
2495 $oldMolecule = getMolecule ($omid);
2496 renderMolecule ($oldMolecule, $object_id);
2497 }
2498 else
2499 echo "nothing";
2500 finishPortlet();
2501
2502 echo '</td><td class=pcright>';
2503
2504 // Right top portlet with new allocation
2505 startPortlet ('New allocation');
2506 if ($nmid)
2507 {
2508 $newMolecule = getMolecule ($nmid);
2509 renderMolecule ($newMolecule, $object_id);
2510 }
2511 else
2512 echo "nothing";
2513 finishPortlet();
737a3f72 2514
e673ee24 2515 echo '</td></tr><tr><td colspan=2>';
737a3f72 2516
e673ee24
DO
2517 // Bottom portlet with list
2518
2519 startPortlet ('Rackspace allocation history');
2520 echo "<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>\n";
7fa7047a 2521 echo "<tr><th>timestamp</th><th>author</th><th>object</th><th>comment</th></tr>\n";
e673ee24
DO
2522 foreach ($history as $row)
2523 {
2524 if ($row['mo_id'] == $op_id)
2525 $class = 'hl';
2526 else
2527 $class = "row_${order}";
4fbb5a00 2528 echo "<tr class=${class}><td><a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'op_id'=>$row['mo_id']))."'>${row['ctime']}</a></td>";
7fa7047a
DO
2529 echo "<td>${row['user_name']}</td><td>";
2530 renderCell (spotEntity ('object', $row['ro_id']));
a0bdf6dc 2531 echo '</td><td>' . stringForTD ($row['comment'], 0) . '</td></tr>';
e673ee24
DO
2532 $order = $nextorder[$order];
2533 }
2534 echo "</table>\n";
2535 finishPortlet();
737a3f72 2536
e673ee24 2537 echo '</td></tr></table>';
e673ee24
DO
2538}
2539
4318ced5 2540function renderIPSpaceRecords ($tree, $baseurl, $target = 0, $level = 1)
d65353ad
DO
2541{
2542 $self = __FUNCTION__;
4318ced5 2543 $knight = (getConfigVar ('IPV4_ENABLE_KNIGHT') == 'yes');
00276508
AA
2544
2545 // scroll page to the highlighted item
2546 if ($target && isset ($_REQUEST['hl_net']))
65e557dd 2547 addAutoScrollScript ("net-$target");
00276508 2548
d65353ad
DO
2549 foreach ($tree as $item)
2550 {
6063fab7 2551 $display_routers = (getConfigVar ('IPV4_TREE_RTR_AS_CELL') != 'none');
4318ced5 2552
0137d53c
DO
2553 if (isset ($item['id']))
2554 {
1f54e1ba 2555 $decor = array ('indent' => $level);
fec0c8da 2556 if ($item['symbol'] == 'node-collapsed')
4318ced5 2557 $decor['symbolurl'] = "${baseurl}&eid=${item['id']}&hl_net=1";
fec0c8da 2558 elseif ($item['symbol'] == 'node-expanded')
4318ced5
AA
2559 $decor['symbolurl'] = $baseurl . ($item['parent_id'] ? "&eid=${item['parent_id']}&hl_net=1" : '');
2560 $tr_class = '';
21ee3351 2561 if ($target == $item['id'] && isset ($_REQUEST['hl_net']))
64347dcf 2562 {
5c717ce5 2563 $decor['tdclass'] = ' highlight';
4318ced5 2564 $tr_class = $decor['tdclass'];
64347dcf 2565 }
4318ced5
AA
2566 echo "<tr valign=top class=\"$tr_class\">";
2567 printIPNetInfoTDs ($item, $decor);
2568
2569 // capacity and usage
2570 echo "<td class=tdcenter>";
2571 echo getRenderedIPNetCapacity ($item);
64347dcf 2572 echo "</td>";
4318ced5
AA
2573
2574 if ($display_routers)
6063fab7 2575 printRoutersTD (findNetRouters ($item), getConfigVar ('IPV4_TREE_RTR_AS_CELL'));
0137d53c 2576 echo "</tr>";
fec0c8da 2577 if ($item['symbol'] == 'node-expanded' or $item['symbol'] == 'node-expanded-static')
4318ced5 2578 $self ($item['kids'], $baseurl, $target, $level + 1);
0137d53c 2579 }
26fc6e7c 2580 elseif (getConfigVar ('IPV4_TREE_SHOW_UNALLOCATED') == 'yes')
0137d53c 2581 {
4318ced5 2582 // non-allocated (spare) IP range
0137d53c 2583 echo "<tr valign=top>";
21ee3351 2584 printIPNetInfoTDs ($item, array ('indent' => $level, 'knight' => $knight, 'tdclass' => 'sparenetwork'));
00276508 2585
4318ced5 2586 // capacity and usage
21ee3351 2587 echo "<td class=tdcenter>";
4318ced5 2588 echo getRenderedIPNetCapacity ($item);
21ee3351 2589 echo "</td>";
4318ced5
AA
2590 if ($display_routers)
2591 echo "<td></td>";
21ee3351 2592 echo "</tr>";
21ee3351 2593 }
21ee3351
AA
2594 }
2595}
2596
4318ced5 2597function renderIPSpace()
e673ee24 2598{
4fbb5a00 2599 global $pageno, $tabno;
4318ced5 2600 $realm = ($pageno == 'ipv4space' ? 'ipv4net' : 'ipv6net');
31c941ec 2601 $cellfilter = getCellFilter();
21ee3351 2602
b99b4167
AA
2603 // expand request can take either natural values or "ALL". Zero means no expanding.
2604 $eid = isset ($_REQUEST['eid']) ? $_REQUEST['eid'] : 0;
2605
21ee3351
AA
2606 echo "<table border=0 class=objectview>\n";
2607 echo "<tr><td class=pcleft>";
950dfa1f
AA
2608
2609 $netlist = array();
b99b4167 2610 if (! ($cellfilter['is_empty'] && ! $eid && renderEmptyResults($cellfilter, 'IP nets', getEntitiesCount ($realm))))
21ee3351 2611 {
950dfa1f
AA
2612 $top = NULL;
2613 foreach (listCells ($realm) as $net)
21ee3351 2614 {
950dfa1f
AA
2615 if (isset ($top) and IPNetContains ($top, $net))
2616 ;
2617 elseif (! count ($cellfilter['expression']) or judgeCell ($net, $cellfilter['expression']))
2618 $top = $net;
2619 else
2620 continue;
2621 $netlist[$net['id']] = $net;
2622 }
2623 $netcount = count ($netlist);
950dfa1f
AA
2624 $tree = prepareIPTree ($netlist, $eid);
2625
2626 if (! renderEmptyResults($cellfilter, 'IP nets', count($tree)))
2627 {
2628 startPortlet ("networks (${netcount})");
2629 echo '<h4>';
2630 $all = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'ALL')) .
2631 $cellfilter['urlextra'] . "'>expand&nbsp;all</a>";
2632 $none = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'NONE')) .
2633 $cellfilter['urlextra'] . "'>collapse&nbsp;all</a>";
2634 $auto = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno)) .
2635 $cellfilter['urlextra'] . "'>auto-collapse</a>";
2636
2637 if ($eid === 0)
2638 echo 'auto-collapsing at threshold ' . getConfigVar ('TREE_THRESHOLD') . " ($all / $none)";
2639 elseif ($eid === 'ALL')
2640 echo "expanding all ($auto / $none)";
2641 elseif ($eid === 'NONE')
2642 echo "collapsing all ($all / $auto)";
2643 else
2644 {
a0f0c0c7
AA
2645 try
2646 {
2647 $netinfo = spotEntity ($realm, $eid);
2648 echo "expanding ${netinfo['ip']}/${netinfo['mask']} ($auto / $all / $none)";
2649 }
2650 catch (EntityNotFoundException $e)
2651 {
2652 // ignore invalid eid error
2653 }
950dfa1f
AA
2654 }
2655 echo "</h4><table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
2656 echo "<tr><th>prefix</th><th>name/tags</th><th>capacity</th>";
2657 if (getConfigVar ('IPV4_TREE_RTR_AS_CELL') != 'none')
2658 echo "<th>routed by</th>";
2659 echo "</tr>\n";
2660 $baseurl = makeHref(array('page'=>$pageno, 'tab'=>$tabno)) . $cellfilter['urlextra'];
2661 renderIPSpaceRecords ($tree, $baseurl, $eid);
2662 echo "</table>\n";
2663 finishPortlet();
21ee3351 2664 }
21ee3351
AA
2665 }
2666
2667 echo '</td><td class=pcright>';
950dfa1f 2668 renderCellFilterPortlet ($cellfilter, $realm, $netlist);
21ee3351
AA
2669 echo "</td></tr></table>\n";
2670}
2671
4318ced5 2672function renderIPSpaceEditor()
21ee3351 2673{
4318ced5
AA
2674 global $pageno;
2675 $realm = ($pageno == 'ipv4space' ? 'ipv4net' : 'ipv6net');
2676 $net_page = $realm; // 'ipv4net', 'ipv6net'
2677 $addrspaceList = listCells ($realm);
93a49e5b
AA
2678 startPortlet ('Manage existing (' . count ($addrspaceList) . ')');
2679 if (count ($addrspaceList))
21ee3351 2680 {
21ee3351
AA
2681 echo "<table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
2682 echo "<tr><th>&nbsp;</th><th>prefix</th><th>name</th><th>capacity</th></tr>";
21ee3351
AA
2683 foreach ($addrspaceList as $netinfo)
2684 {
21ee3351 2685 echo "<tr valign=top><td>";
93a49e5b 2686 if (! isIPNetworkEmpty ($netinfo))
21ee3351
AA
2687 printImageHREF ('nodestroy', 'There are ' . count ($netinfo['addrlist']) . ' allocations inside');
2688 else
93a49e5b 2689 echo getOpLink (array ('op' => 'del', 'id' => $netinfo['id']), '', 'destroy', 'Delete this prefix');
ad3e5e7e 2690 echo '</td><td class=tdleft>' . mkA ("${netinfo['ip']}/${netinfo['mask']}", $net_page, $netinfo['id']) . '</td>';
a0bdf6dc 2691 echo '<td class=tdleft>' . stringForTD ($netinfo['name']);
1f54e1ba
DO
2692 if (count ($netinfo['etags']))
2693 echo '<br><small>' . serializeTags ($netinfo['etags']) . '</small>';
2694 echo '</td><td>';
4318ced5 2695 echo getRenderedIPNetCapacity ($netinfo);
b1e39212 2696 echo '</tr>';
e673ee24 2697 }
04d619d0
DO
2698 echo "</table>";
2699 finishPortlet();
e673ee24 2700 }
93a49e5b
AA
2701}
2702
2703function renderIPNewNetForm ()
2704{
2705 global $pageno;
2706 if ($pageno == 'ipv6space')
2707 {
2708 $realm = 'ipv6net';
3495ec6e 2709 $regexp = '^[a-fA-F0-9:]*:[a-fA-F0-9:\.]*/\d{1,3}$';
93a49e5b
AA
2710 }
2711 else
2712 {
2713 $realm = 'ipv4net';
3495ec6e 2714 $regexp = '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$';
93a49e5b
AA
2715 }
2716
2717 // IP prefix validator
10f5133a 2718 addJS ('js/live_validation.js');
93a49e5b 2719 $regexp = addslashes ($regexp);
10f5133a 2720 addJS (<<<END
93a49e5b 2721$(document).ready(function () {
b895e6b4 2722 $('form#add input[name="range"]').attr('match', '$regexp');
93a49e5b
AA
2723 Validate.init();
2724});
2725END
2726 , TRUE);
dec748f6 2727
93a49e5b 2728 startPortlet ('Add new');
10f5133a 2729 printOpFormIntro ('add');
e7b84015
AB
2730 echo '<table border=0 cellpadding=5 cellspacing=0 align=center>';
2731
93a49e5b
AA
2732 // inputs column
2733 $prefix_value = empty ($_REQUEST['set-prefix']) ? '' : $_REQUEST['set-prefix'];
04c61e7e 2734 echo "<th class=tdright>Prefix:</th><td class=tdleft><input type=text name='range' size=36 class='live-validate' value='${prefix_value}'></td>";
e7b84015 2735 echo '<tr><th class=tdright>VLAN:</th><td class=tdleft>';
04c61e7e
AB
2736 echo getOptionTree ('vlan_ck', getAllVLANOptions(), array ('select_class' => 'vertical')) . '</td></tr>';
2737 echo "<tr><th class=tdright>Name:</th><td class=tdleft><input type=text name='name' size='20'></td></tr>";
e7b84015
AB
2738 echo '<tr><th class=tdright>Tags:</th><td class="tdleft">';
2739 printTagsPicker ();
2740 echo '</td></tr>';
04c61e7e 2741 echo '<tr><td class=tdright><input type=checkbox name="is_connected"></td><th class=tdleft>reserve subnet-router anycast address</th></tr>';
93a49e5b 2742 echo "<tr><td colspan=2>";
04c61e7e 2743 printImageHREF ('CREATE', 'Add a new network', TRUE);
93a49e5b 2744 echo '</td></tr>';
e7b84015 2745 echo "</table></form><br><br>\n";
93a49e5b 2746 finishPortlet();
e673ee24
DO
2747}
2748
4318ced5 2749function getRenderedIPNetBacktrace ($range)
e673ee24 2750{
4318ced5
AA
2751 if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
2752 return array();
e673ee24 2753
4318ced5
AA
2754 $v = ($range['realm'] == 'ipv4net') ? 4 : 6;
2755 $space = "ipv${v}space"; // ipv4space, ipv6space
2756 $tag = "\$ip${v}netid_"; // $ip4netid_, $ip6netid_
dec748f6 2757
4318ced5
AA
2758 $ret = array();
2759 // Build a backtrace from all parent networks.
2760 $clen = $range['mask'];
2761 $backtrace = array();
2762 $backtrace['&rarr;'] = $range;
2763 $key = '';
2764 while (NULL !== ($upperid = getIPAddressNetworkId ($range['ip_bin'], $clen)))
2765 {
2766 $upperinfo = spotEntity ($range['realm'], $upperid);
2767 $clen = $upperinfo['mask'];
2768 $key .= '&uarr;';
2769 $backtrace[$key] = $upperinfo;
2770 }
2771 foreach (array_reverse ($backtrace) as $arrow => $ainfo)
2772 {
2773 $link = '<a href="' . makeHref (array (
2774 'page' => $space,
2775 'tab' => 'default',
2776 'clear-cf' => '',
2777 'cfe' => '{' . $tag . $ainfo['id'] . '}',
2778 'hl_net' => 1,
2779 'eid' => $range['id'],
2780 )) . '" title="View IP tree with this net as root">' . $arrow . '</a>';
2781 $ret[] = array ($link, getOutputOf ('renderCell', $ainfo));
2782 }
2783 return $ret;
2784}
2785
d10401bf 2786function renderIPNetwork ($id)
4318ced5
AA
2787{
2788 global $pageno;
2789 $realm = $pageno; // 'ipv4net', 'ipv6net'
d10401bf 2790 $range = spotEntity ($realm, $id);
4318ced5 2791 loadIPAddrList ($range);
2034d968 2792 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
99ab184f
DO
2793 echo "<tr><td colspan=2 align=center><h1>${range['ip']}/${range['mask']}</h1><h2>";
2794 echo htmlspecialchars ($range['name'], ENT_QUOTES, 'UTF-8') . "</h2></td></tr>\n";
2034d968
DO
2795
2796 echo "<tr><td class=pcleft width='50%'>";
646272be
AA
2797
2798 // render summary portlet
2799 $summary = array();
4318ced5 2800 $summary['%% used'] = getRenderedIPNetCapacity ($range);
2b6b0038 2801 $summary = getRenderedIPNetBacktrace ($range) + $summary;
4318ced5 2802 if ($realm == 'ipv4net')
001e61fa 2803 {
4318ced5
AA
2804 $summary[] = array ('Netmask:', ip4_format ($range['mask_bin']));
2805 $summary[] = array ('Netmask:', "0x" . strtoupper (implode ('', unpack ('H*', $range['mask_bin']))));
2806 $summary['Wildcard bits'] = ip4_format ( ~ $range['mask_bin']);
001e61fa 2807 }
dec748f6 2808
11e3af31
DO
2809 $reuse_domain = considerConfiguredConstraint ($range, '8021Q_MULTILINK_LISTSRC');
2810 $domainclass = array();
2811 foreach (array_count_values (reduceSubarraysToColumn ($range['8021q'], 'domain_id')) as $domain_id => $vlan_count)
2812 $domainclass[$domain_id] = $vlan_count == 1 ? '' : ($reuse_domain ? '{trwarning}' : '{trerror}');
a5f9d1ca 2813 foreach ($range['8021q'] as $item)
3f642516 2814 $summary[] = array ($domainclass[$item['domain_id']] . 'VLAN:', formatVLANAsHyperlink (getVlanRow ($item['domain_id'] . '-' . $item['vlan_id'])));
6063fab7 2815 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes' and count ($routers = findNetRouters ($range)))
04d619d0 2816 {
646272be
AA
2817 $summary['Routed by'] = '';
2818 foreach ($routers as $rtr)
4318ced5 2819 $summary['Routed by'] .= getOutputOf ('renderRouterCell', $rtr['ip_bin'], $rtr['iface'], spotEntity ('object', $rtr['id']));
04d619d0 2820 }
646272be
AA
2821 $summary['tags'] = '';
2822 renderEntitySummary ($range, 'summary', $summary);
e1ae3fb4 2823
99ab184f
DO
2824 if (strlen ($range['comment']))
2825 {
2826 startPortlet ('Comment');
2827 echo '<div class=commentblock>' . string_insert_hrefs (htmlspecialchars ($range['comment'], ENT_QUOTES, 'UTF-8')) . '</div>';
2828 finishPortlet ();
2829 }
2830
d10401bf 2831 renderFilesPortlet ($realm, $id);
2034d968 2832 echo "</td>\n";
e673ee24 2833
2034d968
DO
2834 echo "<td class=pcright>";
2835 startPortlet ('details');
4318ced5
AA
2836 renderIPNetworkAddresses ($range);
2837 finishPortlet();
2838 echo "</td></tr></table>\n";
2839}
e673ee24 2840
4318ced5
AA
2841// Used solely by renderSeparator
2842function renderEmptyIPv6 ($ip_bin, $hl_ip)
2843{
2844 $class = 'tdleft';
2845 if ($ip_bin === $hl_ip)
5c717ce5 2846 $class .= ' highlight';
4318ced5 2847 $fmt = ip6_format ($ip_bin);
235124a6 2848 echo "<tr class='$class'><td><a name='ip-$fmt' href='" . makeHref (array ('page' => 'ipaddress', 'ip' => $fmt)) . "'>" . $fmt;
0b8d8309
AA
2849 $editable = permitted ('ipaddress', 'properties', 'editAddress')
2850 ? 'editable'
2851 : '';
cc2fa820
AD
2852 echo "</a></td><td><span class='rsvtext $editable id-$fmt op-upd-ip-name'></span></td>";
2853 echo "<td><span class='rsvtext $editable id-$fmt op-upd-ip-comment'></span></td><td>&nbsp;</td></tr>\n";
4318ced5
AA
2854}
2855
2856// Renders empty table line to shrink empty IPv6 address ranges.
2857// If the range consists of single address, renders the address instead of empty line.
2858// Renders address $hl_ip inside the range.
2859// Used solely by renderIPv6NetworkAddresses
2860function renderSeparator ($first, $last, $hl_ip)
2861{
2862 $self = __FUNCTION__;
2863 if (strcmp ($first, $last) > 0)
2864 return;
2865 if ($first == $last)
2866 renderEmptyIPv6 ($first, $hl_ip);
2867 elseif (isset ($hl_ip) && strcmp ($hl_ip, $first) >= 0 && strcmp ($hl_ip, $last) <= 0)
2868 { // $hl_ip is inside the range $first - $last
2869 $self ($first, ip_prev ($hl_ip), $hl_ip);
2870 renderEmptyIPv6 ($hl_ip, $hl_ip);
2871 $self (ip_next ($hl_ip), $last, $hl_ip);
2872 }
2873 else
cc2fa820 2874 echo "<tr><td colspan=4 class=tdleft></td></tr>\n";
4318ced5
AA
2875}
2876
75e7c0c6 2877// calculates page number that contains given $ip (used by renderIPv6NetworkAddresses)
4318ced5
AA
2878function getPageNumOfIPv6 ($list, $ip_bin, $maxperpage)
2879{
2880 if (intval ($maxperpage) <= 0 || count ($list) <= $maxperpage)
2881 return 0;
2882 $keys = array_keys ($list);
2883 for ($i = 1; $i <= count ($keys); $i++)
2884 if (strcmp ($keys[$i-1], $ip_bin) >= 0)
2885 return intval ($i / $maxperpage);
2886 return intval (count ($list) / $maxperpage);
2887}
2888
2889function renderIPNetworkAddresses ($range)
2890{
2891 switch (strlen ($range['ip_bin']))
2892 {
2893 case 4: return renderIPv4NetworkAddresses ($range);
2894 case 16: return renderIPv6NetworkAddresses ($range);
2895 default: throw new InvalidArgException ("range['ip_bin']", $range['ip_bin']);
2896 }
2897}
2898
247acd67
KZ
2899function renderIPv4NetworkPageLink ($rangeid, $page, $title)
2900{
2901 global $pageno, $tabno;
2902 return "<a href='".makeHref (array ('page' => $pageno, 'tab' => $tabno, 'id' => $rangeid, 'pg' => $page)) . "' title='".$title."'>".$page."</a> ";
2903}
2904
2905function renderIPv4NetworkPagination ($range, $page, $numpages)
2906{
2907 $rendered_pager = '';
2908 $startip = ip4_bin2int ($range['ip_bin']);
2909 $endip = ip4_bin2int (ip_last ($range));
2910 $rangeid = $range['id'];
2911 // Should make this configurable perhaps
2912 // How many pages before/after current page to show
2913 $prepostpagecount = 8;
2914 // Minimum pages where pagination does not happen
2915 $paginationat = 16; // 16 pages is a /20, 32 is a /19
2916 $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
2917 if ($numpages <= $paginationat)
2918 {
2919 // create original pagination
2920 for ($i = 0; $i < $numpages; $i++)
2921 if ($i == $page)
2922 $rendered_pager .= "<b>".$i."</b> ";
2923 else
2924 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2925 }
2926 else // number of pages > page range, create ranged pagination
2927 {
2928 // page is within first subset
2929 if ($page - $prepostpagecount <= 1)
2930 {
2931 for ($i = 0; $i < $page; $i++)
2932 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2933 }
2934 // render 0 ... [page - prepostpagecount] [page - prepostpagecount + 1] ... [page - 1]
2935 else
2936 {
2937 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, 0, ip4_format(ip4_int2bin($startip)));
2938 $rendered_pager .= "... ";
2939 for ($i = $page - $prepostpagecount; $i < $page; $i++)
2940 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2941 }
2942 // render current page
2943 $rendered_pager .= "<b>".$page."</b> ";
2944 // page is within last subset
2945 if ($page + $prepostpagecount >= $numpages-2)
2946 {
2947 for ($i = $page+1; $i < $numpages; $i++)
2948 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2949 }
2950 // render [page + 1] [page + 2] ... [page + postpagecount] ... [end page]
2951 else
2952 {
2953 for ($i = $page+1; $i <= $page+$prepostpagecount; $i++)
2954 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2955 $rendered_pager .= "... ";
2956 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, ($numpages-1), ip4_format(ip4_int2bin($endip)));
2957 }
2958 }
2959 return $rendered_pager;
2960}
2961
4318ced5
AA
2962function renderIPv4NetworkAddresses ($range)
2963{
9d5f6a80 2964 global $pageno, $tabno, $aac_left;
4318ced5
AA
2965 $startip = ip4_bin2int ($range['ip_bin']);
2966 $endip = ip4_bin2int (ip_last ($range));
2967
2968 if (isset ($_REQUEST['hl_ip']))
00276508 2969 {
4318ced5
AA
2970 $hl_ip = ip4_bin2int (ip4_parse ($_REQUEST['hl_ip']));
2971 addAutoScrollScript ('ip-' . $_REQUEST['hl_ip']); // scroll page to highlighted ip
00276508
AA
2972 }
2973
79b57b18
AA
2974 // pager
2975 $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
2976 $address_count = $endip - $startip + 1;
2977 $page = 0;
446c25c3 2978 $rendered_pager = '';
79b57b18
AA
2979 if ($address_count > $maxperpage && $maxperpage > 0)
2980 {
2981 $page = isset ($_REQUEST['pg']) ? $_REQUEST['pg'] : (isset ($hl_ip) ? intval (($hl_ip - $startip) / $maxperpage) : 0);
2982 if ($numpages = ceil ($address_count / $maxperpage))
2983 {
4318ced5 2984 echo '<h3>' . ip4_format (ip4_int2bin ($startip)) . ' ~ ' . ip4_format (ip4_int2bin ($endip)) . '</h3>';
247acd67 2985 $rendered_pager = renderIPv4NetworkPagination ($range, $page, $numpages);
79b57b18
AA
2986 }
2987 $startip = $startip + $page * $maxperpage;
2988 $endip = min ($startip + $maxperpage - 1, $endip);
2989 }
2990
446c25c3 2991 echo $rendered_pager;
beb9e88a 2992 echo "<table class='widetable' border=0 cellspacing=0 cellpadding=5 align='center' width='100%'>\n";
cc2fa820 2993 echo "<tr><th>Address</th><th>Name</th><th>Comment</th><th>Allocation</th></tr>\n";
e673ee24 2994
4318ced5
AA
2995 markupIPAddrList ($range['addrlist']);
2996 for ($ip = $startip; $ip <= $endip; $ip++)
2997 {
2998 $ip_bin = ip4_int2bin ($ip);
2999 $dottedquad = ip4_format ($ip_bin);
5c717ce5 3000 $tr_class = (isset ($hl_ip) && $hl_ip == $ip ? 'highlight' : '');
0cd7c0f6
AA
3001 if (isset ($range['addrlist'][$ip_bin]))
3002 $addr = $range['addrlist'][$ip_bin];
3003 else
e673ee24 3004 {
235124a6 3005 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
3006 $editable = permitted ('ipaddress', 'properties', 'editAddress')
3007 ? 'editable'
3008 : '';
cc2fa820
AD
3009 echo "<td><span class='rsvtext $editable id-$dottedquad op-upd-ip-name'></span></td>";
3010 echo "<td><span class='rsvtext $editable id-$dottedquad op-upd-ip-comment'></span></td><td></td></tr>\n";
b2035dca
DO
3011 continue;
3012 }
68c91b9c
AA
3013 // render IP change history
3014 $title = '';
3015 $history_class = '';
3016 if (isset ($addr['last_log']))
3017 {
3018 $title = ' title="' . htmlspecialchars ($addr['last_log']['user'] . ', ' . formatAge ($addr['last_log']['time']) , ENT_QUOTES) . '"';
3019 $history_class = 'hover-history underline';
3020 }
4318ced5
AA
3021 $tr_class .= ' ' . $addr['class'];
3022 echo "<tr class='tdleft $tr_class'>";
235124a6 3023 echo "<td><a class='$history_class' $title name='ip-$dottedquad' href='".makeHref(array('page'=>'ipaddress', 'ip'=>$addr['ip']))."'>${addr['ip']}</a></td>";
0b8d8309 3024 $editable =
cc2fa820 3025 (empty ($addr['allocs']) || !empty ($addr['name']) || !empty ($addr['comment']))
0b8d8309
AA
3026 && permitted ('ipaddress', 'properties', 'editAddress')
3027 ? 'editable'
3028 : '';
cc2fa820
AD
3029 echo "<td><span class='rsvtext $editable id-$dottedquad op-upd-ip-name'>${addr['name']}</span></td>";
3030 echo "<td><span class='rsvtext $editable id-$dottedquad op-upd-ip-comment'>${addr['comment']}</span></td>";
0b8d8309 3031 echo "<td>";
b2035dca 3032 $delim = '';
b2035dca
DO
3033 if ( $addr['reserved'] == 'yes')
3034 {
2d318652 3035 echo "<strong>RESERVED</strong> ";
b2035dca
DO
3036 $delim = '; ';
3037 }
212b565a 3038 foreach ($addr['allocs'] as $ref)
b2035dca 3039 {
9d5f6a80 3040 echo $delim . $aac_left[$ref['type']];
e749a34f 3041 echo makeIPAllocLink ($ip_bin, $ref, TRUE);
b2035dca
DO
3042 $delim = '; ';
3043 }
3044 if ($delim != '')
e0d6e38e 3045 $delim = '<br>';
212b565a
AA
3046 foreach ($addr['vslist'] as $vs_id)
3047 {
3048 $vs = spotEntity ('ipv4vs', $vs_id);
ad3e5e7e 3049 echo $delim . mkA ("${vs['name']}:${vs['vport']}/${vs['proto']}", 'ipv4vs', $vs['id']) . '&rarr;';
212b565a
AA
3050 $delim = '<br>';
3051 }
eacc0983
AA
3052 foreach ($addr['vsglist'] as $vs_id)
3053 {
3054 $vs = spotEntity ('ipvs', $vs_id);
3055 echo $delim . mkA ($vs['name'], 'ipvs', $vs['id']) . '&rarr;';
3056 $delim = '<br>';
3057 }
212b565a
AA
3058 foreach ($addr['rsplist'] as $rsp_id)
3059 {
3060 $rsp = spotEntity ('ipv4rspool', $rsp_id);
ad3e5e7e 3061 echo "${delim}&rarr;" . mkA ($rsp['name'], 'ipv4rspool', $rsp['id']);
212b565a
AA
3062 $delim = '<br>';
3063 }
b2035dca 3064 echo "</td></tr>\n";
4318ced5 3065 }
2727c7df 3066 // end of iteration
a8dc2982
AA
3067 if (permitted (NULL, NULL, 'set_reserve_comment'))
3068 addJS ('js/inplace-edit.js');
e673ee24
DO
3069
3070 echo "</table>";
446c25c3
AA
3071 if (! empty ($rendered_pager))
3072 echo '<p>' . $rendered_pager . '</p>';
21ee3351
AA
3073}
3074
3075function renderIPv6NetworkAddresses ($netinfo)
3076{
9d5f6a80 3077 global $pageno, $tabno, $aac_left;
21ee3351 3078 echo "<table class='widetable' border=0 cellspacing=0 cellpadding=5 align='center' width='100%'>\n";
cc2fa820 3079 echo "<tr><th>Address</th><th>Name</th><th>Comment</th><th>Allocation</th></tr>\n";
21ee3351 3080
4318ced5
AA
3081 $hl_ip = NULL;
3082 if (isset ($_REQUEST['hl_ip']))
3083 {
3084 $hl_ip = ip6_parse ($_REQUEST['hl_ip']);
3085 addAutoScrollScript ('ip-' . ip6_format ($hl_ip));
3086 }
21ee3351 3087
21ee3351
AA
3088 $addresses = $netinfo['addrlist'];
3089 ksort ($addresses);
4318ced5 3090 markupIPAddrList ($addresses);
21ee3351
AA
3091
3092 // pager
3093 $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
3094 if (count ($addresses) > $maxperpage && $maxperpage > 0)
3095 {
3096 $page = isset ($_REQUEST['pg']) ? $_REQUEST['pg'] : (isset ($hl_ip) ? getPageNumOfIPv6 ($addresses, $hl_ip, $maxperpage) : 0);
3097 $numpages = ceil (count ($addresses) / $maxperpage);
3098 echo "<center><h3>$numpages pages:</h3>";
3099 for ($i=0; $i<$numpages; $i++)
3100 {
3101 if ($i == $page)
3102 echo "<b>$i</b> ";
3103 else
3104 echo "<a href='" . makeHref (array ('page' => $pageno, 'tab' => $tabno, 'id' => $netinfo['id'], 'pg' => $i)) . "'>$i</a> ";
3105 }
3106 echo "</center>";
3107 }
3108
3109 $i = 0;
dec748f6 3110 $interruped = FALSE;
4318ced5
AA
3111 $prev_ip = ip_prev ($netinfo['ip_bin']);
3112 foreach ($addresses as $ip_bin => $addr)
21ee3351
AA
3113 {
3114 if (isset ($page))
3115 {
3116 ++$i;
3117 if ($i <= $maxperpage * $page)
3118 continue;
3119 elseif ($i > $maxperpage * ($page + 1))
3120 {
3121 $interruped = TRUE;
3122 break;
3123 }
3124 }
3125
4318ced5
AA
3126 if ($ip_bin != ip_next ($prev_ip))
3127 renderSeparator (ip_next ($prev_ip), ip_prev ($ip_bin), $hl_ip);
3128 $prev_ip = $ip_bin;
3129
3130 // render IP change history
3131 $title = '';
3132 $history_class = '';
3133 if (isset ($addr['last_log']))
3134 {
3135 $title = ' title="' . htmlspecialchars ($addr['last_log']['user'] . ', ' . formatAge ($addr['last_log']['time']) , ENT_QUOTES) . '"';
3136 $history_class = 'hover-history underline';
3137 }
3138
5c717ce5 3139 $tr_class = $addr['class'] . ' tdleft' . ($hl_ip === $ip_bin ? ' highlight' : '');
4318ced5 3140 echo "<tr class='$tr_class'>";
235124a6 3141 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
3142 $editable =
3143 (empty ($addr['allocs']) || !empty ($addr['name'])
3144 && permitted ('ipaddress', 'properties', 'editAddress')
3145 ? 'editable'
3146 : '');
cc2fa820
AD
3147 echo "<td><span class='rsvtext $editable id-${addr['ip']} op-upd-ip-name'>${addr['name']}</span></td>";
3148 echo "<td><span class='rsvtext $editable id-${addr['ip']} op-upd-ip-comment'>${addr['comment']}</span></td><td>";
21ee3351 3149 $delim = '';
21ee3351
AA
3150 if ( $addr['reserved'] == 'yes')
3151 {
3152 echo "<strong>RESERVED</strong> ";
3153 $delim = '; ';
3154 }
3155 foreach ($addr['allocs'] as $ref)
3156 {
9d5f6a80 3157 echo $delim . $aac_left[$ref['type']];
e749a34f 3158 echo makeIPAllocLink ($ip_bin, $ref, TRUE);
21ee3351
AA
3159 $delim = '; ';
3160 }
3161 if ($delim != '')
71066ef1
AA
3162 $delim = '<br>';
3163 foreach ($addr['vslist'] as $vs_id)
3164 {
3165 $vs = spotEntity ('ipv4vs', $vs_id);
ad3e5e7e 3166 echo $delim . mkA ("${vs['name']}:${vs['vport']}/${vs['proto']}", 'ipv4vs', $vs['id']) . '&rarr;';
71066ef1
AA
3167 $delim = '<br>';
3168 }
eacc0983
AA
3169 foreach ($addr['vsglist'] as $vs_id)
3170 {
3171 $vs = spotEntity ('ipvs', $vs_id);
3172 echo $delim . mkA ($vs['name'], 'ipvs', $vs['id']) . '