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