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