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