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