refine editing of attributes
[racktables] / wwwroot / inc / interface.php
CommitLineData
b325120a 1<?php
cddbb9fd
DO
2
3# This file is a part of RackTables, a datacenter and server room management
4# framework. See accompanying file "COPYING" for the full copyright and
5# licensing information.
6
e673ee24
DO
7/*
8*
9* This file contains frontend functions for RackTables.
10*
11*/
12
e82deb48 13require_once 'ajax-interface.php';
defd92d8 14require_once 'slb-interface.php';
65e557dd 15
9c0b0016
DO
16// Interface function's special.
17$nextorder['odd'] = 'even';
18$nextorder['even'] = 'odd';
19
52c836b1
DO
20// address allocation type
21$aat = array
22(
23 'regular' => 'Connected',
24 'virtual' => 'Loopback',
25 'shared' => 'Shared',
26 'router' => 'Router',
2f96a91a 27 'point2point' => 'Point-to-point',
52c836b1 28);
beb9e88a 29// address allocation code, IPv4 addresses and objects view
9d5f6a80 30$aac_right = array
52c836b1
DO
31(
32 'regular' => '',
9d5f6a80
AA
33 'virtual' => '<span class="aac-right" title="' . $aat['virtual'] . '">L</span>',
34 'shared' => '<span class="aac-right" title="' . $aat['shared'] . '">S</span>',
35 'router' => '<span class="aac-right" title="' . $aat['router'] . '">R</span>',
2f96a91a 36 'point2point' => '<span class="aac-right" title="' . $aat['point2point'] . '">P</span>',
52c836b1 37);
beb9e88a 38// address allocation code, IPv4 networks view
9d5f6a80 39$aac_left = array
beb9e88a
DO
40(
41 'regular' => '',
9d5f6a80
AA
42 'virtual' => '<span class="aac-left" title="' . $aat['virtual'] . '">L:</span>',
43 'shared' => '<span class="aac-left" title="' . $aat['shared'] . '">S:</span>',
44 'router' => '<span class="aac-left" title="' . $aat['router'] . '">R:</span>',
2f96a91a 45 'point2point' => '<span class="aac-left" title="' . $aat['point2point'] . '">P:</span>',
beb9e88a 46);
52c836b1 47
0dabdc53
DO
48$vtdecoder = array
49(
50 'ondemand' => '',
54b7ced3
DO
51 'compulsory' => 'P',
52# 'alien' => 'NT',
0dabdc53
DO
53);
54
55$vtoptions = array
56(
54b7ced3
DO
57 'ondemand' => 'auto',
58 'compulsory' => 'permanent',
59# 'alien' => 'never touch',
0dabdc53
DO
60);
61
e5c4506d
DO
62// This may be populated later onsite, report rendering function will use it.
63// See the $systemreport for structure.
64$localreports = array();
d6adad3e 65
8d4f7d18
DO
66$attrtypes = array
67(
68 'uint' => '[U] unsigned integer',
69 'float' => '[F] floating point',
70 'string' => '[S] string',
86ff26ae
DO
71 'dict' => '[D] dictionary record',
72 'date' => '[T] date'
8d4f7d18
DO
73);
74
5eb81cf8
AD
75function showLogoutURL ()
76{
55eefced 77 $https = (isset ($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 's' : '';
5eb81cf8
AD
78 $port = (! in_array ($_SERVER['SERVER_PORT'], array (80, 443))) ? ':' . $_SERVER['SERVER_PORT'] : '';
79 $pathinfo = pathinfo ($_SERVER['REQUEST_URI']);
80 $dirname = $pathinfo['dirname'];
81 // add a trailing slash if the installation resides in a subdirectory
82 if ($dirname != '/')
83 $dirname .= '/';
84 printf ('http%s://logout@%s%s?logout', $https, $_SERVER['SERVER_NAME'], $dirname);
85}
86
a1d72d2e
AA
87$quick_links = NULL; // you can override this in your local.php, but first initialize it with getConfiguredQuickLinks()
88
89function renderQuickLinks()
90{
91 global $quick_links;
92 if (! isset ($quick_links))
93 $quick_links = getConfiguredQuickLinks();
94 echo '<ul class="qlinks">';
95 foreach ($quick_links as $link)
96 echo '<li><a href="' . $link['href'] . '">' . str_replace (' ', '&nbsp;', $link['title']) . '</a></li>';
97 echo '</ul>';
98}
99
f97df464
DO
100function renderInterfaceHTML ($pageno, $tabno, $payload)
101{
102?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
103<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
104<head><title><?php echo getTitle ($pageno); ?></title>
105<?php printPageHeaders(); ?>
106</head>
107<body>
b2937330
AA
108<div class="maintable">
109 <div class="mainheader">
832fdbb9 110 <div style="float: right" class=greeting><?php global $remote_displayname; echo mkA ($remote_displayname, 'myaccount', NULL, 'default'); ?> [ <a href='<?php showLogoutURL(); ?>'>logout</a> ]</div>
a1d72d2e 111 <?php echo getConfigVar ('enterprise') ?> RackTables <a href="http://racktables.org" title="Visit RackTables site"><?php echo CODE_VERSION ?></a><?php renderQuickLinks() ?>
b2937330 112 </div>
d688a63f 113 <div class="menubar"><?php showPathAndSearch ($pageno, $tabno); ?></div>
b2937330
AA
114 <div class="tabbar"><?php showTabs ($pageno, $tabno); ?></div>
115 <div class="msgbar"><?php showMessageOrError(); ?></div>
116 <div class="pagebar"><?php echo $payload; ?></div>
117</div>
f97df464
DO
118</body>
119</html>
120<?php
121}
122
e673ee24 123// Main menu.
f2168c4b
DO
124function renderIndexItem ($ypageno)
125{
126 echo (! permitted ($ypageno)) ? " <td>&nbsp;</td>\n" :
127 " <td>\n" .
128 " <h1><a href='" . makeHref (array ('page' => $ypageno)) . "'>" .
129 getPageName ($ypageno) . "<br>\n" . getImageHREF ($ypageno) .
130 "</a></h1>\n" .
131 " </td>\n";
4b680876
DO
132}
133
e673ee24
DO
134function renderIndex ()
135{
67c5c049 136 global $indexlayout;
e673ee24
DO
137?>
138<table border=0 cellpadding=0 cellspacing=0 width='100%'>
139 <tr>
140 <td>
141 <div style='text-align: center; margin: 10px; '>
a544d2c4 142 <table width='100%' cellspacing=0 cellpadding=20 class=mainmenu border=0>
4b680876 143<?php
67c5c049
DO
144foreach ($indexlayout as $row)
145{
146 echo '<tr>';
147 foreach ($row as $column)
148 if ($column === NULL)
149 echo '<td>&nbsp;</td>';
150 else
151 renderIndexItem ($column);
152 echo '</tr>';
153}
154?>
e673ee24
DO
155 </table>
156 </div>
157 </td>
158 </tr>
159</table>
b325120a 160<?php
e673ee24
DO
161}
162
f3505b35
AA
163function getRenderedAlloc ($object_id, $alloc)
164{
165 $ret = array
166 (
167 'tr_class' => '',
40c9fc94 168 'td_name_suffix' => '',
f3505b35
AA
169 'td_ip' => '',
170 'td_network' => '',
171 'td_routed_by' => '',
172 'td_peers' => '',
173 );
174 $dottedquad = $alloc['addrinfo']['ip'];
4318ced5 175 $ip_bin = $alloc['addrinfo']['ip_bin'];
f3505b35 176
4318ced5
AA
177 $hl_ip_bin = NULL;
178 if (isset ($_REQUEST['hl_ip']))
f3505b35 179 {
4318ced5
AA
180 $hl_ip_bin = ip_parse ($_REQUEST['hl_ip']);
181 addAutoScrollScript ("ip-" . $_REQUEST['hl_ip']);
f3505b35
AA
182 }
183
184 $ret['tr_class'] = $alloc['addrinfo']['class'];
4318ced5 185 if ($hl_ip_bin === $ip_bin)
5c717ce5 186 $ret['tr_class'] .= ' highlight';
dec748f6 187
68c91b9c
AA
188 // render IP change history
189 $ip_title = '';
190 $ip_class = '';
191 if (isset ($alloc['addrinfo']['last_log']))
192 {
193 $log = $alloc['addrinfo']['last_log'];
194 $ip_title = "title='" .
195 htmlspecialchars
196 (
197 $log['user'] . ', ' . formatAge ($log['time']),
198 ENT_QUOTES
199 ) . "'";
200 $ip_class = 'hover-history underline';
201 }
202
f3505b35 203 // render IP address td
9d5f6a80 204 global $aac_right;
4318ced5
AA
205 $netinfo = spotNetworkByIP ($ip_bin);
206 $ret['td_ip'] = "<td class='tdleft'>";
207 if (isset ($netinfo))
208 {
209 $title = $dottedquad;
210 if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
211 $title .= '/' . $netinfo['mask'];
68c91b9c 212 $ret['td_ip'] .= "<a name='ip-$dottedquad' class='$ip_class' $ip_title href='" .
f3505b35
AA
213 makeHref (
214 array
215 (
4318ced5 216 'page' => 'ipaddress',
f3505b35
AA
217 'hl_object_id' => $object_id,
218 'ip' => $dottedquad,
219 )
4318ced5
AA
220 ) . "'>$title</a>";
221 }
f3505b35 222 else
68c91b9c 223 $ret['td_ip'] .= "<span class='$ip_class' $ip_title>$dottedquad</span>";
9d5f6a80 224 $ret['td_ip'] .= $aac_right[$alloc['type']];
33973968 225 if ($alloc['addrinfo']['name'] != '')
a0bdf6dc 226 $ret['td_ip'] .= ' (' . stringForLabel ($alloc['addrinfo']['name']) . ')';
f3505b35
AA
227 $ret['td_ip'] .= '</td>';
228
229 // render network and routed_by tds
4318ced5
AA
230 $td_class = 'tdleft';
231 if (! isset ($netinfo))
f3505b35
AA
232 {
233 $ret['td_network'] = "<td class='$td_class sparenetwork'>N/A</td>";
234 $ret['td_routed_by'] = $ret['td_network'];
235 }
236 else
237 {
238 $ret['td_network'] = "<td class='$td_class'>" .
239 getOutputOf ('renderCell', $netinfo) . '</td>';
240
98350e52
AA
241 // render "routed by" td
242 if ($display_routers = (getConfigVar ('IPV4_TREE_RTR_AS_CELL') == 'none'))
9d5f6a80 243 $ret['td_routed_by'] = '<td>&nbsp;</td>';
f3505b35 244 else
98350e52 245 {
98350e52 246 $other_routers = array();
6063fab7 247 foreach (findNetRouters ($netinfo) as $router)
98350e52
AA
248 if ($router['id'] != $object_id)
249 $other_routers[] = $router;
250 if (count ($other_routers))
251 $ret['td_routed_by'] = getOutputOf ('printRoutersTD', $other_routers, $display_routers);
252 else
253 $ret['td_routed_by'] = "<td class='$td_class'>&nbsp;</td>";
254 }
f3505b35 255 }
dec748f6 256
f3505b35
AA
257 // render peers td
258 $ret['td_peers'] = "<td class='$td_class'>";
259 $prefix = '';
e749a34f 260 $separator = '; ';
f3505b35
AA
261 if ($alloc['addrinfo']['reserved'] == 'yes')
262 {
263 $ret['td_peers'] .= $prefix . '<strong>RESERVED</strong>';
e749a34f 264 $prefix = $separator;
f3505b35
AA
265 }
266 foreach ($alloc['addrinfo']['allocs'] as $allocpeer)
2f96a91a
AA
267 if ($allocpeer['object_id'] != $object_id)
268 {
269 $ret['td_peers'] .= $prefix . makeIPAllocLink ($ip_bin, $allocpeer);
270 $prefix = $separator;
271 }
272 elseif ($allocpeer['type'] == 'point2point' && isset ($netinfo))
273 {
274 // show PtP peers in the IP network
6063fab7
AA
275 $addrlist = isset ($netinfo['own_addrlist']) ? $netinfo['own_addrlist'] : getIPAddrList ($netinfo, IPSCAN_DO_ALLOCS);
276 foreach (getPtPNeighbors ($ip_bin, $addrlist) as $p_ip_bin => $p_alloc_list)
2f96a91a
AA
277 foreach ($p_alloc_list as $p_alloc)
278 {
279 $ret['td_peers'] .= $prefix . '&harr;&nbsp;' . makeIPAllocLink ($p_ip_bin, $p_alloc);
280 $prefix = $separator;
281 }
282 }
283
f3505b35
AA
284 $ret['td_peers'] .= '</td>';
285
286 return $ret;
287}
288
e1add254
AD
289function renderLocationFilterPortlet ()
290{
e1add254 291 // Recursive function used to build the location tree
2056822d 292 function renderLocationCheckbox ($subtree, $level = 0)
e1add254
AD
293 {
294 $self = __FUNCTION__;
e1add254 295
2056822d 296 foreach ($subtree as $location_id => $location)
e1add254 297 {
9f68b666 298 echo "<div class=tagbox style='text-align:left; padding-left:" . ($level * 16) . "px;'>";
950dfa1f 299 $checked = (! isset ($_SESSION['locationFilter']) || in_array ($location['id'], $_SESSION['locationFilter'])) ? 'checked' : '';
9c88bba9 300 echo "<label><input type=checkbox name='location_id[]' class=${level} value='${location['id']}'${checked} onClick=checkAll(this)>${location['name']}";
9f68b666 301 echo '</label>';
2056822d 302 if ($location['kidc'])
9f68b666
AD
303 {
304 echo "<a id='lfa" . $location['id'] . "' onclick=\"expand('${location['id']}')\" href\"#\" > - </a>";
305 echo "<div id='lfd" . $location['id'] . "'>";
2056822d 306 $self ($location['kids'], $level + 1);
9f68b666
AD
307 echo '</div>';
308 }
309 echo '</div>';
e1add254
AD
310 }
311 }
312
9c88bba9
AD
313 addJS(<<<END
314function checkAll(bx) {
315 for (var tbls=document.getElementsByTagName("table"), i=tbls.length; i--;)
4e9e1b7a 316 if (tbls[i].id == "locationFilter") {
9c88bba9
AD
317 var bxs=tbls[i].getElementsByTagName("input");
318 var in_tree = false;
9f68b666 319 for (var j=0; j<bxs.length; j++) {
9c88bba9
AD
320 if(in_tree == false && bxs[j].value == bx.value)
321 in_tree = true;
322 else if(parseInt(bxs[j].className) <= parseInt(bx.className))
323 in_tree = false;
324 if (bxs[j].type=="checkbox" && in_tree == true)
325 bxs[j].checked = bx.checked;
326 }
327 }
328}
9f68b666
AD
329
330function collapseAll(bx) {
331 for (var tbls=document.getElementsByTagName("table"), i=tbls.length; i--;)
332 if (tbls[i].id == "locationFilter") {
333 var bxs=tbls[i].getElementsByTagName("div");
334 //loop through divs to hide unchecked
335 for (var j=0; j<bxs.length; j++) {
336 var is_checked = -1;
337 var in_div=bxs[j].getElementsByTagName("input");
338 //loop through input to find if any is checked
339 for (var k=0; k<in_div.length; k++) {
340 if(in_div[k].type="checkbox") {
341 if (in_div[k].checked == true) {
342 is_checked = true;
343 break;
344 }
345 else
346 is_checked = false;
347 }
348 }
349 // nothing selected and element id is lfd, collapse it
350 if (is_checked == false && !bxs[j].id.indexOf("lfd"))
351 expand(bxs[j].id.substr(3));
352 }
353 }
354}
355
356function expand(id) {
357 var divid = document.getElementById("lfd" + id);
358 var iconid = document.getElementById("lfa" + id);
359 if (divid.style.display == 'none') {
360 divid.style.display = 'block';
361 iconid.innerHTML = ' - ';
362 } else {
363 divid.style.display = 'none';
364 iconid.innerHTML = ' + ';
365 }
366}
9c88bba9
AD
367END
368 ,TRUE);
e1add254
AD
369 startPortlet ('Location filter');
370 echo <<<END
9c88bba9 371<table border=0 align=center cellspacing=0 class="tagtree" id="locationFilter">
6b816bde 372 <form method=post>
e1add254
AD
373 <input type=hidden name=page value=rackspace>
374 <input type=hidden name=tab value=default>
375 <input type=hidden name=changeLocationFilter value=true>
376END;
2056822d
AA
377
378 $locationlist = listCells ('location');
e1add254
AD
379 if (count ($locationlist))
380 {
9c88bba9
AD
381 echo "<tr><td class=tagbox style='padding-left: 0px'><label>";
382 echo "<input type=checkbox name='location' onClick=checkAll(this)> Toggle all";
1341dc93 383 echo "<img src=?module=chrome&uri=pix/1x1t.gif onLoad=collapseAll(this)>"; // dirty hack to collapse all when page is displayed
9c88bba9 384 echo "</label></td></tr>\n";
9f68b666 385 echo "<tr><td class=tagbox><hr>\n";
d6409c53 386 renderLocationCheckbox (treeFromList (addTraceToNodes ($locationlist)));
9f68b666
AD
387 echo "<hr></td></tr>\n";
388 echo '<tr><td>';
e1add254
AD
389 printImageHREF ('setfilter', 'set filter', TRUE);
390 echo "</td></tr>\n";
391 }
392 else
393 {
394 echo "<tr><td class='tagbox sparenetwork'>(no locations exist)</td></tr>\n";
395 echo "<tr><td>";
396 printImageHREF ('setfilter gray');
397 echo "</td></tr>\n";
398 }
399
400 echo "</form></table>\n";
401 finishPortlet ();
402}
403
d9bad31a
AD
404function rackspaceCmp ($a, $b)
405{
406 $ret = strnatcasecmp ($a['location_tree'], $b['location_tree']);
407 if (!$ret)
408 $ret = strnatcasecmp ($a['row_name'], $b['row_name']);
409 return $ret;
410}
411
150effa7 412function getRackThumbLink ($rack, $scale = 1, $object_id = NULL)
e10d9597
DO
413{
414 if (! is_int ($scale) || $scale <= 0)
415 throw new InvalidArgException ('scale', $scale, 'must be a natural number');
416 $width = getRackImageWidth() * $scale;
417 $height = getRackImageHeight ($rack['height']) * $scale;
418 $title = "${rack['height']} units";
419 $src = '?module=image' .
150effa7
DO
420 ($scale == 1 && $object_id === NULL ? '&img=minirack' : "&img=midirack&scale=${scale}") .
421 "&rack_id=${rack['id']}" .
422 ($object_id === NULL ? '' : "&object_id=${object_id}");
e10d9597 423 $img = "<img border=0 width=${width} height=${height} title='${title}' src='${src}'>";
168c2acb 424 return mkA ($img, 'rack', $rack['id']);
e10d9597
DO
425}
426
e673ee24
DO
427function renderRackspace ()
428{
e1add254 429 // Handle the location filter
0a00b344 430 startSession();
e1add254
AD
431 if (isset ($_REQUEST['changeLocationFilter']))
432 unset ($_SESSION['locationFilter']);
433 if (isset ($_REQUEST['location_id']))
434 $_SESSION['locationFilter'] = $_REQUEST['location_id'];
5d6352df 435 session_commit();
e1add254 436
950dfa1f
AA
437 echo "<table class=objview border=0 width='100%'><tr><td class=pcleft>";
438
95857b5c 439 $found_racks = array();
a8efc03e 440 $cellfilter = getCellFilter();
950dfa1f 441 if (! ($cellfilter['is_empty'] && !isset ($_SESSION['locationFilter']) && renderEmptyResults ($cellfilter, 'racks', getEntitiesCount ('rack'))))
e1add254 442 {
950dfa1f
AA
443 $rows = array();
444 $rackCount = 0;
524f8a7b 445 foreach (listCells ('row') as $row_id => $rowInfo)
950dfa1f 446 {
2b16c179 447 $rackList = applyCellFilter ('rack', $cellfilter, $row_id);
950dfa1f 448 $found_racks = array_merge ($found_racks, $rackList);
d9bad31a
AD
449 $location_id = $rowInfo['location_id'];
450 $locationIdx = 0;
451 // contains location names in the form of 'grandparent parent child', used for sorting
452 $locationTree = '';
453 // contains location names as well as links
454 $hrefLocationTree = '';
455 while ($location_id)
456 {
457 if ($locationIdx == 20)
458 {
459 showWarning ("Warning: There is likely a circular reference in the location tree. Investigate location ${location_id}.");
460 break;
461 }
462 $parentLocation = spotEntity ('location', $location_id);
463 $locationTree = sprintf ('%s %s', $parentLocation['name'], $locationTree);
464 $hrefLocationTree = "&raquo; <a href='" .
465 makeHref(array('page'=>'location', 'location_id'=>$parentLocation['id'])) .
466 "${cellfilter['urlextra']}'>${parentLocation['name']}</a> " .
467 $hrefLocationTree;
468 $location_id = $parentLocation['parent_id'];
469 $locationIdx++;
470 }
471 $hrefLocationTree = substr ($hrefLocationTree, 8);
950dfa1f
AA
472 $rows[] = array (
473 'location_id' => $rowInfo['location_id'],
d9bad31a
AD
474 'location_tree' => $locationTree,
475 'href_location_tree' => $hrefLocationTree,
950dfa1f
AA
476 'row_id' => $row_id,
477 'row_name' => $rowInfo['name'],
478 'racks' => $rackList
479 );
480 $rackCount += count($rackList);
481 }
1ebbf889 482
d9bad31a
AD
483 // sort by location, then by row
484 usort ($rows, 'rackspaceCmp');
485
950dfa1f 486 if (! renderEmptyResults($cellfilter, 'racks', $rackCount))
1ebbf889 487 {
950dfa1f
AA
488 // generate thumb gallery
489 global $nextorder;
950dfa1f
AA
490 // Zero value effectively disables the limit.
491 $maxPerRow = getConfigVar ('RACKS_PER_ROW');
492 $order = 'odd';
18733c4a
AD
493 if (! count ($rows))
494 echo "<h2>No rows found</h2>\n";
495 else
e1add254 496 {
950dfa1f
AA
497 echo '<table border=0 cellpadding=10 class=cooltable>';
498 echo '<tr><th class=tdleft>Location</th><th class=tdleft>Row</th><th class=tdleft>Racks</th></tr>';
499 foreach ($rows as $row)
c08915c7 500 {
950dfa1f 501 $rackList = $row['racks'];
d1d19f79 502 $location_id = $row['location_id'];
950dfa1f
AA
503
504 if (
55eefced
DO
505 $location_id != '' && isset ($_SESSION['locationFilter']) && ! in_array ($location_id, $_SESSION['locationFilter']) ||
506 empty ($rackList) && ! $cellfilter['is_empty']
950dfa1f
AA
507 )
508 continue;
509 $rackListIdx = 0;
d9bad31a
AD
510 echo "<tr class=row_${order}><th class=tdleft>${row['href_location_tree']}</th>";
511 echo "<th class=tdleft><a href='".makeHref(array('page'=>'row', 'row_id'=>$row['row_id']))."${cellfilter['urlextra']}'>${row['row_name']}</a></th>";
950dfa1f 512 echo "<th class=tdleft><table border=0 cellspacing=5><tr>";
18733c4a
AD
513 if (! count ($rackList))
514 echo '<td>(empty row)</td>';
950dfa1f
AA
515 else
516 foreach ($rackList as $rack)
e1add254 517 {
55eefced 518 if ($rackListIdx > 0 && $maxPerRow > 0 && $rackListIdx % $maxPerRow == 0)
950dfa1f
AA
519 {
520 echo '</tr></table></th></tr>';
d9bad31a 521 echo "<tr class=row_${order}><th class=tdleft></th><th class=tdleft>${row['row_name']} (continued)";
950dfa1f
AA
522 echo "</th><th class=tdleft><table border=0 cellspacing=5><tr>";
523 }
168c2acb
DO
524 echo '<td align=center valign=bottom>' . getRackThumbLink ($rack);
525 echo '<br>' . mkA (stringForLabel ($rack['name']), 'rack', $rack['id']) . '</td>';
950dfa1f 526 $rackListIdx++;
e1add254 527 }
950dfa1f
AA
528 $order = $nextorder[$order];
529 echo "</tr></table></th></tr>\n";
530 }
531 echo "</table>\n";
e1add254 532 }
1ebbf889 533 }
e673ee24 534 }
14ad4f4a
DO
535 echo '</td><td class=pcright width="25%">';
536 renderCellFilterPortlet ($cellfilter, 'rack', $found_racks);
e1add254
AD
537 echo "<br>\n";
538 renderLocationFilterPortlet ();
489a7502 539 echo "</td></tr></table>\n";
e673ee24
DO
540}
541
2056822d 542function renderLocationRowForEditor ($subtree, $level = 0)
e1add254
AD
543{
544 $self = __FUNCTION__;
2056822d
AA
545 foreach ($subtree as $locationinfo)
546 {
547 echo "<tr><td align=left style='padding-left: " . ($locationinfo['kidc'] ? $level : ($level + 1) * 16) . "px;'>";
548 if ($locationinfo['kidc'])
549 printImageHREF ('node-expanded-static');
550 if ($locationinfo['refcnt'] > 0 || $locationinfo['kidc'] > 0)
551 printImageHREF ('nodestroy');
552 else
10f5133a 553 echo getOpLink (array ('op' => 'deleteLocation', 'location_id' => $locationinfo['id']), '', 'destroy', 'Delete location');
2056822d
AA
554 echo '</td><td class=tdleft>';
555 printOpFormIntro ('updateLocation', array ('location_id' => $locationinfo['id']));
556 $parent = isset ($locationinfo['parent_id']) ? $locationinfo['parent_id'] : 0;
557 echo getSelect
558 (
559 array ( $parent => $parent ? htmlspecialchars ($locationinfo['parent_name']) : '-- NONE --'),
560 array ('name' => 'parent_id', 'id' => 'locationid_' . $locationinfo['id'], 'class' => 'locationlist-popup'),
561 $parent,
562 FALSE
563 );
564 echo "</td><td class=tdleft>";
565 echo "<input type=text size=48 name=name value='${locationinfo['name']}'>";
566 echo '</td><td>' . getImageHREF ('save', 'Save changes', TRUE) . "</form></td></tr>\n";
567 if ($locationinfo['kidc'])
568 $self ($locationinfo['kids'], $level + 1);
569 }
e1add254
AD
570}
571
82fa45a9 572function renderLocationSelectTree ($select_name, $selected_id = NULL)
eda41675 573{
82fa45a9 574 echo "<select name='${select_name}'>";
eda41675 575 echo '<option value=0>-- NONE --</option>';
359ea9f9 576 $locationlist = listCells ('location');
d6409c53 577 foreach (treeFromList (addTraceToNodes ($locationlist)) as $location)
eda41675
AD
578 {
579 echo "<option value=${location['id']} style='font-weight: bold' ";
580 if ($location['id'] == $selected_id )
581 echo ' selected';
582 echo ">${location['name']}</option>";
e3648fa6 583 printLocationChildrenSelectOptions ($location, $selected_id);
eda41675
AD
584 }
585 echo '</select>';
586}
587
e1add254
AD
588function renderRackspaceLocationEditor ()
589{
b090b2da
RQ
590 $js = <<<JSTXT
591 function locationeditor_showselectbox(e) {
592 $(this).load('index.php', {module: 'ajax', ac: 'get-location-select', locationid: this.id});
593 $(this).unbind('mousedown', locationeditor_showselectbox);
594 }
595 $(document).ready(function () {
596 $('select.locationlist-popup').bind('mousedown', locationeditor_showselectbox);
597 });
598JSTXT;
599
600 addJS($js, TRUE );
e1add254
AD
601 function printNewItemTR ()
602 {
e1add254 603 printOpFormIntro ('addLocation');
eda41675 604 echo '<tr><td>';
e1add254 605 printImageHREF ('create', 'Add new location', TRUE);
82fa45a9
DO
606 echo '</td><td>';
607 renderLocationSelectTree ('parent_id');
04c61e7e
AB
608 echo '</td><td><input type=text size=48 name=name></td><td>';
609 printImageHREF ('create', 'Add new location', TRUE);
e1add254
AD
610 echo "</td></tr></form>\n";
611 }
e1add254
AD
612
613 startPortlet ('Locations');
614 echo "<table border=0 cellspacing=0 cellpadding=5 align=center class=widetable>\n";
615 echo "<tr><th>&nbsp;</th><th>Parent</th><th>Name</th><th>&nbsp;</th></tr>\n";
616 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
617 printNewItemTR();
2056822d 618
1e0f13f3 619 $locations = listCells ('location');
d6409c53 620 renderLocationRowForEditor (treeFromList (addTraceToNodes ($locations)));
2056822d 621
e1add254
AD
622 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
623 printNewItemTR();
624 echo "</table><br>\n";
625 finishPortlet();
626}
627
345fd640
AD
628function renderRackspaceRowEditor ()
629{
eda41675 630 function printNewItemTR ()
345fd640
AD
631 {
632 printOpFormIntro ('addRow');
eda41675 633 echo '<tr><td>';
7be51fea 634 printImageHREF ('create', 'Add new row', TRUE);
04eee8d2
VS
635 echo '</td><td>&nbsp;';
636 echo '</td><td>&nbsp;';
82fa45a9
DO
637 echo '</td><td>';
638 renderLocationSelectTree ('location_id');
04c61e7e
AB
639 echo '</td><td><input type=text name=name></td><td>';
640 printImageHREF ('create', 'Add new row', TRUE);
aca7e169 641 echo '</td><td>&nbsp;';
eda41675 642 echo '</td></tr></form>';
345fd640 643 }
d732e47e 644 startPortlet ('Rows');
345fd640 645 echo "<table border=0 cellspacing=0 cellpadding=5 align=center class=widetable>\n";
04eee8d2 646 echo "<tr><th>&nbsp;</th><th># Racks</th><th># Devices</th><th>Location</th><th>Name</th><th>&nbsp;</th><th>Row link</th></tr>\n";
345fd640 647 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
eda41675 648 printNewItemTR ();
524f8a7b 649 foreach (listCells ('row') as $row_id => $rowInfo)
345fd640 650 {
eda41675 651 echo '<tr><td>';
04eee8d2
VS
652 $rc = $rowInfo['rackc'];
653 $delete_racks_str = $rc ? " and $rc rack(s)" : '';
654 echo getOpLink (array ('op'=>'deleteRow', 'row_id'=>$row_id), '', 'destroy', 'Delete row'.$delete_racks_str, 'need-confirmation');
c40666f5 655 printOpFormIntro ('updateRow', array ('row_id' => $row_id));
eda41675 656 echo '</td><td>';
04eee8d2
VS
657 echo $rc;
658 echo '</td><td>';
659 echo getRowMountsCount ($row_id);
660 echo '</td><td>';
82fa45a9 661 renderLocationSelectTree ('location_id', $rowInfo['location_id']);
04c61e7e 662 echo "</td><td><input type=text name=name value='${rowInfo['name']}'></td><td>";
7be51fea 663 printImageHREF ('save', 'Save changes', TRUE);
524f8a7b
AA
664 echo "</form></td>";
665 echo "<td>" . mkCellA ($rowInfo) . "</td>";
666 echo "</tr>\n";
345fd640
AD
667 }
668 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
eda41675 669 printNewItemTR ();
345fd640
AD
670 echo "</table><br>\n";
671 finishPortlet();
672}
673
d10401bf 674function renderRow ($row_id)
e673ee24 675{
e16b666c 676 $rowInfo = getRowInfo ($row_id);
a8efc03e 677 $cellfilter = getCellFilter();
2b16c179 678 $rackList = applyCellFilter ('rack', $cellfilter, $row_id);
138655f5
AD
679
680 $summary = array ();
681 $summary['Name'] = $rowInfo['name'];
682 if ($rowInfo['location_id'])
683 $summary['Location'] = mkA ($rowInfo['location'], 'location', $rowInfo['location_id']);
684 $summary['Racks'] = $rowInfo['count'];
685 $summary['Units'] = $rowInfo['sum'];
686 $summary['% used'] = getProgressBar (getRSUforRow ($rackList));
249c78b2 687 foreach (getAttrValuesSorted ($row_id) as $record)
138655f5
AD
688 if
689 (
55eefced 690 $record['value'] != '' &&
138655f5
AD
691 permitted (NULL, NULL, NULL, array (array ('tag' => '$attr_' . $record['id'])))
692 )
5b908204 693 $summary['{sticker}' . $record['name']] = formatAttributeValue ($record, 1561);
138655f5 694
e673ee24
DO
695 // Main layout starts.
696 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
697
698 // Left portlet with row information.
699 echo "<tr><td class=pcleft>";
138655f5 700 renderEntitySummary ($rowInfo, 'Summary', $summary);
7f297114 701 renderCellFilterPortlet ($cellfilter, 'rack', $rackList, array ('row_id' => $row_id));
6ccfd4bd 702 renderFilesPortlet ('row',$row_id);
48325652 703 echo "</td><td class=pcright>";
e673ee24 704
4fbb5a00 705 global $nextorder;
f5883ec1
DO
706 // Maximum number of racks per row is proportionally less, but at least 1.
707 $maxPerRow = max (floor (getConfigVar ('RACKS_PER_ROW') / getConfigVar ('ROW_SCALE')), 1);
708 $rackListIdx = 0;
e673ee24
DO
709 $order = 'odd';
710 startPortlet ('Racks');
711 echo "<table border=0 cellspacing=5 align='center'><tr>";
64b95774 712 foreach ($rackList as $rack)
e673ee24 713 {
f5883ec1
DO
714 if ($rackListIdx % $maxPerRow == 0)
715 {
716 if ($rackListIdx > 0)
717 echo '</tr>';
718 echo '<tr>';
719 }
9b8174d7 720 $class = ($rack['has_problems'] == 'yes') ? 'error' : $order;
168c2acb
DO
721 echo "<td align=center valign=bottom class=row_${class}>" .
722 getRackThumbLink ($rack, getConfigVar ('ROW_SCALE')) .
723 '<br>' . mkA (stringForLabel ($rack['name']), 'rack', $rack['id']) . '</td>';
e673ee24 724 $order = $nextorder[$order];
f5883ec1 725 $rackListIdx++;
e673ee24
DO
726 }
727 echo "</tr></table>\n";
728 finishPortlet();
e673ee24
DO
729 echo "</td></tr></table>";
730}
731
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";
1365 // left column with uknown number of portlets
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";
f0ed1181 1488 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
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'];
21ee3351 1516 if (getConfigVar ('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
7056988c 1589 // After left column we have (surprise!) right column with rackspace portlet only.
e673ee24 1590 echo "<td class=pcright>";
8dc2a6c8 1591 if (!in_array($info['objtype_id'], $virtual_obj_types))
0682218d
AD
1592 {
1593 // rackspace portlet
1594 startPortlet ('rackspace allocation');
1595 foreach (getResidentRacksData ($object_id, FALSE) as $rack_id)
1596 renderRack ($rack_id, $object_id);
1597 echo '<br>';
1598 finishPortlet();
1599 }
e673ee24
DO
1600 echo "</td></tr>";
1601 echo "</table>\n";
1602}
1603
1604function renderRackMultiSelect ($sname, $racks, $selected)
1605{
04571888
DO
1606 // Transform the given flat list into a list of groups, each representing a rack row.
1607 $rdata = array();
e673ee24 1608 foreach ($racks as $rack)
d016010a 1609 {
b956dd24 1610 if ('' != $trail = getLocationTrail ($rack['location_id'], FALSE))
7b6ae79a
AD
1611 $row_name = $trail . ' : ' . $rack['row_name'];
1612 else
1613 $row_name = $rack['row_name'];
d016010a
AD
1614 $rdata[$row_name][$rack['id']] = $rack['name'];
1615 }
4fbb5a00 1616 echo "<select name=${sname} multiple size=" . getConfigVar ('MAXSELSIZE') . " onchange='getElementsByName(\"updateObjectAllocation\")[0].submit()'>\n";
acfc0d89
AA
1617 $row_names = array_keys ($rdata);
1618 natsort ($row_names);
1619 foreach ($row_names as $optgroup)
e673ee24 1620 {
04571888 1621 echo "<optgroup label='${optgroup}'>";
acfc0d89 1622 foreach ($rdata[$optgroup] as $rack_id => $rack_name)
04571888
DO
1623 {
1624 echo "<option value=${rack_id}";
1625 if (!(array_search ($rack_id, $selected) === FALSE))
1626 echo ' selected';
1627 echo">${rack_name}</option>\n";
1628 }
e673ee24
DO
1629 }
1630 echo "</select>\n";
1631}
1632
e673ee24 1633// This function renders a form for port edition.
d10401bf 1634function renderPortsForObject ($object_id)
e673ee24 1635{
3153a326
DO
1636 $prefs = getPortListPrefs();
1637 function printNewItemTR ($prefs)
39b4abd0
DO
1638 {
1639 printOpFormIntro ('addPort');
1640 echo "<tr><td>";
f5ff50f5 1641 printImageHREF ('add', 'add a port', TRUE);
7b30e187 1642 echo "</td><td class='tdleft'><input type=text size=16 name=port_name></td>\n";
04c61e7e 1643 echo "<td><input type=text name=port_label></td><td>";
759b21d2 1644 printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type_id'), $prefs['selected']);
04c61e7e 1645 echo "<td><input type=text name=port_l2address size=18 maxlength=24></td>\n";
318c48a8 1646 echo "<td colspan=4>&nbsp;</td><td>";
04c61e7e 1647 printImageHREF ('add', 'add a port', TRUE);
9318d2ef 1648 echo "</td></tr></form>";
39b4abd0 1649 }
70c0cb08
DO
1650
1651 function printBulkForm ($prefs)
1652 {
a1fc539a
RF
1653 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
1654 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>";
1655 echo "<th class=tdleft>Count</th><th>&nbsp;</th></tr>\n";
1656 printOpFormIntro ('addBulkPorts');
1657 echo "<tr><td>";
1658 printImageHREF ('add', 'add ports', TRUE);
04c61e7e
AB
1659 echo "</td><td><input type=text size=8 name=port_name></td>\n";
1660 echo "<td><input type=text name=port_label></td><td>";
759b21d2 1661 printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type_id'), $prefs['selected']);
04c61e7e
AB
1662 echo "<td><input type=text name=port_numbering_start size=3 maxlength=3></td>\n";
1663 echo "<td><input type=text name=port_numbering_count size=3 maxlength=3></td>\n";
0b09238d 1664 echo "<td>&nbsp;</td><td>";
04c61e7e 1665 printImageHREF ('add', 'add ports', TRUE);
a1fc539a
RF
1666 echo "</td></tr></form>";
1667 echo "</table><br>\n";
1668 }
dec748f6 1669
70c0cb08
DO
1670 startPortlet ('Ports and interfaces');
1671 $object = spotEntity ('object', $object_id);
1672 amplifyCell ($object);
1673 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes' && getConfigVar ('ENABLE_BULKPORT_FORM') == 'yes')
1674 printBulkForm ($prefs);
1675
0b09238d 1676 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
0d59a51c 1677 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 1678 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 1679 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
3153a326 1680 printNewItemTR ($prefs);
45ff9012 1681
0b09238d
DO
1682 // clear ports link
1683 echo getOpLink (array ('op'=>'deleteAll'), 'Clear port list', 'clear', '', 'need-confirmation');
1684
8579b51b
AA
1685 // rename ports link
1686 $n_ports_to_rename = 0;
1687 foreach ($object['ports'] as $port)
1688 if ($port['name'] != shortenPortName ($port['name'], $object['id']))
1689 $n_ports_to_rename++;
1690 if ($n_ports_to_rename)
bd2dcd32 1691 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 1692
00276508
AA
1693 if (isset ($_REQUEST['hl_port_id']))
1694 {
1695 assertUIntArg ('hl_port_id');
1696 $hl_port_id = intval ($_REQUEST['hl_port_id']);
65e557dd 1697 addAutoScrollScript ("port-$hl_port_id");
00276508 1698 }
d10401bf 1699 switchportInfoJS ($object_id); // load JS code to make portnames interactive
9f75e165 1700 foreach ($object['ports'] as $port)
e673ee24 1701 {
8579b51b
AA
1702 // highlight port name with yellow if it's name is not canonical
1703 $canon_pn = shortenPortName ($port['name'], $port['object_id']);
1704 $name_class = $canon_pn == $port['name'] ? '' : 'trwarning';
1705
5c717ce5 1706 $tr_class = isset ($hl_port_id) && $hl_port_id == $port['id'] ? 'class="highlight"' : '';
7056988c 1707 printOpFormIntro ('editPort', array ('port_id' => $port['id']));
d0d40671 1708 echo "<tr $tr_class><td><a name='port-${port['id']}' href='".makeHrefProcess(array('op'=>'delPort', 'port_id'=>$port['id']))."'>";
e673ee24
DO
1709 printImageHREF ('delete', 'Unlink and Delete this port');
1710 echo "</a></td>\n";
65e557dd 1711 $a_class = isEthernetPort ($port) ? 'port-menu' : '';
7b30e187 1712 echo "<td class='tdleft $name_class' NOWRAP><input type=text name=name class='interactive-portname $a_class' value='${port['name']}' size=16></td>";
cd3775e9 1713 echo "<td><input type=text name=label value='${port['label']}'></td>";
70c0cb08 1714 echo '<td class=tdleft>';
8e021b86
AA
1715 if ($port['iif_id'] != 1)
1716 echo '<label>' . $port['iif_name'] . ' ';
4f968fa8 1717 $port_type_opts = array();
78b59ab3 1718 if (! $port['linked'])
759b21d2 1719 $port_type_opts = getUnlinkedPortTypeOptions ($port['iif_id']);
78b59ab3 1720 else
4f968fa8
AA
1721 foreach (getExistingPortTypeOptions ($port) as $oif_id => $opt_str)
1722 $port_type_opts[$port['iif_name']][$port['iif_id'] . '-' . $oif_id] = $opt_str;
4f968fa8 1723 printNiftySelect ($port_type_opts, array ('name' => 'port_type_id'), $port['iif_id'] . '-' . $port['oif_id']);
8e021b86
AA
1724 if ($port['iif_id'] != 1)
1725 echo '</label>';
1726 echo '</td>';
1727
cd3775e9
DO
1728 // 18 is enough to fit 6-byte MAC address in its longest form,
1729 // while 24 should be Ok for WWN
1730 echo "<td><input type=text name=l2address value='${port['l2address']}' size=18 maxlength=24></td>\n";
0b09238d 1731 if ($port['remote_object_id'])
90bf1755 1732 {
f4d00faf
DO
1733 $dname = formatObjectDisplayedName ($port['remote_object_name'], $port['remote_object_tid']);
1734 echo "<td class=tdleft>" .
1735 formatLoggedSpan ($port['last_log'], formatPortLink ($port['remote_object_id'], $dname, $port['remote_id'], NULL)) .
0b09238d 1736 "</td>";
f4d00faf 1737 echo "<td class=tdleft> " . formatLoggedSpan ($port['last_log'], $port['remote_name'], 'underline') .
0b09238d
DO
1738 "<input type=hidden name=reservation_comment value=''></td>";
1739 echo "<td><input type=text name=cable value='${port['cableid']}'></td>";
1740 echo "<td class=tdcenter>";
1741 echo getOpLink (array('op'=>'unlinkPort', 'port_id'=>$port['id'], ), '', 'cut', 'Unlink this port');
1742 echo "</td>";
e673ee24 1743 }
33973968 1744 elseif ($port['reservation_comment'] != '')
e673ee24 1745 {
0b09238d 1746 echo "<td>" . formatLoggedSpan ($port['last_log'], 'Reserved:', 'strong underline') . "</td>";
e673ee24 1747 echo "<td><input type=text name=reservation_comment value='${port['reservation_comment']}'></td>";
318c48a8 1748 echo "<td></td>";
10f5133a
AA
1749 echo "<td class=tdcenter>";
1750 echo getOpLink (array('op'=>'useup', 'port_id'=>$port['id']), '', 'clear', 'Use up this port');
1751 echo "</td>";
e673ee24
DO
1752 }
1753 else
1754 {
6b1ca530 1755 $in_rack = getConfigVar ('NEAREST_RACKS_CHECKBOX');
de55c069
AA
1756 echo "<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td class=tdcenter>";
1757 echo getPopupLink ('portlist', array ('port' => $port['id'], 'in_rack' => ($in_rack == "yes" ? "on" : "")), 'findlink', 'plug', '', 'Link this port');
0b09238d 1758 echo " <input type=text name=reservation_comment></td>\n";
90bf1755 1759 }
0b09238d
DO
1760 echo "<td>";
1761 printImageHREF ('save', 'Save changes', TRUE);
1762 echo "</td></form></tr>\n";
e673ee24 1763 }
39b4abd0 1764 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
3153a326 1765 printNewItemTR ($prefs);
e673ee24 1766 echo "</table><br>\n";
70c0cb08
DO
1767 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes' && getConfigVar ('ENABLE_BULKPORT_FORM') == 'yes')
1768 printBulkForm ($prefs);
e673ee24 1769 finishPortlet();
70c0cb08
DO
1770
1771 if (getConfigVar ('ENABLE_MULTIPORT_FORM') == 'yes' && permitted (NULL, NULL, 'addMultiPorts'))
1772 {
1773 startPortlet ('Add/update multiple ports');
1774 printOpFormIntro ('addMultiPorts');
1775 $formats = array
1776 (
1777 'ssv1' => 'SSV:<interface name> [<MAC address>]',
1778 );
1779 echo 'Format: ' . getSelect ($formats, array ('name' => 'format'), 'ssv1') . ' ';
1780 echo 'Default port type: ';
1781 printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type'), $prefs['selected']);
1782 echo "<input type=submit value='Parse output'><br>\n";
1783 echo "<textarea name=input cols=100 rows=50></textarea><br>\n";
1784 echo '</form>';
1785 finishPortlet();
1786 }
e673ee24
DO
1787}
1788
d10401bf 1789function renderIPForObject ($object_id)
e673ee24 1790{
15e27821 1791 function printNewItemTR ($default_type, $object_id)
21ee3351
AA
1792 {
1793 global $aat;
15e27821 1794
1795 includeJQueryUI (TRUE);
1796
1797 addJS (<<<JSEND
1798 $(document).ready( function() {
1799 $('[name="bond_name"]').autocomplete({
1800 source: "?module=ajax&ac=autocomplete&realm=bond_name&object_id=$object_id",
1801 //minLength: 3,
1802 focus: function(event, ui) {
1803 if( ui.item.value == '' )
1804 event.preventDefault();
1805 },
1806 select: function(event, ui) {
1807 if( ui.item.value == '' )
1808 event.preventDefault();
1809 }
1810 });
1811 });
1812JSEND
1813 , TRUE);
4318ced5
AA
1814 printOpFormIntro ('add');
1815 echo "<tr><td>"; // left btn
dec748f6 1816 printImageHREF ('add', 'allocate', TRUE);
21ee3351 1817 echo "</td>";
04c61e7e
AB
1818 echo "<td class=tdleft><input type='text' size='10' name='bond_name'></td>\n"; // if-name
1819 echo "<td class=tdleft><input type=text name='ip'></td>\n"; // IP
4318ced5
AA
1820 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
1821 echo "<td colspan=2>&nbsp;</td>"; // network, routed by
1822 echo '<td>';
04c61e7e 1823 printSelect ($aat, array ('name' => 'bond_type'), $default_type); // type
4318ced5 1824 echo "</td><td>&nbsp;</td><td>"; // misc
04c61e7e 1825 printImageHREF ('add', 'allocate', TRUE); // right btn
21ee3351
AA
1826 echo "</td></tr></form>";
1827 }
21ee3351 1828 global $aat;
0d5ef6f0 1829
21ee3351 1830 startPortlet ('Allocations');
4318ced5
AA
1831 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'><tr>\n";
1832 echo '<th>&nbsp;</th>';
1833 echo '<th>OS interface</th>';
1834 echo '<th>IP address</th>';
21ee3351 1835 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
4318ced5
AA
1836 {
1837 echo '<th>network</th>';
1838 echo '<th>routed by</th>';
1839 }
1840 echo '<th>type</th>';
1841 echo '<th>misc</th>';
b1b07891 1842 echo '<th>&nbsp;</th>';
4318ced5 1843 echo '</tr>';
21ee3351 1844
710ead7e
AA
1845 $alloc_list = ''; // most of the output is stored here
1846 $used_alloc_types = array();
4318ced5 1847 foreach (getObjectIPAllocations ($object_id) as $alloc)
21ee3351 1848 {
710ead7e
AA
1849 if (! isset ($used_alloc_types[$alloc['type']]))
1850 $used_alloc_types[$alloc['type']] = 0;
1851 $used_alloc_types[$alloc['type']]++;
1852
40c9fc94 1853 $rendered_alloc = callHook ('getRenderedAlloc', $object_id, $alloc);
4318ced5 1854 $alloc_list .= getOutputOf ('printOpFormIntro', 'upd', array ('ip' => $alloc['addrinfo']['ip']));
710ead7e 1855 $alloc_list .= "<tr class='${rendered_alloc['tr_class']}' valign=top>";
f3505b35 1856
10f5133a 1857 $alloc_list .= "<td>" . getOpLink (array ('op' => 'del', 'ip' => $alloc['addrinfo']['ip']), '', 'delete', 'Delete this IP address') . "</td>";
40c9fc94 1858 $alloc_list .= "<td class=tdleft><input type='text' name='bond_name' value='${alloc['osif']}' size=10>" . $rendered_alloc['td_name_suffix'] . "</td>";
710ead7e 1859 $alloc_list .= $rendered_alloc['td_ip'];
21ee3351
AA
1860 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
1861 {
710ead7e
AA
1862 $alloc_list .= $rendered_alloc['td_network'];
1863 $alloc_list .= $rendered_alloc['td_routed_by'];
21ee3351 1864 }
710ead7e
AA
1865 $alloc_list .= '<td>' . getSelect ($aat, array ('name' => 'bond_type'), $alloc['type']) . "</td>";
1866 $alloc_list .= $rendered_alloc['td_peers'];
1867 $alloc_list .= "<td>" .getImageHREF ('save', 'Save changes', TRUE) . "</td>";
f3505b35 1868
710ead7e 1869 $alloc_list .= "</form></tr>\n";
21ee3351 1870 }
710ead7e 1871 asort ($used_alloc_types, SORT_NUMERIC);
589c027b 1872 $most_popular_type = ! count ($used_alloc_types) ? 'regular' : array_last (array_keys ($used_alloc_types));
710ead7e
AA
1873
1874 if ($list_on_top = (getConfigVar ('ADDNEW_AT_TOP') != 'yes'))
1875 echo $alloc_list;
15e27821 1876 printNewItemTR ($most_popular_type, $object_id);
710ead7e
AA
1877 if (! $list_on_top)
1878 echo $alloc_list;
21ee3351
AA
1879
1880 echo "</table><br>\n";
1881 finishPortlet();
f3505b35
AA
1882}
1883
08408472
AA
1884// This function is deprecated. Do not rely on its internals,
1885// it will probably be removed in the next major relese.
1886// Use new showError, showWarning, showSuccess functions.
dec748f6 1887// Log array is stored in global $log_messages. Its format is simple: plain ordered array
8ab645cf 1888// with values having keys 'c' (both message code and severity) and 'a' (sprintf arguments array)
1cb6adf5 1889function showMessageOrError ()
e673ee24 1890{
a2b16202
AA
1891 global $log_messages;
1892
0a00b344 1893 startSession();
a2b16202
AA
1894 if (isset ($_SESSION['log']))
1895 {
1896 $log_messages = array_merge ($_SESSION['log'], $log_messages);
1897 unset ($_SESSION['log']);
1898 }
1899 session_commit();
1900
1901 if (empty ($log_messages))
1cb6adf5 1902 return;
8ab645cf
AA
1903 $msginfo = array
1904 (
dec748f6 1905// records 0~99 with success messages
8ab645cf
AA
1906 0 => array ('code' => 'success', 'format' => '%s'),
1907 5 => array ('code' => 'success', 'format' => 'added record "%s" successfully'),
1908 6 => array ('code' => 'success', 'format' => 'updated record "%s" successfully'),
1909 7 => array ('code' => 'success', 'format' => 'deleted record "%s" successfully'),
1910 8 => array ('code' => 'success', 'format' => 'Port %s successfully linked with %s'),
493e13dd 1911 10 => array ('code' => 'success', 'format' => 'Added %u port(s), updated %u port(s), encountered %u error(s).'),
8ab645cf 1912 21 => array ('code' => 'success', 'format' => 'Generation complete'),
493e13dd
DO
1913 26 => array ('code' => 'success', 'format' => 'updated %u record(s) successfully'),
1914 37 => array ('code' => 'success', 'format' => 'added %u record(s) successfully'),
1915 38 => array ('code' => 'success', 'format' => 'removed %u record(s) successfully'),
8ab645cf 1916 43 => array ('code' => 'success', 'format' => 'Saved successfully.'),
493e13dd 1917 44 => array ('code' => 'success', 'format' => '%s failure(s) and %s successfull change(s).'),
8ab645cf
AA
1918 48 => array ('code' => 'success', 'format' => 'added a record successfully'),
1919 49 => array ('code' => 'success', 'format' => 'deleted a record successfully'),
1920 51 => array ('code' => 'success', 'format' => 'updated a record successfully'),
1921 57 => array ('code' => 'success', 'format' => 'Reset complete'),
04eee8d2 1922 58 => array ('code' => 'success', 'format' => '%u device(s) unmounted successfully'),
8ab645cf 1923 63 => array ('code' => 'success', 'format' => '%u change request(s) have been processed'),
493e13dd 1924 67 => array ('code' => 'success', 'format' => "Tag rolling done, %u object(s) involved"),
8ab645cf
AA
1925 71 => array ('code' => 'success', 'format' => 'File "%s" was linked successfully'),
1926 72 => array ('code' => 'success', 'format' => 'File was unlinked successfully'),
493e13dd
DO
1927 82 => array ('code' => 'success', 'format' => "Bulk port creation was successful. %u port(s) created, %u failed"),
1928 87 => array ('code' => 'success', 'format' => '802.1Q recalculate: %d port(s) changed on %d switch(es)'),
8628ae44 1929// records 100~199 with fatal error messages
8ab645cf
AA
1930 100 => array ('code' => 'error', 'format' => '%s'),
1931 109 => array ('code' => 'error', 'format' => 'failed updating a record'),
8ab645cf 1932 131 => array ('code' => 'error', 'format' => 'invalid format requested'),
493e13dd 1933 141 => array ('code' => 'error', 'format' => 'Encountered %u error(s), updated %u record(s)'),
8ab645cf
AA
1934 149 => array ('code' => 'error', 'format' => 'Turing test failed'),
1935 150 => array ('code' => 'error', 'format' => 'Can only change password under DB authentication.'),
1936 151 => array ('code' => 'error', 'format' => 'Old password doesn\'t match.'),
1937 152 => array ('code' => 'error', 'format' => 'New passwords don\'t match.'),
1938 154 => array ('code' => 'error', 'format' => "Verification error: %s"),
1939 155 => array ('code' => 'error', 'format' => 'Save failed.'),
1940 159 => array ('code' => 'error', 'format' => 'Permission denied moving port %s from VLAN%u to VLAN%u'),
8ab645cf
AA
1941 170 => array ('code' => 'error', 'format' => 'There is no network for IP address "%s"'),
1942 172 => array ('code' => 'error', 'format' => 'Malformed request'),
1943 179 => array ('code' => 'error', 'format' => 'Expired form has been declined.'),
8ab645cf 1944 191 => array ('code' => 'error', 'format' => "deploy was blocked due to conflicting configuration versions"),
f0ff4930 1945
8628ae44 1946// records 200~299 with warnings
8ab645cf
AA
1947 200 => array ('code' => 'warning', 'format' => '%s'),
1948 201 => array ('code' => 'warning', 'format' => 'nothing happened...'),
e1add254 1949 206 => array ('code' => 'warning', 'format' => '%s is not empty'),
7587edca 1950 207 => array ('code' => 'warning', 'format' => 'File upload failed, error: %s'),
57777e4a 1951
24d3182a 1952// records 300~399 with notices
8ab645cf 1953 300 => array ('code' => 'neutral', 'format' => '%s'),
24d3182a 1954
8ab645cf
AA
1955 );
1956 // Handle the arguments. Is there any better way to do it?
a2b16202 1957 foreach ($log_messages as $record)
8ab645cf 1958 {
55eefced 1959 if (! isset ($record['c']) || ! isset ($msginfo[$record['c']]))
8ab645cf 1960 {
41152018
AA
1961 $prefix = isset ($record['c']) ? $record['c'] . ': ' : '';
1962 echo "<div class=msg_neutral>(${prefix}this message was lost)</div>";
8ab645cf
AA
1963 continue;
1964 }
1965 if (isset ($record['a']))
1966 switch (count ($record['a']))
baf37d01 1967 {
8ab645cf
AA
1968 case 1:
1969 $msgtext = sprintf
1970 (
1971 $msginfo[$record['c']]['format'],
1972 $record['a'][0]
1973 );
1974 break;
1975 case 2:
1976 $msgtext = sprintf
1977 (
1978 $msginfo[$record['c']]['format'],
1979 $record['a'][0],
1980 $record['a'][1]
1981 );
1982 break;
1983 case 3:
1984 $msgtext = sprintf
1985 (
1986 $msginfo[$record['c']]['format'],
1987 $record['a'][0],
1988 $record['a'][1],
1989 $record['a'][2]
1990 );
1991 break;
1992 case 4:
1993 default:
1994 $msgtext = sprintf
1995 (
1996 $msginfo[$record['c']]['format'],
1997 $record['a'][0],
1998 $record['a'][1],
1999 $record['a'][2],
2000 $record['a'][3]
2001 );
2002 break;
baf37d01 2003 }
8ab645cf
AA
2004 else
2005 $msgtext = $msginfo[$record['c']]['format'];
2006 echo '<div class=msg_' . $msginfo[$record['c']]['code'] . ">${msgtext}</div>";
baf37d01 2007 }
a2b16202 2008 $log_messages = array();
e673ee24
DO
2009}
2010
65e557dd 2011// renders two tables: port link status and learned MAC list
d10401bf 2012function renderPortsInfo($object_id)
65e557dd 2013{
2d33a804 2014 $breed = detectDeviceBreed ($object_id);
03522bd8
AA
2015 try
2016 {
2017 if (permitted (NULL, NULL, 'get_link_status'))
2d33a804 2018 {
2019 if (validBreedFunction ($breed, 'getportstatus'))
2020 $linkStatus = queryDevice ($object_id, 'getportstatus');
2021 }
03522bd8
AA
2022 else
2023 showWarning ("You don't have permission to view ports link status");
2024
2025 if (permitted (NULL, NULL, 'get_mac_list'))
2d33a804 2026 {
2027 if (validBreedFunction ($breed, 'getmaclist'))
2028 $macList = sortPortList (queryDevice ($object_id, 'getmaclist'));
2029 }
03522bd8
AA
2030 else
2031 showWarning ("You don't have permission to view learned MAC list");
2032 }
2033 catch (RTGatewayError $e)
2034 {
2035 showError ($e->getMessage());
2036 return;
2037 }
2038
65e557dd
AA
2039 global $nextorder;
2040 echo "<table width='100%'><tr>";
03522bd8 2041 if (! empty ($linkStatus))
65e557dd 2042 {
03522bd8
AA
2043 echo "<td valign='top' width='50%'>";
2044 startPortlet('Link status');
c51492f5
DO
2045 echo "<table width='80%' class='widetable' cellspacing=0 cellpadding='5px' align='center'>";
2046 echo '<tr><th>Port</th><th>&nbsp;</th><th>Link status</th><th>Link info</th></tr>';
72c3bebc
DO
2047 $statusmap = array
2048 (
2049 'up' => 'link up',
2050 'down' => 'link down',
2051 'disabled' => 'link disabled',
2052 );
03522bd8
AA
2053 $order = 'even';
2054 foreach ($linkStatus as $pn => $link)
65e557dd 2055 {
03522bd8
AA
2056 echo "<tr class='row_$order'>";
2057 $order = $nextorder[$order];
c51492f5
DO
2058 echo '<td>' . $pn . '</td>';
2059 echo '<td>' . getImageHREF (array_fetch ($statusmap, $link['status'], '16x16t')) . '</td>';
2060 echo '<td>' . $link['status'] . '</td>';
03522bd8
AA
2061 $info = '';
2062 if (isset ($link['speed']))
2063 $info .= $link['speed'];
2064 if (isset ($link['duplex']))
2065 {
cfb42424 2066 if ($info != '')
03522bd8
AA
2067 $info .= ', ';
2068 $info .= $link['duplex'];
65e557dd 2069 }
c51492f5 2070 echo '<td>' . $info . '</td>';
03522bd8 2071 echo '</tr>';
65e557dd 2072 }
03522bd8
AA
2073 echo "</table></td>";
2074 finishPortlet();
65e557dd
AA
2075 }
2076
65e557dd
AA
2077 if (! empty ($macList))
2078 {
2079 echo "<td valign='top' width='50%'>";
2080 $rendered_macs = '';
2081 $mac_count = 0;
c51492f5
DO
2082 $rendered_macs .= "<table width='80%' class='widetable' cellspacing=0 cellpadding='5px' align='center'>";
2083 $rendered_macs .= '<tr><th>MAC address</th><th>VLAN</th><th>Port</th></tr>';
65e557dd
AA
2084 $order = 'even';
2085 foreach ($macList as $pn => $list)
2086 {
2087 $order = $nextorder[$order];
2088 foreach ($list as $item)
2089 {
2090 ++$mac_count;
2091 $rendered_macs .= "<tr class='row_$order'>";
c51492f5
DO
2092 $rendered_macs .= '<td style="font-family: monospace">' . $item['mac'] . '</td>';
2093 $rendered_macs .= '<td>' . $item['vid'] . '</td>';
2094 $rendered_macs .= '<td>' . $pn . '</td>';
65e557dd
AA
2095 $rendered_macs .= '</tr>';
2096 }
2097 }
2098 $rendered_macs .= "</table></td>";
dec748f6 2099
65e557dd
AA
2100 startPortlet("Learned MACs ($mac_count)");
2101 echo $rendered_macs;
2102 finishPortlet();
2103 }
2104
2105 echo "</td></tr></table>";
2106}
2107
e673ee24 2108/*
51c18782 2109The following conditions must be met:
e673ee24
DO
21101. We can mount onto free atoms only. This means: if any record for an atom
2111already exists in RackSpace, it can't be used for mounting.
21122. We can't unmount from 'W' atoms. Operator should review appropriate comments
2113and either delete them before unmounting or refuse to unmount the object.
2114*/
d10401bf 2115function renderRackSpaceForObject ($object_id)
e673ee24 2116{
e673ee24
DO
2117 // Always process occupied racks plus racks chosen by user. First get racks with
2118 // already allocated rackspace...
51c18782 2119 $workingRacksData = getResidentRacksData ($object_id);
e673ee24 2120 // ...and then add those chosen by user (if any).
2135fd83 2121 if (isset($_REQUEST['rackmulti']))
e673ee24 2122 foreach ($_REQUEST['rackmulti'] as $cand_id)
e673ee24
DO
2123 if (!isset ($workingRacksData[$cand_id]))
2124 {
0cc24e9a 2125 $rackData = spotEntity ('rack', $cand_id);
61a1d996 2126 amplifyCell ($rackData);
e673ee24
DO
2127 $workingRacksData[$cand_id] = $rackData;
2128 }
51690ad4 2129
c17a36f0
AA
2130 // Get a list of rack ids that are parents of this object.
2131 $object = spotEntity ('object', $object_id);
2132 $parentRacks = reduceSubarraysToColumn (getParents ($object, 'rack'), 'id');
0a7feebb 2133
e673ee24
DO
2134 // Main layout starts.
2135 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0><tr>";
2136
2137 // Left portlet with rack list.
2138 echo "<td class=pcleft height='1%'>";
2139 startPortlet ('Racks');
a8efc03e 2140 $allRacksData = listCells ('rack');
6d42599c
AA
2141
2142 // filter rack list to match only racks having common tags with the object (reducing $allRacksData)
55eefced 2143 if (! isset ($_REQUEST['show_all_racks']) && getConfigVar ('FILTER_RACKLIST_BY_TAGS') == 'yes')
6d42599c
AA
2144 {
2145 $matching_racks = array();
6d42599c
AA
2146 $matched_tags = array();
2147 foreach ($allRacksData as $rack)
01205bb6 2148 {
b51c1007 2149 $tag_chain = array_merge ($rack['etags'], $rack['itags']);
6d42599c 2150 foreach ($object['etags'] as $tag)
01205bb6 2151 if (tagOnChain ($tag, $tag_chain))
6d42599c
AA
2152 {
2153 $matching_racks[$rack['id']] = $rack;
2154 $matched_tags[$tag['id']] = $tag;
2155 break;
2156 }
01205bb6 2157 }
6d42599c
AA
2158 // add current object's racks even if they dont match filter
2159 foreach ($workingRacksData as $rack_id => $rack)
2160 if (! isset ($matching_racks[$rack_id]))
2161 $matching_racks[$rack_id] = $rack;
2162 // if matching racks found, and rack list is reduced, show 'show all' link
55eefced 2163 if (count ($matching_racks) && count ($matching_racks) != count ($allRacksData))
6d42599c 2164 {
1e9266ad 2165 $tmp = array();
6d42599c 2166 foreach ($matched_tags as $tag)
1e9266ad
DO
2167 $tmp[] = '{' . $tag['tag'] . '}';
2168 $filter_text = implode (' or ', $tmp);
6d42599c
AA
2169 $href_show_all = trim($_SERVER['REQUEST_URI'], '&');
2170 $href_show_all .= htmlspecialchars('&show_all_racks=1');
2171 echo "(filtered by <span class='filter-text'>$filter_text</span>, <a href='$href_show_all'>show all</a>)<p>";
2172 $allRacksData = $matching_racks;
2173 }
2174 }
2175
4b8d413e 2176 if (count ($allRacksData) <= getConfigVar ('RACK_PRESELECT_THRESHOLD'))
61a1d996 2177 foreach ($allRacksData as $rack)
51c18782
DO
2178 if (!array_key_exists ($rack['id'], $workingRacksData))
2179 {
2180 amplifyCell ($rack);
2181 $workingRacksData[$rack['id']] = $rack;
2182 }
2183 foreach (array_keys ($workingRacksData) as $rackId)
2184 applyObjectMountMask ($workingRacksData[$rackId], $object_id);
2185 printOpFormIntro ('updateObjectAllocation');
4b8d413e 2186 renderRackMultiSelect ('rackmulti[]', $allRacksData, array_keys ($workingRacksData));
51c18782 2187 echo "<br><br>";
e673ee24
DO
2188 finishPortlet();
2189 echo "</td>";
2190
2191 // Middle portlet with comment and submit.
2192 echo "<td class=pcleft>";
5c8bf2ba 2193 startPortlet ('Comment (for Rackspace History)');
e673ee24
DO
2194 echo "<textarea name=comment rows=10 cols=40></textarea><br>\n";
2195 echo "<input type=submit value='Save' name=got_atoms>\n";
51c18782 2196 echo "<br><br>";
e673ee24
DO
2197 finishPortlet();
2198 echo "</td>";
2199
2200 // Right portlet with rendered racks. If this form submit is not final, we have to
2201 // reflect the former state of the grid in current form.
2202 echo "<td class=pcright rowspan=2 height='1%'>";
2203 startPortlet ('Working copy');
e8b94513 2204 includeJQueryUI (false);
65e557dd 2205 addJS ('js/racktables.js');
c82023ad 2206 addJS ('js/bulkselector.js');
e673ee24
DO
2207 echo '<table border=0 cellspacing=10 align=center><tr>';
2208 foreach ($workingRacksData as $rack_id => $rackData)
2209 {
c26d97b6 2210 $is_ro = !rackModificationPermitted ($rackData, 'updateObjectAllocation');
e673ee24
DO
2211 // Order is important here: only original allocation is highlighted.
2212 highlightObject ($rackData, $object_id);
2213 markupAtomGrid ($rackData, 'T');
2214 // If we have a form processed, discard user input and show new database
2215 // contents.
c26d97b6 2216 if (!$is_ro && isset ($_REQUEST['rackmulti'][0])) // is an update
e673ee24
DO
2217 mergeGridFormToRack ($rackData);
2218 echo "<td valign=top>";
c26d97b6
VS
2219 echo "<center>\n<h2 style='margin:0px'>${rackData['name']}</h2>\n";
2220 $read_only_text = $is_ro ? '(read-only)' : '&nbsp;';
2221 echo "<p style='color: red; margin-top:0px'>${read_only_text}</p>\n";
e8b94513 2222 echo "<table class=rack id=selectableRack border=0 cellspacing=0 cellpadding=1>\n";
57c69e9c 2223 echo "<tr><th width='10%'>&nbsp;</th>";
bd0bfb76
AD
2224 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
2225 echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
2226 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
c26d97b6 2227 renderAtomGrid ($rackData, $is_ro);
57c69e9c 2228 echo "<tr><th width='10%'>&nbsp;</th>";
bd0bfb76
AD
2229 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
2230 echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
2231 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
0a7feebb
AD
2232 echo "</table>\n<br>\n";
2233 // Determine zero-u checkbox status.
2234 // If form has been submitted, use form data, otherwise use DB data.
c26d97b6 2235 if (!$is_ro && isset($_REQUEST['op']))
0a7feebb
AD
2236 $checked = isset($_REQUEST['zerou_'.$rack_id]) ? 'checked' : '';
2237 else
2238 $checked = in_array($rack_id, $parentRacks) ? 'checked' : '';
c26d97b6
VS
2239 $disabled_text = $is_ro ? ' disabled' : '';
2240 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 2241 echo "<input type='button' onclick='uncheckAll();' value='Uncheck all'>\n";
0a7feebb 2242 echo '</center></td>';
e673ee24
DO
2243 }
2244 echo "</tr></table>";
2245 finishPortlet();
2246 echo "</td>\n";
2247
2248 echo "</form>\n";
2249 echo "</tr></table>\n";
2250}
2251
2252function renderMolecule ($mdata, $object_id)
2253{
2254 // sort data out
2255 $rackpack = array();
2256 global $loclist;
64b95774 2257 foreach ($mdata as $rua)
e673ee24
DO
2258 {
2259 $rack_id = $rua['rack_id'];
2260 $unit_no = $rua['unit_no'];
2261 $atom = $rua['atom'];
2262 if (!isset ($rackpack[$rack_id]))
2263 {
61a1d996
DO
2264 $rackData = spotEntity ('rack', $rack_id);
2265 amplifyCell ($rackData);
e673ee24
DO
2266 for ($i = $rackData['height']; $i > 0; $i--)
2267 for ($locidx = 0; $locidx < 3; $locidx++)
2268 $rackData[$i][$locidx]['state'] = 'F';
2269 $rackpack[$rack_id] = $rackData;
2270 }
2271 $rackpack[$rack_id][$unit_no][$loclist[$atom]]['state'] = 'T';
2272 $rackpack[$rack_id][$unit_no][$loclist[$atom]]['object_id'] = $object_id;
2273 }
2274 // now we have some racks to render
64b95774 2275 foreach ($rackpack as $rackData)
e673ee24
DO
2276 {
2277 markAllSpans ($rackData);
2278 echo "<table class=molecule cellspacing=0>\n";
2279 echo "<caption>${rackData['name']}</caption>\n";
2280 echo "<tr><th width='10%'>&nbsp;</th><th width='20%'>Front</th><th width='50%'>Interior</th><th width='20%'>Back</th></tr>\n";
2281 for ($i = $rackData['height']; $i > 0; $i--)
2282 {
4dcd770e 2283 echo "<tr><th>" . inverseRackUnit ($i, $rackData) . "</th>";
e673ee24
DO
2284 for ($locidx = 0; $locidx < 3; $locidx++)
2285 {
2286 $state = $rackData[$i][$locidx]['state'];
65e557dd 2287 echo "<td class='atom state_${state}'>&nbsp;</td>\n";
e673ee24
DO
2288 }
2289 echo "</tr>\n";
2290 }
2291 echo "</table>\n";
2292 }
2293}
2294
23cdc7e9 2295function renderDepot ()
e673ee24 2296{
23cdc7e9 2297 global $pageno, $nextorder;
23cdc7e9 2298 $cellfilter = getCellFilter();
950dfa1f
AA
2299 $objects = array();
2300 $objects_count = getEntitiesCount ('object');
db8255ac 2301 $showobjecttype = (getConfigVar ('SHOW_OBJECTTYPE') == 'yes');
23cdc7e9
DO
2302
2303 echo "<table border=0 class=objectview>\n";
2304 echo "<tr><td class=pcleft>";
2305
950dfa1f
AA
2306 if ($objects_count == 0)
2307 echo '<h2>No objects exist</h2>';
2308 // 1st attempt: do not fetch all objects if cellfilter is empty and rendering empty result is enabled
2309 elseif (! ($cellfilter['is_empty'] && renderEmptyResults ($cellfilter, 'objects', $objects_count)))
e673ee24 2310 {
2b16c179 2311 $objects = applyCellFilter ('object', $cellfilter);
20506de3 2312 // 2nd attempt: do not render all fetched objects if rendering empty result is enabled
950dfa1f 2313 if (! renderEmptyResults ($cellfilter, 'objects', count($objects)))
1ebbf889 2314 {
4899096d
AD
2315 startPortlet ('Objects (' . count ($objects) . ')');
2316 echo '<br><br><table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
db8255ac
J
2317 echo '<tr><th>Common name</th>';
2318 if ($showobjecttype)
2319 echo '<th>Type</th>';
2320 echo '<th>Visible label</th><th>Asset tag</th><th>Row/Rack or Container</th></tr>';
4899096d 2321 $order = 'odd';
3f8a25d6
DO
2322 # gather IDs of all objects and fetch rackspace info in one pass
2323 $idlist = array();
2324 foreach ($objects as $obj)
2325 $idlist[] = $obj['id'];
2326 $mountinfo = getMountInfo ($idlist);
cae81598 2327 $containerinfo = getContainerInfo ($idlist);
4899096d
AD
2328 foreach ($objects as $obj)
2329 {
ec788aba
AD
2330 $problem = ($obj['has_problems'] == 'yes') ? 'has_problems' : '';
2331 echo "<tr class='row_${order} tdleft ${problem}' valign=top><td>" . mkA ("<strong>${obj['dname']}</strong>", 'object', $obj['id']);
4899096d
AD
2332 if (count ($obj['etags']))
2333 echo '<br><small>' . serializeTags ($obj['etags'], makeHref(array('page'=>$pageno, 'tab'=>'default')) . '&') . '</small>';
db8255ac
J
2334 echo '</td>';
2335 if ($showobjecttype)
9563f56f 2336 echo "<td>" . decodeObjectType ($obj['objtype_id']) . "</td>";
8526b4cc 2337 echo "<td>${obj['label']}</td>";
4318ced5 2338 echo "<td>${obj['asset_no']}</td>";
3f8a25d6 2339 $places = array();
cae81598
AD
2340 if (array_key_exists ($obj['id'], $containerinfo))
2341 foreach ($containerinfo[$obj['id']] as $ci)
20506de3 2342 $places[] = mkA ($ci['container_dname'], 'object', $ci['container_id']);
cae81598 2343 if (array_key_exists ($obj['id'], $mountinfo))
3f8a25d6
DO
2344 foreach ($mountinfo[$obj['id']] as $mi)
2345 $places[] = mkA ($mi['row_name'], 'row', $mi['row_id']) . '/' . mkA ($mi['rack_name'], 'rack', $mi['rack_id']);
cae81598
AD
2346 if (! count ($places))
2347 $places[] = 'Unmounted';
4318ced5 2348 echo "<td>" . implode (', ', $places) . '</td>';
4899096d
AD
2349 echo '</tr>';
2350 $order = $nextorder[$order];
2351 }
2352 echo '</table>';
2353 finishPortlet();
1ebbf889 2354 }
e673ee24 2355 }
23cdc7e9
DO
2356
2357 echo "</td><td class=pcright width='25%'>";
2358
95857b5c 2359 renderCellFilterPortlet ($cellfilter, 'object', $objects);
23cdc7e9 2360 echo "</td></tr></table>\n";
e673ee24
DO
2361}
2362
1ebbf889
AA
2363// This function returns TRUE if the result set is too big to be rendered, and no filter is set.
2364// In this case it renders the describing message instead.
2365function renderEmptyResults($cellfilter, $entities_name, $count = NULL)
2366{
2367 if (!$cellfilter['is_empty'])
2368 return FALSE;
2369 if (isset ($_REQUEST['show_all_objects']))
2370 return FALSE;
2371 $max = intval(getConfigVar('MAX_UNFILTERED_ENTITIES'));
2372 if (0 == $max || $count <= $max)
2373 return FALSE;
2374
2375 $href_show_all = trim($_SERVER['REQUEST_URI'], '&');
2376 $href_show_all .= htmlspecialchars('&show_all_objects=1');
2377 $suffix = isset ($count) ? " ($count)" : '';
2378 echo <<<END
2379<p>Please set a filter to display the corresponging $entities_name.
2380<br><a href="$href_show_all">Show all $entities_name$suffix</a>
2381END;
2382 return TRUE;
2383}
2384
e673ee24 2385// History viewer for history-enabled simple dictionaries.
9b8174d7 2386function renderObjectHistory ($object_id)
e673ee24 2387{
e673ee24
DO
2388 $order = 'odd';
2389 global $nextorder;
9b8174d7
AD
2390 echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
2391 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>';
2392 $result = usePreparedSelectBlade
2393 (
2394 'SELECT ctime, user_name, name, label, asset_no, has_problems, comment FROM ObjectHistory WHERE id=? ORDER BY ctime',
2395 array ($object_id)
2396 );
e673ee24
DO
2397 while ($row = $result->fetch (PDO::FETCH_NUM))
2398 {
2399 echo "<tr class=row_${order}><td>${row[0]}</td>";
9b8174d7 2400 for ($i = 1; $i <= 6; $i++)
e673ee24
DO
2401 echo "<td>" . $row[$i] . "</td>";
2402 echo "</tr>\n";
2403 $order = $nextorder[$order];
2404 }
2405 echo "</table><br>\n";
2406}
2407
2408function renderRackspaceHistory ()
2409{
4fbb5a00 2410 global $nextorder, $pageno, $tabno;
e673ee24
DO
2411 $order = 'odd';
2412 $history = getRackspaceHistory();
2413 // Show the last operation by default.
2414 if (isset ($_REQUEST['op_id']))
2415 $op_id = $_REQUEST['op_id'];
2416 elseif (isset ($history[0]['mo_id']))
2417 $op_id = $history[0]['mo_id'];
2418 else $op_id = NULL;
dec748f6 2419
e673ee24
DO
2420 $omid = NULL;
2421 $nmid = NULL;
2422 $object_id = 1;
2423 if ($op_id)
2424 list ($omid, $nmid) = getOperationMolecules ($op_id);
2425
2426 // Main layout starts.
2427 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
2428
2429 // Left top portlet with old allocation.
2430 echo "<tr><td class=pcleft>";
2431 startPortlet ('Old allocation');
2432 if ($omid)
2433 {
2434 $oldMolecule = getMolecule ($omid);
2435 renderMolecule ($oldMolecule, $object_id);
2436 }
2437 else
2438 echo "nothing";
2439 finishPortlet();
2440
2441 echo '</td><td class=pcright>';
2442
2443 // Right top portlet with new allocation
2444 startPortlet ('New allocation');
2445 if ($nmid)
2446 {
2447 $newMolecule = getMolecule ($nmid);
2448 renderMolecule ($newMolecule, $object_id);
2449 }
2450 else
2451 echo "nothing";
2452 finishPortlet();
737a3f72 2453
e673ee24 2454 echo '</td></tr><tr><td colspan=2>';
737a3f72 2455
e673ee24
DO
2456 // Bottom portlet with list
2457
2458 startPortlet ('Rackspace allocation history');
2459 echo "<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>\n";
7fa7047a 2460 echo "<tr><th>timestamp</th><th>author</th><th>object</th><th>comment</th></tr>\n";
e673ee24
DO
2461 foreach ($history as $row)
2462 {
2463 if ($row['mo_id'] == $op_id)
2464 $class = 'hl';
2465 else
2466 $class = "row_${order}";
4fbb5a00 2467 echo "<tr class=${class}><td><a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'op_id'=>$row['mo_id']))."'>${row['ctime']}</a></td>";
7fa7047a
DO
2468 echo "<td>${row['user_name']}</td><td>";
2469 renderCell (spotEntity ('object', $row['ro_id']));
a0bdf6dc 2470 echo '</td><td>' . stringForTD ($row['comment'], 0) . '</td></tr>';
e673ee24
DO
2471 $order = $nextorder[$order];
2472 }
2473 echo "</table>\n";
2474 finishPortlet();
737a3f72 2475
e673ee24 2476 echo '</td></tr></table>';
e673ee24
DO
2477}
2478
4318ced5 2479function renderIPSpaceRecords ($tree, $baseurl, $target = 0, $level = 1)
d65353ad
DO
2480{
2481 $self = __FUNCTION__;
4318ced5 2482 $knight = (getConfigVar ('IPV4_ENABLE_KNIGHT') == 'yes');
774968ea 2483 $display_routers = (getConfigVar ('IPV4_TREE_RTR_AS_CELL') != 'none');
00276508
AA
2484
2485 // scroll page to the highlighted item
2486 if ($target && isset ($_REQUEST['hl_net']))
65e557dd 2487 addAutoScrollScript ("net-$target");
00276508 2488
d65353ad
DO
2489 foreach ($tree as $item)
2490 {
0137d53c
DO
2491 if (isset ($item['id']))
2492 {
1f54e1ba 2493 $decor = array ('indent' => $level);
fec0c8da 2494 if ($item['symbol'] == 'node-collapsed')
4318ced5 2495 $decor['symbolurl'] = "${baseurl}&eid=${item['id']}&hl_net=1";
fec0c8da 2496 elseif ($item['symbol'] == 'node-expanded')
4318ced5
AA
2497 $decor['symbolurl'] = $baseurl . ($item['parent_id'] ? "&eid=${item['parent_id']}&hl_net=1" : '');
2498 $tr_class = '';
21ee3351 2499 if ($target == $item['id'] && isset ($_REQUEST['hl_net']))
64347dcf 2500 {
5c717ce5 2501 $decor['tdclass'] = ' highlight';
4318ced5 2502 $tr_class = $decor['tdclass'];
64347dcf 2503 }
4318ced5
AA
2504 echo "<tr valign=top class=\"$tr_class\">";
2505 printIPNetInfoTDs ($item, $decor);
2506
2507 // capacity and usage
2508 echo "<td class=tdcenter>";
2509 echo getRenderedIPNetCapacity ($item);
64347dcf 2510 echo "</td>";
4318ced5
AA
2511
2512 if ($display_routers)
6063fab7 2513 printRoutersTD (findNetRouters ($item), getConfigVar ('IPV4_TREE_RTR_AS_CELL'));
0137d53c 2514 echo "</tr>";
55eefced 2515 if ($item['symbol'] == 'node-expanded' || $item['symbol'] == 'node-expanded-static')
4318ced5 2516 $self ($item['kids'], $baseurl, $target, $level + 1);
0137d53c 2517 }
26fc6e7c 2518 elseif (getConfigVar ('IPV4_TREE_SHOW_UNALLOCATED') == 'yes')
0137d53c 2519 {
4318ced5 2520 // non-allocated (spare) IP range
0137d53c 2521 echo "<tr valign=top>";
21ee3351 2522 printIPNetInfoTDs ($item, array ('indent' => $level, 'knight' => $knight, 'tdclass' => 'sparenetwork'));
00276508 2523
4318ced5 2524 // capacity and usage
21ee3351 2525 echo "<td class=tdcenter>";
4318ced5 2526 echo getRenderedIPNetCapacity ($item);
21ee3351 2527 echo "</td>";
4318ced5
AA
2528 if ($display_routers)
2529 echo "<td></td>";
21ee3351 2530 echo "</tr>";
21ee3351 2531 }
21ee3351
AA
2532 }
2533}
2534
4318ced5 2535function renderIPSpace()
e673ee24 2536{
4fbb5a00 2537 global $pageno, $tabno;
4318ced5 2538 $realm = ($pageno == 'ipv4space' ? 'ipv4net' : 'ipv6net');
31c941ec 2539 $cellfilter = getCellFilter();
21ee3351 2540
b99b4167
AA
2541 // expand request can take either natural values or "ALL". Zero means no expanding.
2542 $eid = isset ($_REQUEST['eid']) ? $_REQUEST['eid'] : 0;
2543
21ee3351
AA
2544 echo "<table border=0 class=objectview>\n";
2545 echo "<tr><td class=pcleft>";
950dfa1f
AA
2546
2547 $netlist = array();
b99b4167 2548 if (! ($cellfilter['is_empty'] && ! $eid && renderEmptyResults($cellfilter, 'IP nets', getEntitiesCount ($realm))))
21ee3351 2549 {
950dfa1f
AA
2550 $top = NULL;
2551 foreach (listCells ($realm) as $net)
21ee3351 2552 {
55eefced 2553 if (isset ($top) && IPNetContains ($top, $net))
950dfa1f 2554 ;
55eefced 2555 elseif (! count ($cellfilter['expression']) || judgeCell ($net, $cellfilter['expression']))
950dfa1f
AA
2556 $top = $net;
2557 else
2558 continue;
2559 $netlist[$net['id']] = $net;
2560 }
2561 $netcount = count ($netlist);
950dfa1f
AA
2562 $tree = prepareIPTree ($netlist, $eid);
2563
2564 if (! renderEmptyResults($cellfilter, 'IP nets', count($tree)))
2565 {
2566 startPortlet ("networks (${netcount})");
2567 echo '<h4>';
2568 $all = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'ALL')) .
2569 $cellfilter['urlextra'] . "'>expand&nbsp;all</a>";
2570 $none = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'NONE')) .
2571 $cellfilter['urlextra'] . "'>collapse&nbsp;all</a>";
2572 $auto = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno)) .
2573 $cellfilter['urlextra'] . "'>auto-collapse</a>";
2574
2575 if ($eid === 0)
2576 echo 'auto-collapsing at threshold ' . getConfigVar ('TREE_THRESHOLD') . " ($all / $none)";
2577 elseif ($eid === 'ALL')
2578 echo "expanding all ($auto / $none)";
2579 elseif ($eid === 'NONE')
2580 echo "collapsing all ($all / $auto)";
2581 else
2582 {
a0f0c0c7
AA
2583 try
2584 {
2585 $netinfo = spotEntity ($realm, $eid);
2586 echo "expanding ${netinfo['ip']}/${netinfo['mask']} ($auto / $all / $none)";
2587 }
2588 catch (EntityNotFoundException $e)
2589 {
2590 // ignore invalid eid error
2591 }
950dfa1f 2592 }
6edc799c 2593 echo "</h4><table class='widetable zebra' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
950dfa1f
AA
2594 echo "<tr><th>prefix</th><th>name/tags</th><th>capacity</th>";
2595 if (getConfigVar ('IPV4_TREE_RTR_AS_CELL') != 'none')
2596 echo "<th>routed by</th>";
2597 echo "</tr>\n";
2598 $baseurl = makeHref(array('page'=>$pageno, 'tab'=>$tabno)) . $cellfilter['urlextra'];
2599 renderIPSpaceRecords ($tree, $baseurl, $eid);
2600 echo "</table>\n";
2601 finishPortlet();
21ee3351 2602 }
21ee3351
AA
2603 }
2604
2605 echo '</td><td class=pcright>';
950dfa1f 2606 renderCellFilterPortlet ($cellfilter, $realm, $netlist);
21ee3351
AA
2607 echo "</td></tr></table>\n";
2608}
2609
4318ced5 2610function renderIPSpaceEditor()
21ee3351 2611{
4318ced5
AA
2612 global $pageno;
2613 $realm = ($pageno == 'ipv4space' ? 'ipv4net' : 'ipv6net');
4318ced5 2614 $addrspaceList = listCells ($realm);
93a49e5b
AA
2615 startPortlet ('Manage existing (' . count ($addrspaceList) . ')');
2616 if (count ($addrspaceList))
21ee3351 2617 {
21ee3351
AA
2618 echo "<table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
2619 echo "<tr><th>&nbsp;</th><th>prefix</th><th>name</th><th>capacity</th></tr>";
21ee3351
AA
2620 foreach ($addrspaceList as $netinfo)
2621 {
21ee3351 2622 echo "<tr valign=top><td>";
93a49e5b 2623 if (! isIPNetworkEmpty ($netinfo))
21ee3351
AA
2624 printImageHREF ('nodestroy', 'There are ' . count ($netinfo['addrlist']) . ' allocations inside');
2625 else
93a49e5b 2626 echo getOpLink (array ('op' => 'del', 'id' => $netinfo['id']), '', 'destroy', 'Delete this prefix');
6d8424f8 2627 echo '</td><td class=tdleft>' . mkCellA ($netinfo) . '</td>';
a0bdf6dc 2628 echo '<td class=tdleft>' . stringForTD ($netinfo['name']);
1f54e1ba
DO
2629 if (count ($netinfo['etags']))
2630 echo '<br><small>' . serializeTags ($netinfo['etags']) . '</small>';
2631 echo '</td><td>';
4318ced5 2632 echo getRenderedIPNetCapacity ($netinfo);
b1e39212 2633 echo '</tr>';
e673ee24 2634 }
04d619d0
DO
2635 echo "</table>";
2636 finishPortlet();
e673ee24 2637 }
93a49e5b
AA
2638}
2639
2640function renderIPNewNetForm ()
2641{
2642 global $pageno;
2643 if ($pageno == 'ipv6space')
2644 {
2645 $realm = 'ipv6net';
3495ec6e 2646 $regexp = '^[a-fA-F0-9:]*:[a-fA-F0-9:\.]*/\d{1,3}$';
93a49e5b
AA
2647 }
2648 else
2649 {
2650 $realm = 'ipv4net';
3495ec6e 2651 $regexp = '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$';
93a49e5b
AA
2652 }
2653
2654 // IP prefix validator
10f5133a 2655 addJS ('js/live_validation.js');
93a49e5b 2656 $regexp = addslashes ($regexp);
10f5133a 2657 addJS (<<<END
93a49e5b 2658$(document).ready(function () {
b895e6b4 2659 $('form#add input[name="range"]').attr('match', '$regexp');
93a49e5b
AA
2660 Validate.init();
2661});
2662END
2663 , TRUE);
dec748f6 2664
93a49e5b 2665 startPortlet ('Add new');
10f5133a 2666 printOpFormIntro ('add');
e7b84015
AB
2667 echo '<table border=0 cellpadding=5 cellspacing=0 align=center>';
2668
93a49e5b
AA
2669 // inputs column
2670 $prefix_value = empty ($_REQUEST['set-prefix']) ? '' : $_REQUEST['set-prefix'];
04c61e7e 2671 echo "<th class=tdright>Prefix:</th><td class=tdleft><input type=text name='range' size=36 class='live-validate' value='${prefix_value}'></td>";
e7b84015 2672 echo '<tr><th class=tdright>VLAN:</th><td class=tdleft>';
04c61e7e
AB
2673 echo getOptionTree ('vlan_ck', getAllVLANOptions(), array ('select_class' => 'vertical')) . '</td></tr>';
2674 echo "<tr><th class=tdright>Name:</th><td class=tdleft><input type=text name='name' size='20'></td></tr>";
e7b84015
AB
2675 echo '<tr><th class=tdright>Tags:</th><td class="tdleft">';
2676 printTagsPicker ();
2677 echo '</td></tr>';
38707670
DO
2678 echo '<tr><td class=tdright><input type=checkbox name="is_connected" id="is_connected"></td>';
2679 echo '<th class=tdleft><label for="is_connected">reserve subnet-router anycast address</label></th></tr>';
93a49e5b 2680 echo "<tr><td colspan=2>";
04c61e7e 2681 printImageHREF ('CREATE', 'Add a new network', TRUE);
93a49e5b 2682 echo '</td></tr>';
e7b84015 2683 echo "</table></form><br><br>\n";
93a49e5b 2684 finishPortlet();
e673ee24
DO
2685}
2686
4318ced5 2687function getRenderedIPNetBacktrace ($range)
e673ee24 2688{
4318ced5
AA
2689 if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
2690 return array();
e673ee24 2691
4318ced5
AA
2692 $v = ($range['realm'] == 'ipv4net') ? 4 : 6;
2693 $space = "ipv${v}space"; // ipv4space, ipv6space
2694 $tag = "\$ip${v}netid_"; // $ip4netid_, $ip6netid_
dec748f6 2695
4318ced5
AA
2696 $ret = array();
2697 // Build a backtrace from all parent networks.
2698 $clen = $range['mask'];
2699 $backtrace = array();
2700 $backtrace['&rarr;'] = $range;
2701 $key = '';
2702 while (NULL !== ($upperid = getIPAddressNetworkId ($range['ip_bin'], $clen)))
2703 {
2704 $upperinfo = spotEntity ($range['realm'], $upperid);
2705 $clen = $upperinfo['mask'];
2706 $key .= '&uarr;';
2707 $backtrace[$key] = $upperinfo;
2708 }
2709 foreach (array_reverse ($backtrace) as $arrow => $ainfo)
2710 {
2711 $link = '<a href="' . makeHref (array (
2712 'page' => $space,
2713 'tab' => 'default',
2714 'clear-cf' => '',
2715 'cfe' => '{' . $tag . $ainfo['id'] . '}',
2716 'hl_net' => 1,
2717 'eid' => $range['id'],
2718 )) . '" title="View IP tree with this net as root">' . $arrow . '</a>';
2719 $ret[] = array ($link, getOutputOf ('renderCell', $ainfo));
2720 }
2721 return $ret;
2722}
2723
d10401bf 2724function renderIPNetwork ($id)
4318ced5
AA
2725{
2726 global $pageno;
2727 $realm = $pageno; // 'ipv4net', 'ipv6net'
d10401bf 2728 $range = spotEntity ($realm, $id);
4318ced5 2729 loadIPAddrList ($range);
2034d968 2730 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
99ab184f
DO
2731 echo "<tr><td colspan=2 align=center><h1>${range['ip']}/${range['mask']}</h1><h2>";
2732 echo htmlspecialchars ($range['name'], ENT_QUOTES, 'UTF-8') . "</h2></td></tr>\n";
2034d968
DO
2733
2734 echo "<tr><td class=pcleft width='50%'>";
646272be
AA
2735
2736 // render summary portlet
2737 $summary = array();
4318ced5 2738 $summary['%% used'] = getRenderedIPNetCapacity ($range);
2b6b0038 2739 $summary = getRenderedIPNetBacktrace ($range) + $summary;
4318ced5 2740 if ($realm == 'ipv4net')
001e61fa 2741 {
4318ced5
AA
2742 $summary[] = array ('Netmask:', ip4_format ($range['mask_bin']));
2743 $summary[] = array ('Netmask:', "0x" . strtoupper (implode ('', unpack ('H*', $range['mask_bin']))));
2744 $summary['Wildcard bits'] = ip4_format ( ~ $range['mask_bin']);
001e61fa 2745 }
dec748f6 2746
11e3af31
DO
2747 $reuse_domain = considerConfiguredConstraint ($range, '8021Q_MULTILINK_LISTSRC');
2748 $domainclass = array();
2749 foreach (array_count_values (reduceSubarraysToColumn ($range['8021q'], 'domain_id')) as $domain_id => $vlan_count)
2750 $domainclass[$domain_id] = $vlan_count == 1 ? '' : ($reuse_domain ? '{trwarning}' : '{trerror}');
a5f9d1ca 2751 foreach ($range['8021q'] as $item)
3f642516 2752 $summary[] = array ($domainclass[$item['domain_id']] . 'VLAN:', formatVLANAsHyperlink (getVlanRow ($item['domain_id'] . '-' . $item['vlan_id'])));
55eefced 2753 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes' && count ($routers = findNetRouters ($range)))
04d619d0 2754 {
646272be
AA
2755 $summary['Routed by'] = '';
2756 foreach ($routers as $rtr)
4318ced5 2757 $summary['Routed by'] .= getOutputOf ('renderRouterCell', $rtr['ip_bin'], $rtr['iface'], spotEntity ('object', $rtr['id']));
04d619d0 2758 }
646272be
AA
2759 $summary['tags'] = '';
2760 renderEntitySummary ($range, 'summary', $summary);
e1ae3fb4 2761
33973968 2762 if ($range['comment'] != '')
99ab184f
DO
2763 {
2764 startPortlet ('Comment');
2765 echo '<div class=commentblock>' . string_insert_hrefs (htmlspecialchars ($range['comment'], ENT_QUOTES, 'UTF-8')) . '</div>';
2766 finishPortlet ();
2767 }
2768
d10401bf 2769 renderFilesPortlet ($realm, $id);
2034d968 2770 echo "</td>\n";
e673ee24 2771
2034d968
DO
2772 echo "<td class=pcright>";
2773 startPortlet ('details');
4318ced5
AA
2774 renderIPNetworkAddresses ($range);
2775 finishPortlet();
2776 echo "</td></tr></table>\n";
2777}
e673ee24 2778
4318ced5
AA
2779// Used solely by renderSeparator
2780function renderEmptyIPv6 ($ip_bin, $hl_ip)
2781{
2782 $class = 'tdleft';
2783 if ($ip_bin === $hl_ip)
5c717ce5 2784 $class .= ' highlight';
4318ced5 2785 $fmt = ip6_format ($ip_bin);
235124a6 2786 echo "<tr class='$class'><td><a name='ip-$fmt' href='" . makeHref (array ('page' => 'ipaddress', 'ip' => $fmt)) . "'>" . $fmt;
0b8d8309
AA
2787 $editable = permitted ('ipaddress', 'properties', 'editAddress')
2788 ? 'editable'
2789 : '';
cc2fa820
AD
2790 echo "</a></td><td><span class='rsvtext $editable id-$fmt op-upd-ip-name'></span></td>";
2791 echo "<td><span class='rsvtext $editable id-$fmt op-upd-ip-comment'></span></td><td>&nbsp;</td></tr>\n";
4318ced5
AA
2792}
2793
2794// Renders empty table line to shrink empty IPv6 address ranges.
2795// If the range consists of single address, renders the address instead of empty line.
2796// Renders address $hl_ip inside the range.
2797// Used solely by renderIPv6NetworkAddresses
2798function renderSeparator ($first, $last, $hl_ip)
2799{
2800 $self = __FUNCTION__;
2801 if (strcmp ($first, $last) > 0)
2802 return;
2803 if ($first == $last)
2804 renderEmptyIPv6 ($first, $hl_ip);
2805 elseif (isset ($hl_ip) && strcmp ($hl_ip, $first) >= 0 && strcmp ($hl_ip, $last) <= 0)
2806 { // $hl_ip is inside the range $first - $last
2807 $self ($first, ip_prev ($hl_ip), $hl_ip);
2808 renderEmptyIPv6 ($hl_ip, $hl_ip);
2809 $self (ip_next ($hl_ip), $last, $hl_ip);
2810 }
2811 else
cc2fa820 2812 echo "<tr><td colspan=4 class=tdleft></td></tr>\n";
4318ced5
AA
2813}
2814
75e7c0c6 2815// calculates page number that contains given $ip (used by renderIPv6NetworkAddresses)
4318ced5
AA
2816function getPageNumOfIPv6 ($list, $ip_bin, $maxperpage)
2817{
2818 if (intval ($maxperpage) <= 0 || count ($list) <= $maxperpage)
2819 return 0;
2820 $keys = array_keys ($list);
2821 for ($i = 1; $i <= count ($keys); $i++)
2822 if (strcmp ($keys[$i-1], $ip_bin) >= 0)
2823 return intval ($i / $maxperpage);
2824 return intval (count ($list) / $maxperpage);
2825}
2826
2827function renderIPNetworkAddresses ($range)
2828{
2829 switch (strlen ($range['ip_bin']))
2830 {
2831 case 4: return renderIPv4NetworkAddresses ($range);
2832 case 16: return renderIPv6NetworkAddresses ($range);
2833 default: throw new InvalidArgException ("range['ip_bin']", $range['ip_bin']);
2834 }
2835}
2836
247acd67
KZ
2837function renderIPv4NetworkPageLink ($rangeid, $page, $title)
2838{
2839 global $pageno, $tabno;
2840 return "<a href='".makeHref (array ('page' => $pageno, 'tab' => $tabno, 'id' => $rangeid, 'pg' => $page)) . "' title='".$title."'>".$page."</a> ";
2841}
2842
2843function renderIPv4NetworkPagination ($range, $page, $numpages)
2844{
2845 $rendered_pager = '';
2846 $startip = ip4_bin2int ($range['ip_bin']);
2847 $endip = ip4_bin2int (ip_last ($range));
2848 $rangeid = $range['id'];
2849 // Should make this configurable perhaps
2850 // How many pages before/after current page to show
2851 $prepostpagecount = 8;
2852 // Minimum pages where pagination does not happen
2853 $paginationat = 16; // 16 pages is a /20, 32 is a /19
2854 $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
2855 if ($numpages <= $paginationat)
2856 {
2857 // create original pagination
2858 for ($i = 0; $i < $numpages; $i++)
2859 if ($i == $page)
2860 $rendered_pager .= "<b>".$i."</b> ";
2861 else
2862 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2863 }
2864 else // number of pages > page range, create ranged pagination
2865 {
2866 // page is within first subset
2867 if ($page - $prepostpagecount <= 1)
2868 {
2869 for ($i = 0; $i < $page; $i++)
2870 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2871 }
2872 // render 0 ... [page - prepostpagecount] [page - prepostpagecount + 1] ... [page - 1]
2873 else
2874 {
2875 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, 0, ip4_format(ip4_int2bin($startip)));
2876 $rendered_pager .= "... ";
2877 for ($i = $page - $prepostpagecount; $i < $page; $i++)
2878 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2879 }
2880 // render current page
2881 $rendered_pager .= "<b>".$page."</b> ";
2882 // page is within last subset
2883 if ($page + $prepostpagecount >= $numpages-2)
2884 {
2885 for ($i = $page+1; $i < $numpages; $i++)
2886 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2887 }
2888 // render [page + 1] [page + 2] ... [page + postpagecount] ... [end page]
2889 else
2890 {
2891 for ($i = $page+1; $i <= $page+$prepostpagecount; $i++)
2892 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2893 $rendered_pager .= "... ";
2894 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, ($numpages-1), ip4_format(ip4_int2bin($endip)));
2895 }
2896 }
2897 return $rendered_pager;
2898}
2899
4318ced5
AA
2900function renderIPv4NetworkAddresses ($range)
2901{
9d5f6a80 2902 global $pageno, $tabno, $aac_left;
4318ced5
AA
2903 $startip = ip4_bin2int ($range['ip_bin']);
2904 $endip = ip4_bin2int (ip_last ($range));
2905
2906 if (isset ($_REQUEST['hl_ip']))
00276508 2907 {
4318ced5
AA
2908 $hl_ip = ip4_bin2int (ip4_parse ($_REQUEST['hl_ip']));
2909 addAutoScrollScript ('ip-' . $_REQUEST['hl_ip']); // scroll page to highlighted ip
00276508
AA
2910 }
2911
79b57b18
AA
2912 // pager
2913 $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
2914 $address_count = $endip - $startip + 1;
2915 $page = 0;
446c25c3 2916 $rendered_pager = '';
79b57b18
AA
2917 if ($address_count > $maxperpage && $maxperpage > 0)
2918 {
2919 $page = isset ($_REQUEST['pg']) ? $_REQUEST['pg'] : (isset ($hl_ip) ? intval (($hl_ip - $startip) / $maxperpage) : 0);
2920 if ($numpages = ceil ($address_count / $maxperpage))
2921 {
4318ced5 2922 echo '<h3>' . ip4_format (ip4_int2bin ($startip)) . ' ~ ' . ip4_format (ip4_int2bin ($endip)) . '</h3>';
247acd67 2923 $rendered_pager = renderIPv4NetworkPagination ($range, $page, $numpages);
79b57b18
AA
2924 }
2925 $startip = $startip + $page * $maxperpage;
2926 $endip = min ($startip + $maxperpage - 1, $endip);
2927 }
2928
446c25c3 2929 echo $rendered_pager;
1ce89bdd 2930 echo "<table class='widetable' border=0 cellspacing=0 cellpadding=5 align='center' width='100%'>\n";
cc2fa820 2931 echo "<tr><th>Address</th><th>Name</th><th>Comment</th><th>Allocation</th></tr>\n";
e673ee24 2932
4318ced5
AA
2933 markupIPAddrList ($range['addrlist']);
2934 for ($ip = $startip; $ip <= $endip; $ip++)
2935 {
2936 $ip_bin = ip4_int2bin ($ip);
2937 $dottedquad = ip4_format ($ip_bin);
5c717ce5 2938 $tr_class = (isset ($hl_ip) && $hl_ip == $ip ? 'highlight' : '');
0cd7c0f6
AA
2939 if (isset ($range['addrlist'][$ip_bin]))
2940 $addr = $range['addrlist'][$ip_bin];
2941 else
e673ee24 2942 {
235124a6 2943 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
2944 $editable = permitted ('ipaddress', 'properties', 'editAddress')
2945 ? 'editable'
2946 : '';
cc2fa820
AD
2947 echo "<td><span class='rsvtext $editable id-$dottedquad op-upd-ip-name'></span></td>";
2948 echo "<td><span class='rsvtext $editable id-$dottedquad op-upd-ip-comment'></span></td><td></td></tr>\n";
b2035dca
DO
2949 continue;
2950 }
68c91b9c
AA
2951 // render IP change history
2952 $title = '';
2953 $history_class = '';
2954 if (isset ($addr['last_log']))
2955 {
2956 $title = ' title="' . htmlspecialchars ($addr['last_log']['user'] . ', ' . formatAge ($addr['last_log']['time']) , ENT_QUOTES) . '"';
2957 $history_class = 'hover-history underline';
2958 }
4318ced5
AA
2959 $tr_class .= ' ' . $addr['class'];
2960 echo "<tr class='tdleft $tr_class'>";
235124a6 2961 echo "<td><a class='$history_class' $title name='ip-$dottedquad' href='".makeHref(array('page'=>'ipaddress', 'ip'=>$addr['ip']))."'>${addr['ip']}</a></td>";
0b8d8309 2962 $editable =
cc2fa820 2963 (empty ($addr['allocs']) || !empty ($addr['name']) || !empty ($addr['comment']))
0b8d8309
AA
2964 && permitted ('ipaddress', 'properties', 'editAddress')
2965 ? 'editable'
2966 : '';
cc2fa820
AD
2967 echo "<td><span class='rsvtext $editable id-$dottedquad op-upd-ip-name'>${addr['name']}</span></td>";
2968 echo "<td><span class='rsvtext $editable id-$dottedquad op-upd-ip-comment'>${addr['comment']}</span></td>";
0b8d8309 2969 echo "<td>";
b2035dca 2970 $delim = '';
b2035dca
DO
2971 if ( $addr['reserved'] == 'yes')
2972 {
2d318652 2973 echo "<strong>RESERVED</strong> ";
b2035dca
DO
2974 $delim = '; ';
2975 }
212b565a 2976 foreach ($addr['allocs'] as $ref)
b2035dca 2977 {
9d5f6a80 2978 echo $delim . $aac_left[$ref['type']];
e749a34f 2979 echo makeIPAllocLink ($ip_bin, $ref, TRUE);
b2035dca
DO
2980 $delim = '; ';
2981 }
2982 if ($delim != '')
e0d6e38e 2983 $delim = '<br>';
212b565a
AA
2984 foreach ($addr['vslist'] as $vs_id)
2985 {
2986 $vs = spotEntity ('ipv4vs', $vs_id);
ad3e5e7e 2987 echo $delim . mkA ("${vs['name']}:${vs['vport']}/${vs['proto']}", 'ipv4vs', $vs['id']) . '&rarr;';
212b565a
AA
2988 $delim = '<br>';
2989 }
eacc0983
AA
2990 foreach ($addr['vsglist'] as $vs_id)
2991 {
2992 $vs = spotEntity ('ipvs', $vs_id);
2993 echo $delim . mkA ($vs['name'], 'ipvs', $vs['id']) . '&rarr;';
2994 $delim = '<br>';
2995 }
212b565a
AA
2996 foreach ($addr['rsplist'] as $rsp_id)
2997 {
2998 $rsp = spotEntity ('ipv4rspool', $rsp_id);
ad3e5e7e 2999 echo "${delim}&rarr;" . mkA ($rsp['name'], 'ipv4rspool', $rsp['id']);
212b565a
AA
3000 $delim = '<br>';
3001 }
b2035dca 3002 echo "</td></tr>\n";
4318ced5 3003 }
2727c7df 3004 // end of iteration
a8dc2982
AA
3005 if (permitted (NULL, NULL, 'set_reserve_comment'))
3006 addJS ('js/inplace-edit.js');
e673ee24
DO
3007
3008 echo "</table>";
b956dd24 3009 if ($rendered_pager != '')
446c25c3 3010 echo '<p>' . $rendered_pager . '</p>';
21ee3351
AA
3011}
3012
3013function renderIPv6NetworkAddresses ($netinfo)
3014{
9d5f6a80 3015 global $pageno, $tabno, $aac_left;
1ce89bdd 3016 echo "<table class='widetable' border=0 cellspacing=0 cellpadding=5 align='center' width='100%'>\n";
cc2fa820 3017 echo "<tr><th>Address</th><th>Name</th><th>Comment</th><th>Allocation</th></tr>\n";
21ee3351 3018
4318ced5
AA
3019 $hl_ip = NULL;
3020 if (isset ($_REQUEST['hl_ip']))
3021 {
3022 $hl_ip = ip6_parse ($_REQUEST['hl_ip']);
3023 addAutoScrollScript ('ip-' . ip6_format ($hl_ip));
3024 }
21ee3351 3025
21ee3351
AA
3026 $addresses = $netinfo['addrlist'];
3027 ksort ($addresses);
4318ced5 3028 markupIPAddrList ($addresses);
21ee3351
AA
3029
3030 // pager
3031 $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
3032 if (count ($addresses) > $maxperpage && $maxperpage > 0)
3033 {
3034 $page = isset ($_REQUEST['pg']) ? $_REQUEST['pg'] : (isset ($hl_ip) ? getPageNumOfIPv6 ($addresses, $hl_ip, $maxperpage) : 0);
3035 $numpages = ceil (count ($addresses) / $maxperpage);
3036 echo "<center><h3>$numpages pages:</h3>";
3037 for ($i=0; $i<$numpages; $i++)
3038 {
3039 if ($i == $page)
3040 echo "<b>$i</b> ";
3041 else
3042 echo "<a href='" . makeHref (array ('page' => $pageno, 'tab' => $tabno, 'id' => $netinfo['id'], 'pg' => $i)) . "'>$i</a> ";
3043 }
3044 echo "</center>";
3045 }
3046
3047 $i = 0;
dec748f6 3048 $interruped = FALSE;
4318ced5
AA
3049 $prev_ip = ip_prev ($netinfo['ip_bin']);
3050 foreach ($addresses as $ip_bin => $addr)
21ee3351
AA
3051 {
3052 if (isset ($page))
3053 {
3054 ++$i;
3055 if ($i <= $maxperpage * $page)
3056 continue;
3057 elseif ($i > $maxperpage * ($page + 1))
3058 {
3059 $interruped = TRUE;
3060 break;
3061 }
3062 }
3063
4318ced5
AA
3064 if ($ip_bin != ip_next ($prev_ip))
3065 renderSeparator (ip_next ($prev_ip), ip_prev ($ip_bin), $hl_ip);
3066 $prev_ip = $ip_bin;
3067
3068 // render IP change history
3069 $title = '';
3070 $history_class = '';
3071 if (isset ($addr['last_log']))
3072 {
3073 $title = ' title="' . htmlspecialchars ($addr['last_log']['user'] . ', ' . formatAge ($addr['last_log']['time']) , ENT_QUOTES) . '"';
3074 $history_class = 'hover-history underline';
3075 }
3076
5c717ce5 3077 $tr_class = $addr['class'] . ' tdleft' . ($hl_ip === $ip_bin ? ' highlight' : '');
4318ced5 3078 echo "<tr class='$tr_class'>";
235124a6 3079 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
3080 $editable =
3081 (empty ($addr['allocs']) || !empty ($addr['name'])
3082 && permitted ('ipaddress', 'properties', 'editAddress')
3083 ? 'editable'
3084 : '');
cc2fa820
AD
3085 echo "<td><span class='rsvtext $editable id-${addr['ip']} op-upd-ip-name'>${addr['name']}</span></td>";
3086 echo "<td><span class='rsvtext $editable id-${addr['ip']} op-upd-ip-comment'>${addr['comment']}</span></td><td>";
21ee3351 3087 $delim = '';
21ee3351
AA
3088 if ( $addr['reserved'] == 'yes')
3089 {
3090 echo "<strong>RESERVED</strong> ";
3091 $delim = '; ';
3092 }
3093 foreach ($addr['allocs'] as $ref)
3094 {
9d5f6a80 3095 echo $delim . $aac_left[$ref['type']];
e749a34f 3096 echo makeIPAllocLink ($ip_bin, $ref, TRUE);
21ee3351
AA
3097 $delim = '; ';
3098 }
3099 if ($delim != '')
71066ef1
AA
3100 $delim = '<br>';
3101 foreach ($addr['vslist'] as $vs_id)
3102 {
3103 $vs = spotEntity ('ipv4vs', $vs_id);
ad3e5e7e 3104 echo $delim . mkA ("${vs['name']}:${vs['vport']}/${vs['proto']}", 'ipv4vs', $vs['id']) . '&rarr;';
71066ef1
AA
3105 $delim = '<br>';
3106 }
eacc0983
AA
3107 foreach ($addr['vsglist'] as $vs_id)
3108 {
3109 $vs = spotEntity ('ipvs', $vs_id);
3110 echo $delim . mkA ($vs['name'], 'ipvs', $vs['id']) . '&rarr;';
3111 $delim = '<br>';
3112 }
71066ef1 3113 foreach ($addr['rsplist'] as $rsp_id)
21ee3351 3114 {
71066ef1 3115 $rsp = spotEntity ('ipv4rspool', $rsp_id);
ad3e5e7e 3116 echo "${delim}&rarr;" . mkA ($rsp['name'], 'ipv4rspool', $rsp['id']);
71066ef1 3117 $delim = '<br>';
21ee3351
AA
3118 }
3119 echo "</td></tr>\n";
3120 }
3121 if (! $interruped)
4318ced5 3122 renderSeparator (ip_next ($prev_ip), ip_last ($netinfo), $hl_ip);
21ee3351
AA
3123 if (isset ($page))
3124 { // bottom pager
3125 echo "<tr><td colspan=3>";
3126 if ($page > 0)
3127 echo "<a href='" . makeHref (array ('page' => $pageno, 'tab' => $tabno, 'id' => $netinfo['id'], 'pg' => $page - 1)) . "'><< prev</a> ";
3128 if ($page < $numpages - 1)
3129 echo "<a href='" . makeHref (array ('page' => $pageno, 'tab' => $tabno, 'id' => $netinfo['id'], 'pg' => $page + 1)) . "'>next >></a> ";
3130 echo "</td></tr>";
3131 }
3132 echo "</table>";
a8dc2982
AA
3133 if (permitted (NULL, NULL, 'set_reserve_comment'))
3134 addJS ('js/inplace-edit.js');
21ee3351
AA
3135}
3136
d10401bf 3137function renderIPNetworkProperties ($id)
e673ee24 3138{
20a87be1 3139 global $pageno;
78ba6f56 3140 $netdata = spotEntity (etypeByPageno(), $id);
53ef3908 3141 echo "<center><h1>${netdata['ip']}/${netdata['mask']}</h1></center>\n";
7056988c 3142 printOpFormIntro ('editRange');
e7b84015
AB
3143 echo "<table border=0 cellpadding=5 cellspacing=0 align='center'>\n";
3144 echo '<tr><th class=tdright><label for=nameinput>Name:</label></th>';