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