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