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