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