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