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