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