button to fix non-canonical port names (#827)
[racktables-incomplete-works] / wwwroot / inc / interface-lib.php
CommitLineData
ca8aa8e9
DO
1<?php
2
cddbb9fd
DO
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
ca8aa8e9
DO
7// Let's have it here, so extensions can add their own images.
8$image = array();
ca8aa8e9
DO
9$image['rackspace']['path'] = 'pix/racks.png';
10$image['rackspace']['width'] = 218;
11$image['rackspace']['height'] = 200;
12$image['objects']['path'] = 'pix/server.png';
13$image['objects']['width'] = 218;
14$image['objects']['height'] = 200;
15$image['depot']['path'] = 'pix/server.png';
16$image['depot']['width'] = 218;
17$image['depot']['height'] = 200;
18$image['files']['path'] = 'pix/files.png';
19$image['files']['width'] = 218;
20$image['files']['height'] = 200;
21$image['ipv4space']['path'] = 'pix/addressspace.png';
22$image['ipv4space']['width'] = 218;
23$image['ipv4space']['height'] = 200;
24$image['ipv6space']['path'] = 'pix/addressspacev6.png';
25$image['ipv6space']['width'] = 218;
26$image['ipv6space']['height'] = 200;
27$image['ipv4slb']['path'] = 'pix/slb.png';
28$image['ipv4slb']['width'] = 218;
29$image['ipv4slb']['height'] = 200;
30$image['config']['path'] = 'pix/configuration.png';
31$image['config']['width'] = 218;
32$image['config']['height'] = 200;
33$image['reports']['path'] = 'pix/report.png';
34$image['reports']['width'] = 218;
35$image['reports']['height'] = 200;
36$image['8021q']['path'] = 'pix/8021q.png';
37$image['8021q']['width'] = 218;
38$image['8021q']['height'] = 200;
39$image['objectlog']['path'] = 'pix/crystal-mimetypes-shellscript-218x200.png';
40$image['objectlog']['width'] = 218;
41$image['objectlog']['height'] = 200;
42$image['virtual']['path'] = 'pix/virtualresources.png';
43$image['virtual']['width'] = 218;
44$image['virtual']['height'] = 200;
45$image['download']['path'] = 'pix/download.png';
46$image['download']['width'] = 16;
47$image['download']['height'] = 16;
48$image['DOWNLOAD']['path'] = 'pix/download-big.png';
49$image['DOWNLOAD']['width'] = 32;
50$image['DOWNLOAD']['height'] = 32;
51$image['plug']['path'] = 'pix/tango-network-wired.png';
52$image['plug']['width'] = 16;
53$image['plug']['height'] = 16;
54$image['cut']['path'] = 'pix/tango-edit-cut-16x16.png';
55$image['cut']['width'] = 16;
56$image['cut']['height'] = 16;
57$image['Cut']['path'] = 'pix/tango-edit-cut-22x22.png';
58$image['Cut']['width'] = 22;
59$image['Cut']['height'] = 22;
75a01117
DO
60$image['Cut gray']['path'] = 'pix/tango-edit-cut-22x22-gray.png';
61$image['Cut gray']['width'] = 22;
62$image['Cut gray']['height'] = 22;
ca8aa8e9
DO
63$image['CUT']['path'] = 'pix/tango-edit-cut-32x32.png';
64$image['CUT']['width'] = 32;
65$image['CUT']['height'] = 32;
66$image['add']['path'] = 'pix/tango-list-add.png';
67$image['add']['width'] = 16;
68$image['add']['height'] = 16;
69$image['ADD']['path'] = 'pix/tango-list-add-big.png';
70$image['ADD']['width'] = 32;
71$image['ADD']['height'] = 32;
72$image['delete']['path'] = 'pix/tango-list-remove.png';
73$image['delete']['width'] = 16;
74$image['delete']['height'] = 16;
75$image['DELETE']['path'] = 'pix/tango-list-remove-32x32.png';
76$image['DELETE']['width'] = 32;
77$image['DELETE']['height'] = 32;
78$image['destroy']['path'] = 'pix/tango-user-trash-16x16.png';
79$image['destroy']['width'] = 16;
80$image['destroy']['height'] = 16;
81$image['nodestroy']['path'] = 'pix/tango-user-trash-16x16-gray.png';
82$image['nodestroy']['width'] = 16;
83$image['nodestroy']['height'] = 16;
84$image['NODESTROY']['path'] = 'pix/tango-user-trash-32x32-gray.png';
85$image['NODESTROY']['width'] = 32;
86$image['NODESTROY']['height'] = 32;
87$image['DESTROY']['path'] = 'pix/tango-user-trash-32x32.png';
88$image['DESTROY']['width'] = 32;
89$image['DESTROY']['height'] = 32;
90$image['nodelete']['path'] = 'pix/tango-list-remove-shadow.png';
91$image['nodelete']['width'] = 16;
92$image['nodelete']['height'] = 16;
93$image['inservice']['path'] = 'pix/tango-emblem-system.png';
94$image['inservice']['width'] = 16;
95$image['inservice']['height'] = 16;
96$image['notinservice']['path'] = 'pix/tango-dialog-error.png';
97$image['notinservice']['width'] = 16;
98$image['notinservice']['height'] = 16;
99$image['find']['path'] = 'pix/tango-system-search.png';
100$image['find']['width'] = 16;
101$image['find']['height'] = 16;
102$image['next']['path'] = 'pix/tango-go-next.png';
103$image['next']['width'] = 32;
104$image['next']['height'] = 32;
105$image['prev']['path'] = 'pix/tango-go-previous.png';
106$image['prev']['width'] = 32;
107$image['prev']['height'] = 32;
108$image['COMMIT']['path'] = 'pix/tango-go-prev-next-32x32.png';
109$image['COMMIT']['width'] = 32;
110$image['COMMIT']['height'] = 32;
111$image['COMMIT gray']['path'] = 'pix/tango-go-prev-next-gray-32x32.png';
112$image['COMMIT gray']['width'] = 32;
113$image['COMMIT gray']['height'] = 32;
114$image['RECALC']['path'] = 'pix/tango-view-refresh-32x32.png';
115$image['RECALC']['width'] = 32;
116$image['RECALC']['height'] = 32;
8579b51b
AA
117$image['recalc']['path'] = 'pix/tango-view-refresh-16x16.png';
118$image['recalc']['width'] = 16;
119$image['recalc']['height'] = 16;
ca8aa8e9
DO
120$image['clear']['path'] = 'pix/tango-edit-clear.png';
121$image['clear']['width'] = 16;
122$image['clear']['height'] = 16;
123$image['CLEAR']['path'] = 'pix/tango-edit-clear-big.png';
124$image['CLEAR']['width'] = 32;
125$image['CLEAR']['height'] = 32;
126$image['CLEAR gray']['path'] = 'pix/tango-edit-clear-gray-32x32.png';
127$image['CLEAR gray']['width'] = 32;
128$image['CLEAR gray']['height'] = 32;
129$image['save']['path'] = 'pix/tango-document-save-16x16.png';
130$image['save']['width'] = 16;
131$image['save']['height'] = 16;
132$image['SAVE']['path'] = 'pix/tango-document-save-32x32.png';
133$image['SAVE']['width'] = 32;
134$image['SAVE']['height'] = 32;
135$image['NOSAVE']['path'] = 'pix/tango-document-save-32x32-gray.png';
136$image['NOSAVE']['width'] = 32;
137$image['NOSAVE']['height'] = 32;
138$image['create']['path'] = 'pix/tango-document-new.png';
139$image['create']['width'] = 16;
140$image['create']['height'] = 16;
141$image['CREATE']['path'] = 'pix/tango-document-new-big.png';
142$image['CREATE']['width'] = 32;
143$image['CREATE']['height'] = 32;
144$image['DENIED']['path'] = 'pix/tango-dialog-error-big.png';
145$image['DENIED']['width'] = 32;
146$image['DENIED']['height'] = 32;
147$image['node-collapsed']['path'] = 'pix/node-collapsed.png';
148$image['node-collapsed']['width'] = 16;
149$image['node-collapsed']['height'] = 16;
150$image['node-expanded']['path'] = 'pix/node-expanded.png';
151$image['node-expanded']['width'] = 16;
152$image['node-expanded']['height'] = 16;
153$image['node-expanded-static']['path'] = 'pix/node-expanded-static.png';
154$image['node-expanded-static']['width'] = 16;
155$image['node-expanded-static']['height'] = 16;
156$image['dragons']['path'] = 'pix/mitsudragon.png';
157$image['dragons']['width'] = 195;
158$image['dragons']['height'] = 33;
159$image['LB']['path'] = 'pix/loadbalancer.png';
160$image['LB']['width'] = 32;
161$image['LB']['height'] = 32;
162$image['RS pool']['path'] = 'pix/serverpool.png';
163$image['RS pool']['width'] = 48;
164$image['RS pool']['height'] = 16;
165$image['VS']['path'] = 'pix/servicesign.png';
166$image['VS']['width'] = 39;
167$image['VS']['height'] = 62;
168$image['router']['path'] = 'pix/router.png';
169$image['router']['width'] = 32;
170$image['router']['height'] = 32;
171$image['object']['path'] = 'pix/bracket-16x16.png';
172$image['object']['width'] = 16;
173$image['object']['height'] = 16;
174$image['OBJECT']['path'] = 'pix/bracket-32x32.png';
175$image['OBJECT']['width'] = 32;
176$image['OBJECT']['height'] = 32;
3f61a116
AD
177$image['LOCATION']['path'] = 'pix/tango-internet-32x32.png';
178$image['LOCATION']['width'] = 32;
179$image['LOCATION']['height'] = 32;
ca8aa8e9
DO
180$image['attach']['path'] = 'pix/tango-mail-attachment-16x16.png';
181$image['attach']['width'] = 16;
182$image['attach']['height'] = 16;
183$image['Attach']['path'] = 'pix/tango-mail-attachment-22x22.png';
184$image['Attach']['width'] = 22;
185$image['Attach']['height'] = 22;
186$image['ATTACH']['path'] = 'pix/tango-mail-attachment-32x32.png';
187$image['ATTACH']['width'] = 32;
188$image['ATTACH']['height'] = 32;
189$image['favorite']['path'] = 'pix/tango-emblem-favorite.png';
190$image['favorite']['width'] = 16;
191$image['favorite']['height'] = 16;
192$image['computer']['path'] = 'pix/tango-computer.png';
193$image['computer']['width'] = 16;
194$image['computer']['height'] = 16;
195$image['empty file']['path'] = 'pix/crystal-file-empty-32x32.png';
196$image['empty file']['width'] = 32;
197$image['empty file']['height'] = 32;
198$image['text file']['path'] = 'pix/crystal-file-text-32x32.png';
199$image['text file']['width'] = 32;
200$image['text file']['height'] = 32;
201$image['image file']['path'] = 'pix/crystal-file-image-32x32.png';
202$image['image file']['width'] = 32;
203$image['image file']['height'] = 32;
204$image['text']['path'] = 'pix/tango-text-x-generic-16x16.png';
205$image['text']['width'] = 16;
206$image['text']['height'] = 16;
207$image['NET']['path'] = 'pix/crystal-network_local-32x32.png';
208$image['NET']['width'] = 32;
209$image['NET']['height'] = 32;
210$image['net']['path'] = 'pix/crystal-network_local-16x16.png';
211$image['net']['width'] = 16;
212$image['net']['height'] = 16;
213$image['USER']['path'] = 'pix/crystal-edit-user-32x32.png';
214$image['USER']['width'] = 32;
215$image['USER']['height'] = 32;
216$image['setfilter']['path'] = 'pix/pgadmin3-viewfiltereddata.png';
217$image['setfilter']['width'] = 32;
218$image['setfilter']['height'] = 32;
219$image['setfilter gray']['path'] = 'pix/pgadmin3-viewfiltereddata-grayscale.png';
220$image['setfilter gray']['width'] = 32;
221$image['setfilter gray']['height'] = 32;
222$image['resetfilter']['path'] = 'pix/pgadmin3-viewdata.png';
223$image['resetfilter']['width'] = 32;
224$image['resetfilter']['height'] = 32;
225$image['resetfilter gray']['path'] = 'pix/pgadmin3-viewdata-grayscale.png';
226$image['resetfilter gray']['width'] = 32;
227$image['resetfilter gray']['height'] = 32;
228$image['knight']['path'] = 'pix/smiley_knight.png';
229$image['knight']['width'] = 72;
230$image['knight']['height'] = 33;
231$image['Zoom']['path'] = 'pix/tango-system-search-22x22.png';
232$image['Zoom']['width'] = 22;
233$image['Zoom']['height'] = 22;
234$image['Zooming']['path'] = 'pix/tango-view-fullscreen-22x22.png';
235$image['Zooming']['width'] = 22;
236$image['Zooming']['height'] = 22;
237$image['UNLOCK']['path'] = 'pix/crystal-actions-unlock-32x32.png';
238$image['UNLOCK']['width'] = 32;
239$image['UNLOCK']['height'] = 32;
240$image['CLOCK']['path'] = 'pix/tango-appointment-32x32.png';
241$image['CLOCK']['width'] = 32;
242$image['CLOCK']['height'] = 32;
243$image['DQUEUE done']['path'] = 'pix/crystal-ok-32x32.png';
244$image['DQUEUE done']['width'] = 32;
245$image['DQUEUE done']['height'] = 32;
246$image['DQUEUE sync_aging']['path'] = 'pix/tango-appointment-32x32.png';
247$image['DQUEUE sync_aging']['width'] = 32;
248$image['DQUEUE sync_aging']['height'] = 32;
249$image['DQUEUE resync_aging']['path'] = 'pix/tango-appointment-32x32.png';
250$image['DQUEUE resync_aging']['width'] = 32;
251$image['DQUEUE resync_aging']['height'] = 32;
252$image['DQUEUE sync_ready']['path'] = 'pix/tango-emblem-system-32x32.png';
253$image['DQUEUE sync_ready']['width'] = 32;
254$image['DQUEUE sync_ready']['height'] = 32;
255$image['DQUEUE resync_ready']['path'] = 'pix/tango-emblem-important-32x32.png';
256$image['DQUEUE resync_ready']['width'] = 32;
257$image['DQUEUE resync_ready']['height'] = 32;
258$image['DQUEUE failed']['path'] = 'pix/tango-emblem-unreadable-32x32.png';
259$image['DQUEUE failed']['width'] = 32;
260$image['DQUEUE failed']['height'] = 32;
261$image['DQUEUE disabled']['path'] = 'pix/tango-emblem-readonly-32x32.png';
262$image['DQUEUE disabled']['width'] = 32;
263$image['DQUEUE disabled']['height'] = 32;
d60c7760
AA
264$image['copy']['path'] = 'pix/tango-edit-copy-16x16.png';
265$image['copy']['width'] = 16;
266$image['copy']['height'] = 16;
ca8aa8e9
DO
267$image['COPY']['path'] = 'pix/tango-edit-copy-32x32.png';
268$image['COPY']['width'] = 32;
269$image['COPY']['height'] = 32;
270$image['html']['path'] = 'pix/tango-text-html.png';
271$image['html']['width'] = 16;
272$image['html']['height'] = 16;
2147167d
AA
273$image['pencil']['path'] = 'pix/pencil-icon.png';
274$image['pencil']['width'] = 12;
275$image['pencil']['height'] = 12;
ca8aa8e9 276
646272be
AA
277$page_by_realm = array();
278$page_by_realm['object'] = 'depot';
279$page_by_realm['rack'] = 'rackspace';
280$page_by_realm['ipv4net'] = 'ipv4space';
281$page_by_realm['ipv6net'] = 'ipv6space';
e2f674be
AA
282$page_by_realm['ipv4vs'] = 'ipv4slb';
283$page_by_realm['ipv4rspool'] = 'ipv4slb';
646272be
AA
284$page_by_realm['file'] = 'files';
285$page_by_realm['user'] = 'userlist';
286
ca8aa8e9
DO
287function printSelect ($optionList, $select_attrs = array(), $selected_id = NULL)
288{
289 echo getSelect ($optionList, $select_attrs, $selected_id);
290}
291
292// Input array keys are OPTION VALUEs and input array values are OPTION text.
c4ad9ac0 293function getSelect ($optionList, $select_attrs = array(), $selected_id = NULL, $treat_single_special = TRUE)
ca8aa8e9
DO
294{
295 $ret = '';
296 if (!array_key_exists ('name', $select_attrs))
297 return '';
298 // handle two corner cases in a specific way
299 if (count ($optionList) == 0)
300 return '(none)';
c4ad9ac0 301 if (count ($optionList) == 1 && $treat_single_special)
ca8aa8e9 302 {
a20a4e3c
DO
303 foreach ($optionList as $key => $value)
304 break;
ca8aa8e9
DO
305 return "<input type=hidden name=${select_attrs['name']} id=${select_attrs['name']} value=${key}>" . $value;
306 }
307 if (!array_key_exists ('id', $select_attrs))
308 $select_attrs['id'] = $select_attrs['name'];
309 $ret .= '<select';
310 foreach ($select_attrs as $attr_name => $attr_value)
311 $ret .= " ${attr_name}=${attr_value}";
312 $ret .= '>';
313 foreach ($optionList as $dict_key => $dict_value)
314 $ret .= "<option value='${dict_key}'" . ($dict_key == $selected_id ? ' selected' : '') . ">${dict_value}</option>";
315 $ret .= '</select>';
316 return $ret;
317}
318
319function printNiftySelect ($groupList, $select_attrs = array(), $selected_id = NULL, $autocomplete = false)
320{
321 echo getNiftySelect ($groupList, $select_attrs, $selected_id);
322}
323
324// Input is a cooked list of OPTGROUPs, each with own sub-list of OPTIONs in the same
325// format as printSelect() expects.
7232ef01
AD
326// If tree is true, hierarchical drop-boxes are used, otherwise optgroups are used.
327function getNiftySelect ($groupList, $select_attrs, $selected_id = NULL, $tree = false)
ca8aa8e9
DO
328{
329 // special treatment for ungrouped data
330 if (count ($groupList) == 1 and isset ($groupList['other']))
331 return getSelect ($groupList['other'], $select_attrs, $selected_id);
332 if (!array_key_exists ('name', $select_attrs))
333 return '';
334 if (!array_key_exists ('id', $select_attrs))
335 $select_attrs['id'] = $select_attrs['name'];
7232ef01 336 if ($tree)
ca8aa8e9 337 {
2f06f6be
DO
338 # it is safe to call many times for the same file
339 addJS ('js/jquery.optionTree.js');
7232ef01
AD
340 $ret = "<input type=hidden name=${select_attrs['name']}>\n";
341 $ret .= "<script type='text/javascript'>\n";
342 $ret .= "\$(function() {\n";
343 $ret .= " var option_tree = {\n";
344 foreach ($groupList as $groupname => $groupdata)
345 {
346 $ret .= " '${groupname}': {";
347 foreach ($groupdata as $dict_key => $dict_value)
348 $ret .= "\"${dict_value}\":'${dict_key}', ";
349 $ret .= "},\n";
350 }
351 $ret .= " };\n";
30dce055 352 $ret .= " var options = {empty_value: '', choose: 'select...'};\n";
7232ef01
AD
353 $ret .= " \$('input[name=${select_attrs['name']}]').optionTree(option_tree, options);\n";
354 $ret .= "});\n";
355 $ret .= "</script>\n";
356 }
357 else
358 {
359 $ret = '<select';
360 foreach ($select_attrs as $attr_name => $attr_value)
361 $ret .= " ${attr_name}=${attr_value}";
362 $ret .= ">\n";
363 foreach ($groupList as $groupname => $groupdata)
364 {
365 $ret .= "<optgroup label='${groupname}'>\n";
366 foreach ($groupdata as $dict_key => $dict_value)
367 $ret .= "<option value='${dict_key}'" . ($dict_key == $selected_id ? ' selected' : '') . ">${dict_value}</option>\n";
368 $ret .= "</optgroup>\n";
369 }
370 $ret .= "</select>\n";
ca8aa8e9 371 }
ca8aa8e9
DO
372 return $ret;
373}
374
16d86b28
DO
375function getOptionTree ($tree_name, $tree_options, $tree_config = array())
376{
77b5137a
DO
377 function serializeJSArray ($options)
378 {
379 $tmp = array();
380 foreach ($options as $key => $value)
381 $tmp[] = "'${key}': \"${value}\"";
382 return '{' . implode (', ', $tmp) . "}\n";
383 }
384 function serializeJSTree ($tree_options)
16d86b28
DO
385 {
386 $self = __FUNCTION__;
387 $tmp = array();
77b5137a
DO
388 # Leaves on the PHP tree are stored "value => label" way,
389 # non-leaves are stored "label => array" way, and the JS
390 # tree is always built "label => value" or "label => array"
391 # way, hence a structure transform is required.
392 foreach ($tree_options as $key => $value)
393 $tmp[] = is_array ($value) ?
1b655fac
AA
394 '"' . str_replace ('"', '\"', $key) . '": ' . $self ($value) :
395 '"' . str_replace ('"', '\"', $value) . '": "' . str_replace ('"', '\"', $key) . '"';
16d86b28
DO
396 return '{' . implode (', ', $tmp) . "}\n";
397 }
398
399 $default_config = array
400 (
401 'choose' => 'select...',
402 'empty_value' => '',
403 );
404 foreach ($tree_config as $cfgoption_name => $cfgoption_value)
405 $default_config[$cfgoption_name] = $cfgoption_value;
406 # it is safe to call many times for the same file
407 addJS ('js/jquery.optionTree.js');
408 $ret = "<input type=hidden name=${tree_name}>\n";
409 $ret .= "<script type='text/javascript'>\n";
410 $ret .= "\$(function() {\n";
77b5137a 411 $ret .= " var option_tree = " . serializeJSTree ($tree_options) . ";\n";
16d86b28
DO
412 $ret .= " var options = " . serializeJSArray ($default_config) . ";\n";
413 $ret .= " \$('input[name=${tree_name}]').optionTree(option_tree, options);\n";
414 $ret .= "});\n";
415 $ret .= "</script>\n";
416 return $ret;
417}
418
ca8aa8e9
DO
419function printImageHREF ($tag, $title = '', $do_input = FALSE, $tabindex = 0)
420{
421 echo getImageHREF ($tag, $title, $do_input, $tabindex);
422}
423
424// this would be better called mkIMG(), make "IMG" HTML element
425function getImageHREF ($tag, $title = '', $do_input = FALSE, $tabindex = 0)
426{
427 global $image;
428 if (!isset ($image[$tag]))
429 $tag = 'error';
430 $img = $image[$tag];
19f22ad8 431 $img['path'] = '?module=chrome&uri=' . $img['path'];
ca8aa8e9
DO
432 if ($do_input == TRUE)
433 return
434 "<input type=image name=submit class=icon " .
435 "src='${img['path']}' " .
436 "border=0 " .
437 ($tabindex ? "tabindex=${tabindex}" : '') .
438 (!strlen ($title) ? '' : " title='${title}'") . // JT: Add title to input hrefs too
439 ">";
440 else
441 return
442 "<img " .
443 "src='${img['path']}' " .
444 "width=${img['width']} " .
445 "height=${img['height']} " .
446 "border=0 " .
447 (!strlen ($title) ? '' : "title='${title}'") .
448 ">";
449}
450
967e3845
DO
451function escapeString ($value, $do_db_escape = FALSE)
452{
453 $ret = htmlspecialchars ($value, ENT_QUOTES, 'UTF-8');
454 if ($do_db_escape)
455 {
456 global $dbxlink;
457 $ret = substr ($dbxlink->quote ($ret), 1, -1);
458 }
459 return $ret;
460}
461
462function transformRequestData()
463{
464 global $sic;
465 // Magic quotes feature is deprecated, but just in case the local system
466 // still has it activated, reverse its effect.
dbd20c7e
AA
467 $do_magic_quotes = (function_exists ('get_magic_quotes_gpc') and get_magic_quotes_gpc());
468 $seen_keys = array();
469
967e3845
DO
470 // Escape any globals before we ever try to use them, but keep a copy of originals.
471 $sic = array();
4675424f 472 // walk through merged GET and POST instead of REQUEST array because it
75e7c0c6 473 // can contain cookies with data that could not be decoded from UTF-8
2b6b0038 474 foreach (($_POST + $_GET) as $key => $value)
967e3845 475 {
5a7dc9e7
AA
476 if (is_array ($value))
477 $_REQUEST[$key] = $value;
478 else
479 {
480 $value = dos2unix ($value);
481 if ($do_magic_quotes)
482 $value = stripslashes ($value);
483 $_REQUEST[$key] = escapeString ($value);
484 }
485 $sic[$key] = $value;
dbd20c7e 486 $seen_keys[$key] = 1;
967e3845
DO
487 }
488
dbd20c7e
AA
489 // delete cookie information from the $_REQUEST array
490 foreach (array_keys ($_REQUEST) as $key)
491 if (! isset ($seen_keys[$key]))
492 unset ($_REQUEST[$key]);
493
967e3845
DO
494 if (isset ($_SERVER['PHP_AUTH_USER']))
495 $_SERVER['PHP_AUTH_USER'] = escapeString ($_SERVER['PHP_AUTH_USER']);
496 if (isset ($_SERVER['REMOTE_USER']))
497 $_SERVER['REMOTE_USER'] = escapeString ($_SERVER['REMOTE_USER']);
498}
499
ca6698d8
AA
500// JS scripts should be included through this function.
501// They automatically appear in the <head> of your page.
502// $data is a JS filename, or JS code w/o tags around, if $inline = TRUE
503// Scripts are included in the order of adding within the same group, and groups are sorted alphabetically.
504function addJS ($data, $inline = FALSE, $group = 'default')
505{
506 static $javascript = array();
507 static $seen_filenames = array();
dec748f6 508
ca6698d8
AA
509 if (! isset ($data))
510 {
511 ksort ($javascript);
512 return $javascript;
513 }
514 // Add jquery.js and racktables.js the first time a Javascript file is added.
515 if (empty($javascript))
516 {
517 $javascript = array
518 (
519 'a_core' => array
520 (
521 array('type' => 'file', 'script' => 'js/jquery-1.4.4.min.js'),
522 array('type' => 'file', 'script' => 'js/racktables.js'),
523 ),
524 );
525
526 // initialize core js filelist
527 foreach ($javascript as $group_name => $group_array)
528 foreach ($group_array as $item)
529 if ($item['type'] == 'file')
530 $seen_filenames[$item['script']] = 1;
531 }
532
533 if ($inline)
534 $javascript[$group][] = array
535 (
536 'type' => 'inline',
537 'script' => $data,
538 );
539 elseif (! isset ($seen_filenames[$data]))
540 {
541 $javascript[$group][] = array
542 (
543 'type' => 'file',
544 'script' => $data,
545 );
546 $seen_filenames[$data] = 1;
547 }
548}
549
550// CSS styles should be included through this function.
551// They automatically appear in the <head> of your page.
552// $data is a CSS filename, or CSS code w/o tags around, if $inline = TRUE
553// Styles are included in the order of adding.
554function addCSS ($data, $inline = FALSE)
555{
556 static $styles = array();
557 static $seen_filenames = array();
dec748f6 558
ca6698d8
AA
559 if (! isset ($data))
560 return $styles;
561 if ($inline)
562 $styles[] = array
563 (
564 'type' => 'inline',
565 'style' => $data,
566 );
567 elseif (! isset ($seen_filenames[$data]))
568 {
569 $styles[] = array
570 (
571 'type' => 'file',
572 'style' => $data,
573 );
574 $seen_filenames[$data] = 1;
575 }
576}
577
4318ced5
AA
578function getRenderedIPNetCapacity ($range)
579{
580 switch (strlen ($range['ip_bin']))
581 {
582 case 4: return getRenderedIPv4NetCapacity ($range);
583 case 16: return getRenderedIPv6NetCapacity ($range);
584 default: throw new InvalidArgException ('range["ip_bin"]', $range['ip_bin'], "Invalid binary IP");
585 }
586}
587
588function getRenderedIPv4NetCapacity ($range)
589{
590 $class = 'net-usage';
591 if (isset ($range['addrc']))
592 {
593 // full mode
f59e175c 594 // $a is "aquamarine zone", $b is "gray zone"
4318ced5 595 $total = ip4_range_size ($range);
91fcd2c0
AA
596
597 // compute $a_total: own range size, without subranges
6a95327a 598 if ($range['kidc'] == 0)
91fcd2c0
AA
599 $a_total = $total;
600 else
601 {
602 $a_total = 0;
603 foreach ($range['spare_ranges'] as $mask => $spare_list)
fc338a7b 604 $a_total = bcadd ($a_total, bcmul (count ($spare_list), ip4_mask_size ($mask)), 0);
91fcd2c0 605 }
f59e175c 606 $a_used = $range['own_addrc'];
fc338a7b 607 $b_total = bcsub ($total, $a_total, 0);
f59e175c 608 $b_used = $range['addrc'] - $a_used;
4318ced5
AA
609
610 // generate link to progress bar image
611 $width = 100;
91fcd2c0 612 if ($total != 0)
4318ced5 613 {
fc338a7b
AA
614 $px_a = round (bcdiv ($a_total, $total, 4) * $width);
615 $px1 = round (bcdiv ($a_used, $total, 4) * $width);
4318ced5 616 $px2 = $px_a - $px1;
fc338a7b 617 $px3 = round (bcdiv ($b_used, $total, 4) * $width);
4318ced5
AA
618 if ($px3 + $px1 + $px2 > $width)
619 $px3 = $width - $px1 - $px2;
620 }
621 else
622 $px1 = $px2 = $px3 = 0;
623
4318ced5
AA
624 $title_items = array();
625 $title2_items = array();
91fcd2c0 626 if ($a_total != 0)
4318ced5
AA
627 {
628 $title_items[] = "$a_used / $a_total";
fc338a7b 629 $title2_items[] = sprintf ("%d%% used", bcdiv ($a_used, $a_total, 4) * 100);
4318ced5 630 }
91fcd2c0 631 if ($b_total != 0)
4318ced5
AA
632 {
633 $title_items[] = ($b_used ? "$b_used / " : "") . $b_total;
fc338a7b 634 $title2_items[] = sprintf ("%d%% sub-allocated", bcdiv ($b_total, $total, 4) * 100);
4318ced5
AA
635 }
636 $title = implode (', ', $title_items);
637 $title2 = implode (', ', $title2_items);
638 $text = "<img width='$width' height=10 border=0 title='$title2' src='?module=progressbar4&px1=$px1&px2=$px2&px3=$px3'>" .
639 " <small class='title'>$title</small>";
640 }
641 else
642 {
643 // fast mode
644 $class .= ' pending';
645 addJS ('js/net-usage.js');
646
647 $free_text = '';
648 if (isset ($range['kidc']) and $range['kidc'] > 0)
649 {
650 $free_masks = array_keys ($range['spare_ranges']);
651 sort ($free_masks, SORT_NUMERIC);
652 if ($mask = array_shift ($free_masks))
653 {
654 $cnt = count ($range['spare_ranges'][$mask]);
655 $free_text = ', ' . ($cnt > 1 ? "<small>${cnt}x</small>" : "") . "/$mask free";
656 }
657 }
658 $text = ip4_range_size ($range) . $free_text;
659 }
660
661 $div_id = $range['ip'] . '/' . $range['mask'];
662
663 return "<div class=\"$class\" id=\"$div_id\">" . $text . "</div>";
664}
665
666function getRenderedIPv6NetCapacity ($range)
667{
668 $div_id = $range['ip'] . '/' . $range['mask'];
669 $class = 'net-usage';
670 if (isset ($range['addrc']))
671 $used = $range['addrc'];
672 else
673 {
674 $used = NULL;
675 $class .= ' pending';
676 addJS ('js/net-usage.js');
677 }
678
679 static $prefixes = array
680 (
681 0 => '',
682 3 => 'k',
683 6 => 'M',
684 9 => 'G',
685 12 => 'T',
686 15 => 'P',
687 18 => 'E',
688 21 => 'Z',
689 24 => 'Y',
690 );
691
692 if ($range['mask'] <= 64)
693 {
694 $what = 'net';
695 $preposition = 'in';
696 $range['mask'] += 64;
697 }
698 else
699 {
700 $what = 'IP';
701 $preposition = 'of';
702 }
703 $what .= (0 == $range['mask'] % 64 ? '' : 's');
704 $addrc = isset ($used) ? "$used $preposition " : '';
705
706 $dec_order = intval ((128 - $range['mask']) / 10) * 3;
707 $mult = isset ($prefixes[$dec_order]) ? $prefixes[$dec_order] : '??';
dec748f6 708
4318ced5
AA
709 $cnt = 1 << ((128 - $range['mask']) % 10);
710 if ($cnt == 1 && $mult == '')
711 $cnt = '1';
712
713 return "<div class=\"$class\" id=\"$div_id\">" . "{$addrc}${cnt}${mult} ${what}" . "</div>";
714}
715
ca6698d8
AA
716// print part of HTML HEAD block
717function printPageHeaders ()
718{
719 global $pageheaders;
720 ksort ($pageheaders);
721 foreach ($pageheaders as $s)
722 echo $s . "\n";
723
724 // add CSS styles
725 foreach (addCSS (NULL) as $item)
726 if ($item['type'] == 'inline')
727 echo '<style type="text/css">' . "\n" . trim ($item['style'], "\r\n") . "\n</style>\n";
728 elseif ($item['type'] == 'file')
729 echo "<link rel=stylesheet type='text/css' href='?module=chrome&uri=${item['style']}' />\n";
730
731 // add JS scripts
732 foreach (addJS (NULL) as $group_name => $js_list)
733 foreach ($js_list as $item)
734 if ($item['type'] == 'inline')
735 echo '<script type="text/javascript">' . "\n" . trim ($item['script'], "\r\n") . "\n</script>\n";
736 elseif ($item['type'] == 'file')
737 echo "<script type='text/javascript' src='?module=chrome&uri=${item['script']}'></script>\n";
738}
739
0dc380e9
AA
740function cmpTags ($a, $b)
741{
742 global $taglist;
743 if (isset ($a['id']) && isset ($b['id']))
744 {
c09cdace
AA
745 $a_root = array_first ($taglist[$a['id']]['trace']);
746 $b_root = array_first ($taglist[$b['id']]['trace']);
747 if ($a_root < $b_root)
748 return -1;
749 elseif ($a_root > $b_root)
750 return 1;
0dc380e9
AA
751 }
752 elseif (isset ($a['id']))
753 return -1;
754 elseif (isset ($b['id']))
755 return 1;
c09cdace
AA
756
757 return strcmp ($a['tag'], $b['tag']);
0dc380e9
AA
758}
759
d7bc2390
AA
760function getTagClassName ($tagid)
761{
762 global $taglist;
763
764 $class = '';
765 foreach ($taglist[$tagid]['trace'] as $parent)
766 $class .= 'tag-' . $parent . ' ';
767 $class .= 'tag-' . $tagid . ' etag-' . $tagid;
768
769 return $class;
770}
771
221327e3
DO
772function serializeTags ($chain, $baseurl = '')
773{
c8c0fda8 774 global $taglist;
221327e3 775 $tmp = array();
0dc380e9 776 usort ($chain, 'cmpTags');
221327e3 777 foreach ($chain as $taginfo)
30bb83bd 778 {
d7bc2390
AA
779 $title = '';
780 if (isset ($taginfo['user']) and isset ($taginfo['time']))
c8c0fda8
AB
781 $title = htmlspecialchars ($taginfo['user'] . ', ' . formatAge ($taginfo['time']), ENT_QUOTES);
782 if (isset($taginfo['parent_id']))
783 {
2cb04fec
AA
784 $parent_info = array();
785 foreach ($taglist[$taginfo['id']]['trace'] as $tag_id)
c8c0fda8 786 $parent_info[] = $taglist[$tag_id]['tag'];
2cb04fec
AA
787 $parent_info[] = $taginfo['tag'];
788 if (strlen ($title))
c8c0fda8
AB
789 $title .= "\n";
790 $title .= implode (" \xE2\x86\x92 ", $parent_info); # right arrow
791 }
2cb04fec 792 if (strlen ($title))
c8c0fda8 793 $title = "title='$title'";
d7bc2390
AA
794
795 $class = '';
796 if (isset ($taginfo['id']))
797 $class = 'class="' . getTagClassName ($taginfo['id']) . '"';
798
799 $href = '';
d3ee17b7 800 if ($baseurl == '')
d7bc2390 801 $tag = 'span';
30bb83bd 802 else
d3ee17b7 803 {
d7bc2390
AA
804 $tag = 'a';
805 $href = "href='${baseurl}cft[]=${taginfo['id']}'";
d3ee17b7 806 }
d7bc2390 807 $tmp[] = "<$tag $href $title $class>" . $taginfo['tag'] . "</$tag>";
30bb83bd 808 }
221327e3
DO
809 return implode (', ', $tmp);
810}
811
2030a7f2
AA
812function startPortlet ($title = '')
813{
814 echo "<div class=portlet><h2>${title}</h2>";
815}
816
817function finishPortlet ()
818{
819 echo "</div>\n";
820}
821
a1d72d2e
AA
822function getPageName ($page_code)
823{
824 global $page;
298893f9 825 $title = isset ($page[$page_code]['title']) ? $page[$page_code]['title'] : callHook ('dynamic_title_decoder', $page_code);
a1d72d2e
AA
826 if (is_array ($title))
827 $title = $title['name'];
828 return $title;
829}
830
646272be
AA
831function printTagTRs ($cell, $baseurl = '')
832{
833 if (getConfigVar ('SHOW_EXPLICIT_TAGS') == 'yes' and count ($cell['etags']))
834 {
835 echo "<tr><th width='50%' class=tagchain>Explicit tags:</th><td class=tagchain>";
836 echo serializeTags ($cell['etags'], $baseurl) . "</td></tr>\n";
837 }
838 if (getConfigVar ('SHOW_IMPLICIT_TAGS') == 'yes' and count ($cell['itags']))
839 {
840 echo "<tr><th width='50%' class=tagchain>Implicit tags:</th><td class=tagchain>";
841 echo serializeTags ($cell['itags'], $baseurl) . "</td></tr>\n";
842 }
843 if (getConfigVar ('SHOW_AUTOMATIC_TAGS') == 'yes' and count ($cell['atags']))
844 {
845 echo "<tr><th width='50%' class=tagchain>Automatic tags:</th><td class=tagchain>";
846 echo serializeTags ($cell['atags']) . "</td></tr>\n";
847 }
848}
849
ec143409
AA
850// stub function to override it by chain-connected hooks
851function modifyEntitySummary ($cell, $summary)
852{
853 return $summary;
854}
855
646272be
AA
856// renders 'summary' portlet, which persist on default tab of every realm page.
857// $values is a tricky array.
858// if its value is a string, it is treated as right td inner html, and the key is treated as left th text, colon appends there automatically.
defd92d8 859// 'tags' key has a special meaning: instead of value, the result of printTagTRs call is appended to output
646272be
AA
860// if the value is a single-element array, its value rendered as-is instead of <tr> tag and all its contents.
861// if the value is an array, its first 2 items are treated as left and right contents of row, no colon is appended. Used to enable non-unique titles
862function renderEntitySummary ($cell, $title, $values = array())
863{
864 global $page_by_realm;
54f3cd0a 865 // allow plugins to override summary table
ec143409 866 $values = callHook ('modifyEntitySummary', $cell, $values);
54f3cd0a 867
646272be
AA
868 startPortlet ($title);
869 echo "<table border=0 cellspacing=0 cellpadding=3 width='100%'>\n";
870 foreach ($values as $name => $value)
871 {
872 if (is_array ($value) and count ($value) == 1)
873 {
874 $value = array_shift ($value);
875 echo $value;
876 continue;
877 }
878 if (is_array ($value))
879 {
880 $name = array_shift ($value);
881 $value = array_shift ($value);
882 }
883 elseif (! is_array ($value))
884 $name .= ':';
885 $class = 'tdright';
886 $m = array();
887 if (preg_match('/^\{(.*?)\}(.*)/', $name, $m))
888 {
889 $class .= ' ' . $m[1];
890 $name = $m[2];
891 }
dec748f6 892 if ($name == 'tags:')
646272be
AA
893 {
894 $baseurl = '';
895 if (isset ($page_by_realm[$cell['realm']]))
896 $baseurl = makeHref(array('page'=>$page_by_realm[$cell['realm']], 'tab'=>'default'))."&";
897 printTagTRs ($cell, $baseurl);
898 }
899 else
900 echo "<tr><th width='50%' class='$class'>$name</th><td class=tdleft>$value</td></tr>";
901 }
902 echo "</table>\n";
903 finishPortlet();
904}
905
93a49e5b
AA
906function getOpLink ($params, $title, $img_name = '', $comment = '', $class = '')
907{
908 if (isset ($params))
909 $ret = '<a href="' . makeHrefProcess ($params) . '"';
910 else
911 {
912 $ret = '<a href="#" onclick="return false;"';
913 $class .= ' noclick';
914 }
915 if (! empty ($comment))
916 $ret .= ' title="' . htmlspecialchars ($comment, ENT_QUOTES) . '"';
917 $class = trim ($class);
918 if (! empty ($class))
919 $ret .= ' class="' . htmlspecialchars ($class, ENT_QUOTES) . '"';
93a49e5b
AA
920 $ret .= '>';
921 if (! empty ($img_name))
922 {
923 $ret .= getImageHREF ($img_name, $comment);
924 if (! empty ($title))
925 $ret .= ' ';
926 }
10f5133a
AA
927 if (FALSE !== strpos ($class, 'need-confirmation'))
928 addJS ('js/racktables.js');
93a49e5b
AA
929 $ret .= $title . '</a>';
930 return $ret;
931}
932
4318ced5
AA
933function renderProgressBar ($percentage = 0, $theme = '', $inline = FALSE)
934{
935 echo getProgressBar ($percentage, $theme, $inline);
936}
937
938function getProgressBar ($percentage = 0, $theme = '', $inline = FALSE)
939{
940 $done = ((int) ($percentage * 100));
941 if (! $inline)
942 $src = "?module=progressbar&done=$done" . (empty ($theme) ? '' : "&theme=${theme}");
943 else
944 {
945 $bk_request = $_REQUEST;
946 $_REQUEST['theme'] = $theme;
947 $src = 'data:image/png;base64,' . chunk_split (base64_encode (getOutputOf ('renderProgressBarImage', $done)));
948 $_REQUEST = $bk_request;
949 header ('Content-type: text/html');
950 }
951 $ret = "<img width=100 height=10 border=0 title='${done}%' src='$src'>";
952 return $ret;
953}
954
0dc380e9
AA
955function renderNetVLAN ($cell)
956{
927889c3 957 if (empty ($cell['8021q']))
fec43c43
DO
958 return;
959 $links = array();
960 foreach ($cell['8021q'] as $vi)
961 $links[] = mkA ($vi['vlan_id'], 'vlan', "${vi['domain_id']}-${vi['vlan_id']}");
962 $noun = count ($cell['8021q']) > 1 ? 'VLANs' : 'VLAN';
963 echo "<div class='vlan'><strong><small>${noun}</small> " . implode (', ', $links) . '</strong></div>';
0dc380e9
AA
964}
965
2b8c5aad
AA
966function includeJQueryUI ($do_css = TRUE)
967{
968 addJS ('js/jquery-ui-1.8.21.min.js');
969 if ($do_css)
970 addCSS ('css/jquery-ui-1.8.22.redmond.css');
971}
972
7419401e
AA
973function getRenderedIPPortPair ($ip, $port = NULL)
974{
975 return "<a href=\"" .
976 makeHref (array ('page' => 'ipaddress', 'tab'=>'default', 'ip' => $ip)) .
977 "\">" . $ip . "</a>" .
978 (isset ($port) ? ":" . $port : "");
979}
980
e24dbf15
AB
981// Print common operation form prologue, include bypass argument, if
982// appropriate, and some extra hidden inputs, if requested.
983// Use special encoding for upload forms
984function printOpFormIntro ($opname, $extra = array(), $upload = FALSE)
985{
986 global $pageno, $tabno, $page;
987
90bf1755 988 echo "<form method=post id=${opname} name=${opname} action='?module=redirect&page=${pageno}&tab=${tabno}&op=${opname}'";
e24dbf15 989 if ($upload)
90bf1755
DO
990 echo " enctype='multipart/form-data'";
991 echo ">";
e24dbf15
AB
992 fillBypassValues ($pageno, $extra);
993 foreach ($extra as $inputname => $inputvalue)
90bf1755 994 printf ('<input type=hidden name="%s" value="%s">', htmlspecialchars ($inputname, ENT_QUOTES), htmlspecialchars ($inputvalue, ENT_QUOTES));
e24dbf15
AB
995}
996
90bf1755 997
10f5133a
AA
998// Display hrefs for all of a file's parents. If scissors are requested,
999// prepend cutting button to each of them.
1000function serializeFileLinks ($links, $scissors = FALSE)
1001{
10f5133a
AA
1002 $comma = '';
1003 $ret = '';
1004 foreach ($links as $link_id => $li)
8911e6ec
AA
1005 {
1006 $cell = spotEntity ($li['entity_type'], $li['entity_id']);
1007 $ret .= $comma;
1008 if ($scissors)
1009 $ret .= getOpLink (array('op'=>'unlinkFile', 'link_id'=>$link_id), '', 'cut', 'Unlink file') . ' ';
1010 $ret .= mkCellA ($cell);
1011 $comma = '<br>';
1012 }
10f5133a
AA
1013 return $ret;
1014}
1015
65542bd8
DO
1016// This is a dual-purpose formating function:
1017// 1. Replace empty strings with nbsp.
1018// 2. Cut strings that are too long: append "cut here" indicator and provide a mouse hint.
1019function niftyString ($string, $maxlen = 30, $usetags = TRUE)
1020{
1021 $cutind = '&hellip;'; // length is 1
1022 if (!mb_strlen ($string))
1023 return '&nbsp;';
1024 // a tab counts for a space
1025 $string = preg_replace ("/\t/", ' ', $string);
1026 if (!$maxlen or mb_strlen ($string) <= $maxlen)
1027 return htmlspecialchars ($string, ENT_QUOTES, 'UTF-8');
1028 return
1029 ($usetags ? ("<span title='" . htmlspecialchars ($string, ENT_QUOTES, 'UTF-8') . "'>") : '') .
1030 str_replace (' ', '&nbsp;', htmlspecialchars (mb_substr ($string, 0, $maxlen - 1), ENT_QUOTES, 'UTF-8')) .
1031 $cutind .
1032 ($usetags ? '</span>' : '');
1033}
1034
e7b84015
AB
1035function printTagsPicker ($preselect=NULL)
1036{
1037 printTagsPickerInput ();
1038 printTagsPickerUl ($preselect);
1039 enableTagsPicker ();
1040}
1041
1042function printTagsPickerInput ($input_name="taglist")
1043{
1044 # use data-attribute as identifier for tagit
1045 echo "<input type='text' data-tagit-valuename='" . $input_name . "' data-tagit='yes' placeholder='new tags here...' class='ui-autocomplete-input' autocomplete='off' role='textbox' aria-autocomplete='list' aria-haspopup='true'>";
1046 echo "<span title='show tag tree' class='icon-folder-open tagit_input_" . $input_name . "'></span>";
1047}
1048
1049function printTagsPickerUl ($preselect=NULL, $input_name="taglist")
1050{
1051 global $target_given_tags;
1052 if ($preselect === NULL)
1053 $preselect = $target_given_tags;
623ec8b9
AA
1054 foreach ($preselect as $key => $value) # readable time format
1055 $preselect[$key]['time_parsed'] = formatAge ($value['time']);
e7b84015
AB
1056 usort ($preselect, 'cmpTags');
1057 $preselect_hidden = "";
e7b84015
AB
1058 foreach ($preselect as $value){
1059 $preselect_hidden .= "<input type=hidden name=" . $input_name . "[] value=" . $value['id'] . ">";
1060 }
1061 echo $preselect_hidden; # print preselected tags id that used in case javascript problems
1062 echo "<ul data-tagit='yes' data-tagit-valuename='" . $input_name . "' data-tagit-preselect='" . json_encode($preselect) . "' class='tagit-vertical'></ul>";
1063}
1064
1065function enableTagsPicker ()
1066{
1067 global $taglist;
1068 static $taglist_inserted;
1069 includeJQueryUI ();
1070 addCSS ('css/tagit.css');
1071 addJS ('js/tag-it.js');
1072 addJS ('js/tag-it-local.js');
1073 if (! $taglist_inserted)
1074 {
623ec8b9
AA
1075 $taglist_filtered = array();
1076 foreach ($taglist as $key => $taginfo) # remove unused fields
1077 $taglist_filtered[$key] = array_sub ($taginfo, array("tag", "is_assignable", "trace"));
1078 addJS ('var taglist = ' . json_encode ($taglist_filtered) . ';', TRUE);
e7b84015
AB
1079 $taglist_inserted = TRUE;
1080 }
1081}
e749a34f
AA
1082
1083function makeIPAllocLink ($ip_bin, $alloc, $display_ifname = FALSE)
1084{
1085 $object_name = ! isset ($object_name) || ! strlen ($object_name) ?
1086 formatEntityName (spotEntity ('object', $alloc['object_id'])) :
1087 $alloc['object_name'];
1088 $title = $display_ifname ?
1089 '' :
1090 "{$alloc['name']} @ {$object_name}";
1091 return
1092 '<a href="' . makeHref (array ('page' => 'object', 'tab' => 'default', 'object_id' => $alloc['object_id'], 'hl_ip' => ip_format ($ip_bin))) . '"' .
1093 ' title="' . htmlspecialchars ($title, ENT_QUOTES) . '"' .
1094 ">" . ($display_ifname ? $alloc['name'] . '@' : '') . $object_name . "</a>";
1095}
1096
ca8aa8e9 1097?>