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