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