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