simplify a few functions
[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 (
d842dbbc 1851 'ssv1' => 'SSV:<interface name> [<MAC address>]',
8331b4e4 1852 );
decba4dd 1853 echo 'Format: ' . getSelect ($formats, array ('name' => 'format'), 'ssv1') . ' ';
e673ee24 1854 echo 'Default port type: ';
759b21d2 1855 printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type'), $prefs['selected']);
04c61e7e
AB
1856 echo "<input type=submit value='Parse output'><br>\n";
1857 echo "<textarea name=input cols=100 rows=50></textarea><br>\n";
e673ee24
DO
1858 echo '</form>';
1859 finishPortlet();
1860}
1861
d10401bf 1862function renderIPForObject ($object_id)
e673ee24 1863{
15e27821 1864 function printNewItemTR ($default_type, $object_id)
21ee3351
AA
1865 {
1866 global $aat;
15e27821 1867
1868 includeJQueryUI (TRUE);
1869
1870 addJS (<<<JSEND
1871 $(document).ready( function() {
1872 $('[name="bond_name"]').autocomplete({
1873 source: "?module=ajax&ac=autocomplete&realm=bond_name&object_id=$object_id",
1874 //minLength: 3,
1875 focus: function(event, ui) {
1876 if( ui.item.value == '' )
1877 event.preventDefault();
1878 },
1879 select: function(event, ui) {
1880 if( ui.item.value == '' )
1881 event.preventDefault();
1882 }
1883 });
1884 });
1885JSEND
1886 , TRUE);
4318ced5
AA
1887 printOpFormIntro ('add');
1888 echo "<tr><td>"; // left btn
dec748f6 1889 printImageHREF ('add', 'allocate', TRUE);
21ee3351 1890 echo "</td>";
04c61e7e
AB
1891 echo "<td class=tdleft><input type='text' size='10' name='bond_name'></td>\n"; // if-name
1892 echo "<td class=tdleft><input type=text name='ip'></td>\n"; // IP
4318ced5
AA
1893 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
1894 echo "<td colspan=2>&nbsp;</td>"; // network, routed by
1895 echo '<td>';
04c61e7e 1896 printSelect ($aat, array ('name' => 'bond_type'), $default_type); // type
4318ced5 1897 echo "</td><td>&nbsp;</td><td>"; // misc
04c61e7e 1898 printImageHREF ('add', 'allocate', TRUE); // right btn
21ee3351
AA
1899 echo "</td></tr></form>";
1900 }
21ee3351 1901 global $aat;
0d5ef6f0 1902
21ee3351 1903 startPortlet ('Allocations');
4318ced5
AA
1904 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'><tr>\n";
1905 echo '<th>&nbsp;</th>';
1906 echo '<th>OS interface</th>';
1907 echo '<th>IP address</th>';
21ee3351 1908 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
4318ced5
AA
1909 {
1910 echo '<th>network</th>';
1911 echo '<th>routed by</th>';
1912 }
1913 echo '<th>type</th>';
1914 echo '<th>misc</th>';
b1b07891 1915 echo '<th>&nbsp;</th>';
4318ced5 1916 echo '</tr>';
21ee3351 1917
710ead7e
AA
1918 $alloc_list = ''; // most of the output is stored here
1919 $used_alloc_types = array();
4318ced5 1920 foreach (getObjectIPAllocations ($object_id) as $alloc)
21ee3351 1921 {
710ead7e
AA
1922 if (! isset ($used_alloc_types[$alloc['type']]))
1923 $used_alloc_types[$alloc['type']] = 0;
1924 $used_alloc_types[$alloc['type']]++;
1925
40c9fc94 1926 $rendered_alloc = callHook ('getRenderedAlloc', $object_id, $alloc);
4318ced5 1927 $alloc_list .= getOutputOf ('printOpFormIntro', 'upd', array ('ip' => $alloc['addrinfo']['ip']));
710ead7e 1928 $alloc_list .= "<tr class='${rendered_alloc['tr_class']}' valign=top>";
f3505b35 1929
10f5133a 1930 $alloc_list .= "<td>" . getOpLink (array ('op' => 'del', 'ip' => $alloc['addrinfo']['ip']), '', 'delete', 'Delete this IP address') . "</td>";
40c9fc94 1931 $alloc_list .= "<td class=tdleft><input type='text' name='bond_name' value='${alloc['osif']}' size=10>" . $rendered_alloc['td_name_suffix'] . "</td>";
710ead7e 1932 $alloc_list .= $rendered_alloc['td_ip'];
21ee3351
AA
1933 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
1934 {
710ead7e
AA
1935 $alloc_list .= $rendered_alloc['td_network'];
1936 $alloc_list .= $rendered_alloc['td_routed_by'];
21ee3351 1937 }
710ead7e
AA
1938 $alloc_list .= '<td>' . getSelect ($aat, array ('name' => 'bond_type'), $alloc['type']) . "</td>";
1939 $alloc_list .= $rendered_alloc['td_peers'];
1940 $alloc_list .= "<td>" .getImageHREF ('save', 'Save changes', TRUE) . "</td>";
f3505b35 1941
710ead7e 1942 $alloc_list .= "</form></tr>\n";
21ee3351 1943 }
710ead7e 1944 asort ($used_alloc_types, SORT_NUMERIC);
589c027b 1945 $most_popular_type = ! count ($used_alloc_types) ? 'regular' : array_last (array_keys ($used_alloc_types));
710ead7e
AA
1946
1947 if ($list_on_top = (getConfigVar ('ADDNEW_AT_TOP') != 'yes'))
1948 echo $alloc_list;
15e27821 1949 printNewItemTR ($most_popular_type, $object_id);
710ead7e
AA
1950 if (! $list_on_top)
1951 echo $alloc_list;
21ee3351
AA
1952
1953 echo "</table><br>\n";
1954 finishPortlet();
f3505b35
AA
1955}
1956
08408472
AA
1957// This function is deprecated. Do not rely on its internals,
1958// it will probably be removed in the next major relese.
1959// Use new showError, showWarning, showSuccess functions.
dec748f6 1960// Log array is stored in global $log_messages. Its format is simple: plain ordered array
8ab645cf 1961// with values having keys 'c' (both message code and severity) and 'a' (sprintf arguments array)
1cb6adf5 1962function showMessageOrError ()
e673ee24 1963{
a2b16202
AA
1964 global $log_messages;
1965
0a00b344 1966 startSession();
a2b16202
AA
1967 if (isset ($_SESSION['log']))
1968 {
1969 $log_messages = array_merge ($_SESSION['log'], $log_messages);
1970 unset ($_SESSION['log']);
1971 }
1972 session_commit();
1973
1974 if (empty ($log_messages))
1cb6adf5 1975 return;
8ab645cf
AA
1976 $msginfo = array
1977 (
dec748f6 1978// records 0~99 with success messages
8ab645cf
AA
1979 0 => array ('code' => 'success', 'format' => '%s'),
1980 5 => array ('code' => 'success', 'format' => 'added record "%s" successfully'),
1981 6 => array ('code' => 'success', 'format' => 'updated record "%s" successfully'),
1982 7 => array ('code' => 'success', 'format' => 'deleted record "%s" successfully'),
1983 8 => array ('code' => 'success', 'format' => 'Port %s successfully linked with %s'),
493e13dd 1984 10 => array ('code' => 'success', 'format' => 'Added %u port(s), updated %u port(s), encountered %u error(s).'),
8ab645cf 1985 21 => array ('code' => 'success', 'format' => 'Generation complete'),
493e13dd
DO
1986 26 => array ('code' => 'success', 'format' => 'updated %u record(s) successfully'),
1987 37 => array ('code' => 'success', 'format' => 'added %u record(s) successfully'),
1988 38 => array ('code' => 'success', 'format' => 'removed %u record(s) successfully'),
8ab645cf 1989 43 => array ('code' => 'success', 'format' => 'Saved successfully.'),
493e13dd 1990 44 => array ('code' => 'success', 'format' => '%s failure(s) and %s successfull change(s).'),
8ab645cf
AA
1991 48 => array ('code' => 'success', 'format' => 'added a record successfully'),
1992 49 => array ('code' => 'success', 'format' => 'deleted a record successfully'),
1993 51 => array ('code' => 'success', 'format' => 'updated a record successfully'),
1994 57 => array ('code' => 'success', 'format' => 'Reset complete'),
04eee8d2 1995 58 => array ('code' => 'success', 'format' => '%u device(s) unmounted successfully'),
8ab645cf 1996 63 => array ('code' => 'success', 'format' => '%u change request(s) have been processed'),
493e13dd 1997 67 => array ('code' => 'success', 'format' => "Tag rolling done, %u object(s) involved"),
8ab645cf
AA
1998 71 => array ('code' => 'success', 'format' => 'File "%s" was linked successfully'),
1999 72 => array ('code' => 'success', 'format' => 'File was unlinked successfully'),
493e13dd
DO
2000 82 => array ('code' => 'success', 'format' => "Bulk port creation was successful. %u port(s) created, %u failed"),
2001 87 => array ('code' => 'success', 'format' => '802.1Q recalculate: %d port(s) changed on %d switch(es)'),
8628ae44 2002// records 100~199 with fatal error messages
8ab645cf
AA
2003 100 => array ('code' => 'error', 'format' => '%s'),
2004 109 => array ('code' => 'error', 'format' => 'failed updating a record'),
8ab645cf 2005 131 => array ('code' => 'error', 'format' => 'invalid format requested'),
493e13dd 2006 141 => array ('code' => 'error', 'format' => 'Encountered %u error(s), updated %u record(s)'),
8ab645cf
AA
2007 149 => array ('code' => 'error', 'format' => 'Turing test failed'),
2008 150 => array ('code' => 'error', 'format' => 'Can only change password under DB authentication.'),
2009 151 => array ('code' => 'error', 'format' => 'Old password doesn\'t match.'),
2010 152 => array ('code' => 'error', 'format' => 'New passwords don\'t match.'),
2011 154 => array ('code' => 'error', 'format' => "Verification error: %s"),
2012 155 => array ('code' => 'error', 'format' => 'Save failed.'),
2013 159 => array ('code' => 'error', 'format' => 'Permission denied moving port %s from VLAN%u to VLAN%u'),
8ab645cf
AA
2014 170 => array ('code' => 'error', 'format' => 'There is no network for IP address "%s"'),
2015 172 => array ('code' => 'error', 'format' => 'Malformed request'),
2016 179 => array ('code' => 'error', 'format' => 'Expired form has been declined.'),
8ab645cf 2017 191 => array ('code' => 'error', 'format' => "deploy was blocked due to conflicting configuration versions"),
f0ff4930 2018
8628ae44 2019// records 200~299 with warnings
8ab645cf
AA
2020 200 => array ('code' => 'warning', 'format' => '%s'),
2021 201 => array ('code' => 'warning', 'format' => 'nothing happened...'),
e1add254 2022 206 => array ('code' => 'warning', 'format' => '%s is not empty'),
7587edca 2023 207 => array ('code' => 'warning', 'format' => 'File upload failed, error: %s'),
57777e4a 2024
24d3182a 2025// records 300~399 with notices
8ab645cf 2026 300 => array ('code' => 'neutral', 'format' => '%s'),
24d3182a 2027
8ab645cf
AA
2028 );
2029 // Handle the arguments. Is there any better way to do it?
a2b16202 2030 foreach ($log_messages as $record)
8ab645cf 2031 {
55eefced 2032 if (! isset ($record['c']) || ! isset ($msginfo[$record['c']]))
8ab645cf 2033 {
41152018
AA
2034 $prefix = isset ($record['c']) ? $record['c'] . ': ' : '';
2035 echo "<div class=msg_neutral>(${prefix}this message was lost)</div>";
8ab645cf
AA
2036 continue;
2037 }
2038 if (isset ($record['a']))
2039 switch (count ($record['a']))
baf37d01 2040 {
8ab645cf
AA
2041 case 1:
2042 $msgtext = sprintf
2043 (
2044 $msginfo[$record['c']]['format'],
2045 $record['a'][0]
2046 );
2047 break;
2048 case 2:
2049 $msgtext = sprintf
2050 (
2051 $msginfo[$record['c']]['format'],
2052 $record['a'][0],
2053 $record['a'][1]
2054 );
2055 break;
2056 case 3:
2057 $msgtext = sprintf
2058 (
2059 $msginfo[$record['c']]['format'],
2060 $record['a'][0],
2061 $record['a'][1],
2062 $record['a'][2]
2063 );
2064 break;
2065 case 4:
2066 default:
2067 $msgtext = sprintf
2068 (
2069 $msginfo[$record['c']]['format'],
2070 $record['a'][0],
2071 $record['a'][1],
2072 $record['a'][2],
2073 $record['a'][3]
2074 );
2075 break;
baf37d01 2076 }
8ab645cf
AA
2077 else
2078 $msgtext = $msginfo[$record['c']]['format'];
2079 echo '<div class=msg_' . $msginfo[$record['c']]['code'] . ">${msgtext}</div>";
baf37d01 2080 }
a2b16202 2081 $log_messages = array();
e673ee24
DO
2082}
2083
65e557dd 2084// renders two tables: port link status and learned MAC list
d10401bf 2085function renderPortsInfo($object_id)
65e557dd 2086{
2d33a804 2087 $breed = detectDeviceBreed ($object_id);
03522bd8
AA
2088 try
2089 {
2090 if (permitted (NULL, NULL, 'get_link_status'))
2d33a804 2091 {
2092 if (validBreedFunction ($breed, 'getportstatus'))
2093 $linkStatus = queryDevice ($object_id, 'getportstatus');
2094 }
03522bd8
AA
2095 else
2096 showWarning ("You don't have permission to view ports link status");
2097
2098 if (permitted (NULL, NULL, 'get_mac_list'))
2d33a804 2099 {
2100 if (validBreedFunction ($breed, 'getmaclist'))
2101 $macList = sortPortList (queryDevice ($object_id, 'getmaclist'));
2102 }
03522bd8
AA
2103 else
2104 showWarning ("You don't have permission to view learned MAC list");
2105 }
2106 catch (RTGatewayError $e)
2107 {
2108 showError ($e->getMessage());
2109 return;
2110 }
2111
65e557dd
AA
2112 global $nextorder;
2113 echo "<table width='100%'><tr>";
03522bd8 2114 if (! empty ($linkStatus))
65e557dd 2115 {
03522bd8
AA
2116 echo "<td valign='top' width='50%'>";
2117 startPortlet('Link status');
c51492f5
DO
2118 echo "<table width='80%' class='widetable' cellspacing=0 cellpadding='5px' align='center'>";
2119 echo '<tr><th>Port</th><th>&nbsp;</th><th>Link status</th><th>Link info</th></tr>';
72c3bebc
DO
2120 $statusmap = array
2121 (
2122 'up' => 'link up',
2123 'down' => 'link down',
2124 'disabled' => 'link disabled',
2125 );
03522bd8
AA
2126 $order = 'even';
2127 foreach ($linkStatus as $pn => $link)
65e557dd 2128 {
03522bd8
AA
2129 echo "<tr class='row_$order'>";
2130 $order = $nextorder[$order];
c51492f5
DO
2131 echo '<td>' . $pn . '</td>';
2132 echo '<td>' . getImageHREF (array_fetch ($statusmap, $link['status'], '16x16t')) . '</td>';
2133 echo '<td>' . $link['status'] . '</td>';
03522bd8
AA
2134 $info = '';
2135 if (isset ($link['speed']))
2136 $info .= $link['speed'];
2137 if (isset ($link['duplex']))
2138 {
cfb42424 2139 if ($info != '')
03522bd8
AA
2140 $info .= ', ';
2141 $info .= $link['duplex'];
65e557dd 2142 }
c51492f5 2143 echo '<td>' . $info . '</td>';
03522bd8 2144 echo '</tr>';
65e557dd 2145 }
03522bd8
AA
2146 echo "</table></td>";
2147 finishPortlet();
65e557dd
AA
2148 }
2149
65e557dd
AA
2150 if (! empty ($macList))
2151 {
2152 echo "<td valign='top' width='50%'>";
2153 $rendered_macs = '';
2154 $mac_count = 0;
c51492f5
DO
2155 $rendered_macs .= "<table width='80%' class='widetable' cellspacing=0 cellpadding='5px' align='center'>";
2156 $rendered_macs .= '<tr><th>MAC address</th><th>VLAN</th><th>Port</th></tr>';
65e557dd
AA
2157 $order = 'even';
2158 foreach ($macList as $pn => $list)
2159 {
2160 $order = $nextorder[$order];
2161 foreach ($list as $item)
2162 {
2163 ++$mac_count;
2164 $rendered_macs .= "<tr class='row_$order'>";
c51492f5
DO
2165 $rendered_macs .= '<td style="font-family: monospace">' . $item['mac'] . '</td>';
2166 $rendered_macs .= '<td>' . $item['vid'] . '</td>';
2167 $rendered_macs .= '<td>' . $pn . '</td>';
65e557dd
AA
2168 $rendered_macs .= '</tr>';
2169 }
2170 }
2171 $rendered_macs .= "</table></td>";
dec748f6 2172
65e557dd
AA
2173 startPortlet("Learned MACs ($mac_count)");
2174 echo $rendered_macs;
2175 finishPortlet();
2176 }
2177
2178 echo "</td></tr></table>";
2179}
2180
e673ee24 2181/*
51c18782 2182The following conditions must be met:
e673ee24
DO
21831. We can mount onto free atoms only. This means: if any record for an atom
2184already exists in RackSpace, it can't be used for mounting.
21852. We can't unmount from 'W' atoms. Operator should review appropriate comments
2186and either delete them before unmounting or refuse to unmount the object.
2187*/
d10401bf 2188function renderRackSpaceForObject ($object_id)
e673ee24 2189{
e673ee24
DO
2190 // Always process occupied racks plus racks chosen by user. First get racks with
2191 // already allocated rackspace...
51c18782 2192 $workingRacksData = getResidentRacksData ($object_id);
e673ee24 2193 // ...and then add those chosen by user (if any).
2135fd83 2194 if (isset($_REQUEST['rackmulti']))
e673ee24 2195 foreach ($_REQUEST['rackmulti'] as $cand_id)
e673ee24
DO
2196 if (!isset ($workingRacksData[$cand_id]))
2197 {
0cc24e9a 2198 $rackData = spotEntity ('rack', $cand_id);
61a1d996 2199 amplifyCell ($rackData);
e673ee24
DO
2200 $workingRacksData[$cand_id] = $rackData;
2201 }
51690ad4 2202
c17a36f0
AA
2203 // Get a list of rack ids that are parents of this object.
2204 $object = spotEntity ('object', $object_id);
2205 $parentRacks = reduceSubarraysToColumn (getParents ($object, 'rack'), 'id');
0a7feebb 2206
e673ee24
DO
2207 // Main layout starts.
2208 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0><tr>";
2209
2210 // Left portlet with rack list.
2211 echo "<td class=pcleft height='1%'>";
2212 startPortlet ('Racks');
a8efc03e 2213 $allRacksData = listCells ('rack');
6d42599c
AA
2214
2215 // filter rack list to match only racks having common tags with the object (reducing $allRacksData)
55eefced 2216 if (! isset ($_REQUEST['show_all_racks']) && getConfigVar ('FILTER_RACKLIST_BY_TAGS') == 'yes')
6d42599c
AA
2217 {
2218 $matching_racks = array();
6d42599c
AA
2219 $matched_tags = array();
2220 foreach ($allRacksData as $rack)
01205bb6 2221 {
b51c1007 2222 $tag_chain = array_merge ($rack['etags'], $rack['itags']);
6d42599c 2223 foreach ($object['etags'] as $tag)
01205bb6 2224 if (tagOnChain ($tag, $tag_chain))
6d42599c
AA
2225 {
2226 $matching_racks[$rack['id']] = $rack;
2227 $matched_tags[$tag['id']] = $tag;
2228 break;
2229 }
01205bb6 2230 }
6d42599c
AA
2231 // add current object's racks even if they dont match filter
2232 foreach ($workingRacksData as $rack_id => $rack)
2233 if (! isset ($matching_racks[$rack_id]))
2234 $matching_racks[$rack_id] = $rack;
2235 // if matching racks found, and rack list is reduced, show 'show all' link
55eefced 2236 if (count ($matching_racks) && count ($matching_racks) != count ($allRacksData))
6d42599c 2237 {
1e9266ad 2238 $tmp = array();
6d42599c 2239 foreach ($matched_tags as $tag)
1e9266ad
DO
2240 $tmp[] = '{' . $tag['tag'] . '}';
2241 $filter_text = implode (' or ', $tmp);
6d42599c
AA
2242 $href_show_all = trim($_SERVER['REQUEST_URI'], '&');
2243 $href_show_all .= htmlspecialchars('&show_all_racks=1');
2244 echo "(filtered by <span class='filter-text'>$filter_text</span>, <a href='$href_show_all'>show all</a>)<p>";
2245 $allRacksData = $matching_racks;
2246 }
2247 }
2248
4b8d413e 2249 if (count ($allRacksData) <= getConfigVar ('RACK_PRESELECT_THRESHOLD'))
61a1d996 2250 foreach ($allRacksData as $rack)
51c18782
DO
2251 if (!array_key_exists ($rack['id'], $workingRacksData))
2252 {
2253 amplifyCell ($rack);
2254 $workingRacksData[$rack['id']] = $rack;
2255 }
2256 foreach (array_keys ($workingRacksData) as $rackId)
2257 applyObjectMountMask ($workingRacksData[$rackId], $object_id);
2258 printOpFormIntro ('updateObjectAllocation');
4b8d413e 2259 renderRackMultiSelect ('rackmulti[]', $allRacksData, array_keys ($workingRacksData));
51c18782 2260 echo "<br><br>";
e673ee24
DO
2261 finishPortlet();
2262 echo "</td>";
2263
2264 // Middle portlet with comment and submit.
2265 echo "<td class=pcleft>";
5c8bf2ba 2266 startPortlet ('Comment (for Rackspace History)');
e673ee24
DO
2267 echo "<textarea name=comment rows=10 cols=40></textarea><br>\n";
2268 echo "<input type=submit value='Save' name=got_atoms>\n";
51c18782 2269 echo "<br><br>";
e673ee24
DO
2270 finishPortlet();
2271 echo "</td>";
2272
2273 // Right portlet with rendered racks. If this form submit is not final, we have to
2274 // reflect the former state of the grid in current form.
2275 echo "<td class=pcright rowspan=2 height='1%'>";
2276 startPortlet ('Working copy');
e8b94513 2277 includeJQueryUI (false);
65e557dd 2278 addJS ('js/racktables.js');
c82023ad 2279 addJS ('js/bulkselector.js');
e673ee24
DO
2280 echo '<table border=0 cellspacing=10 align=center><tr>';
2281 foreach ($workingRacksData as $rack_id => $rackData)
2282 {
c26d97b6 2283 $is_ro = !rackModificationPermitted ($rackData, 'updateObjectAllocation');
e673ee24
DO
2284 // Order is important here: only original allocation is highlighted.
2285 highlightObject ($rackData, $object_id);
2286 markupAtomGrid ($rackData, 'T');
2287 // If we have a form processed, discard user input and show new database
2288 // contents.
c26d97b6 2289 if (!$is_ro && isset ($_REQUEST['rackmulti'][0])) // is an update
e673ee24
DO
2290 mergeGridFormToRack ($rackData);
2291 echo "<td valign=top>";
c26d97b6
VS
2292 echo "<center>\n<h2 style='margin:0px'>${rackData['name']}</h2>\n";
2293 $read_only_text = $is_ro ? '(read-only)' : '&nbsp;';
2294 echo "<p style='color: red; margin-top:0px'>${read_only_text}</p>\n";
e8b94513 2295 echo "<table class=rack id=selectableRack border=0 cellspacing=0 cellpadding=1>\n";
57c69e9c 2296 echo "<tr><th width='10%'>&nbsp;</th>";
bd0bfb76
AD
2297 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
2298 echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
2299 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
c26d97b6 2300 renderAtomGrid ($rackData, $is_ro);
57c69e9c 2301 echo "<tr><th width='10%'>&nbsp;</th>";
bd0bfb76
AD
2302 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
2303 echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
2304 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
0a7feebb
AD
2305 echo "</table>\n<br>\n";
2306 // Determine zero-u checkbox status.
2307 // If form has been submitted, use form data, otherwise use DB data.
c26d97b6 2308 if (!$is_ro && isset($_REQUEST['op']))
0a7feebb
AD
2309 $checked = isset($_REQUEST['zerou_'.$rack_id]) ? 'checked' : '';
2310 else
2311 $checked = in_array($rack_id, $parentRacks) ? 'checked' : '';
c26d97b6
VS
2312 $disabled_text = $is_ro ? ' disabled' : '';
2313 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 2314 echo "<input type='button' onclick='uncheckAll();' value='Uncheck all'>\n";
0a7feebb 2315 echo '</center></td>';
e673ee24
DO
2316 }
2317 echo "</tr></table>";
2318 finishPortlet();
2319 echo "</td>\n";
2320
2321 echo "</form>\n";
2322 echo "</tr></table>\n";
2323}
2324
2325function renderMolecule ($mdata, $object_id)
2326{
2327 // sort data out
2328 $rackpack = array();
2329 global $loclist;
64b95774 2330 foreach ($mdata as $rua)
e673ee24
DO
2331 {
2332 $rack_id = $rua['rack_id'];
2333 $unit_no = $rua['unit_no'];
2334 $atom = $rua['atom'];
2335 if (!isset ($rackpack[$rack_id]))
2336 {
61a1d996
DO
2337 $rackData = spotEntity ('rack', $rack_id);
2338 amplifyCell ($rackData);
e673ee24
DO
2339 for ($i = $rackData['height']; $i > 0; $i--)
2340 for ($locidx = 0; $locidx < 3; $locidx++)
2341 $rackData[$i][$locidx]['state'] = 'F';
2342 $rackpack[$rack_id] = $rackData;
2343 }
2344 $rackpack[$rack_id][$unit_no][$loclist[$atom]]['state'] = 'T';
2345 $rackpack[$rack_id][$unit_no][$loclist[$atom]]['object_id'] = $object_id;
2346 }
2347 // now we have some racks to render
64b95774 2348 foreach ($rackpack as $rackData)
e673ee24
DO
2349 {
2350 markAllSpans ($rackData);
2351 echo "<table class=molecule cellspacing=0>\n";
2352 echo "<caption>${rackData['name']}</caption>\n";
2353 echo "<tr><th width='10%'>&nbsp;</th><th width='20%'>Front</th><th width='50%'>Interior</th><th width='20%'>Back</th></tr>\n";
2354 for ($i = $rackData['height']; $i > 0; $i--)
2355 {
4dcd770e 2356 echo "<tr><th>" . inverseRackUnit ($i, $rackData) . "</th>";
e673ee24
DO
2357 for ($locidx = 0; $locidx < 3; $locidx++)
2358 {
2359 $state = $rackData[$i][$locidx]['state'];
65e557dd 2360 echo "<td class='atom state_${state}'>&nbsp;</td>\n";
e673ee24
DO
2361 }
2362 echo "</tr>\n";
2363 }
2364 echo "</table>\n";
2365 }
2366}
2367
23cdc7e9 2368function renderDepot ()
e673ee24 2369{
23cdc7e9 2370 global $pageno, $nextorder;
23cdc7e9 2371 $cellfilter = getCellFilter();
950dfa1f
AA
2372 $objects = array();
2373 $objects_count = getEntitiesCount ('object');
db8255ac 2374 $showobjecttype = (getConfigVar ('SHOW_OBJECTTYPE') == 'yes');
23cdc7e9
DO
2375
2376 echo "<table border=0 class=objectview>\n";
2377 echo "<tr><td class=pcleft>";
2378
950dfa1f
AA
2379 if ($objects_count == 0)
2380 echo '<h2>No objects exist</h2>';
2381 // 1st attempt: do not fetch all objects if cellfilter is empty and rendering empty result is enabled
2382 elseif (! ($cellfilter['is_empty'] && renderEmptyResults ($cellfilter, 'objects', $objects_count)))
e673ee24 2383 {
2b16c179 2384 $objects = applyCellFilter ('object', $cellfilter);
20506de3 2385 // 2nd attempt: do not render all fetched objects if rendering empty result is enabled
950dfa1f 2386 if (! renderEmptyResults ($cellfilter, 'objects', count($objects)))
1ebbf889 2387 {
4899096d
AD
2388 startPortlet ('Objects (' . count ($objects) . ')');
2389 echo '<br><br><table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
db8255ac
J
2390 echo '<tr><th>Common name</th>';
2391 if ($showobjecttype)
2392 echo '<th>Type</th>';
2393 echo '<th>Visible label</th><th>Asset tag</th><th>Row/Rack or Container</th></tr>';
4899096d 2394 $order = 'odd';
3f8a25d6
DO
2395 # gather IDs of all objects and fetch rackspace info in one pass
2396 $idlist = array();
2397 foreach ($objects as $obj)
2398 $idlist[] = $obj['id'];
2399 $mountinfo = getMountInfo ($idlist);
cae81598 2400 $containerinfo = getContainerInfo ($idlist);
4899096d
AD
2401 foreach ($objects as $obj)
2402 {
ec788aba
AD
2403 $problem = ($obj['has_problems'] == 'yes') ? 'has_problems' : '';
2404 echo "<tr class='row_${order} tdleft ${problem}' valign=top><td>" . mkA ("<strong>${obj['dname']}</strong>", 'object', $obj['id']);
4899096d
AD
2405 if (count ($obj['etags']))
2406 echo '<br><small>' . serializeTags ($obj['etags'], makeHref(array('page'=>$pageno, 'tab'=>'default')) . '&') . '</small>';
db8255ac
J
2407 echo '</td>';
2408 if ($showobjecttype)
9563f56f 2409 echo "<td>" . decodeObjectType ($obj['objtype_id']) . "</td>";
8526b4cc 2410 echo "<td>${obj['label']}</td>";
4318ced5 2411 echo "<td>${obj['asset_no']}</td>";
3f8a25d6 2412 $places = array();
cae81598
AD
2413 if (array_key_exists ($obj['id'], $containerinfo))
2414 foreach ($containerinfo[$obj['id']] as $ci)
20506de3 2415 $places[] = mkA ($ci['container_dname'], 'object', $ci['container_id']);
cae81598 2416 if (array_key_exists ($obj['id'], $mountinfo))
3f8a25d6
DO
2417 foreach ($mountinfo[$obj['id']] as $mi)
2418 $places[] = mkA ($mi['row_name'], 'row', $mi['row_id']) . '/' . mkA ($mi['rack_name'], 'rack', $mi['rack_id']);
cae81598
AD
2419 if (! count ($places))
2420 $places[] = 'Unmounted';
4318ced5 2421 echo "<td>" . implode (', ', $places) . '</td>';
4899096d
AD
2422 echo '</tr>';
2423 $order = $nextorder[$order];
2424 }
2425 echo '</table>';
2426 finishPortlet();
1ebbf889 2427 }
e673ee24 2428 }
23cdc7e9
DO
2429
2430 echo "</td><td class=pcright width='25%'>";
2431
95857b5c 2432 renderCellFilterPortlet ($cellfilter, 'object', $objects);
23cdc7e9 2433 echo "</td></tr></table>\n";
e673ee24
DO
2434}
2435
1ebbf889
AA
2436// This function returns TRUE if the result set is too big to be rendered, and no filter is set.
2437// In this case it renders the describing message instead.
2438function renderEmptyResults($cellfilter, $entities_name, $count = NULL)
2439{
2440 if (!$cellfilter['is_empty'])
2441 return FALSE;
2442 if (isset ($_REQUEST['show_all_objects']))
2443 return FALSE;
2444 $max = intval(getConfigVar('MAX_UNFILTERED_ENTITIES'));
2445 if (0 == $max || $count <= $max)
2446 return FALSE;
2447
2448 $href_show_all = trim($_SERVER['REQUEST_URI'], '&');
2449 $href_show_all .= htmlspecialchars('&show_all_objects=1');
2450 $suffix = isset ($count) ? " ($count)" : '';
2451 echo <<<END
2452<p>Please set a filter to display the corresponging $entities_name.
2453<br><a href="$href_show_all">Show all $entities_name$suffix</a>
2454END;
2455 return TRUE;
2456}
2457
e673ee24 2458// History viewer for history-enabled simple dictionaries.
9b8174d7 2459function renderObjectHistory ($object_id)
e673ee24 2460{
e673ee24
DO
2461 $order = 'odd';
2462 global $nextorder;
9b8174d7
AD
2463 echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
2464 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>';
2465 $result = usePreparedSelectBlade
2466 (
2467 'SELECT ctime, user_name, name, label, asset_no, has_problems, comment FROM ObjectHistory WHERE id=? ORDER BY ctime',
2468 array ($object_id)
2469 );
e673ee24
DO
2470 while ($row = $result->fetch (PDO::FETCH_NUM))
2471 {
2472 echo "<tr class=row_${order}><td>${row[0]}</td>";
9b8174d7 2473 for ($i = 1; $i <= 6; $i++)
e673ee24
DO
2474 echo "<td>" . $row[$i] . "</td>";
2475 echo "</tr>\n";
2476 $order = $nextorder[$order];
2477 }
2478 echo "</table><br>\n";
2479}
2480
2481function renderRackspaceHistory ()
2482{
4fbb5a00 2483 global $nextorder, $pageno, $tabno;
e673ee24
DO
2484 $order = 'odd';
2485 $history = getRackspaceHistory();
2486 // Show the last operation by default.
2487 if (isset ($_REQUEST['op_id']))
2488 $op_id = $_REQUEST['op_id'];
2489 elseif (isset ($history[0]['mo_id']))
2490 $op_id = $history[0]['mo_id'];
2491 else $op_id = NULL;
dec748f6 2492
e673ee24
DO
2493 $omid = NULL;
2494 $nmid = NULL;
2495 $object_id = 1;
2496 if ($op_id)
2497 list ($omid, $nmid) = getOperationMolecules ($op_id);
2498
2499 // Main layout starts.
2500 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
2501
2502 // Left top portlet with old allocation.
2503 echo "<tr><td class=pcleft>";
2504 startPortlet ('Old allocation');
2505 if ($omid)
2506 {
2507 $oldMolecule = getMolecule ($omid);
2508 renderMolecule ($oldMolecule, $object_id);
2509 }
2510 else
2511 echo "nothing";
2512 finishPortlet();
2513
2514 echo '</td><td class=pcright>';
2515
2516 // Right top portlet with new allocation
2517 startPortlet ('New allocation');
2518 if ($nmid)
2519 {
2520 $newMolecule = getMolecule ($nmid);
2521 renderMolecule ($newMolecule, $object_id);
2522 }
2523 else
2524 echo "nothing";
2525 finishPortlet();
737a3f72 2526
e673ee24 2527 echo '</td></tr><tr><td colspan=2>';
737a3f72 2528
e673ee24
DO
2529 // Bottom portlet with list
2530
2531 startPortlet ('Rackspace allocation history');
2532 echo "<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>\n";
7fa7047a 2533 echo "<tr><th>timestamp</th><th>author</th><th>object</th><th>comment</th></tr>\n";
e673ee24
DO
2534 foreach ($history as $row)
2535 {
2536 if ($row['mo_id'] == $op_id)
2537 $class = 'hl';
2538 else
2539 $class = "row_${order}";
4fbb5a00 2540 echo "<tr class=${class}><td><a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'op_id'=>$row['mo_id']))."'>${row['ctime']}</a></td>";
7fa7047a
DO
2541 echo "<td>${row['user_name']}</td><td>";
2542 renderCell (spotEntity ('object', $row['ro_id']));
a0bdf6dc 2543 echo '</td><td>' . stringForTD ($row['comment'], 0) . '</td></tr>';
e673ee24
DO
2544 $order = $nextorder[$order];
2545 }
2546 echo "</table>\n";
2547 finishPortlet();
737a3f72 2548
e673ee24 2549 echo '</td></tr></table>';
e673ee24
DO
2550}
2551
4318ced5 2552function renderIPSpaceRecords ($tree, $baseurl, $target = 0, $level = 1)
d65353ad
DO
2553{
2554 $self = __FUNCTION__;
4318ced5 2555 $knight = (getConfigVar ('IPV4_ENABLE_KNIGHT') == 'yes');
774968ea 2556 $display_routers = (getConfigVar ('IPV4_TREE_RTR_AS_CELL') != 'none');
00276508
AA
2557
2558 // scroll page to the highlighted item
2559 if ($target && isset ($_REQUEST['hl_net']))
65e557dd 2560 addAutoScrollScript ("net-$target");
00276508 2561
d65353ad
DO
2562 foreach ($tree as $item)
2563 {
0137d53c
DO
2564 if (isset ($item['id']))
2565 {
1f54e1ba 2566 $decor = array ('indent' => $level);
fec0c8da 2567 if ($item['symbol'] == 'node-collapsed')
4318ced5 2568 $decor['symbolurl'] = "${baseurl}&eid=${item['id']}&hl_net=1";
fec0c8da 2569 elseif ($item['symbol'] == 'node-expanded')
4318ced5
AA
2570 $decor['symbolurl'] = $baseurl . ($item['parent_id'] ? "&eid=${item['parent_id']}&hl_net=1" : '');
2571 $tr_class = '';
21ee3351 2572 if ($target == $item['id'] && isset ($_REQUEST['hl_net']))
64347dcf 2573 {
5c717ce5 2574 $decor['tdclass'] = ' highlight';
4318ced5 2575 $tr_class = $decor['tdclass'];
64347dcf 2576 }
4318ced5
AA
2577 echo "<tr valign=top class=\"$tr_class\">";
2578 printIPNetInfoTDs ($item, $decor);
2579
2580 // capacity and usage
2581 echo "<td class=tdcenter>";
2582 echo getRenderedIPNetCapacity ($item);
64347dcf 2583 echo "</td>";
4318ced5
AA
2584
2585 if ($display_routers)
6063fab7 2586 printRoutersTD (findNetRouters ($item), getConfigVar ('IPV4_TREE_RTR_AS_CELL'));
0137d53c 2587 echo "</tr>";
55eefced 2588 if ($item['symbol'] == 'node-expanded' || $item['symbol'] == 'node-expanded-static')
4318ced5 2589 $self ($item['kids'], $baseurl, $target, $level + 1);
0137d53c 2590 }
26fc6e7c 2591 elseif (getConfigVar ('IPV4_TREE_SHOW_UNALLOCATED') == 'yes')
0137d53c 2592 {
4318ced5 2593 // non-allocated (spare) IP range
0137d53c 2594 echo "<tr valign=top>";
21ee3351 2595 printIPNetInfoTDs ($item, array ('indent' => $level, 'knight' => $knight, 'tdclass' => 'sparenetwork'));
00276508 2596
4318ced5 2597 // capacity and usage
21ee3351 2598 echo "<td class=tdcenter>";
4318ced5 2599 echo getRenderedIPNetCapacity ($item);
21ee3351 2600 echo "</td>";
4318ced5
AA
2601 if ($display_routers)
2602 echo "<td></td>";
21ee3351 2603 echo "</tr>";
21ee3351 2604 }
21ee3351
AA
2605 }
2606}
2607
4318ced5 2608function renderIPSpace()
e673ee24 2609{
4fbb5a00 2610 global $pageno, $tabno;
4318ced5 2611 $realm = ($pageno == 'ipv4space' ? 'ipv4net' : 'ipv6net');
31c941ec 2612 $cellfilter = getCellFilter();
21ee3351 2613
b99b4167
AA
2614 // expand request can take either natural values or "ALL". Zero means no expanding.
2615 $eid = isset ($_REQUEST['eid']) ? $_REQUEST['eid'] : 0;
2616
21ee3351
AA
2617 echo "<table border=0 class=objectview>\n";
2618 echo "<tr><td class=pcleft>";
950dfa1f
AA
2619
2620 $netlist = array();
b99b4167 2621 if (! ($cellfilter['is_empty'] && ! $eid && renderEmptyResults($cellfilter, 'IP nets', getEntitiesCount ($realm))))
21ee3351 2622 {
950dfa1f
AA
2623 $top = NULL;
2624 foreach (listCells ($realm) as $net)
21ee3351 2625 {
55eefced 2626 if (isset ($top) && IPNetContains ($top, $net))
950dfa1f 2627 ;
55eefced 2628 elseif (! count ($cellfilter['expression']) || judgeCell ($net, $cellfilter['expression']))
950dfa1f
AA
2629 $top = $net;
2630 else
2631 continue;
2632 $netlist[$net['id']] = $net;
2633 }
2634 $netcount = count ($netlist);
950dfa1f
AA
2635 $tree = prepareIPTree ($netlist, $eid);
2636
2637 if (! renderEmptyResults($cellfilter, 'IP nets', count($tree)))
2638 {
2639 startPortlet ("networks (${netcount})");
2640 echo '<h4>';
2641 $all = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'ALL')) .
2642 $cellfilter['urlextra'] . "'>expand&nbsp;all</a>";
2643 $none = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'NONE')) .
2644 $cellfilter['urlextra'] . "'>collapse&nbsp;all</a>";
2645 $auto = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno)) .
2646 $cellfilter['urlextra'] . "'>auto-collapse</a>";
2647
2648 if ($eid === 0)
2649 echo 'auto-collapsing at threshold ' . getConfigVar ('TREE_THRESHOLD') . " ($all / $none)";
2650 elseif ($eid === 'ALL')
2651 echo "expanding all ($auto / $none)";
2652 elseif ($eid === 'NONE')
2653 echo "collapsing all ($all / $auto)";
2654 else
2655 {
a0f0c0c7
AA
2656 try
2657 {
2658 $netinfo = spotEntity ($realm, $eid);
2659 echo "expanding ${netinfo['ip']}/${netinfo['mask']} ($auto / $all / $none)";
2660 }
2661 catch (EntityNotFoundException $e)
2662 {
2663 // ignore invalid eid error
2664 }
950dfa1f 2665 }
6edc799c 2666 echo "</h4><table class='widetable zebra' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
950dfa1f
AA
2667 echo "<tr><th>prefix</th><th>name/tags</th><th>capacity</th>";
2668 if (getConfigVar ('IPV4_TREE_RTR_AS_CELL') != 'none')
2669 echo "<th>routed by</th>";
2670 echo "</tr>\n";
2671 $baseurl = makeHref(array('page'=>$pageno, 'tab'=>$tabno)) . $cellfilter['urlextra'];
2672 renderIPSpaceRecords ($tree, $baseurl, $eid);
2673 echo "</table>\n";
2674 finishPortlet();
21ee3351 2675 }
21ee3351
AA
2676 }
2677
2678 echo '</td><td class=pcright>';
950dfa1f 2679 renderCellFilterPortlet ($cellfilter, $realm, $netlist);
21ee3351
AA
2680 echo "</td></tr></table>\n";
2681}
2682
4318ced5 2683function renderIPSpaceEditor()
21ee3351 2684{
4318ced5
AA
2685 global $pageno;
2686 $realm = ($pageno == 'ipv4space' ? 'ipv4net' : 'ipv6net');
4318ced5 2687 $addrspaceList = listCells ($realm);
93a49e5b
AA
2688 startPortlet ('Manage existing (' . count ($addrspaceList) . ')');
2689 if (count ($addrspaceList))
21ee3351 2690 {
21ee3351
AA
2691 echo "<table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
2692 echo "<tr><th>&nbsp;</th><th>prefix</th><th>name</th><th>capacity</th></tr>";
21ee3351
AA
2693 foreach ($addrspaceList as $netinfo)
2694 {
21ee3351 2695 echo "<tr valign=top><td>";
93a49e5b 2696 if (! isIPNetworkEmpty ($netinfo))
21ee3351
AA
2697 printImageHREF ('nodestroy', 'There are ' . count ($netinfo['addrlist']) . ' allocations inside');
2698 else
93a49e5b 2699 echo getOpLink (array ('op' => 'del', 'id' => $netinfo['id']), '', 'destroy', 'Delete this prefix');
6d8424f8 2700 echo '</td><td class=tdleft>' . mkCellA ($netinfo) . '</td>';
a0bdf6dc 2701 echo '<td class=tdleft>' . stringForTD ($netinfo['name']);
1f54e1ba
DO
2702 if (count ($netinfo['etags']))
2703 echo '<br><small>' . serializeTags ($netinfo['etags']) . '</small>';
2704 echo '</td><td>';
4318ced5 2705 echo getRenderedIPNetCapacity ($netinfo);
b1e39212 2706 echo '</tr>';
e673ee24 2707 }
04d619d0
DO
2708 echo "</table>";
2709 finishPortlet();
e673ee24 2710 }
93a49e5b
AA
2711}
2712
2713function renderIPNewNetForm ()
2714{
2715 global $pageno;
2716 if ($pageno == 'ipv6space')
2717 {
2718 $realm = 'ipv6net';
3495ec6e 2719 $regexp = '^[a-fA-F0-9:]*:[a-fA-F0-9:\.]*/\d{1,3}$';
93a49e5b
AA
2720 }
2721 else
2722 {
2723 $realm = 'ipv4net';
3495ec6e 2724 $regexp = '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$';
93a49e5b
AA
2725 }
2726
2727 // IP prefix validator
10f5133a 2728 addJS ('js/live_validation.js');
93a49e5b 2729 $regexp = addslashes ($regexp);
10f5133a 2730 addJS (<<<END
93a49e5b 2731$(document).ready(function () {
b895e6b4 2732 $('form#add input[name="range"]').attr('match', '$regexp');
93a49e5b
AA
2733 Validate.init();
2734});
2735END
2736 , TRUE);
dec748f6 2737
93a49e5b 2738 startPortlet ('Add new');
10f5133a 2739 printOpFormIntro ('add');
e7b84015
AB
2740 echo '<table border=0 cellpadding=5 cellspacing=0 align=center>';
2741
93a49e5b
AA
2742 // inputs column
2743 $prefix_value = empty ($_REQUEST['set-prefix']) ? '' : $_REQUEST['set-prefix'];
04c61e7e 2744 echo "<th class=tdright>Prefix:</th><td class=tdleft><input type=text name='range' size=36 class='live-validate' value='${prefix_value}'></td>";
e7b84015 2745 echo '<tr><th class=tdright>VLAN:</th><td class=tdleft>';
04c61e7e
AB
2746 echo getOptionTree ('vlan_ck', getAllVLANOptions(), array ('select_class' => 'vertical')) . '</td></tr>';
2747 echo "<tr><th class=tdright>Name:</th><td class=tdleft><input type=text name='name' size='20'></td></tr>";
e7b84015
AB
2748 echo '<tr><th class=tdright>Tags:</th><td class="tdleft">';
2749 printTagsPicker ();
2750 echo '</td></tr>';
38707670
DO
2751 echo '<tr><td class=tdright><input type=checkbox name="is_connected" id="is_connected"></td>';
2752 echo '<th class=tdleft><label for="is_connected">reserve subnet-router anycast address</label></th></tr>';
93a49e5b 2753 echo "<tr><td colspan=2>";
04c61e7e 2754 printImageHREF ('CREATE', 'Add a new network', TRUE);
93a49e5b 2755 echo '</td></tr>';
e7b84015 2756 echo "</table></form><br><br>\n";
93a49e5b 2757 finishPortlet();
e673ee24
DO
2758}
2759
4318ced5 2760function getRenderedIPNetBacktrace ($range)
e673ee24 2761{
4318ced5
AA
2762 if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
2763 return array();
e673ee24 2764
4318ced5
AA
2765 $v = ($range['realm'] == 'ipv4net') ? 4 : 6;
2766 $space = "ipv${v}space"; // ipv4space, ipv6space
2767 $tag = "\$ip${v}netid_"; // $ip4netid_, $ip6netid_
dec748f6 2768
4318ced5
AA
2769 $ret = array();
2770 // Build a backtrace from all parent networks.
2771 $clen = $range['mask'];
2772 $backtrace = array();
2773 $backtrace['&rarr;'] = $range;
2774 $key = '';
2775 while (NULL !== ($upperid = getIPAddressNetworkId ($range['ip_bin'], $clen)))
2776 {
2777 $upperinfo = spotEntity ($range['realm'], $upperid);
2778 $clen = $upperinfo['mask'];
2779 $key .= '&uarr;';
2780 $backtrace[$key] = $upperinfo;
2781 }
2782 foreach (array_reverse ($backtrace) as $arrow => $ainfo)
2783 {
2784 $link = '<a href="' . makeHref (array (
2785 'page' => $space,
2786 'tab' => 'default',
2787 'clear-cf' => '',
2788 'cfe' => '{' . $tag . $ainfo['id'] . '}',
2789 'hl_net' => 1,
2790 'eid' => $range['id'],
2791 )) . '" title="View IP tree with this net as root">' . $arrow . '</a>';
2792 $ret[] = array ($link, getOutputOf ('renderCell', $ainfo));
2793 }
2794 return $ret;
2795}
2796
d10401bf 2797function renderIPNetwork ($id)
4318ced5
AA
2798{
2799 global $pageno;
2800 $realm = $pageno; // 'ipv4net', 'ipv6net'
d10401bf 2801 $range = spotEntity ($realm, $id);
4318ced5 2802 loadIPAddrList ($range);
2034d968 2803 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
99ab184f
DO
2804 echo "<tr><td colspan=2 align=center><h1>${range['ip']}/${range['mask']}</h1><h2>";
2805 echo htmlspecialchars ($range['name'], ENT_QUOTES, 'UTF-8') . "</h2></td></tr>\n";
2034d968
DO
2806
2807 echo "<tr><td class=pcleft width='50%'>";
646272be
AA
2808
2809 // render summary portlet
2810 $summary = array();
4318ced5 2811 $summary['%% used'] = getRenderedIPNetCapacity ($range);
2b6b0038 2812 $summary = getRenderedIPNetBacktrace ($range) + $summary;
4318ced5 2813 if ($realm == 'ipv4net')
001e61fa 2814 {
4318ced5
AA
2815 $summary[] = array ('Netmask:', ip4_format ($range['mask_bin']));
2816 $summary[] = array ('Netmask:', "0x" . strtoupper (implode ('', unpack ('H*', $range['mask_bin']))));
2817 $summary['Wildcard bits'] = ip4_format ( ~ $range['mask_bin']);
001e61fa 2818 }
dec748f6 2819
11e3af31
DO
2820 $reuse_domain = considerConfiguredConstraint ($range, '8021Q_MULTILINK_LISTSRC');
2821 $domainclass = array();
2822 foreach (array_count_values (reduceSubarraysToColumn ($range['8021q'], 'domain_id')) as $domain_id => $vlan_count)
2823 $domainclass[$domain_id] = $vlan_count == 1 ? '' : ($reuse_domain ? '{trwarning}' : '{trerror}');
a5f9d1ca 2824 foreach ($range['8021q'] as $item)
3f642516 2825 $summary[] = array ($domainclass[$item['domain_id']] . 'VLAN:', formatVLANAsHyperlink (getVlanRow ($item['domain_id'] . '-' . $item['vlan_id'])));
55eefced 2826 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes' && count ($routers = findNetRouters ($range)))
04d619d0 2827 {
646272be
AA
2828 $summary['Routed by'] = '';
2829 foreach ($routers as $rtr)
4318ced5 2830 $summary['Routed by'] .= getOutputOf ('renderRouterCell', $rtr['ip_bin'], $rtr['iface'], spotEntity ('object', $rtr['id']));
04d619d0 2831 }
646272be
AA
2832 $summary['tags'] = '';
2833 renderEntitySummary ($range, 'summary', $summary);
e1ae3fb4 2834
33973968 2835 if ($range['comment'] != '')
99ab184f
DO
2836 {
2837 startPortlet ('Comment');
2838 echo '<div class=commentblock>' . string_insert_hrefs (htmlspecialchars ($range['comment'], ENT_QUOTES, 'UTF-8')) . '</div>';
2839 finishPortlet ();
2840 }
2841
d10401bf 2842 renderFilesPortlet ($realm, $id);
2034d968 2843 echo "</td>\n";
e673ee24 2844
2034d968
DO
2845 echo "<td class=pcright>";
2846 startPortlet ('details');
4318ced5
AA
2847 renderIPNetworkAddresses ($range);
2848 finishPortlet();
2849 echo "</td></tr></table>\n";
2850}
e673ee24 2851
4318ced5
AA
2852// Used solely by renderSeparator
2853function renderEmptyIPv6 ($ip_bin, $hl_ip)
2854{
2855 $class = 'tdleft';
2856 if ($ip_bin === $hl_ip)
5c717ce5 2857 $class .= ' highlight';
4318ced5 2858 $fmt = ip6_format ($ip_bin);
235124a6 2859 echo "<tr class='$class'><td><a name='ip-$fmt' href='" . makeHref (array ('page' => 'ipaddress', 'ip' => $fmt)) . "'>" . $fmt;
0b8d8309
AA
2860 $editable = permitted ('ipaddress', 'properties', 'editAddress')
2861 ? 'editable'
2862 : '';
cc2fa820
AD
2863 echo "</a></td><td><span class='rsvtext $editable id-$fmt op-upd-ip-name'></span></td>";
2864 echo "<td><span class='rsvtext $editable id-$fmt op-upd-ip-comment'></span></td><td>&nbsp;</td></tr>\n";
4318ced5
AA
2865}
2866
2867// Renders empty table line to shrink empty IPv6 address ranges.
2868// If the range consists of single address, renders the address instead of empty line.
2869// Renders address $hl_ip inside the range.
2870// Used solely by renderIPv6NetworkAddresses
2871function renderSeparator ($first, $last, $hl_ip)
2872{
2873 $self = __FUNCTION__;
2874 if (strcmp ($first, $last) > 0)
2875 return;
2876 if ($first == $last)
2877 renderEmptyIPv6 ($first, $hl_ip);
2878 elseif (isset ($hl_ip) && strcmp ($hl_ip, $first) >= 0 && strcmp ($hl_ip, $last) <= 0)
2879 { // $hl_ip is inside the range $first - $last
2880 $self ($first, ip_prev ($hl_ip), $hl_ip);
2881 renderEmptyIPv6 ($hl_ip, $hl_ip);
2882 $self (ip_next ($hl_ip), $last, $hl_ip);
2883 }
2884 else
cc2fa820 2885 echo "<tr><td colspan=4 class=tdleft></td></tr>\n";
4318ced5
AA
2886}
2887
75e7c0c6 2888// calculates page number that contains given $ip (used by renderIPv6NetworkAddresses)
4318ced5
AA
2889function getPageNumOfIPv6 ($list, $ip_bin, $maxperpage)
2890{
2891 if (intval ($maxperpage) <= 0 || count ($list) <= $maxperpage)
2892 return 0;
2893 $keys = array_keys ($list);
2894 for ($i = 1; $i <= count ($keys); $i++)
2895 if (strcmp ($keys[$i-1], $ip_bin) >= 0)
2896 return intval ($i / $maxperpage);
2897 return intval (count ($list) / $maxperpage);
2898}
2899
2900function renderIPNetworkAddresses ($range)
2901{
2902 switch (strlen ($range['ip_bin']))
2903 {
2904 case 4: return renderIPv4NetworkAddresses ($range);
2905 case 16: return renderIPv6NetworkAddresses ($range);
2906 default: throw new InvalidArgException ("range['ip_bin']", $range['ip_bin']);
2907 }
2908}
2909
247acd67
KZ
2910function renderIPv4NetworkPageLink ($rangeid, $page, $title)
2911{
2912 global $pageno, $tabno;
2913 return "<a href='".makeHref (array ('page' => $pageno, 'tab' => $tabno, 'id' => $rangeid, 'pg' => $page)) . "' title='".$title."'>".$page."</a> ";
2914}
2915
2916function renderIPv4NetworkPagination ($range, $page, $numpages)
2917{
2918 $rendered_pager = '';
2919 $startip = ip4_bin2int ($range['ip_bin']);
2920 $endip = ip4_bin2int (ip_last ($range));
2921 $rangeid = $range['id'];
2922 // Should make this configurable perhaps
2923 // How many pages before/after current page to show
2924 $prepostpagecount = 8;
2925 // Minimum pages where pagination does not happen
2926 $paginationat = 16; // 16 pages is a /20, 32 is a /19
2927 $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
2928 if ($numpages <= $paginationat)
2929 {
2930 // create original pagination
2931 for ($i = 0; $i < $numpages; $i++)
2932 if ($i == $page)
2933 $rendered_pager .= "<b>".$i."</b> ";
2934 else
2935 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2936 }
2937 else // number of pages > page range, create ranged pagination
2938 {
2939 // page is within first subset
2940 if ($page - $prepostpagecount <= 1)
2941 {
2942 for ($i = 0; $i < $page; $i++)
2943 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2944 }
2945 // render 0 ... [page - prepostpagecount] [page - prepostpagecount + 1] ... [page - 1]
2946 else
2947 {
2948 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, 0, ip4_format(ip4_int2bin($startip)));
2949 $rendered_pager .= "... ";
2950 for ($i = $page - $prepostpagecount; $i < $page; $i++)
2951 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2952 }
2953 // render current page
2954 $rendered_pager .= "<b>".$page."</b> ";
2955 // page is within last subset
2956 if ($page + $prepostpagecount >= $numpages-2)
2957 {
2958 for ($i = $page+1; $i < $numpages; $i++)
2959 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2960 }
2961 // render [page + 1] [page + 2] ... [page + postpagecount] ... [end page]
2962 else
2963 {
2964 for ($i = $page+1; $i <= $page+$prepostpagecount; $i++)
2965 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2966 $rendered_pager .= "... ";
2967 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, ($numpages-1), ip4_format(ip4_int2bin($endip)));
2968 }
2969 }
2970 return $rendered_pager;
2971}
2972
4318ced5
AA
2973function renderIPv4NetworkAddresses ($range)
2974{
9d5f6a80 2975 global $pageno, $tabno, $aac_left;
4318ced5
AA
2976 $startip = ip4_bin2int ($range['ip_bin']);
2977 $endip = ip4_bin2int (ip_last ($range));
2978
2979 if (isset ($_REQUEST['hl_ip']))
00276508 2980 {
4318ced5
AA
2981 $hl_ip = ip4_bin2int (ip4_parse ($_REQUEST['hl_ip']));
2982 addAutoScrollScript ('ip-' . $_REQUEST['hl_ip']); // scroll page to highlighted ip
00276508
AA
2983 }
2984
79b57b18
AA
2985 // pager
2986 $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
2987 $address_count = $endip - $startip + 1;
2988 $page = 0;
446c25c3 2989 $rendered_pager = '';
79b57b18
AA
2990 if ($address_count > $maxperpage && $maxperpage > 0)
2991 {
2992 $page = isset ($_REQUEST['pg']) ? $_REQUEST['pg'] : (isset ($hl_ip) ? intval (($hl_ip - $startip) / $maxperpage) : 0);
2993 if ($numpages = ceil ($address_count / $maxperpage))
2994 {
4318ced5 2995 echo '<h3>' . ip4_format (ip4_int2bin ($startip)) . ' ~ ' . ip4_format (ip4_int2bin ($endip)) . '</h3>';
247acd67 2996 $rendered_pager = renderIPv4NetworkPagination ($range, $page, $numpages);
79b57b18
AA
2997 }
2998 $startip = $startip + $page * $maxperpage;
2999 $endip = min ($startip + $maxperpage - 1, $endip);
3000 }
3001
446c25c3 3002 echo $rendered_pager;
1ce89bdd 3003 echo "<table class='widetable' border=0 cellspacing=0 cellpadding=5 align='center' width='100%'>\n";
cc2fa820 3004 echo "<tr><th>Address</th><th>Name</th><th>Comment</th><th>Allocation</th></tr>\n";
e673ee24 3005
4318ced5
AA
3006 markupIPAddrList ($range['addrlist']);
3007 for ($ip = $startip; $ip <= $endip; $ip++)
3008 {
3009 $ip_bin = ip4_int2bin ($ip);
3010 $dottedquad = ip4_format ($ip_bin);
5c717ce5 3011 $tr_class = (isset ($hl_ip) && $hl_ip == $ip ? 'highlight' : '');
0cd7c0f6
AA
3012 if (isset ($range['addrlist'][$ip_bin]))
3013 $addr = $range['addrlist'][$ip_bin];
3014 else
e673ee24 3015 {
235124a6 3016 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
3017 $editable = permitted ('ipaddress', 'properties', 'editAddress')
3018 ? 'editable'
3019 : '';
cc2fa820
AD
3020 echo "<td><span class='rsvtext $editable id-$dottedquad op-upd-ip-name'></span></td>";
3021 echo "<td><span class='rsvtext $editable id-$dottedquad op-upd-ip-comment'></span></td><td></td></tr>\n";
b2035dca
DO
3022 continue;
3023 }
68c91b9c
AA
3024 // render IP change history
3025 $title = '';
3026 $history_class = '';
3027 if (isset ($addr['last_log']))
3028 {
3029 $title = ' title="' . htmlspecialchars ($addr['last_log']['user'] . ', ' . formatAge ($addr['last_log']['time']) , ENT_QUOTES) . '"';
3030 $history_class = 'hover-history underline';
3031 }
4318ced5
AA
3032 $tr_class .= ' ' . $addr['class'];
3033 echo "<tr class='tdleft $tr_class'>";
235124a6 3034 echo "<td><a class='$history_class' $title name='ip-$dottedquad' href='".makeHref(array('page'=>'ipaddress', 'ip'=>$addr['ip']))."'>${addr['ip']}</a></td>";
0b8d8309 3035 $editable =
cc2fa820 3036 (empty ($addr['allocs']) || !empty ($addr['name']) || !empty ($addr['comment']))
0b8d8309
AA
3037 && permitted ('ipaddress', 'properties', 'editAddress')
3038 ? 'editable'
3039 : '';
cc2fa820
AD
3040 echo "<td><span class='rsvtext $editable id-$dottedquad op-upd-ip-name'>${addr['name']}</span></td>";
3041 echo "<td><span class='rsvtext $editable id-$dottedquad op-upd-ip-comment'>${addr['comment']}</span></td>";
0b8d8309 3042 echo "<td>";
b2035dca 3043 $delim = '';
b2035dca
DO
3044 if ( $addr['reserved'] == 'yes')
3045 {
2d318652 3046 echo "<strong>RESERVED</strong> ";
b2035dca
DO
3047 $delim = '; ';
3048 }
212b565a 3049 foreach ($addr['allocs'] as $ref)
b2035dca 3050 {
9d5f6a80 3051 echo $delim . $aac_left[$ref['type']];
e749a34f 3052 echo makeIPAllocLink ($ip_bin, $ref, TRUE);
b2035dca
DO
3053 $delim = '; ';
3054 }
3055 if ($delim != '')
e0d6e38e 3056 $delim = '<br>';
212b565a
AA
3057 foreach ($addr['vslist'] as $vs_id)
3058 {
3059 $vs = spotEntity ('ipv4vs', $vs_id);
ad3e5e7e 3060 echo $delim . mkA ("${vs['name']}:${vs['vport']}/${vs['proto']}", 'ipv4vs', $vs['id']) . '&rarr;';
212b565a
AA
3061 $delim = '<br>';
3062 }
eacc0983
AA
3063 foreach ($addr['vsglist'] as $vs_id)
3064 {
3065 $vs = spotEntity ('ipvs', $vs_id);
3066 echo $delim . mkA ($vs['name'], 'ipvs', $vs['id']) . '&rarr;';
3067 $delim = '<br>';
3068 }
212b565a
AA
3069 foreach ($addr['rsplist'] as $rsp_id)
3070 {
3071 $rsp = spotEntity ('ipv4rspool', $rsp_id);
ad3e5e7e 3072 echo "${delim}&rarr;" . mkA ($rsp['name'], 'ipv4rspool', $rsp['id']);
212b565a
AA
3073 $delim = '<br>';
3074 }
b2035dca 3075 echo "</td></tr>\n";
4318ced5 3076 }
2727c7df 3077 // end of iteration
a8dc2982
AA
3078 if (permitted (NULL, NULL, 'set_reserve_comment'))
3079 addJS ('js/inplace-edit.js');
e673ee24
DO
3080
3081 echo "</table>";
b956dd24 3082 if ($rendered_pager != '')
446c25c3 3083 echo '<p>' . $rendered_pager . '</p>';
21ee3351
AA
3084}
3085
3086function renderIPv6NetworkAddresses ($netinfo)
3087{
9d5f6a80 3088 global $pageno, $tabno, $aac_left;
1ce89bdd 3089 echo "<table class='widetable' border=0 cellspacing=0 cellpadding=5 align='center' width='100%'>\n";
cc2fa820 3090 echo "<tr><th>Address</th><th>Name</th><th>Comment</th><th>Allocation</th></tr>\n";
21ee3351 3091
4318ced5
AA
3092 $hl_ip = NULL;
3093 if (isset ($_REQUEST['hl_ip']))
3094 {
3095 $hl_ip = ip6_parse ($_REQUEST['hl_ip']);
3096 addAutoScrollScript ('ip-' . ip6_format ($hl_ip));
3097 }
21ee3351 3098
21ee3351
AA
3099 $addresses = $netinfo['addrlist'];
3100 ksort ($addresses);
4318ced5 3101 markupIPAddrList ($addresses);
21ee3351
AA
3102
3103 // pager
3104 $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
3105 if (count ($addresses) > $maxperpage && $maxperpage > 0)
3106 {
3107 $page = isset ($_REQUEST['pg']) ? $_REQUEST['pg'] : (isset ($hl_ip) ? getPageNumOfIPv6 ($addresses, $hl_ip, $maxperpage) : 0);
3108 $numpages = ceil (count ($addresses) / $maxperpage);
3109 echo "<center><h3>$numpages pages:</h3>";
3110 for ($i=0; $i<$numpages; $i++)
3111 {
3112 if ($i == $page)
3113 echo "<b>$i</b> ";
3114 else
3115 echo "<a href='" . makeHref (array ('page' => $pageno, 'tab' => $tabno, 'id' => $netinfo['id'], 'pg' => $i)) . "'>$i</a> ";
3116 }
3117 echo "</center>";
3118 }
3119
3120 $i = 0;
dec748f6 3121 $interruped = FALSE;
4318ced5
AA
3122 $prev_ip = ip_prev ($netinfo['ip_bin']);
3123 foreach ($addresses as $ip_bin => $addr)
21ee3351
AA
3124 {
3125 if (isset ($page))
3126 {
3127 ++$i;
3128 if ($i <= $maxperpage * $page)
3129 continue;
3130 elseif ($i > $maxperpage * ($page + 1))
3131 {
3132 $interruped = TRUE;
3133 break;
3134 }