remove a stale comment
[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.
7e1db771 1305function renderGridForm ($rack_id, $filter, $header, $submit, $state1, $state2)
e673ee24 1306{
61a1d996
DO
1307 $rackData = spotEntity ('rack', $rack_id);
1308 amplifyCell ($rackData);
254673dd 1309 $filter ($rackData);
e673ee24 1310
e673ee24
DO
1311 // Render the result whatever it is.
1312 // Main layout.
1313 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
1314 echo "<tr><td colspan=2 align=center><h1>${rackData['name']}</h1></td></tr>\n";
1315
1316 // Left column with information portlet.
1317 echo "<tr><td class=pcleft height='1%' width='50%'>";
254673dd 1318 renderRackInfoPortlet ($rackData);
e673ee24 1319 echo "</td>\n";
bb09cca0 1320 echo "<td class=pcright>";
e673ee24
DO
1321
1322 // Grid form.
c26d97b6 1323 $is_ro = !rackModificationPermitted ($rackData, 'updateRack');
e673ee24 1324 startPortlet ($header);
65e557dd 1325 addJS ('js/racktables.js');
e673ee24 1326 echo "<center>\n";
c26d97b6
VS
1327 $read_only_text = $is_ro ? '(read-only)' : '&nbsp;';
1328 echo "<p style='color: red; margin-top:0px'>${read_only_text}</p>\n";
e673ee24 1329 echo "<table class=rack border=0 cellspacing=0 cellpadding=1>\n";
57c69e9c 1330 echo "<tr><th width='10%'>&nbsp;</th>";
bd0bfb76
AD
1331 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
1332 echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
1333 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
4fbb5a00 1334 printOpFormIntro ('updateRack');
e673ee24 1335 markupAtomGrid ($rackData, $state2);
c26d97b6 1336 renderAtomGrid ($rackData, $is_ro);
e673ee24
DO
1337 echo "</table></center>\n";
1338 echo "<br><input type=submit name=do_update value='${submit}'></form><br><br>\n";
1339 finishPortlet();
bb09cca0 1340 echo "</td></tr></table>\n";
e673ee24
DO
1341}
1342
1343function renderRackDesign ($rack_id)
1344{
bb09cca0 1345 renderGridForm ($rack_id, 'applyRackDesignMask', 'Rack design', 'Set rack design', 'A', 'F');
e673ee24
DO
1346}
1347
7e1db771 1348function renderRackProblems ($rack_id)
e673ee24 1349{
bb09cca0 1350 renderGridForm ($rack_id, 'applyRackProblemMask', 'Rack problems', 'Mark unusable atoms', 'F', 'U');
e673ee24
DO
1351}
1352
21928333
MH
1353function renderObjectPortHeaderRow()
1354{
1355 // Renders the headers for the ports table on the default page
1356
1357 echo '<tr><th class=tdleft>Local name</th><th class=tdleft>Visible label</th>';
1358 echo '<th class=tdleft>Interface</th><th class=tdleft>L2 address</th>';
1359 echo '<th class=tdcenter colspan=2>Remote object and port</th>';
1360 echo '<th class=tdleft>Cable ID</th></tr>';
1361}
1362
40c9fc94
AA
1363function renderObjectPortRow ($port, $is_highlighted)
1364{
acb01e71 1365 // highlight port name with yellow if its name is not canonical
8579b51b
AA
1366 $canon_pn = shortenPortName ($port['name'], $port['object_id']);
1367 $name_class = $canon_pn == $port['name'] ? '' : 'trwarning';
1368
90bf1755
DO
1369 echo '<tr';
1370 if ($is_highlighted)
1371 echo ' class=highlight';
40c9fc94 1372 $a_class = isEthernetPort ($port) ? 'port-menu' : '';
8579b51b 1373 echo "><td class='tdleft $name_class' NOWRAP><a name='port-${port['id']}' class='interactive-portname nolink $a_class'>${port['name']}</a></td>";
40c9fc94 1374 echo "<td class=tdleft>${port['label']}</td>";
bba495fc 1375 echo "<td class=tdleft>" . formatPortIIFOIF ($port) . "</td><td class='tdleft l2address'>${port['l2address']}</td>";
0b09238d 1376 if ($port['remote_object_id'])
90bf1755 1377 {
f4d00faf 1378 $dname = formatObjectDisplayedName ($port['remote_object_name'], $port['remote_object_tid']);
0b09238d 1379 echo "<td class=tdleft>" .
f4d00faf 1380 formatPortLink ($port['remote_object_id'], $dname, $port['remote_id'], NULL) .
0b09238d
DO
1381 "</td>";
1382 echo "<td class=tdleft>" . formatLoggedSpan ($port['last_log'], $port['remote_name'], 'underline') . "</td>";
1383 $editable = permitted ('object', 'ports', 'editPort')
1384 ? 'editable'
1385 : '';
1386 echo "<td class=tdleft><span class='rsvtext $editable id-${port['id']} op-upd-reservation-cable'>${port['cableid']}</span></td>";
40c9fc94 1387 }
90bf1755
DO
1388 else
1389 echo implode ('', formatPortReservation ($port)) . '<td></td>';
0b09238d 1390 echo "</tr>";
40c9fc94
AA
1391}
1392
d10401bf 1393function renderObject ($object_id)
e673ee24 1394{
8dc2a6c8 1395 global $nextorder, $virtual_obj_types;
fb6e2f57 1396 $info = spotEntity ('object', $object_id);
94fe6370 1397 amplifyCell ($info);
e673ee24
DO
1398 // Main layout starts.
1399 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
1400 echo "<tr><td colspan=2 align=center><h1>${info['dname']}</h1></td></tr>\n";
3a243872 1401 // A mandatory left column with varying number of portlets.
e673ee24 1402 echo "<tr><td class=pcleft>";
646272be
AA
1403
1404 // display summary portlet
1405 $summary = array();
33973968 1406 if ($info['name'] != '')
646272be 1407 $summary['Common name'] = $info['name'];
94fe6370 1408 elseif (considerConfiguredConstraint ($info, 'NAMEWARN_LISTSRC'))
646272be
AA
1409 $summary[] = array ('<tr><td colspan=2 class=msg_error>Common name is missing.</td></tr>');
1410 $summary['Object type'] = '<a href="' . makeHref (array (
341fd895
DO
1411 'page' => 'depot',
1412 'tab' => 'default',
1413 'cfe' => '{$typeid_' . $info['objtype_id'] . '}'
eb11ba46 1414 )) . '">' . decodeObjectType ($info['objtype_id']) . '</a>';
33973968 1415 if ($info['label'] != '')
646272be 1416 $summary['Visible label'] = $info['label'];
33973968 1417 if ($info['asset_no'] != '')
646272be 1418 $summary['Asset tag'] = $info['asset_no'];
d4e8ddeb 1419 elseif (considerConfiguredConstraint ($info, 'ASSETWARN_LISTSRC'))
646272be 1420 $summary[] = array ('<tr><td colspan=2 class=msg_error>Asset tag is missing.</td></tr>');
9563f56f 1421 $parents = getParents ($info, 'object');
af8916e9
AD
1422 // lookup the human-readable object type, sort by it
1423 foreach ($parents as $parent_id => $parent)
1424 $parents[$parent_id]['object_type'] = decodeObjectType ($parent['objtype_id']);
1425 $grouped_parents = groupBy ($parents, 'object_type');
1426 ksort ($grouped_parents);
1427 foreach ($grouped_parents as $parents_group)
0682218d 1428 {
9563f56f 1429 uasort ($parents_group, 'compare_name');
af8916e9 1430 $label = $parents_group[key ($parents_group)]['object_type'] . (count($parents_group) > 1 ? ' containers' : ' container');
9563f56f
AA
1431 $fmt_parents = array();
1432 foreach ($parents_group as $parent)
1433 $fmt_parents[] = mkCellA ($parent);
af8916e9 1434 $summary[$label] = implode ('<br>', $fmt_parents);
0682218d 1435 }
9563f56f
AA
1436 $children = getChildren ($info, 'object');
1437 foreach (groupBy ($children, 'objtype_id') as $objtype_id => $children_group)
42fb3aa2 1438 {
9563f56f
AA
1439 uasort ($children_group, 'compare_name');
1440 $fmt_children = array();
1441 foreach ($children_group as $child)
1442 $fmt_children[] = mkCellA ($child);
c49fddcc 1443 $summary["Contains " . mb_strtolower(decodeObjectType ($objtype_id))] = implode ('<br>', $fmt_children);
42fb3aa2 1444 }
e673ee24 1445 if ($info['has_problems'] == 'yes')
646272be 1446 $summary[] = array ('<tr><td colspan=2 class=msg_error>Has problems</td></tr>');
249c78b2 1447 foreach (getAttrValuesSorted ($object_id) as $record)
4969aa31
DO
1448 if
1449 (
55eefced 1450 $record['value'] != '' &&
4969aa31
DO
1451 permitted (NULL, NULL, NULL, array (array ('tag' => '$attr_' . $record['id'])))
1452 )
5b908204 1453 $summary['{sticker}' . $record['name']] = formatAttributeValue ($record, $info['objtype_id']);
a2c597ea
AA
1454 $summary[] = array (getOutputOf ('printTagTRs',
1455 $info,
1456 makeHref
1457 (
1458 array
1459 (
1460 'page'=>'depot',
1461 'tab'=>'default',
1462 'andor' => 'and',
1463 'cfe' => '{$typeid_' . $info['objtype_id'] . '}',
1464 )
1465 )."&"
1466 ));
0b09238d 1467 renderEntitySummary ($info, 'summary', $summary);
e673ee24 1468
33973968 1469 if ($info['comment'] != '')
e673ee24
DO
1470 {
1471 startPortlet ('Comment');
6cc5d6cd 1472 echo '<div class=commentblock>' . string_insert_hrefs ($info['comment']) . '</div>';
e673ee24
DO
1473 finishPortlet ();
1474 }
1475
b5700329
AA
1476 $logrecords = getLogRecordsForObject ($_REQUEST['object_id']);
1477 if (count ($logrecords))
9a90adc4
DO
1478 {
1479 startPortlet ('log records');
1480 echo "<table cellspacing=0 cellpadding=5 align=center class=widetable width='100%'>";
1481 $order = 'odd';
b5700329 1482 foreach ($logrecords as $row)
9a90adc4
DO
1483 {
1484 echo "<tr class=row_${order} valign=top>";
1485 echo '<td class=tdleft>' . $row['date'] . '<br>' . $row['user'] . '</td>';
0b8d8309 1486 echo '<td class="logentry">' . string_insert_hrefs (htmlspecialchars ($row['content'], ENT_NOQUOTES)) . '</td>';
9a90adc4
DO
1487 echo '</tr>';
1488 $order = $nextorder[$order];
1489 }
1490 echo '</table>';
1491 finishPortlet();
1492 }
1493
65e557dd 1494 switchportInfoJS ($object_id); // load JS code to make portnames interactive
e1ae3fb4
AD
1495 renderFilesPortlet ('object', $object_id);
1496
9f75e165 1497 if (count ($info['ports']))
e673ee24 1498 {
0b09238d 1499 startPortlet ('ports and links');
9f75e165
DO
1500 $hl_port_id = 0;
1501 if (isset ($_REQUEST['hl_port_id']))
e673ee24 1502 {
3db0d8ce 1503 genericAssertion ('hl_port_id', 'natural');
9f75e165 1504 $hl_port_id = $_REQUEST['hl_port_id'];
65e557dd 1505 addAutoScrollScript ("port-$hl_port_id");
9f75e165 1506 }
0b09238d 1507 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>";
21928333 1508 callHook ('renderObjectPortHeaderRow');
9f75e165 1509 foreach ($info['ports'] as $port)
40c9fc94 1510 callHook ('renderObjectPortRow', $port, ($hl_port_id == $port['id']));
fd5edda8 1511 if (permitted (NULL, 'ports', 'set_reserve_comment'))
a8dc2982 1512 addJS ('js/inplace-edit.js');
9f75e165 1513 echo "</table><br>";
e673ee24
DO
1514 finishPortlet();
1515 }
85970da2 1516
21ee3351 1517 if (count ($info['ipv4']) + count ($info['ipv6']))
e673ee24 1518 {
21ee3351 1519 startPortlet ('IP addresses');
e673ee24 1520 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
e9446e3b 1521 if ('yes' == $ext_ipv4_view = getConfigVar ('EXT_IPV4_VIEW'))
9d5f6a80 1522 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 1523 else
9d5f6a80 1524 echo "<tr class=tdleft><th>OS interface</th><th>IP address</th><th>peers</th></tr>\n";
21ee3351
AA
1525
1526 // group IP allocations by interface name instead of address family
1527 $allocs_by_iface = array();
1528 foreach (array ('ipv4', 'ipv6') as $ip_v)
4318ced5
AA
1529 foreach ($info[$ip_v] as $ip_bin => $alloc)
1530 $allocs_by_iface[$alloc['osif']][$ip_bin] = $alloc;
dec748f6 1531
21ee3351
AA
1532 // sort allocs array by portnames
1533 foreach (sortPortList ($allocs_by_iface) as $iface_name => $alloclist)
1534 {
1535 $is_first_row = TRUE;
f3505b35 1536 foreach ($alloclist as $alloc)
b90c3907 1537 {
40c9fc94 1538 $rendered_alloc = callHook ('getRenderedAlloc', $object_id, $alloc);
f3505b35 1539 echo "<tr class='${rendered_alloc['tr_class']}' valign=top>";
21ee3351 1540
f3505b35 1541 // display iface name, same values are grouped into single cell
21ee3351
AA
1542 if ($is_first_row)
1543 {
1544 $rowspan = count ($alloclist) > 1 ? 'rowspan="' . count ($alloclist) . '"' : '';
40c9fc94 1545 echo "<td class=tdleft $rowspan>" . $iface_name . $rendered_alloc['td_name_suffix'] . "</td>";
21ee3351
AA
1546 $is_first_row = FALSE;
1547 }
f3505b35 1548 echo $rendered_alloc['td_ip'];
e9446e3b 1549 if ($ext_ipv4_view == 'yes')
83fdf85f 1550 {
f3505b35
AA
1551 echo $rendered_alloc['td_network'];
1552 echo $rendered_alloc['td_routed_by'];
21ee3351 1553 }
f3505b35
AA
1554 echo $rendered_alloc['td_peers'];
1555
1556 echo "</tr>\n";
b90c3907 1557 }
e673ee24
DO
1558 }
1559 echo "</table><br>\n";
1560 finishPortlet();
1561 }
1562
94fe6370 1563 $forwards = $info['nat4'];
55eefced 1564 if (count ($forwards['in']) || count ($forwards['out']))
e673ee24 1565 {
f28fbe8b 1566 startPortlet('NATv4');
e673ee24
DO
1567
1568 if (count($forwards['out']))
1569 {
1570
f28fbe8b 1571 echo "<h3>locally performed NAT</h3>";
e673ee24 1572
105a3bc8 1573 echo "<table class='widetable' cellpadding=5 cellspacing=0 border=0 align='center'>\n";
f28fbe8b 1574 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
1575
1576 foreach ($forwards['out'] as $pf)
1577 {
2c817354
DO
1578 $class = 'trerror';
1579 $osif = '';
6a3c2dc5
AA
1580 $localip_bin = ip_parse ($pf['localip']);
1581 if (array_key_exists ($localip_bin, $info['ipv4']))
2c817354 1582 {
6a3c2dc5
AA
1583 $class = $info['ipv4'][$localip_bin]['addrinfo']['class'];
1584 $osif = $info['ipv4'][$localip_bin]['osif'] . ': ';
2c817354 1585 }
e673ee24 1586 echo "<tr class='$class'>";
7419401e
AA
1587 echo "<td>${pf['proto']}</td><td class=tdleft>${osif}" . getRenderedIPPortPair ($pf['localip'], $pf['localport']) . "</td>";
1588 echo "<td class=tdleft>" . getRenderedIPPortPair ($pf['remoteip'], $pf['remoteport']) . "</td>";
4318ced5 1589 $address = getIPAddress (ip4_parse ($pf['remoteip']));
e673ee24 1590 echo "<td class='description'>";
53ef3908
DO
1591 if (count ($address['allocs']))
1592 foreach($address['allocs'] as $bond)
ad3e5e7e 1593 echo mkA ("${bond['object_name']}(${bond['name']})", 'object', $bond['object_id']) . ' ';
33973968 1594 elseif ($pf['remote_addr_name'] != '')
f28fbe8b 1595 echo '(' . $pf['remote_addr_name'] . ')';
53ef3908 1596 echo "</td><td class='description'>${pf['description']}</td></tr>";
e673ee24
DO
1597 }
1598 echo "</table><br><br>";
1599 }
1600 if (count($forwards['in']))
1601 {
f28fbe8b 1602 echo "<h3>arriving NAT connections</h3>";
105a3bc8 1603 echo "<table class='widetable' cellpadding=5 cellspacing=0 border=0 align='center'>\n";
f28fbe8b 1604 echo "<tr><th>Matched endpoint</th><th>Source object</th><th>Translated to</th><th>Rule comment</th></tr>\n";
e673ee24
DO
1605 foreach ($forwards['in'] as $pf)
1606 {
1607 echo "<tr>";
7419401e 1608 echo "<td>${pf['proto']}/" . getRenderedIPPortPair ($pf['localip'], $pf['localport']) . "</td>";
ad3e5e7e 1609 echo '<td class="description">' . mkA ($pf['object_name'], 'object', $pf['object_id']);
7419401e 1610 echo "</td><td>" . getRenderedIPPortPair ($pf['remoteip'], $pf['remoteport']) . "</td>";
e673ee24
DO
1611 echo "<td class='description'>${pf['description']}</td></tr>";
1612 }
e673ee24
DO
1613 echo "</table><br><br>";
1614 }
1615 finishPortlet();
1616 }
1617
eacc0983 1618 renderSLBTriplets2 ($info);
defd92d8 1619 renderSLBTriplets ($info);
e673ee24
DO
1620 echo "</td>\n";
1621
3a243872
DO
1622 // A conditional right column with the rackspace portlet only.
1623 if
1624 (
1625 ! in_array ($info['objtype_id'], $virtual_obj_types) &&
950d4117 1626 count ($rack_ids = getResidentRackIDs ($object_id))
3a243872 1627 )
0682218d 1628 {
3a243872 1629 echo '<td class=pcright>';
0682218d 1630 startPortlet ('rackspace allocation');
3a243872 1631 foreach ($rack_ids as $rack_id)
0682218d
AD
1632 renderRack ($rack_id, $object_id);
1633 echo '<br>';
1634 finishPortlet();
3a243872 1635 echo '</td>';
0682218d 1636 }
3a243872 1637 echo "<tr></table>\n";
e673ee24
DO
1638}
1639
1640function renderRackMultiSelect ($sname, $racks, $selected)
1641{
04571888
DO
1642 // Transform the given flat list into a list of groups, each representing a rack row.
1643 $rdata = array();
e673ee24 1644 foreach ($racks as $rack)
d016010a 1645 {
b956dd24 1646 if ('' != $trail = getLocationTrail ($rack['location_id'], FALSE))
7b6ae79a
AD
1647 $row_name = $trail . ' : ' . $rack['row_name'];
1648 else
1649 $row_name = $rack['row_name'];
d016010a
AD
1650 $rdata[$row_name][$rack['id']] = $rack['name'];
1651 }
4fbb5a00 1652 echo "<select name=${sname} multiple size=" . getConfigVar ('MAXSELSIZE') . " onchange='getElementsByName(\"updateObjectAllocation\")[0].submit()'>\n";
acfc0d89
AA
1653 $row_names = array_keys ($rdata);
1654 natsort ($row_names);
1655 foreach ($row_names as $optgroup)
e673ee24 1656 {
04571888 1657 echo "<optgroup label='${optgroup}'>";
acfc0d89 1658 foreach ($rdata[$optgroup] as $rack_id => $rack_name)
04571888
DO
1659 {
1660 echo "<option value=${rack_id}";
1661 if (!(array_search ($rack_id, $selected) === FALSE))
1662 echo ' selected';
1663 echo">${rack_name}</option>\n";
1664 }
e673ee24
DO
1665 }
1666 echo "</select>\n";
1667}
1668
e673ee24 1669// This function renders a form for port edition.
d10401bf 1670function renderPortsForObject ($object_id)
e673ee24 1671{
3153a326
DO
1672 $prefs = getPortListPrefs();
1673 function printNewItemTR ($prefs)
39b4abd0
DO
1674 {
1675 printOpFormIntro ('addPort');
1676 echo "<tr><td>";
f5ff50f5 1677 printImageHREF ('add', 'add a port', TRUE);
7b30e187 1678 echo "</td><td class='tdleft'><input type=text size=16 name=port_name></td>\n";
04c61e7e 1679 echo "<td><input type=text name=port_label></td><td>";
759b21d2 1680 printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type_id'), $prefs['selected']);
97792a8a 1681 echo "<td><input type=text name=port_l2address size=17 maxlength=59></td>\n";
318c48a8 1682 echo "<td colspan=4>&nbsp;</td><td>";
04c61e7e 1683 printImageHREF ('add', 'add a port', TRUE);
9318d2ef 1684 echo "</td></tr></form>";
39b4abd0 1685 }
70c0cb08
DO
1686
1687 function printBulkForm ($prefs)
1688 {
a1fc539a
RF
1689 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
1690 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>";
1691 echo "<th class=tdleft>Count</th><th>&nbsp;</th></tr>\n";
1692 printOpFormIntro ('addBulkPorts');
1693 echo "<tr><td>";
1694 printImageHREF ('add', 'add ports', TRUE);
04c61e7e
AB
1695 echo "</td><td><input type=text size=8 name=port_name></td>\n";
1696 echo "<td><input type=text name=port_label></td><td>";
759b21d2 1697 printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type_id'), $prefs['selected']);
04c61e7e
AB
1698 echo "<td><input type=text name=port_numbering_start size=3 maxlength=3></td>\n";
1699 echo "<td><input type=text name=port_numbering_count size=3 maxlength=3></td>\n";
0b09238d 1700 echo "<td>&nbsp;</td><td>";
04c61e7e 1701 printImageHREF ('add', 'add ports', TRUE);
a1fc539a
RF
1702 echo "</td></tr></form>";
1703 echo "</table><br>\n";
1704 }
dec748f6 1705
70c0cb08
DO
1706 startPortlet ('Ports and interfaces');
1707 $object = spotEntity ('object', $object_id);
1708 amplifyCell ($object);
1709 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes' && getConfigVar ('ENABLE_BULKPORT_FORM') == 'yes')
1710 printBulkForm ($prefs);
1711
0b09238d 1712 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
0d59a51c 1713 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 1714 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 1715 if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
3153a326 1716 printNewItemTR ($prefs);
45ff9012 1717
0b09238d
DO
1718 // clear ports link
1719 echo getOpLink (array ('op'=>'deleteAll'), 'Clear port list', 'clear', '', 'need-confirmation');
1720
8579b51b
AA
1721 // rename ports link
1722 $n_ports_to_rename = 0;
1723 foreach ($object['ports'] as $port)
1724 if ($port['name'] != shortenPortName ($port['name'], $object['id']))
1725 $n_ports_to_rename++;
1726 if ($n_ports_to_rename)
bd2dcd32 1727 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 1728
00276508
AA
1729 if (isset ($_REQUEST['hl_port_id']))
1730 {
3db0d8ce 1731 genericAssertion ('hl_port_id', 'natural');
00276508 1732 $hl_port_id = intval ($_REQUEST['hl_port_id']);
65e557dd 1733 addAutoScrollScript ("port-$hl_port_id");
00276508 1734 }
d10401bf 1735 switchportInfoJS ($object_id); // load JS code to make portnames interactive
9f75e165 1736 foreach ($object['ports'] as $port)
e673ee24 1737 {
acb01e71 1738 // highlight port name with yellow if its name is not canonical
8579b51b
AA
1739 $canon_pn = shortenPortName ($port['name'], $port['object_id']);
1740 $name_class = $canon_pn == $port['name'] ? '' : 'trwarning';
1741
5c717ce5 1742 $tr_class = isset ($hl_port_id) && $hl_port_id == $port['id'] ? 'class="highlight"' : '';
7056988c 1743 printOpFormIntro ('editPort', array ('port_id' => $port['id']));
d0d40671 1744 echo "<tr $tr_class><td><a name='port-${port['id']}' href='".makeHrefProcess(array('op'=>'delPort', 'port_id'=>$port['id']))."'>";
e673ee24
DO
1745 printImageHREF ('delete', 'Unlink and Delete this port');
1746 echo "</a></td>\n";
65e557dd 1747 $a_class = isEthernetPort ($port) ? 'port-menu' : '';
7b30e187 1748 echo "<td class='tdleft $name_class' NOWRAP><input type=text name=name class='interactive-portname $a_class' value='${port['name']}' size=16></td>";
cd3775e9 1749 echo "<td><input type=text name=label value='${port['label']}'></td>";
70c0cb08 1750 echo '<td class=tdleft>';
8e021b86
AA
1751 if ($port['iif_id'] != 1)
1752 echo '<label>' . $port['iif_name'] . ' ';
4f968fa8 1753 $port_type_opts = array();
78b59ab3 1754 if (! $port['linked'])
759b21d2 1755 $port_type_opts = getUnlinkedPortTypeOptions ($port['iif_id']);
78b59ab3 1756 else
4f968fa8
AA
1757 foreach (getExistingPortTypeOptions ($port) as $oif_id => $opt_str)
1758 $port_type_opts[$port['iif_name']][$port['iif_id'] . '-' . $oif_id] = $opt_str;
4f968fa8 1759 printNiftySelect ($port_type_opts, array ('name' => 'port_type_id'), $port['iif_id'] . '-' . $port['oif_id']);
8e021b86
AA
1760 if ($port['iif_id'] != 1)
1761 echo '</label>';
1762 echo '</td>';
1763
97792a8a
DO
1764 // 17 is the full notation length of a MAC address, 23 -- of a WWN address and 59 -- of an IPoIB address.
1765 echo "<td><input type=text name=l2address value='${port['l2address']}' size=17 maxlength=59></td>\n";
0b09238d 1766 if ($port['remote_object_id'])
90bf1755 1767 {
f4d00faf
DO
1768 $dname = formatObjectDisplayedName ($port['remote_object_name'], $port['remote_object_tid']);
1769 echo "<td class=tdleft>" .
1770 formatLoggedSpan ($port['last_log'], formatPortLink ($port['remote_object_id'], $dname, $port['remote_id'], NULL)) .
0b09238d 1771 "</td>";
f4d00faf 1772 echo "<td class=tdleft> " . formatLoggedSpan ($port['last_log'], $port['remote_name'], 'underline') .
0b09238d
DO
1773 "<input type=hidden name=reservation_comment value=''></td>";
1774 echo "<td><input type=text name=cable value='${port['cableid']}'></td>";
1775 echo "<td class=tdcenter>";
1776 echo getOpLink (array('op'=>'unlinkPort', 'port_id'=>$port['id'], ), '', 'cut', 'Unlink this port');
1777 echo "</td>";
e673ee24 1778 }
33973968 1779 elseif ($port['reservation_comment'] != '')
e673ee24 1780 {
0b09238d 1781 echo "<td>" . formatLoggedSpan ($port['last_log'], 'Reserved:', 'strong underline') . "</td>";
e673ee24 1782 echo "<td><input type=text name=reservation_comment value='${port['reservation_comment']}'></td>";
318c48a8 1783 echo "<td></td>";
10f5133a
AA
1784 echo "<td class=tdcenter>";
1785 echo getOpLink (array('op'=>'useup', 'port_id'=>$port['id']), '', 'clear', 'Use up this port');
1786 echo "</td>";
e673ee24
DO
1787 }
1788 else
1789 {
6b1ca530 1790 $in_rack = getConfigVar ('NEAREST_RACKS_CHECKBOX');
de55c069
AA
1791 echo "<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td class=tdcenter>";
1792 echo getPopupLink ('portlist', array ('port' => $port['id'], 'in_rack' => ($in_rack == "yes" ? "on" : "")), 'findlink', 'plug', '', 'Link this port');
0b09238d 1793 echo " <input type=text name=reservation_comment></td>\n";
90bf1755 1794 }
0b09238d
DO
1795 echo "<td>";
1796 printImageHREF ('save', 'Save changes', TRUE);
1797 echo "</td></form></tr>\n";
e673ee24 1798 }
39b4abd0 1799 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
3153a326 1800 printNewItemTR ($prefs);
e673ee24 1801 echo "</table><br>\n";
70c0cb08
DO
1802 if (getConfigVar ('ADDNEW_AT_TOP') != 'yes' && getConfigVar ('ENABLE_BULKPORT_FORM') == 'yes')
1803 printBulkForm ($prefs);
e673ee24 1804 finishPortlet();
70c0cb08
DO
1805
1806 if (getConfigVar ('ENABLE_MULTIPORT_FORM') == 'yes' && permitted (NULL, NULL, 'addMultiPorts'))
1807 {
1808 startPortlet ('Add/update multiple ports');
1809 printOpFormIntro ('addMultiPorts');
1810 $formats = array
1811 (
1812 'ssv1' => 'SSV:<interface name> [<MAC address>]',
1813 );
1814 echo 'Format: ' . getSelect ($formats, array ('name' => 'format'), 'ssv1') . ' ';
1815 echo 'Default port type: ';
1816 printNiftySelect (getNewPortTypeOptions(), array ('name' => 'port_type'), $prefs['selected']);
1817 echo "<input type=submit value='Parse output'><br>\n";
1818 echo "<textarea name=input cols=100 rows=50></textarea><br>\n";
1819 echo '</form>';
1820 finishPortlet();
1821 }
e673ee24
DO
1822}
1823
d10401bf 1824function renderIPForObject ($object_id)
e673ee24 1825{
15e27821 1826 function printNewItemTR ($default_type, $object_id)
21ee3351
AA
1827 {
1828 global $aat;
15e27821 1829
1830 includeJQueryUI (TRUE);
1831
a6e090f0
DO
1832 // Heredoc, not nowdoc!
1833 addJS (<<<"JSEND"
15e27821 1834 $(document).ready( function() {
1835 $('[name="bond_name"]').autocomplete({
1836 source: "?module=ajax&ac=autocomplete&realm=bond_name&object_id=$object_id",
1837 //minLength: 3,
1838 focus: function(event, ui) {
1839 if( ui.item.value == '' )
1840 event.preventDefault();
1841 },
1842 select: function(event, ui) {
1843 if( ui.item.value == '' )
1844 event.preventDefault();
1845 }
1846 });
1847 });
1848JSEND
1849 , TRUE);
4318ced5
AA
1850 printOpFormIntro ('add');
1851 echo "<tr><td>"; // left btn
dec748f6 1852 printImageHREF ('add', 'allocate', TRUE);
21ee3351 1853 echo "</td>";
04c61e7e
AB
1854 echo "<td class=tdleft><input type='text' size='10' name='bond_name'></td>\n"; // if-name
1855 echo "<td class=tdleft><input type=text name='ip'></td>\n"; // IP
4318ced5
AA
1856 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
1857 echo "<td colspan=2>&nbsp;</td>"; // network, routed by
1858 echo '<td>';
04c61e7e 1859 printSelect ($aat, array ('name' => 'bond_type'), $default_type); // type
4318ced5 1860 echo "</td><td>&nbsp;</td><td>"; // misc
04c61e7e 1861 printImageHREF ('add', 'allocate', TRUE); // right btn
21ee3351
AA
1862 echo "</td></tr></form>";
1863 }
21ee3351 1864 global $aat;
0d5ef6f0 1865
21ee3351 1866 startPortlet ('Allocations');
4318ced5
AA
1867 echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'><tr>\n";
1868 echo '<th>&nbsp;</th>';
1869 echo '<th>OS interface</th>';
1870 echo '<th>IP address</th>';
21ee3351 1871 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
4318ced5
AA
1872 {
1873 echo '<th>network</th>';
1874 echo '<th>routed by</th>';
1875 }
1876 echo '<th>type</th>';
1877 echo '<th>misc</th>';
b1b07891 1878 echo '<th>&nbsp;</th>';
4318ced5 1879 echo '</tr>';
21ee3351 1880
710ead7e
AA
1881 $alloc_list = ''; // most of the output is stored here
1882 $used_alloc_types = array();
e9446e3b 1883 $ext_ipv4_view = getConfigVar ('EXT_IPV4_VIEW');
4318ced5 1884 foreach (getObjectIPAllocations ($object_id) as $alloc)
21ee3351 1885 {
710ead7e
AA
1886 if (! isset ($used_alloc_types[$alloc['type']]))
1887 $used_alloc_types[$alloc['type']] = 0;
1888 $used_alloc_types[$alloc['type']]++;
1889
40c9fc94 1890 $rendered_alloc = callHook ('getRenderedAlloc', $object_id, $alloc);
4318ced5 1891 $alloc_list .= getOutputOf ('printOpFormIntro', 'upd', array ('ip' => $alloc['addrinfo']['ip']));
710ead7e 1892 $alloc_list .= "<tr class='${rendered_alloc['tr_class']}' valign=top>";
f3505b35 1893
10f5133a 1894 $alloc_list .= "<td>" . getOpLink (array ('op' => 'del', 'ip' => $alloc['addrinfo']['ip']), '', 'delete', 'Delete this IP address') . "</td>";
40c9fc94 1895 $alloc_list .= "<td class=tdleft><input type='text' name='bond_name' value='${alloc['osif']}' size=10>" . $rendered_alloc['td_name_suffix'] . "</td>";
710ead7e 1896 $alloc_list .= $rendered_alloc['td_ip'];
e9446e3b 1897 if ($ext_ipv4_view == 'yes')
21ee3351 1898 {
710ead7e
AA
1899 $alloc_list .= $rendered_alloc['td_network'];
1900 $alloc_list .= $rendered_alloc['td_routed_by'];
21ee3351 1901 }
710ead7e
AA
1902 $alloc_list .= '<td>' . getSelect ($aat, array ('name' => 'bond_type'), $alloc['type']) . "</td>";
1903 $alloc_list .= $rendered_alloc['td_peers'];
1904 $alloc_list .= "<td>" .getImageHREF ('save', 'Save changes', TRUE) . "</td>";
f3505b35 1905
710ead7e 1906 $alloc_list .= "</form></tr>\n";
21ee3351 1907 }
710ead7e 1908 asort ($used_alloc_types, SORT_NUMERIC);
589c027b 1909 $most_popular_type = ! count ($used_alloc_types) ? 'regular' : array_last (array_keys ($used_alloc_types));
710ead7e
AA
1910
1911 if ($list_on_top = (getConfigVar ('ADDNEW_AT_TOP') != 'yes'))
1912 echo $alloc_list;
15e27821 1913 printNewItemTR ($most_popular_type, $object_id);
710ead7e
AA
1914 if (! $list_on_top)
1915 echo $alloc_list;
21ee3351
AA
1916
1917 echo "</table><br>\n";
1918 finishPortlet();
f3505b35
AA
1919}
1920
08408472
AA
1921// This function is deprecated. Do not rely on its internals,
1922// it will probably be removed in the next major relese.
1923// Use new showError, showWarning, showSuccess functions.
dec748f6 1924// Log array is stored in global $log_messages. Its format is simple: plain ordered array
8ab645cf 1925// with values having keys 'c' (both message code and severity) and 'a' (sprintf arguments array)
1cb6adf5 1926function showMessageOrError ()
e673ee24 1927{
a2b16202
AA
1928 global $log_messages;
1929
0a00b344 1930 startSession();
a2b16202
AA
1931 if (isset ($_SESSION['log']))
1932 {
1933 $log_messages = array_merge ($_SESSION['log'], $log_messages);
1934 unset ($_SESSION['log']);
1935 }
1936 session_commit();
1937
1938 if (empty ($log_messages))
1cb6adf5 1939 return;
8ab645cf
AA
1940 $msginfo = array
1941 (
dec748f6 1942// records 0~99 with success messages
8ab645cf
AA
1943 0 => array ('code' => 'success', 'format' => '%s'),
1944 5 => array ('code' => 'success', 'format' => 'added record "%s" successfully'),
1945 6 => array ('code' => 'success', 'format' => 'updated record "%s" successfully'),
1946 7 => array ('code' => 'success', 'format' => 'deleted record "%s" successfully'),
1947 8 => array ('code' => 'success', 'format' => 'Port %s successfully linked with %s'),
493e13dd 1948 10 => array ('code' => 'success', 'format' => 'Added %u port(s), updated %u port(s), encountered %u error(s).'),
8ab645cf 1949 21 => array ('code' => 'success', 'format' => 'Generation complete'),
493e13dd
DO
1950 26 => array ('code' => 'success', 'format' => 'updated %u record(s) successfully'),
1951 37 => array ('code' => 'success', 'format' => 'added %u record(s) successfully'),
1952 38 => array ('code' => 'success', 'format' => 'removed %u record(s) successfully'),
8ab645cf 1953 43 => array ('code' => 'success', 'format' => 'Saved successfully.'),
493e13dd 1954 44 => array ('code' => 'success', 'format' => '%s failure(s) and %s successfull change(s).'),
8ab645cf
AA
1955 48 => array ('code' => 'success', 'format' => 'added a record successfully'),
1956 49 => array ('code' => 'success', 'format' => 'deleted a record successfully'),
1957 51 => array ('code' => 'success', 'format' => 'updated a record successfully'),
1958 57 => array ('code' => 'success', 'format' => 'Reset complete'),
04eee8d2 1959 58 => array ('code' => 'success', 'format' => '%u device(s) unmounted successfully'),
8ab645cf 1960 63 => array ('code' => 'success', 'format' => '%u change request(s) have been processed'),
493e13dd 1961 67 => array ('code' => 'success', 'format' => "Tag rolling done, %u object(s) involved"),
8ab645cf
AA
1962 71 => array ('code' => 'success', 'format' => 'File "%s" was linked successfully'),
1963 72 => array ('code' => 'success', 'format' => 'File was unlinked successfully'),
493e13dd
DO
1964 82 => array ('code' => 'success', 'format' => "Bulk port creation was successful. %u port(s) created, %u failed"),
1965 87 => array ('code' => 'success', 'format' => '802.1Q recalculate: %d port(s) changed on %d switch(es)'),
8628ae44 1966// records 100~199 with fatal error messages
8ab645cf
AA
1967 100 => array ('code' => 'error', 'format' => '%s'),
1968 109 => array ('code' => 'error', 'format' => 'failed updating a record'),
8ab645cf 1969 131 => array ('code' => 'error', 'format' => 'invalid format requested'),
493e13dd 1970 141 => array ('code' => 'error', 'format' => 'Encountered %u error(s), updated %u record(s)'),
8ab645cf
AA
1971 149 => array ('code' => 'error', 'format' => 'Turing test failed'),
1972 150 => array ('code' => 'error', 'format' => 'Can only change password under DB authentication.'),
1973 151 => array ('code' => 'error', 'format' => 'Old password doesn\'t match.'),
1974 152 => array ('code' => 'error', 'format' => 'New passwords don\'t match.'),
1975 154 => array ('code' => 'error', 'format' => "Verification error: %s"),
1976 155 => array ('code' => 'error', 'format' => 'Save failed.'),
1977 159 => array ('code' => 'error', 'format' => 'Permission denied moving port %s from VLAN%u to VLAN%u'),
8ab645cf
AA
1978 170 => array ('code' => 'error', 'format' => 'There is no network for IP address "%s"'),
1979 172 => array ('code' => 'error', 'format' => 'Malformed request'),
1980 179 => array ('code' => 'error', 'format' => 'Expired form has been declined.'),
8ab645cf 1981 191 => array ('code' => 'error', 'format' => "deploy was blocked due to conflicting configuration versions"),
f0ff4930 1982
8628ae44 1983// records 200~299 with warnings
8ab645cf
AA
1984 200 => array ('code' => 'warning', 'format' => '%s'),
1985 201 => array ('code' => 'warning', 'format' => 'nothing happened...'),
e1add254 1986 206 => array ('code' => 'warning', 'format' => '%s is not empty'),
7587edca 1987 207 => array ('code' => 'warning', 'format' => 'File upload failed, error: %s'),
57777e4a 1988
24d3182a 1989// records 300~399 with notices
8ab645cf 1990 300 => array ('code' => 'neutral', 'format' => '%s'),
24d3182a 1991
8ab645cf
AA
1992 );
1993 // Handle the arguments. Is there any better way to do it?
a2b16202 1994 foreach ($log_messages as $record)
8ab645cf 1995 {
55eefced 1996 if (! isset ($record['c']) || ! isset ($msginfo[$record['c']]))
8ab645cf 1997 {
41152018
AA
1998 $prefix = isset ($record['c']) ? $record['c'] . ': ' : '';
1999 echo "<div class=msg_neutral>(${prefix}this message was lost)</div>";
8ab645cf
AA
2000 continue;
2001 }
2002 if (isset ($record['a']))
2003 switch (count ($record['a']))
baf37d01 2004 {
8ab645cf
AA
2005 case 1:
2006 $msgtext = sprintf
2007 (
2008 $msginfo[$record['c']]['format'],
2009 $record['a'][0]
2010 );
2011 break;
2012 case 2:
2013 $msgtext = sprintf
2014 (
2015 $msginfo[$record['c']]['format'],
2016 $record['a'][0],
2017 $record['a'][1]
2018 );
2019 break;
2020 case 3:
2021 $msgtext = sprintf
2022 (
2023 $msginfo[$record['c']]['format'],
2024 $record['a'][0],
2025 $record['a'][1],
2026 $record['a'][2]
2027 );
2028 break;
2029 case 4:
2030 default:
2031 $msgtext = sprintf
2032 (
2033 $msginfo[$record['c']]['format'],
2034 $record['a'][0],
2035 $record['a'][1],
2036 $record['a'][2],
2037 $record['a'][3]
2038 );
2039 break;
baf37d01 2040 }
8ab645cf
AA
2041 else
2042 $msgtext = $msginfo[$record['c']]['format'];
2043 echo '<div class=msg_' . $msginfo[$record['c']]['code'] . ">${msgtext}</div>";
baf37d01 2044 }
a2b16202 2045 $log_messages = array();
e673ee24
DO
2046}
2047
65e557dd 2048// renders two tables: port link status and learned MAC list
d10401bf 2049function renderPortsInfo($object_id)
65e557dd 2050{
2d33a804 2051 $breed = detectDeviceBreed ($object_id);
03522bd8
AA
2052 try
2053 {
2054 if (permitted (NULL, NULL, 'get_link_status'))
2d33a804 2055 {
2056 if (validBreedFunction ($breed, 'getportstatus'))
2057 $linkStatus = queryDevice ($object_id, 'getportstatus');
2058 }
03522bd8
AA
2059 else
2060 showWarning ("You don't have permission to view ports link status");
2061
2062 if (permitted (NULL, NULL, 'get_mac_list'))
2d33a804 2063 {
2064 if (validBreedFunction ($breed, 'getmaclist'))
2065 $macList = sortPortList (queryDevice ($object_id, 'getmaclist'));
2066 }
03522bd8
AA
2067 else
2068 showWarning ("You don't have permission to view learned MAC list");
2069 }
2070 catch (RTGatewayError $e)
2071 {
2072 showError ($e->getMessage());
2073 return;
2074 }
2075
65e557dd
AA
2076 global $nextorder;
2077 echo "<table width='100%'><tr>";
03522bd8 2078 if (! empty ($linkStatus))
65e557dd 2079 {
03522bd8
AA
2080 echo "<td valign='top' width='50%'>";
2081 startPortlet('Link status');
c51492f5
DO
2082 echo "<table width='80%' class='widetable' cellspacing=0 cellpadding='5px' align='center'>";
2083 echo '<tr><th>Port</th><th>&nbsp;</th><th>Link status</th><th>Link info</th></tr>';
72c3bebc
DO
2084 $statusmap = array
2085 (
2086 'up' => 'link up',
2087 'down' => 'link down',
2088 'disabled' => 'link disabled',
2089 );
03522bd8
AA
2090 $order = 'even';
2091 foreach ($linkStatus as $pn => $link)
65e557dd 2092 {
03522bd8
AA
2093 echo "<tr class='row_$order'>";
2094 $order = $nextorder[$order];
c51492f5
DO
2095 echo '<td>' . $pn . '</td>';
2096 echo '<td>' . getImageHREF (array_fetch ($statusmap, $link['status'], '16x16t')) . '</td>';
2097 echo '<td>' . $link['status'] . '</td>';
03522bd8
AA
2098 $info = '';
2099 if (isset ($link['speed']))
2100 $info .= $link['speed'];
2101 if (isset ($link['duplex']))
2102 {
cfb42424 2103 if ($info != '')
03522bd8
AA
2104 $info .= ', ';
2105 $info .= $link['duplex'];
65e557dd 2106 }
c51492f5 2107 echo '<td>' . $info . '</td>';
03522bd8 2108 echo '</tr>';
65e557dd 2109 }
03522bd8
AA
2110 echo "</table></td>";
2111 finishPortlet();
65e557dd
AA
2112 }
2113
65e557dd
AA
2114 if (! empty ($macList))
2115 {
2116 echo "<td valign='top' width='50%'>";
2117 $rendered_macs = '';
2118 $mac_count = 0;
c51492f5
DO
2119 $rendered_macs .= "<table width='80%' class='widetable' cellspacing=0 cellpadding='5px' align='center'>";
2120 $rendered_macs .= '<tr><th>MAC address</th><th>VLAN</th><th>Port</th></tr>';
65e557dd
AA
2121 $order = 'even';
2122 foreach ($macList as $pn => $list)
2123 {
2124 $order = $nextorder[$order];
2125 foreach ($list as $item)
2126 {
2127 ++$mac_count;
2128 $rendered_macs .= "<tr class='row_$order'>";
c51492f5
DO
2129 $rendered_macs .= '<td style="font-family: monospace">' . $item['mac'] . '</td>';
2130 $rendered_macs .= '<td>' . $item['vid'] . '</td>';
2131 $rendered_macs .= '<td>' . $pn . '</td>';
65e557dd
AA
2132 $rendered_macs .= '</tr>';
2133 }
2134 }
2135 $rendered_macs .= "</table></td>";
dec748f6 2136
65e557dd
AA
2137 startPortlet("Learned MACs ($mac_count)");
2138 echo $rendered_macs;
2139 finishPortlet();
2140 }
2141
2142 echo "</td></tr></table>";
2143}
2144
d18eac58
DO
2145// An object can be mounted onto free atoms only, that is, if any record for an atom
2146// already exists in RackSpace, it cannot be used for mounting.
d10401bf 2147function renderRackSpaceForObject ($object_id)
e673ee24 2148{
e673ee24
DO
2149 // Always process occupied racks plus racks chosen by user. First get racks with
2150 // already allocated rackspace...
950d4117
DO
2151 $workingRacksData = array();
2152 foreach (getResidentRackIDs ($object_id) as $rack_id)
2153 {
2154 $rackData = spotEntity ('rack', $rack_id);
2155 amplifyCell ($rackData);
2156 $workingRacksData[$rack_id] = $rackData;
2157 }
e673ee24 2158 // ...and then add those chosen by user (if any).
2135fd83 2159 if (isset($_REQUEST['rackmulti']))
e673ee24 2160 foreach ($_REQUEST['rackmulti'] as $cand_id)
e673ee24
DO
2161 if (!isset ($workingRacksData[$cand_id]))
2162 {
0cc24e9a 2163 $rackData = spotEntity ('rack', $cand_id);
61a1d996 2164 amplifyCell ($rackData);
e673ee24
DO
2165 $workingRacksData[$cand_id] = $rackData;
2166 }
51690ad4 2167
c17a36f0
AA
2168 // Get a list of rack ids that are parents of this object.
2169 $object = spotEntity ('object', $object_id);
2170 $parentRacks = reduceSubarraysToColumn (getParents ($object, 'rack'), 'id');
0a7feebb 2171
e673ee24
DO
2172 // Main layout starts.
2173 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0><tr>";
2174
2175 // Left portlet with rack list.
2176 echo "<td class=pcleft height='1%'>";
2177 startPortlet ('Racks');
a8efc03e 2178 $allRacksData = listCells ('rack');
6d42599c
AA
2179
2180 // filter rack list to match only racks having common tags with the object (reducing $allRacksData)
55eefced 2181 if (! isset ($_REQUEST['show_all_racks']) && getConfigVar ('FILTER_RACKLIST_BY_TAGS') == 'yes')
6d42599c
AA
2182 {
2183 $matching_racks = array();
6d42599c
AA
2184 $matched_tags = array();
2185 foreach ($allRacksData as $rack)
01205bb6 2186 {
b51c1007 2187 $tag_chain = array_merge ($rack['etags'], $rack['itags']);
6d42599c 2188 foreach ($object['etags'] as $tag)
01205bb6 2189 if (tagOnChain ($tag, $tag_chain))
6d42599c
AA
2190 {
2191 $matching_racks[$rack['id']] = $rack;
2192 $matched_tags[$tag['id']] = $tag;
2193 break;
2194 }
01205bb6 2195 }
6d42599c
AA
2196 // add current object's racks even if they dont match filter
2197 foreach ($workingRacksData as $rack_id => $rack)
2198 if (! isset ($matching_racks[$rack_id]))
2199 $matching_racks[$rack_id] = $rack;
2200 // if matching racks found, and rack list is reduced, show 'show all' link
55eefced 2201 if (count ($matching_racks) && count ($matching_racks) != count ($allRacksData))
6d42599c 2202 {
1e9266ad 2203 $tmp = array();
6d42599c 2204 foreach ($matched_tags as $tag)
1e9266ad
DO
2205 $tmp[] = '{' . $tag['tag'] . '}';
2206 $filter_text = implode (' or ', $tmp);
6d42599c
AA
2207 $href_show_all = trim($_SERVER['REQUEST_URI'], '&');
2208 $href_show_all .= htmlspecialchars('&show_all_racks=1');
2209 echo "(filtered by <span class='filter-text'>$filter_text</span>, <a href='$href_show_all'>show all</a>)<p>";
2210 $allRacksData = $matching_racks;
2211 }
2212 }
2213
4b8d413e 2214 if (count ($allRacksData) <= getConfigVar ('RACK_PRESELECT_THRESHOLD'))
61a1d996 2215 foreach ($allRacksData as $rack)
51c18782
DO
2216 if (!array_key_exists ($rack['id'], $workingRacksData))
2217 {
2218 amplifyCell ($rack);
2219 $workingRacksData[$rack['id']] = $rack;
2220 }
2221 foreach (array_keys ($workingRacksData) as $rackId)
2222 applyObjectMountMask ($workingRacksData[$rackId], $object_id);
2223 printOpFormIntro ('updateObjectAllocation');
4b8d413e 2224 renderRackMultiSelect ('rackmulti[]', $allRacksData, array_keys ($workingRacksData));
51c18782 2225 echo "<br><br>";
e673ee24
DO
2226 finishPortlet();
2227 echo "</td>";
2228
2229 // Middle portlet with comment and submit.
2230 echo "<td class=pcleft>";
5c8bf2ba 2231 startPortlet ('Comment (for Rackspace History)');
e673ee24
DO
2232 echo "<textarea name=comment rows=10 cols=40></textarea><br>\n";
2233 echo "<input type=submit value='Save' name=got_atoms>\n";
51c18782 2234 echo "<br><br>";
e673ee24
DO
2235 finishPortlet();
2236 echo "</td>";
2237
d18eac58
DO
2238 // Right portlet with rendered racks. If this form submit is not final,
2239 // the former state of the grid needs to make it to the current form.
e673ee24
DO
2240 echo "<td class=pcright rowspan=2 height='1%'>";
2241 startPortlet ('Working copy');
e8b94513 2242 includeJQueryUI (false);
65e557dd 2243 addJS ('js/racktables.js');
c82023ad 2244 addJS ('js/bulkselector.js');
e673ee24
DO
2245 echo '<table border=0 cellspacing=10 align=center><tr>';
2246 foreach ($workingRacksData as $rack_id => $rackData)
2247 {
c26d97b6 2248 $is_ro = !rackModificationPermitted ($rackData, 'updateObjectAllocation');
e673ee24
DO
2249 // Order is important here: only original allocation is highlighted.
2250 highlightObject ($rackData, $object_id);
2251 markupAtomGrid ($rackData, 'T');
d18eac58 2252 // If an HTTP form has been processed, discard user input and show new database
e673ee24 2253 // contents.
c26d97b6 2254 if (!$is_ro && isset ($_REQUEST['rackmulti'][0])) // is an update
e673ee24
DO
2255 mergeGridFormToRack ($rackData);
2256 echo "<td valign=top>";
c26d97b6
VS
2257 echo "<center>\n<h2 style='margin:0px'>${rackData['name']}</h2>\n";
2258 $read_only_text = $is_ro ? '(read-only)' : '&nbsp;';
2259 echo "<p style='color: red; margin-top:0px'>${read_only_text}</p>\n";
e8b94513 2260 echo "<table class=rack id=selectableRack border=0 cellspacing=0 cellpadding=1>\n";
57c69e9c 2261 echo "<tr><th width='10%'>&nbsp;</th>";
bd0bfb76
AD
2262 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
2263 echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
2264 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
c26d97b6 2265 renderAtomGrid ($rackData, $is_ro);
57c69e9c 2266 echo "<tr><th width='10%'>&nbsp;</th>";
bd0bfb76
AD
2267 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
2268 echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
2269 echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
0a7feebb
AD
2270 echo "</table>\n<br>\n";
2271 // Determine zero-u checkbox status.
2272 // If form has been submitted, use form data, otherwise use DB data.
c26d97b6 2273 if (!$is_ro && isset($_REQUEST['op']))
0a7feebb
AD
2274 $checked = isset($_REQUEST['zerou_'.$rack_id]) ? 'checked' : '';
2275 else
2276 $checked = in_array($rack_id, $parentRacks) ? 'checked' : '';
c26d97b6
VS
2277 $disabled_text = $is_ro ? ' disabled' : '';
2278 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 2279 echo "<input type='button' onclick='uncheckAll();' value='Uncheck all'>\n";
0a7feebb 2280 echo '</center></td>';
e673ee24
DO
2281 }
2282 echo "</tr></table>";
2283 finishPortlet();
2284 echo "</td>\n";
2285
2286 echo "</form>\n";
2287 echo "</tr></table>\n";
2288}
2289
2290function renderMolecule ($mdata, $object_id)
2291{
2292 // sort data out
2293 $rackpack = array();
2294 global $loclist;
64b95774 2295 foreach ($mdata as $rua)
e673ee24
DO
2296 {
2297 $rack_id = $rua['rack_id'];
2298 $unit_no = $rua['unit_no'];
2299 $atom = $rua['atom'];
2300 if (!isset ($rackpack[$rack_id]))
2301 {
61a1d996
DO
2302 $rackData = spotEntity ('rack', $rack_id);
2303 amplifyCell ($rackData);
e673ee24
DO
2304 for ($i = $rackData['height']; $i > 0; $i--)
2305 for ($locidx = 0; $locidx < 3; $locidx++)
2306 $rackData[$i][$locidx]['state'] = 'F';
2307 $rackpack[$rack_id] = $rackData;
2308 }
2309 $rackpack[$rack_id][$unit_no][$loclist[$atom]]['state'] = 'T';
2310 $rackpack[$rack_id][$unit_no][$loclist[$atom]]['object_id'] = $object_id;
2311 }
d18eac58 2312 // Now there are some racks to render.
64b95774 2313 foreach ($rackpack as $rackData)
e673ee24
DO
2314 {
2315 markAllSpans ($rackData);
2316 echo "<table class=molecule cellspacing=0>\n";
2317 echo "<caption>${rackData['name']}</caption>\n";
2318 echo "<tr><th width='10%'>&nbsp;</th><th width='20%'>Front</th><th width='50%'>Interior</th><th width='20%'>Back</th></tr>\n";
2319 for ($i = $rackData['height']; $i > 0; $i--)
2320 {
4dcd770e 2321 echo "<tr><th>" . inverseRackUnit ($i, $rackData) . "</th>";
e673ee24
DO
2322 for ($locidx = 0; $locidx < 3; $locidx++)
2323 {
2324 $state = $rackData[$i][$locidx]['state'];
65e557dd 2325 echo "<td class='atom state_${state}'>&nbsp;</td>\n";
e673ee24
DO
2326 }
2327 echo "</tr>\n";
2328 }
2329 echo "</table>\n";
2330 }
2331}
2332
23cdc7e9 2333function renderDepot ()
e673ee24 2334{
23cdc7e9 2335 global $pageno, $nextorder;
23cdc7e9 2336 $cellfilter = getCellFilter();
950dfa1f
AA
2337 $objects = array();
2338 $objects_count = getEntitiesCount ('object');
db8255ac 2339 $showobjecttype = (getConfigVar ('SHOW_OBJECTTYPE') == 'yes');
23cdc7e9
DO
2340
2341 echo "<table border=0 class=objectview>\n";
2342 echo "<tr><td class=pcleft>";
2343
950dfa1f
AA
2344 if ($objects_count == 0)
2345 echo '<h2>No objects exist</h2>';
2346 // 1st attempt: do not fetch all objects if cellfilter is empty and rendering empty result is enabled
2347 elseif (! ($cellfilter['is_empty'] && renderEmptyResults ($cellfilter, 'objects', $objects_count)))
e673ee24 2348 {
2b16c179 2349 $objects = applyCellFilter ('object', $cellfilter);
20506de3 2350 // 2nd attempt: do not render all fetched objects if rendering empty result is enabled
950dfa1f 2351 if (! renderEmptyResults ($cellfilter, 'objects', count($objects)))
1ebbf889 2352 {
4899096d
AD
2353 startPortlet ('Objects (' . count ($objects) . ')');
2354 echo '<br><br><table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
db8255ac
J
2355 echo '<tr><th>Common name</th>';
2356 if ($showobjecttype)
2357 echo '<th>Type</th>';
2358 echo '<th>Visible label</th><th>Asset tag</th><th>Row/Rack or Container</th></tr>';
4899096d 2359 $order = 'odd';
3f8a25d6
DO
2360 # gather IDs of all objects and fetch rackspace info in one pass
2361 $idlist = array();
2362 foreach ($objects as $obj)
2363 $idlist[] = $obj['id'];
2364 $mountinfo = getMountInfo ($idlist);
cae81598 2365 $containerinfo = getContainerInfo ($idlist);
4899096d
AD
2366 foreach ($objects as $obj)
2367 {
ec788aba 2368 $problem = ($obj['has_problems'] == 'yes') ? 'has_problems' : '';
25526a97
ME
2369
2370 setEntityColors ($obj);
5abb804e 2371 $class = getObjectClass ($obj, ($order == 'even' ? 'background:white;' : ''));
25526a97
ME
2372
2373 echo "<tr class='row_${order} tdleft ${problem}${class}' valign=top><td>" . mkA ("<strong>${obj['dname']}</strong>", 'object', $obj['id']);
4899096d
AD
2374 if (count ($obj['etags']))
2375 echo '<br><small>' . serializeTags ($obj['etags'], makeHref(array('page'=>$pageno, 'tab'=>'default')) . '&') . '</small>';
db8255ac
J
2376 echo '</td>';
2377 if ($showobjecttype)
9563f56f 2378 echo "<td>" . decodeObjectType ($obj['objtype_id']) . "</td>";
8526b4cc 2379 echo "<td>${obj['label']}</td>";
4318ced5 2380 echo "<td>${obj['asset_no']}</td>";
3f8a25d6 2381 $places = array();
cae81598
AD
2382 if (array_key_exists ($obj['id'], $containerinfo))
2383 foreach ($containerinfo[$obj['id']] as $ci)
20506de3 2384 $places[] = mkA ($ci['container_dname'], 'object', $ci['container_id']);
cae81598 2385 if (array_key_exists ($obj['id'], $mountinfo))
3f8a25d6
DO
2386 foreach ($mountinfo[$obj['id']] as $mi)
2387 $places[] = mkA ($mi['row_name'], 'row', $mi['row_id']) . '/' . mkA ($mi['rack_name'], 'rack', $mi['rack_id']);
cae81598
AD
2388 if (! count ($places))
2389 $places[] = 'Unmounted';
4318ced5 2390 echo "<td>" . implode (', ', $places) . '</td>';
4899096d
AD
2391 echo '</tr>';
2392 $order = $nextorder[$order];
2393 }
2394 echo '</table>';
2395 finishPortlet();
1ebbf889 2396 }
e673ee24 2397 }
23cdc7e9 2398
d4e27fe5 2399 echo "</td><td class='pcright ${pageno}' width='25%'>";
23cdc7e9 2400
95857b5c 2401 renderCellFilterPortlet ($cellfilter, 'object', $objects);
23cdc7e9 2402 echo "</td></tr></table>\n";
e673ee24
DO
2403}
2404
1ebbf889
AA
2405// This function returns TRUE if the result set is too big to be rendered, and no filter is set.
2406// In this case it renders the describing message instead.
2407function renderEmptyResults($cellfilter, $entities_name, $count = NULL)
2408{
2409 if (!$cellfilter['is_empty'])
2410 return FALSE;
2411 if (isset ($_REQUEST['show_all_objects']))
2412 return FALSE;
7c91a821 2413 $max = getConfigVar('MAX_UNFILTERED_ENTITIES');
1ebbf889
AA
2414 if (0 == $max || $count <= $max)
2415 return FALSE;
2416
2417 $href_show_all = trim($_SERVER['REQUEST_URI'], '&');
2418 $href_show_all .= htmlspecialchars('&show_all_objects=1');
2419 $suffix = isset ($count) ? " ($count)" : '';
a6e090f0
DO
2420 // Heredoc, not nowdoc!
2421 echo <<<"END"
1ebbf889
AA
2422<p>Please set a filter to display the corresponging $entities_name.
2423<br><a href="$href_show_all">Show all $entities_name$suffix</a>
2424END;
2425 return TRUE;
2426}
2427
e673ee24 2428// History viewer for history-enabled simple dictionaries.
9b8174d7 2429function renderObjectHistory ($object_id)
e673ee24 2430{
e673ee24
DO
2431 $order = 'odd';
2432 global $nextorder;
9b8174d7
AD
2433 echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
2434 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>';
2435 $result = usePreparedSelectBlade
2436 (
2437 'SELECT ctime, user_name, name, label, asset_no, has_problems, comment FROM ObjectHistory WHERE id=? ORDER BY ctime',
2438 array ($object_id)
2439 );
e673ee24
DO
2440 while ($row = $result->fetch (PDO::FETCH_NUM))
2441 {
2442 echo "<tr class=row_${order}><td>${row[0]}</td>";
9b8174d7 2443 for ($i = 1; $i <= 6; $i++)
e673ee24
DO
2444 echo "<td>" . $row[$i] . "</td>";
2445 echo "</tr>\n";
2446 $order = $nextorder[$order];
2447 }
2448 echo "</table><br>\n";
2449}
2450
2451function renderRackspaceHistory ()
2452{
4fbb5a00 2453 global $nextorder, $pageno, $tabno;
e673ee24
DO
2454 $order = 'odd';
2455 $history = getRackspaceHistory();
2456 // Show the last operation by default.
2457 if (isset ($_REQUEST['op_id']))
2458 $op_id = $_REQUEST['op_id'];
2459 elseif (isset ($history[0]['mo_id']))
2460 $op_id = $history[0]['mo_id'];
2461 else $op_id = NULL;
dec748f6 2462
e673ee24
DO
2463 $omid = NULL;
2464 $nmid = NULL;
2465 $object_id = 1;
2466 if ($op_id)
2467 list ($omid, $nmid) = getOperationMolecules ($op_id);
2468
2469 // Main layout starts.
2470 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
2471
2472 // Left top portlet with old allocation.
2473 echo "<tr><td class=pcleft>";
2474 startPortlet ('Old allocation');
2475 if ($omid)
2476 {
2477 $oldMolecule = getMolecule ($omid);
2478 renderMolecule ($oldMolecule, $object_id);
2479 }
2480 else
2481 echo "nothing";
2482 finishPortlet();
2483
2484 echo '</td><td class=pcright>';
2485
2486 // Right top portlet with new allocation
2487 startPortlet ('New allocation');
2488 if ($nmid)
2489 {
2490 $newMolecule = getMolecule ($nmid);
2491 renderMolecule ($newMolecule, $object_id);
2492 }
2493 else
2494 echo "nothing";
2495 finishPortlet();
737a3f72 2496
e673ee24 2497 echo '</td></tr><tr><td colspan=2>';
737a3f72 2498
e673ee24
DO
2499 // Bottom portlet with list
2500
2501 startPortlet ('Rackspace allocation history');
2502 echo "<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>\n";
7fa7047a 2503 echo "<tr><th>timestamp</th><th>author</th><th>object</th><th>comment</th></tr>\n";
e673ee24
DO
2504 foreach ($history as $row)
2505 {
2506 if ($row['mo_id'] == $op_id)
2507 $class = 'hl';
2508 else
2509 $class = "row_${order}";
4fbb5a00 2510 echo "<tr class=${class}><td><a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'op_id'=>$row['mo_id']))."'>${row['ctime']}</a></td>";
7fa7047a
DO
2511 echo "<td>${row['user_name']}</td><td>";
2512 renderCell (spotEntity ('object', $row['ro_id']));
a0bdf6dc 2513 echo '</td><td>' . stringForTD ($row['comment'], 0) . '</td></tr>';
e673ee24
DO
2514 $order = $nextorder[$order];
2515 }
2516 echo "</table>\n";
2517 finishPortlet();
737a3f72 2518
e673ee24 2519 echo '</td></tr></table>';
e673ee24
DO
2520}
2521
4318ced5 2522function renderIPSpaceRecords ($tree, $baseurl, $target = 0, $level = 1)
d65353ad
DO
2523{
2524 $self = __FUNCTION__;
4318ced5 2525 $knight = (getConfigVar ('IPV4_ENABLE_KNIGHT') == 'yes');
774968ea 2526 $display_routers = (getConfigVar ('IPV4_TREE_RTR_AS_CELL') != 'none');
00276508
AA
2527
2528 // scroll page to the highlighted item
2529 if ($target && isset ($_REQUEST['hl_net']))
65e557dd 2530 addAutoScrollScript ("net-$target");
00276508 2531
d65353ad
DO
2532 foreach ($tree as $item)
2533 {
0137d53c
DO
2534 if (isset ($item['id']))
2535 {
1f54e1ba 2536 $decor = array ('indent' => $level);
fec0c8da 2537 if ($item['symbol'] == 'node-collapsed')
4318ced5 2538 $decor['symbolurl'] = "${baseurl}&eid=${item['id']}&hl_net=1";
fec0c8da 2539 elseif ($item['symbol'] == 'node-expanded')
4318ced5
AA
2540 $decor['symbolurl'] = $baseurl . ($item['parent_id'] ? "&eid=${item['parent_id']}&hl_net=1" : '');
2541 $tr_class = '';
25526a97 2542 $extrastyle = '';
21ee3351 2543 if ($target == $item['id'] && isset ($_REQUEST['hl_net']))
64347dcf 2544 {
25526a97
ME
2545 if (isset ($item['colors'][0]))
2546 $extrastyle = 'outline: 3px solid #0aff0a;';
2547 else
2548 {
2549 $decor['tdclass'] = ' highlight';
2550 $tr_class = $decor['tdclass'];
2551 }
2552
64347dcf 2553 }
25526a97
ME
2554
2555 setEntityColors ($item);
5abb804e 2556 $tr_class .= getObjectClass ($item, $extrastyle);
25526a97 2557
4318ced5
AA
2558 echo "<tr valign=top class=\"$tr_class\">";
2559 printIPNetInfoTDs ($item, $decor);
2560
2561 // capacity and usage
2562 echo "<td class=tdcenter>";
2563 echo getRenderedIPNetCapacity ($item);
64347dcf 2564 echo "</td>";
4318ced5
AA
2565
2566 if ($display_routers)
6063fab7 2567 printRoutersTD (findNetRouters ($item), getConfigVar ('IPV4_TREE_RTR_AS_CELL'));
0137d53c 2568 echo "</tr>";
55eefced 2569 if ($item['symbol'] == 'node-expanded' || $item['symbol'] == 'node-expanded-static')
4318ced5 2570 $self ($item['kids'], $baseurl, $target, $level + 1);
0137d53c 2571 }
26fc6e7c 2572 elseif (getConfigVar ('IPV4_TREE_SHOW_UNALLOCATED') == 'yes')
0137d53c 2573 {
4318ced5 2574 // non-allocated (spare) IP range
0137d53c 2575 echo "<tr valign=top>";
21ee3351 2576 printIPNetInfoTDs ($item, array ('indent' => $level, 'knight' => $knight, 'tdclass' => 'sparenetwork'));
00276508 2577
4318ced5 2578 // capacity and usage
21ee3351 2579 echo "<td class=tdcenter>";
4318ced5 2580 echo getRenderedIPNetCapacity ($item);
21ee3351 2581 echo "</td>";
4318ced5
AA
2582 if ($display_routers)
2583 echo "<td></td>";
21ee3351 2584 echo "</tr>";
21ee3351 2585 }
21ee3351
AA
2586 }
2587}
2588
4318ced5 2589function renderIPSpace()
e673ee24 2590{
4fbb5a00 2591 global $pageno, $tabno;
4318ced5 2592 $realm = ($pageno == 'ipv4space' ? 'ipv4net' : 'ipv6net');
31c941ec 2593 $cellfilter = getCellFilter();
21ee3351 2594
b99b4167
AA
2595 // expand request can take either natural values or "ALL". Zero means no expanding.
2596 $eid = isset ($_REQUEST['eid']) ? $_REQUEST['eid'] : 0;
2597
21ee3351
AA
2598 echo "<table border=0 class=objectview>\n";
2599 echo "<tr><td class=pcleft>";
950dfa1f
AA
2600
2601 $netlist = array();
b99b4167 2602 if (! ($cellfilter['is_empty'] && ! $eid && renderEmptyResults($cellfilter, 'IP nets', getEntitiesCount ($realm))))
21ee3351 2603 {
950dfa1f
AA
2604 $top = NULL;
2605 foreach (listCells ($realm) as $net)
21ee3351 2606 {
55eefced 2607 if (isset ($top) && IPNetContains ($top, $net))
950dfa1f 2608 ;
55eefced 2609 elseif (! count ($cellfilter['expression']) || judgeCell ($net, $cellfilter['expression']))
950dfa1f
AA
2610 $top = $net;
2611 else
2612 continue;
2613 $netlist[$net['id']] = $net;
2614 }
2615 $netcount = count ($netlist);
950dfa1f
AA
2616 $tree = prepareIPTree ($netlist, $eid);
2617
2618 if (! renderEmptyResults($cellfilter, 'IP nets', count($tree)))
2619 {
2620 startPortlet ("networks (${netcount})");
2621 echo '<h4>';
2622 $all = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'ALL')) .
2623 $cellfilter['urlextra'] . "'>expand&nbsp;all</a>";
2624 $none = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'NONE')) .
2625 $cellfilter['urlextra'] . "'>collapse&nbsp;all</a>";
2626 $auto = "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno)) .
2627 $cellfilter['urlextra'] . "'>auto-collapse</a>";
2628
2629 if ($eid === 0)
2630 echo 'auto-collapsing at threshold ' . getConfigVar ('TREE_THRESHOLD') . " ($all / $none)";
2631 elseif ($eid === 'ALL')
2632 echo "expanding all ($auto / $none)";
2633 elseif ($eid === 'NONE')
2634 echo "collapsing all ($all / $auto)";
2635 else
2636 {
a0f0c0c7
AA
2637 try
2638 {
2639 $netinfo = spotEntity ($realm, $eid);
2640 echo "expanding ${netinfo['ip']}/${netinfo['mask']} ($auto / $all / $none)";
2641 }
2642 catch (EntityNotFoundException $e)
2643 {
2644 // ignore invalid eid error
2645 }
950dfa1f 2646 }
6edc799c 2647 echo "</h4><table class='widetable zebra' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
950dfa1f
AA
2648 echo "<tr><th>prefix</th><th>name/tags</th><th>capacity</th>";
2649 if (getConfigVar ('IPV4_TREE_RTR_AS_CELL') != 'none')
2650 echo "<th>routed by</th>";
2651 echo "</tr>\n";
2652 $baseurl = makeHref(array('page'=>$pageno, 'tab'=>$tabno)) . $cellfilter['urlextra'];
2653 renderIPSpaceRecords ($tree, $baseurl, $eid);
2654 echo "</table>\n";
2655 finishPortlet();
21ee3351 2656 }
21ee3351
AA
2657 }
2658
d4e27fe5 2659 echo "</td><td class='pcright ${pageno}'>";
950dfa1f 2660 renderCellFilterPortlet ($cellfilter, $realm, $netlist);
21ee3351
AA
2661 echo "</td></tr></table>\n";
2662}
2663
4318ced5 2664function renderIPSpaceEditor()
21ee3351 2665{
4318ced5
AA
2666 global $pageno;
2667 $realm = ($pageno == 'ipv4space' ? 'ipv4net' : 'ipv6net');
4318ced5 2668 $addrspaceList = listCells ($realm);
93a49e5b
AA
2669 startPortlet ('Manage existing (' . count ($addrspaceList) . ')');
2670 if (count ($addrspaceList))
21ee3351 2671 {
21ee3351
AA
2672 echo "<table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
2673 echo "<tr><th>&nbsp;</th><th>prefix</th><th>name</th><th>capacity</th></tr>";
21ee3351
AA
2674 foreach ($addrspaceList as $netinfo)
2675 {
21ee3351 2676 echo "<tr valign=top><td>";
93a49e5b 2677 if (! isIPNetworkEmpty ($netinfo))
21ee3351
AA
2678 printImageHREF ('nodestroy', 'There are ' . count ($netinfo['addrlist']) . ' allocations inside');
2679 else
93a49e5b 2680 echo getOpLink (array ('op' => 'del', 'id' => $netinfo['id']), '', 'destroy', 'Delete this prefix');
6d8424f8 2681 echo '</td><td class=tdleft>' . mkCellA ($netinfo) . '</td>';
a0bdf6dc 2682 echo '<td class=tdleft>' . stringForTD ($netinfo['name']);
1f54e1ba
DO
2683 if (count ($netinfo['etags']))
2684 echo '<br><small>' . serializeTags ($netinfo['etags']) . '</small>';
2685 echo '</td><td>';
4318ced5 2686 echo getRenderedIPNetCapacity ($netinfo);
b1e39212 2687 echo '</tr>';
e673ee24 2688 }
04d619d0
DO
2689 echo "</table>";
2690 finishPortlet();
e673ee24 2691 }
93a49e5b
AA
2692}
2693
2694function renderIPNewNetForm ()
2695{
2696 global $pageno;
2697 if ($pageno == 'ipv6space')
2698 {
2699 $realm = 'ipv6net';
3495ec6e 2700 $regexp = '^[a-fA-F0-9:]*:[a-fA-F0-9:\.]*/\d{1,3}$';
93a49e5b
AA
2701 }
2702 else
2703 {
2704 $realm = 'ipv4net';
3495ec6e 2705 $regexp = '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$';
93a49e5b
AA
2706 }
2707
2708 // IP prefix validator
10f5133a 2709 addJS ('js/live_validation.js');
93a49e5b 2710 $regexp = addslashes ($regexp);
a6e090f0
DO
2711 // Heredoc, not nowdoc!
2712 addJS (<<<"END"
93a49e5b 2713$(document).ready(function () {
b895e6b4 2714 $('form#add input[name="range"]').attr('match', '$regexp');
93a49e5b
AA
2715 Validate.init();
2716});
2717END
2718 , TRUE);
dec748f6 2719
93a49e5b 2720 startPortlet ('Add new');
10f5133a 2721 printOpFormIntro ('add');
e7b84015
AB
2722 echo '<table border=0 cellpadding=5 cellspacing=0 align=center>';
2723
93a49e5b
AA
2724 // inputs column
2725 $prefix_value = empty ($_REQUEST['set-prefix']) ? '' : $_REQUEST['set-prefix'];
04c61e7e 2726 echo "<th class=tdright>Prefix:</th><td class=tdleft><input type=text name='range' size=36 class='live-validate' value='${prefix_value}'></td>";
e7b84015 2727 echo '<tr><th class=tdright>VLAN:</th><td class=tdleft>';
04c61e7e
AB
2728 echo getOptionTree ('vlan_ck', getAllVLANOptions(), array ('select_class' => 'vertical')) . '</td></tr>';
2729 echo "<tr><th class=tdright>Name:</th><td class=tdleft><input type=text name='name' size='20'></td></tr>";
e7b84015
AB
2730 echo '<tr><th class=tdright>Tags:</th><td class="tdleft">';
2731 printTagsPicker ();
2732 echo '</td></tr>';
38707670
DO
2733 echo '<tr><td class=tdright><input type=checkbox name="is_connected" id="is_connected"></td>';
2734 echo '<th class=tdleft><label for="is_connected">reserve subnet-router anycast address</label></th></tr>';
93a49e5b 2735 echo "<tr><td colspan=2>";
04c61e7e 2736 printImageHREF ('CREATE', 'Add a new network', TRUE);
93a49e5b 2737 echo '</td></tr>';
e7b84015 2738 echo "</table></form><br><br>\n";
93a49e5b 2739 finishPortlet();
e673ee24
DO
2740}
2741
4318ced5 2742function getRenderedIPNetBacktrace ($range)
e673ee24 2743{
4318ced5
AA
2744 if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
2745 return array();
e673ee24 2746
4318ced5
AA
2747 $v = ($range['realm'] == 'ipv4net') ? 4 : 6;
2748 $space = "ipv${v}space"; // ipv4space, ipv6space
2749 $tag = "\$ip${v}netid_"; // $ip4netid_, $ip6netid_
dec748f6 2750
4318ced5
AA
2751 $ret = array();
2752 // Build a backtrace from all parent networks.
2753 $clen = $range['mask'];
2754 $backtrace = array();
2755 $backtrace['&rarr;'] = $range;
2756 $key = '';
2757 while (NULL !== ($upperid = getIPAddressNetworkId ($range['ip_bin'], $clen)))
2758 {
2759 $upperinfo = spotEntity ($range['realm'], $upperid);
2760 $clen = $upperinfo['mask'];
2761 $key .= '&uarr;';
2762 $backtrace[$key] = $upperinfo;
2763 }
2764 foreach (array_reverse ($backtrace) as $arrow => $ainfo)
2765 {
2766 $link = '<a href="' . makeHref (array (
2767 'page' => $space,
2768 'tab' => 'default',
2769 'clear-cf' => '',
2770 'cfe' => '{' . $tag . $ainfo['id'] . '}',
2771 'hl_net' => 1,
2772 'eid' => $range['id'],
2773 )) . '" title="View IP tree with this net as root">' . $arrow . '</a>';
2774 $ret[] = array ($link, getOutputOf ('renderCell', $ainfo));
2775 }
2776 return $ret;
2777}
2778
d10401bf 2779function renderIPNetwork ($id)
4318ced5
AA
2780{
2781 global $pageno;
2782 $realm = $pageno; // 'ipv4net', 'ipv6net'
d10401bf 2783 $range = spotEntity ($realm, $id);
4318ced5 2784 loadIPAddrList ($range);
25526a97 2785 setEntityColors ($range);
2034d968 2786 echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
99ab184f
DO
2787 echo "<tr><td colspan=2 align=center><h1>${range['ip']}/${range['mask']}</h1><h2>";
2788 echo htmlspecialchars ($range['name'], ENT_QUOTES, 'UTF-8') . "</h2></td></tr>\n";
2034d968
DO
2789
2790 echo "<tr><td class=pcleft width='50%'>";
646272be
AA
2791
2792 // render summary portlet
2793 $summary = array();
967bd0b1 2794 $summary['% used'] = getRenderedIPNetCapacity ($range);
2b6b0038 2795 $summary = getRenderedIPNetBacktrace ($range) + $summary;
4318ced5 2796 if ($realm == 'ipv4net')
001e61fa 2797 {
4318ced5
AA
2798 $summary[] = array ('Netmask:', ip4_format ($range['mask_bin']));
2799 $summary[] = array ('Netmask:', "0x" . strtoupper (implode ('', unpack ('H*', $range['mask_bin']))));
2800 $summary['Wildcard bits'] = ip4_format ( ~ $range['mask_bin']);
001e61fa 2801 }
dec748f6 2802
11e3af31
DO
2803 $reuse_domain = considerConfiguredConstraint ($range, '8021Q_MULTILINK_LISTSRC');
2804 $domainclass = array();
2805 foreach (array_count_values (reduceSubarraysToColumn ($range['8021q'], 'domain_id')) as $domain_id => $vlan_count)
2806 $domainclass[$domain_id] = $vlan_count == 1 ? '' : ($reuse_domain ? '{trwarning}' : '{trerror}');
a5f9d1ca 2807 foreach ($range['8021q'] as $item)
3f642516 2808 $summary[] = array ($domainclass[$item['domain_id']] . 'VLAN:', formatVLANAsHyperlink (getVlanRow ($item['domain_id'] . '-' . $item['vlan_id'])));
55eefced 2809 if (getConfigVar ('EXT_IPV4_VIEW') == 'yes' && count ($routers = findNetRouters ($range)))
04d619d0 2810 {
646272be
AA
2811 $summary['Routed by'] = '';
2812 foreach ($routers as $rtr)
4318ced5 2813 $summary['Routed by'] .= getOutputOf ('renderRouterCell', $rtr['ip_bin'], $rtr['iface'], spotEntity ('object', $rtr['id']));
04d619d0 2814 }
646272be
AA
2815 $summary['tags'] = '';
2816 renderEntitySummary ($range, 'summary', $summary);
e1ae3fb4 2817
33973968 2818 if ($range['comment'] != '')
99ab184f
DO
2819 {
2820 startPortlet ('Comment');
2821 echo '<div class=commentblock>' . string_insert_hrefs (htmlspecialchars ($range['comment'], ENT_QUOTES, 'UTF-8')) . '</div>';
2822 finishPortlet ();
2823 }
2824
d10401bf 2825 renderFilesPortlet ($realm, $id);
2034d968 2826 echo "</td>\n";
e673ee24 2827
2034d968
DO
2828 echo "<td class=pcright>";
2829 startPortlet ('details');
4318ced5
AA
2830 renderIPNetworkAddresses ($range);
2831 finishPortlet();
2832 echo "</td></tr></table>\n";
2833}
e673ee24 2834
4318ced5
AA
2835// Used solely by renderSeparator
2836function renderEmptyIPv6 ($ip_bin, $hl_ip)
2837{
2838 $class = 'tdleft';
2839 if ($ip_bin === $hl_ip)
5c717ce5 2840 $class .= ' highlight';
4318ced5 2841 $fmt = ip6_format ($ip_bin);
235124a6 2842 echo "<tr class='$class'><td><a name='ip-$fmt' href='" . makeHref (array ('page' => 'ipaddress', 'ip' => $fmt)) . "'>" . $fmt;
0b8d8309
AA
2843 $editable = permitted ('ipaddress', 'properties', 'editAddress')
2844 ? 'editable'
2845 : '';
cc2fa820
AD
2846 echo "</a></td><td><span class='rsvtext $editable id-$fmt op-upd-ip-name'></span></td>";
2847 echo "<td><span class='rsvtext $editable id-$fmt op-upd-ip-comment'></span></td><td>&nbsp;</td></tr>\n";
4318ced5
AA
2848}
2849
2850// Renders empty table line to shrink empty IPv6 address ranges.
2851// If the range consists of single address, renders the address instead of empty line.
2852// Renders address $hl_ip inside the range.
2853// Used solely by renderIPv6NetworkAddresses
2854function renderSeparator ($first, $last, $hl_ip)
2855{
2856 $self = __FUNCTION__;
2857 if (strcmp ($first, $last) > 0)
2858 return;
2859 if ($first == $last)
2860 renderEmptyIPv6 ($first, $hl_ip);
2861 elseif (isset ($hl_ip) && strcmp ($hl_ip, $first) >= 0 && strcmp ($hl_ip, $last) <= 0)
2862 { // $hl_ip is inside the range $first - $last
2863 $self ($first, ip_prev ($hl_ip), $hl_ip);
2864 renderEmptyIPv6 ($hl_ip, $hl_ip);
2865 $self (ip_next ($hl_ip), $last, $hl_ip);
2866 }
2867 else
cc2fa820 2868 echo "<tr><td colspan=4 class=tdleft></td></tr>\n";
4318ced5
AA
2869}
2870
75e7c0c6 2871// calculates page number that contains given $ip (used by renderIPv6NetworkAddresses)
4318ced5
AA
2872function getPageNumOfIPv6 ($list, $ip_bin, $maxperpage)
2873{
7c91a821 2874 if ($maxperpage <= 0 || count ($list) <= $maxperpage)
4318ced5
AA
2875 return 0;
2876 $keys = array_keys ($list);
2877 for ($i = 1; $i <= count ($keys); $i++)
2878 if (strcmp ($keys[$i-1], $ip_bin) >= 0)
2879 return intval ($i / $maxperpage);
2880 return intval (count ($list) / $maxperpage);
2881}
2882
2883function renderIPNetworkAddresses ($range)
2884{
2885 switch (strlen ($range['ip_bin']))
2886 {
2887 case 4: return renderIPv4NetworkAddresses ($range);
2888 case 16: return renderIPv6NetworkAddresses ($range);
2889 default: throw new InvalidArgException ("range['ip_bin']", $range['ip_bin']);
2890 }
2891}
2892
247acd67
KZ
2893function renderIPv4NetworkPageLink ($rangeid, $page, $title)
2894{
2895 global $pageno, $tabno;
2896 return "<a href='".makeHref (array ('page' => $pageno, 'tab' => $tabno, 'id' => $rangeid, 'pg' => $page)) . "' title='".$title."'>".$page."</a> ";
2897}
2898
2899function renderIPv4NetworkPagination ($range, $page, $numpages)
2900{
2901 $rendered_pager = '';
2902 $startip = ip4_bin2int ($range['ip_bin']);
2903 $endip = ip4_bin2int (ip_last ($range));
2904 $rangeid = $range['id'];
2905 // Should make this configurable perhaps
2906 // How many pages before/after current page to show
2907 $prepostpagecount = 8;
2908 // Minimum pages where pagination does not happen
2909 $paginationat = 16; // 16 pages is a /20, 32 is a /19
2910 $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
2911 if ($numpages <= $paginationat)
2912 {
2913 // create original pagination
2914 for ($i = 0; $i < $numpages; $i++)
2915 if ($i == $page)
2916 $rendered_pager .= "<b>".$i."</b> ";
2917 else
2918 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2919 }
2920 else // number of pages > page range, create ranged pagination
2921 {
2922 // page is within first subset
2923 if ($page - $prepostpagecount <= 1)
2924 {
2925 for ($i = 0; $i < $page; $i++)
2926 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2927 }
2928 // render 0 ... [page - prepostpagecount] [page - prepostpagecount + 1] ... [page - 1]
2929 else
2930 {
2931 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, 0, ip4_format(ip4_int2bin($startip)));
2932 $rendered_pager .= "... ";
2933 for ($i = $page - $prepostpagecount; $i < $page; $i++)
2934 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2935 }
2936 // render current page
2937 $rendered_pager .= "<b>".$page."</b> ";
2938 // page is within last subset
2939 if ($page + $prepostpagecount >= $numpages-2)
2940 {
2941 for ($i = $page+1; $i < $numpages; $i++)
2942 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2943 }
2944 // render [page + 1] [page + 2] ... [page + postpagecount] ... [end page]
2945 else
2946 {
2947 for ($i = $page+1; $i <= $page+$prepostpagecount; $i++)
2948 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, $i, ip4_format(ip4_int2bin($startip + $i * $maxperpage)));
2949 $rendered_pager .= "... ";
2950 $rendered_pager .= renderIPv4NetworkPageLink($rangeid, ($numpages-1), ip4_format(ip4_int2bin($endip)));
2951 }
2952 }
2953 return $rendered_pager;
2954}
2955
4318ced5
AA
2956function renderIPv4NetworkAddresses ($range)
2957{
9d5f6a80 2958 global $pageno, $tabno, $aac_left;
4318ced5
AA
2959 $startip = ip4_bin2int ($range['ip_bin']);
2960 $endip = ip4_bin2int (ip_last ($range));
2961
2962 if (isset ($_REQUEST['hl_ip']))
00276508 2963 {
4318ced5
AA
2964 $hl_ip = ip4_bin2int (ip4_parse ($_REQUEST['hl_ip']));
2965 addAutoScrollScript ('ip-' . $_REQUEST['hl_ip']); // scroll page to highlighted ip
00276508
AA
2966 }
2967
79b57b18
AA
2968 // pager
2969 $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
2970 $address_count = $endip - $startip + 1;
2971 $page = 0;
446c25c3 2972 $rendered_pager = '';
79b57b18
AA
2973 if ($address_count > $maxperpage && $maxperpage > 0)
2974 {
2975 $page = isset ($_REQUEST['pg']) ? $_REQUEST['pg'] : (isset ($hl_ip) ? intval (($hl_ip - $startip) / $maxperpage) : 0);
2976 if ($numpages = ceil ($address_count / $maxperpage))
2977 {
4318ced5 2978 echo '<h3>' . ip4_format (ip4_int2bin ($startip)) . ' ~ ' . ip4_format (ip4_int2bin ($endip)) . '</h3>';
247acd67 2979 $rendered_pager = renderIPv4NetworkPagination ($range, $page, $numpages);
79b57b18
AA
2980 }
2981 $startip = $startip + $page * $maxperpage;
2982 $endip = min ($startip + $maxperpage - 1, $endip);
2983 }
2984
446c25c3 2985 echo $rendered_pager;
1ce89bdd 2986 echo "<table class='widetable' border=0 cellspacing=0 cellpadding=5 align='center' width='100%'>\n";
5731b2cb
JV
2987
2988 ob_start ();
cc2fa820 2989 echo "<tr><th>Address</th><th>Name</th><th>Comment</th><th>Allocation</th></tr>\n";
5731b2cb
JV
2990 $row_html = ob_get_clean ();
2991 $override = callHook ('renderIPv4NetworkAddressesHeaderRow_hook', $row_html);
2992 echo is_string ($override) ? $override : $row_html;
e673ee24 2993
4318ced5
AA
2994 markupIPAddrList ($range['addrlist']);
2995 for ($ip = $startip; $ip <= $endip; $ip++)
2996 {
2997 $ip_bin = ip4_int2bin ($ip);
2998 $dottedquad = ip4_format ($ip_bin);
5c717ce5 2999 $tr_class = (isset ($hl_ip) && $hl_ip == $ip ? 'highlight' : '');
0cd7c0f6
AA
3000 if (isset ($range['addrlist'][$ip_bin]))
3001 $addr = $range['addrlist'][$ip_bin];
3002 else
e673ee24 3003 {
5731b2cb 3004 ob_start ();
235124a6 3005 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
3006 $editable = permitted ('ipaddress', 'properties', 'editAddress')
3007 ? 'editable'
3008 : '';
cc2fa820
AD
3009 echo "<td><span class='rsvtext $editable id-$dottedquad op-upd-ip-name'></span></td>";
3010 echo "<td><span class='rsvtext $editable id-$dottedquad op-upd-ip-comment'></span></td><td></td></tr>\n";
5731b2cb 3011 $row_html = ob_get_clean ();
97d8533e 3012 $override = callHook ('renderIPv4NetworkAddressesRow_hook', $row_html, $ip_bin, null);
5731b2cb 3013 echo is_string ($override) ? $override : $row_html;
b2035dca
DO
3014 continue;
3015 }
68c91b9c
AA
3016 // render IP change history
3017 $title = '';
3018 $history_class = '';
3019 if (isset ($addr['last_log']))
3020 {
3021 $title = ' title="' . htmlspecialchars ($addr['last_log']['user'] . ', ' . formatAge ($addr['last_log']['time']) , ENT_QUOTES) . '"';
3022 $history_class = 'hover-history underline';
3023 }
4318ced5 3024 $tr_class .= ' ' . $addr['class'];
5731b2cb 3025 ob_start ();
4318ced5 3026 echo "<tr class='tdleft $tr_class'>";
235124a6 3027 echo "<td><a class='$history_class' $title name='ip-$dottedquad' href='".makeHref(array('page'=>'ipaddress', 'ip'=>$addr['ip']))."'>${addr['ip']}</a></td>";
0b8d8309 3028 $editable =
cc2fa820 3029 (empty ($addr['allocs']) || !empty ($addr['name']) || !empty ($addr['comment']))
0b8d8309
AA
3030 && permitted ('ipaddress', 'properties', 'editAddress')
3031 ? 'editable'
3032 : '';
cc2fa820
AD
3033 echo "<td><span class='rsvtext $editable id-$dottedquad op-upd-ip-name'>${addr['name']}</span></td>";
3034 echo "<td><span class='rsvtext $editable id-$dottedquad op-upd-ip-comment'>${addr['comment']}</span></td>";
0b8d8309 3035 echo "<td>";
b2035dca 3036 $delim = '';
b2035dca
DO
3037 if ( $addr['reserved'] == 'yes')
3038 {
2d318652 3039 echo "<strong>RESERVED</strong> ";
b2035dca
DO
3040 $delim = '; ';
3041 }
212b565a 3042 foreach ($addr['allocs'] as $ref)
b2035dca 3043 {
9d5f6a80 3044 echo $delim . $aac_left[$ref['type']];
e749a34f 3045 echo makeIPAllocLink ($ip_bin, $ref, TRUE);
531b1ce3 3046 $delim = ';<br/>';
b2035dca
DO
3047 }
3048 if ($delim != '')
e0d6e38e 3049 $delim = '<br>';
212b565a
AA
3050 foreach ($addr['vslist'] as $vs_id)
3051 {
3052 $vs = spotEntity ('ipv4vs', $vs_id);
ad3e5e7e 3053 echo $delim . mkA ("${vs['name']}:${vs['vport']}/${vs['proto']}", 'ipv4vs', $vs['id']) . '&rarr;';
212b565a
AA
3054 $delim = '<br>';
3055 }
eacc0983
AA
3056 foreach ($addr['vsglist'] as $vs_id)
3057 {
3058 $vs = spotEntity ('ipvs', $vs_id);
3059 echo $delim . mkA ($vs['name'], 'ipvs', $vs['id']) . '&rarr;';
3060 $delim = '<br>';
3061 }
212b565a
AA
3062 foreach ($addr['rsplist'] as $rsp_id)
3063 {
3064 $rsp = spotEntity ('ipv4rspool', $rsp_id);
ad3e5e7e 3065 echo "${delim}&rarr;" . mkA ($rsp['name'], 'ipv4rspool', $rsp['id']);
212b565a
AA
3066 $delim = '<br>';
3067 }
b2035dca 3068 echo "</td></tr>\n";
5731b2cb 3069 $row_html = ob_get_clean ();
97d8533e 3070 $override = callHook ('renderIPv4NetworkAddressesRow_hook', $row_html, $ip_bin, $addr);
5731b2cb 3071 echo is_string ($override) ? $override : $row_html;
4318ced5 3072 }
2727c7df 3073 // end of iteration
a8dc2982
AA
3074 if (permitted (NULL, NULL, 'set_reserve_comment'))
3075 addJS ('js/inplace-edit.js');
e673ee24
DO
3076
3077 echo "</table>";
b956dd24 3078 if ($rendered_pager != '')
446c25c3 3079 echo '<p>' . $rendered_pager . '</p>';
21ee3351
AA
3080}
3081
3082function renderIPv6NetworkAddresses ($netinfo)
3083{
9d5f6a80 3084 global $pageno, $tabno, $aac_left;
1ce89bdd 3085 echo "<table class='widetable' border=0 cellspacing=0 cellpadding=5 align='center' width='100%'>\n";
5731b2cb 3086 ob_start ();
cc2fa820 3087 echo "<tr><th>Address</th><th>Name</th><th>Comment</th><th>Allocation</th></tr>\n";
5731b2cb
JV
3088 $row_html = ob_get_clean ();
3089 $override = callHook ('renderIPv6NetworkAddressesHeaderRow_hook', $row_html);
3090 echo is_string ($override) ? $override : $row_html;
21ee3351 3091
4318ced5
AA
3092 $hl_ip = NULL;
3093 if (isset ($_REQUEST['hl_ip']))
3094 {
3095 $hl_ip = ip6_parse ($_REQUEST['hl_ip']);
3096 addAutoScrollScript ('ip-' . ip6_format ($hl_ip));
3097 }
21ee3351 3098
21ee3351
AA
3099 $addresses = $netinfo['addrlist'];
3100 ksort ($addresses);
4318ced5 3101 markupIPAddrList ($addresses);
21ee3351
AA
3102
3103 // pager
3104 $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
3105 if (count ($addresses) > $maxperpage && $maxperpage > 0)
3106 {
3107 $page = isset ($_REQUEST['pg']) ? $_REQUEST['pg'] : (isset ($hl_ip) ? getPageNumOfIPv6 ($addresses, $hl_ip, $maxperpage) : 0);
3108 $numpages = ceil (count ($addresses) / $maxperpage);
3109 echo "<center><h3>$numpages pages:</h3>";
3110 for ($i=0; $i<$numpages; $i++)
3111 {
3112 if ($i == $page)
3113 echo "<b>$i</b> ";
3114 else
3115 echo "<a href='" . makeHref (array ('page' => $pageno, 'tab' => $tabno, 'id' => $netinfo['id'], 'pg' => $i)) . "'>$i</a> ";
3116 }
3117 echo "</center>";
3118 }
3119