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