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