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