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