1<?php 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 7// Let's have it here, so extensions can add their own images. 8$image = array(); 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['cables']['path'] = 'pix/patch_cables.png'; 46$image['cables']['width'] = 218; 47$image['cables']['height'] = 200; 48$image['download']['path'] = 'pix/download.png'; 49$image['download']['width'] = 16; 50$image['download']['height'] = 16; 51$image['DOWNLOAD']['path'] = 'pix/download-big.png'; 52$image['DOWNLOAD']['width'] = 32; 53$image['DOWNLOAD']['height'] = 32; 54$image['plug']['path'] = 'pix/tango-network-wired.png'; 55$image['plug']['width'] = 16; 56$image['plug']['height'] = 16; 57$image['cut']['path'] = 'pix/tango-edit-cut-16x16.png'; 58$image['cut']['width'] = 16; 59$image['cut']['height'] = 16; 60$image['Cut']['path'] = 'pix/tango-edit-cut-22x22.png'; 61$image['Cut']['width'] = 22; 62$image['Cut']['height'] = 22; 63$image['Cut gray']['path'] = 'pix/tango-edit-cut-22x22-gray.png'; 64$image['Cut gray']['width'] = 22; 65$image['Cut gray']['height'] = 22; 66$image['CUT']['path'] = 'pix/tango-edit-cut-32x32.png'; 67$image['CUT']['width'] = 32; 68$image['CUT']['height'] = 32; 69$image['CUT gray']['path'] = 'pix/tango-edit-cut-32x32-gray.png'; 70$image['CUT gray']['width'] = 32; 71$image['CUT gray']['height'] = 32; 72$image['add']['path'] = 'pix/tango-list-add.png'; 73$image['add']['width'] = 16; 74$image['add']['height'] = 16; 75$image['ADD']['path'] = 'pix/tango-list-add-big.png'; 76$image['ADD']['width'] = 32; 77$image['ADD']['height'] = 32; 78$image['delete']['path'] = 'pix/tango-list-remove.png'; 79$image['delete']['width'] = 16; 80$image['delete']['height'] = 16; 81$image['DELETE']['path'] = 'pix/tango-list-remove-32x32.png'; 82$image['DELETE']['width'] = 32; 83$image['DELETE']['height'] = 32; 84$image['destroy']['path'] = 'pix/tango-user-trash-16x16.png'; 85$image['destroy']['width'] = 16; 86$image['destroy']['height'] = 16; 87$image['nodestroy']['path'] = 'pix/tango-user-trash-16x16-gray.png'; 88$image['nodestroy']['width'] = 16; 89$image['nodestroy']['height'] = 16; 90$image['NODESTROY']['path'] = 'pix/tango-user-trash-32x32-gray.png'; 91$image['NODESTROY']['width'] = 32; 92$image['NODESTROY']['height'] = 32; 93$image['DESTROY']['path'] = 'pix/tango-user-trash-32x32.png'; 94$image['DESTROY']['width'] = 32; 95$image['DESTROY']['height'] = 32; 96$image['nodelete']['path'] = 'pix/tango-list-remove-shadow.png'; 97$image['nodelete']['width'] = 16; 98$image['nodelete']['height'] = 16; 99$image['inservice']['path'] = 'pix/tango-emblem-system.png'; 100$image['inservice']['width'] = 16; 101$image['inservice']['height'] = 16; 102$image['notinservice']['path'] = 'pix/tango-dialog-error.png'; 103$image['notinservice']['width'] = 16; 104$image['notinservice']['height'] = 16; 105$image['find']['path'] = 'pix/tango-system-search.png'; 106$image['find']['width'] = 16; 107$image['find']['height'] = 16; 108$image['next']['path'] = 'pix/tango-go-next.png'; 109$image['next']['width'] = 32; 110$image['next']['height'] = 32; 111$image['prev']['path'] = 'pix/tango-go-previous.png'; 112$image['prev']['width'] = 32; 113$image['prev']['height'] = 32; 114$image['COMMIT']['path'] = 'pix/tango-go-prev-next-32x32.png'; 115$image['COMMIT']['width'] = 32; 116$image['COMMIT']['height'] = 32; 117$image['COMMIT gray']['path'] = 'pix/tango-go-prev-next-gray-32x32.png'; 118$image['COMMIT gray']['width'] = 32; 119$image['COMMIT gray']['height'] = 32; 120$image['RECALC']['path'] = 'pix/tango-view-refresh-32x32.png'; 121$image['RECALC']['width'] = 32; 122$image['RECALC']['height'] = 32; 123$image['recalc']['path'] = 'pix/tango-view-refresh-16x16.png'; 124$image['recalc']['width'] = 16; 125$image['recalc']['height'] = 16; 126$image['clear']['path'] = 'pix/tango-edit-clear.png'; 127$image['clear']['width'] = 16; 128$image['clear']['height'] = 16; 129$image['CLEAR']['path'] = 'pix/tango-edit-clear-big.png'; 130$image['CLEAR']['width'] = 32; 131$image['CLEAR']['height'] = 32; 132$image['CLEAR gray']['path'] = 'pix/tango-edit-clear-gray-32x32.png'; 133$image['CLEAR gray']['width'] = 32; 134$image['CLEAR gray']['height'] = 32; 135$image['save']['path'] = 'pix/tango-document-save-16x16.png'; 136$image['save']['width'] = 16; 137$image['save']['height'] = 16; 138$image['SAVE']['path'] = 'pix/tango-document-save-32x32.png'; 139$image['SAVE']['width'] = 32; 140$image['SAVE']['height'] = 32; 141$image['NOSAVE']['path'] = 'pix/tango-document-save-32x32-gray.png'; 142$image['NOSAVE']['width'] = 32; 143$image['NOSAVE']['height'] = 32; 144$image['create']['path'] = 'pix/tango-document-new.png'; 145$image['create']['width'] = 16; 146$image['create']['height'] = 16; 147$image['CREATE']['path'] = 'pix/tango-document-new-big.png'; 148$image['CREATE']['width'] = 32; 149$image['CREATE']['height'] = 32; 150$image['DENIED']['path'] = 'pix/tango-dialog-error-big.png'; 151$image['DENIED']['width'] = 32; 152$image['DENIED']['height'] = 32; 153$image['node-collapsed']['path'] = 'pix/node-collapsed.png'; 154$image['node-collapsed']['width'] = 16; 155$image['node-collapsed']['height'] = 16; 156$image['node-expanded']['path'] = 'pix/node-expanded.png'; 157$image['node-expanded']['width'] = 16; 158$image['node-expanded']['height'] = 16; 159$image['node-expanded-static']['path'] = 'pix/node-expanded-static.png'; 160$image['node-expanded-static']['width'] = 16; 161$image['node-expanded-static']['height'] = 16; 162$image['dragons']['path'] = 'pix/mitsudragon.png'; 163$image['dragons']['width'] = 195; 164$image['dragons']['height'] = 33; 165$image['LB']['path'] = 'pix/loadbalancer.png'; 166$image['LB']['width'] = 32; 167$image['LB']['height'] = 32; 168$image['RS pool']['path'] = 'pix/serverpool.png'; 169$image['RS pool']['width'] = 48; 170$image['RS pool']['height'] = 16; 171$image['VS']['path'] = 'pix/servicesign.png'; 172$image['VS']['width'] = 39; 173$image['VS']['height'] = 62; 174$image['router']['path'] = 'pix/router.png'; 175$image['router']['width'] = 32; 176$image['router']['height'] = 32; 177$image['object']['path'] = 'pix/bracket-16x16.png'; 178$image['object']['width'] = 16; 179$image['object']['height'] = 16; 180$image['OBJECT']['path'] = 'pix/bracket-32x32.png'; 181$image['OBJECT']['width'] = 32; 182$image['OBJECT']['height'] = 32; 183$image['LOCATION']['path'] = 'pix/tango-internet-32x32.png'; 184$image['LOCATION']['width'] = 32; 185$image['LOCATION']['height'] = 32; 186$image['attach']['path'] = 'pix/tango-mail-attachment-16x16.png'; 187$image['attach']['width'] = 16; 188$image['attach']['height'] = 16; 189$image['Attach']['path'] = 'pix/tango-mail-attachment-22x22.png'; 190$image['Attach']['width'] = 22; 191$image['Attach']['height'] = 22; 192$image['ATTACH']['path'] = 'pix/tango-mail-attachment-32x32.png'; 193$image['ATTACH']['width'] = 32; 194$image['ATTACH']['height'] = 32; 195$image['favorite']['path'] = 'pix/tango-emblem-favorite.png'; 196$image['favorite']['width'] = 16; 197$image['favorite']['height'] = 16; 198$image['computer']['path'] = 'pix/tango-computer.png'; 199$image['computer']['width'] = 16; 200$image['computer']['height'] = 16; 201$image['empty file']['path'] = 'pix/crystal-file-empty-32x32.png'; 202$image['empty file']['width'] = 32; 203$image['empty file']['height'] = 32; 204$image['text file']['path'] = 'pix/crystal-file-text-32x32.png'; 205$image['text file']['width'] = 32; 206$image['text file']['height'] = 32; 207$image['image file']['path'] = 'pix/crystal-file-image-32x32.png'; 208$image['image file']['width'] = 32; 209$image['image file']['height'] = 32; 210$image['text']['path'] = 'pix/tango-text-x-generic-16x16.png'; 211$image['text']['width'] = 16; 212$image['text']['height'] = 16; 213$image['NET']['path'] = 'pix/crystal-network_local-32x32.png'; 214$image['NET']['width'] = 32; 215$image['NET']['height'] = 32; 216$image['net']['path'] = 'pix/crystal-network_local-16x16.png'; 217$image['net']['width'] = 16; 218$image['net']['height'] = 16; 219$image['USER']['path'] = 'pix/crystal-edit-user-32x32.png'; 220$image['USER']['width'] = 32; 221$image['USER']['height'] = 32; 222$image['setfilter']['path'] = 'pix/pgadmin3-viewfiltereddata.png'; 223$image['setfilter']['width'] = 32; 224$image['setfilter']['height'] = 32; 225$image['setfilter gray']['path'] = 'pix/pgadmin3-viewfiltereddata-grayscale.png'; 226$image['setfilter gray']['width'] = 32; 227$image['setfilter gray']['height'] = 32; 228$image['resetfilter']['path'] = 'pix/pgadmin3-viewdata.png'; 229$image['resetfilter']['width'] = 32; 230$image['resetfilter']['height'] = 32; 231$image['resetfilter gray']['path'] = 'pix/pgadmin3-viewdata-grayscale.png'; 232$image['resetfilter gray']['width'] = 32; 233$image['resetfilter gray']['height'] = 32; 234$image['knight']['path'] = 'pix/smiley_knight.png'; 235$image['knight']['width'] = 72; 236$image['knight']['height'] = 33; 237$image['Zoom']['path'] = 'pix/tango-system-search-22x22.png'; 238$image['Zoom']['width'] = 22; 239$image['Zoom']['height'] = 22; 240$image['Zooming']['path'] = 'pix/tango-view-fullscreen-22x22.png'; 241$image['Zooming']['width'] = 22; 242$image['Zooming']['height'] = 22; 243$image['UNLOCK']['path'] = 'pix/crystal-actions-unlock-32x32.png'; 244$image['UNLOCK']['width'] = 32; 245$image['UNLOCK']['height'] = 32; 246$image['CLOCK']['path'] = 'pix/tango-appointment-32x32.png'; 247$image['CLOCK']['width'] = 32; 248$image['CLOCK']['height'] = 32; 249$image['DQUEUE done']['path'] = 'pix/crystal-ok-32x32.png'; 250$image['DQUEUE done']['width'] = 32; 251$image['DQUEUE done']['height'] = 32; 252$image['DQUEUE sync_aging']['path'] = 'pix/tango-appointment-32x32.png'; 253$image['DQUEUE sync_aging']['width'] = 32; 254$image['DQUEUE sync_aging']['height'] = 32; 255$image['DQUEUE resync_aging']['path'] = 'pix/tango-appointment-32x32.png'; 256$image['DQUEUE resync_aging']['width'] = 32; 257$image['DQUEUE resync_aging']['height'] = 32; 258$image['DQUEUE sync_ready']['path'] = 'pix/tango-emblem-system-32x32.png'; 259$image['DQUEUE sync_ready']['width'] = 32; 260$image['DQUEUE sync_ready']['height'] = 32; 261$image['DQUEUE resync_ready']['path'] = 'pix/tango-emblem-important-32x32.png'; 262$image['DQUEUE resync_ready']['width'] = 32; 263$image['DQUEUE resync_ready']['height'] = 32; 264$image['DQUEUE failed']['path'] = 'pix/tango-emblem-unreadable-32x32.png'; 265$image['DQUEUE failed']['width'] = 32; 266$image['DQUEUE failed']['height'] = 32; 267$image['DQUEUE disabled']['path'] = 'pix/tango-emblem-readonly-32x32.png'; 268$image['DQUEUE disabled']['width'] = 32; 269$image['DQUEUE disabled']['height'] = 32; 270$image['copy']['path'] = 'pix/tango-edit-copy-16x16.png'; 271$image['copy']['width'] = 16; 272$image['copy']['height'] = 16; 273$image['COPY']['path'] = 'pix/tango-edit-copy-32x32.png'; 274$image['COPY']['width'] = 32; 275$image['COPY']['height'] = 32; 276$image['html']['path'] = 'pix/tango-text-html.png'; 277$image['html']['width'] = 16; 278$image['html']['height'] = 16; 279$image['pencil']['path'] = 'pix/pencil-icon.png'; 280$image['pencil']['width'] = 12; 281$image['pencil']['height'] = 12; 282$image['link up']['path'] = 'pix/link-up.png'; 283$image['link up']['width'] = 16; 284$image['link up']['height'] = 16; 285$image['link down']['path'] = 'pix/link-down.png'; 286$image['link down']['width'] = 16; 287$image['link down']['height'] = 16; 288$image['link disabled']['path'] = 'pix/link-disabled.png'; 289$image['link disabled']['width'] = 16; 290$image['link disabled']['height'] = 16; 291$image['16x16t']['path'] = 'pix/1x1t.gif'; 292$image['16x16t']['width'] = 16; 293$image['16x16t']['height'] = 16; 294$image['disable']['path'] = 'pix/link-disabled.png'; 295$image['disable']['width'] = 16; 296$image['disable']['height'] = 16; 297$image['enable']['path'] = 'pix/link-up.png'; 298$image['enable']['width'] = 16; 299$image['enable']['height'] = 16; 300$image['upgrade']['path'] = 'pix/tango-go-up.png'; 301$image['upgrade']['width'] = 16; 302$image['upgrade']['height'] = 16; 303 304// Use the same workflow as for the array above: 305// 1. This file is required early, it assigns the default elements. 306// 2. Then plugin(s) load and can modify the array if necessary. 307// 3. Then interface-config.php, when conditionally required, can read it. 308$tag_palette = array 309( 310 'FFFFFF', 311 'C0C0C0', 312 '808080', 313 '000000', 314 'FF0000', 315 '800000', 316 'FF8000', 317 'FFFF00', 318 '808000', 319 '00FF00', 320 '008000', 321 '00FFFF', 322 '008080', 323 '0000FF', 324 '000080', 325 'FF00FF', 326 '800080', 327); 328 329$page_by_realm = array(); 330$page_by_realm['object'] = 'depot'; 331$page_by_realm['rack'] = 'rackspace'; 332$page_by_realm['ipv4net'] = 'ipv4space'; 333$page_by_realm['ipv6net'] = 'ipv6space'; 334$page_by_realm['ipv4vs'] = 'ipv4slb'; 335$page_by_realm['ipv4rspool'] = 'ipv4slb'; 336$page_by_realm['file'] = 'files'; 337$page_by_realm['user'] = 'userlist'; 338 339function getSelectOptions ($options, $selected_id = NULL) 340{ 341 $ret = ''; 342 foreach ($options as $key => $value) 343 { 344 $selected = is_array ($selected_id) ? in_array ($key, $selected_id) : $key == $selected_id; 345 $ret .= "<option value='${key}'" . ($selected ? ' selected' : '') . '>'; 346 $ret .= stringForOption ($value) . '</option>'; 347 } 348 return $ret; 349} 350 351function printSelect ($optionList, $select_attrs = array(), $selected_id = NULL) 352{ 353 echo getSelect ($optionList, $select_attrs, $selected_id); 354} 355 356// $selected_id can be an array if you want to use multiselect 357// in order to use multiselect $select_attrs should contain smth like this: 358// [ 'name' => 'some_name[]', 'multiple' => 'multiple', 'size' => 5 ] 359// Input array keys are OPTION VALUEs and input array values are OPTION text. 360function getSelect ($optionList, $select_attrs = array(), $selected_id = NULL, $treat_single_special = TRUE) 361{ 362 $ret = ''; 363 if (!array_key_exists ('name', $select_attrs)) 364 throw new InvalidArgException ('select_attrs[\'name\']', '(not set)', 'must be set'); 365 // handle two corner cases in a specific way 366 if (count ($optionList) == 0) 367 return '(none)'; 368 if (count ($optionList) == 1 && $treat_single_special) 369 { 370 foreach ($optionList as $key => $value) 371 break; 372 return "<input type=hidden name=${select_attrs['name']} id=${select_attrs['name']} value=${key}>" . 373 stringForLabel ($value, 64); 374 } 375 if (!array_key_exists ('id', $select_attrs)) 376 $select_attrs['id'] = $select_attrs['name']; 377 $ret .= '<select'; 378 foreach ($select_attrs as $attr_name => $attr_value) 379 $ret .= " ${attr_name}=${attr_value}"; 380 $ret .= '>' . getSelectOptions ($optionList, $selected_id) . '</select>'; 381 return $ret; 382} 383 384function printNiftySelect ($groupList, $select_attrs = array(), $selected_id = NULL) 385{ 386 echo getNiftySelect ($groupList, $select_attrs, $selected_id); 387} 388 389// Input is a cooked list of OPTGROUPs, each with own sub-list of OPTIONs in the same 390// format as printSelect() expects. 391// If tree is true, hierarchical drop-boxes are used, otherwise optgroups are used. 392function getNiftySelect ($groupList, $select_attrs, $selected_id = NULL) 393{ 394 // special treatment for ungrouped data 395 if (count ($groupList) == 1 && isset ($groupList['other'])) 396 return getSelect ($groupList['other'], $select_attrs, $selected_id); 397 if (!array_key_exists ('name', $select_attrs)) 398 throw new InvalidArgException ('select_attrs[\'name\']', '(not set)', 'must be set'); 399 if (!array_key_exists ('id', $select_attrs)) 400 $select_attrs['id'] = $select_attrs['name']; 401 402 $ret = '<select'; 403 foreach ($select_attrs as $attr_name => $attr_value) 404 $ret .= " ${attr_name}=${attr_value}"; 405 $ret .= ">\n"; 406 foreach ($groupList as $groupname => $groupdata) 407 { 408 $ret .= "<optgroup label='${groupname}'>\n"; 409 foreach ($groupdata as $dict_key => $dict_value) 410 { 411 if (is_array ($selected_id)) 412 $is_selected = in_array ($dict_key, $selected_id); 413 else 414 $is_selected = $dict_key == $selected_id; 415 $ret .= "<option value='${dict_key}'" . ($is_selected ? ' selected' : '') . ">${dict_value}</option>\n"; 416 } 417 $ret .= "</optgroup>\n"; 418 } 419 $ret .= "</select>\n"; 420 return $ret; 421} 422 423function getOptionTree ($tree_name, $tree_options, $tree_config = array()) 424{ 425 $default_config = array 426 ( 427 'choose' => 'select...', 428 'empty_value' => '', 429 'indexed' => TRUE, 430 ); 431 addJSInternal ('js/jquery.optionTree.js'); 432 addJSText (" 433$(function() { 434 var option_tree = " . json_encode ($tree_options) . "; 435 var options = " . json_encode ($tree_config + $default_config) . "; 436 $('input[name=${tree_name}]').optionTree(option_tree, options); 437});" 438 ); // addJSText() 439 440 return "<input type=hidden name=${tree_name}>"; 441} 442 443function printImageHREF ($tag, $title = '', $do_input = FALSE) 444{ 445 echo getImageHREF ($tag, $title, $do_input); 446} 447 448// this would be better called mkIMG(), make "IMG" HTML element 449function getImageHREF ($tag, $title = '', $do_input = FALSE) 450{ 451 global $image; 452 $attrs = array 453 ( 454 'src' => array_key_exists ($tag, $image) ? 455 '?module=chrome&uri=' . $image[$tag]['path'] : 456 '?module=image&img=error', 457 'border' => 0, 458 ); 459 if ($title != '') 460 $attrs['title'] = $title; 461 if ($do_input) 462 { 463 $element = 'input'; 464 $attrs['type'] = 'image'; 465 $attrs['name'] = 'submit'; 466 $attrs['class'] = 'icon'; 467 // Width and height for INPUT only appear in HTML 5. 468 } 469 else 470 { 471 $element = 'img'; 472 if (array_key_exists ($tag, $image)) 473 { 474 $attrs['width'] = $image[$tag]['width']; 475 $attrs['height'] = $image[$tag]['height']; 476 } 477 } 478 return makeHtmlTag ($element, $attrs); 479} 480 481// This function is DEPRECATED and will be removed in 0.22.0. See the set 482// of stringForXXXXX() functions for a proper replacement. 483function escapeString ($value, $do_db_escape = FALSE) 484{ 485 $ret = htmlspecialchars ($value, ENT_QUOTES, 'UTF-8'); 486 if ($do_db_escape) 487 { 488 global $dbxlink; 489 $ret = substr ($dbxlink->quote ($ret), 1, -1); 490 } 491 return $ret; 492} 493 494function transformRequestData() 495{ 496 global $sic; 497 $seen_keys = array(); 498 499 // Escape all globals before using and keep a copy of the original values. 500 $sic = array(); 501 // walk through merged GET and POST instead of REQUEST array because it 502 // can contain cookies with data that could not be decoded from UTF-8 503 foreach (($_POST + $_GET) as $key => $value) 504 { 505 if (is_array ($value)) 506 $_REQUEST[$key] = $value; 507 else 508 { 509 $value = dos2unix ($value); 510 $_REQUEST[$key] = htmlspecialchars ($value, ENT_QUOTES, 'UTF-8'); 511 } 512 $sic[$key] = $value; 513 $seen_keys[$key] = 1; 514 } 515 516 // delete cookie information from the $_REQUEST array 517 foreach (array_keys ($_REQUEST) as $key) 518 if (! isset ($seen_keys[$key])) 519 unset ($_REQUEST[$key]); 520 521 if (isset ($_SERVER['PHP_AUTH_USER'])) 522 $_SERVER['PHP_AUTH_USER'] = htmlspecialchars ($_SERVER['PHP_AUTH_USER'], ENT_QUOTES, 'UTF-8'); 523 if (isset ($_SERVER['REMOTE_USER'])) 524 $_SERVER['REMOTE_USER'] = htmlspecialchars ($_SERVER['REMOTE_USER'], ENT_QUOTES, 'UTF-8'); 525} 526 527// Return whether value passed is likely to be a URI. 528function isUri ($uri) 529{ 530 return preg_match (RE_STATIC_URI, $uri); 531} 532 533// Return whether value passed is likely to be a URL. 534function isUrl ($url) 535{ 536 return preg_match ('~^[\w]+:\/\/[\w \-\.\_\/]+$~', $url); 537} 538 539// JS scripts should be included through the addJS* functions. They will automatically appear in the <head> of your page. 540// the addJS function has been replaced by addJSInternal, addJSExternal and addJSText() functions. This function will be 541// removed in 2.22.0 542function addJS ($data, $inline = FALSE, $group = 'default') 543{ 544 if ($inline) 545 addJSText ($data, $group); 546 else if (isURL ($data)) 547 addJSExternal ($data, $group); 548 else 549 addJSInternal ($data, $group); 550} 551 552// addJSExternal links to external scripts via URL and must be prefixed with 553// either http:// or https:// 554function addJSExternal ($url, $group = 'default') 555{ 556 if (! isUrl ($url)) 557 throw new InvalidArgException ('url', $url, 'Value passed is not a URL'); 558 559 addPageHeader ("<script type='text/javascript' src='${url}'></script>\n", $group); 560} 561 562// addJSInternal adds links that go through the Chrome module of index.php 563function addJSInternal ($uri, $group = 'default') 564{ 565 global $html_headers; 566 567 // Add jquery.js and racktables.js the first time a Javascript file is added. 568 // FIXME: Would it be better to do this initialization elsewhere? 569 if (! array_key_exists ('a_core', $html_headers)) 570 { 571 // Prevent infinite recursion. 572 $html_headers['a_core'] = array(); 573 574 addJSInternal('js/jquery-1.4.4.min.js', 'a_core'); 575 addJSInternal('js/racktables.js', 'a_core'); 576 } 577 578 if (! isUri ($uri)) 579 throw new InvalidArgException ('uri', $uri, 'Value passed is not a valid URI'); 580 581 addPageHeader ("<script type='text/javascript' src='?module=chrome&uri=${uri}'></script>\n", $group); 582} 583 584// This function adds script blocks that automatically appear in the <head> of your page. 585// Scripts are included in the order of adding within the same group, and groups are sorted alphabetically. 586function addJSText ($text, $group = 'default') 587{ 588 if (isUrl ($text)) 589 throw new InvalidArgException ('text', $text, 'Value passed is most likely a URL and should use addJSExternal()'); 590 591 if (isUri ($text)) 592 throw new InvalidArgException ('text', $text, 'Value passed is most likely a URI and should use addJSInternal()'); 593 594 addPageHeader ('<script type="text/javascript">' . "\n" . trim ($text, "\r\n") . "\n</script>\n", $group); 595} 596 597// CSS styles should be included through this function. 598// They automatically appear in the <head> of your page. 599// $data is a CSS filename, or CSS code w/o tags around, if $inline = TRUE 600// Styles are included in the order of adding. 601function addCSS ($data, $inline = FALSE, $group = 'default') 602{ 603 if ($inline) 604 addCSSText ($data, $group); 605 else if (isURL ($data)) 606 addCSSExternal ($data, $group); 607 else 608 addCSSInternal ($data, $group); 609} 610 611// CSS styles should be included through this function. 612// They automatically appear in the <head> of your page. 613// $data is a CSS filename, or CSS code w/o tags around, if $inline = TRUE 614// Styles are included in the order of adding. 615function addCSSExternal ($url, $group = 'default') 616{ 617 if (! isUrl ($url)) 618 throw new InvalidArgException ('url', $url, 'Value passed is not a valid URL'); 619 620 addPageHeader ("<link rel=stylesheet type='text/css' href='$url' />\n", $group); 621} 622 623// CSS styles should be included through this function. 624// They automatically appear in the <head> of your page. 625// $data is a CSS filename, or CSS code w/o tags around, if $inline = TRUE 626// Styles are included in the order of adding. 627function addCSSInternal ($uri, $group = 'default') 628{ 629 if (! isUri ($uri)) 630 throw new InvalidArgException ('uri', $uri, 'Value passed is not a valid URI'); 631 632 addPageHeader ("<link rel=stylesheet type='text/css' href='?module=chrome&uri=$uri' />\n", $group); 633} 634 635// CSS styles should be included through this function. 636// They automatically appear in the <head> of your page. 637// $data is a CSS filename, or CSS code w/o tags around, if $inline = TRUE 638// Styles are included in the order of adding. 639function addCSSText ($text, $group = 'default') 640{ 641 if (isUrl ($text)) 642 throw new InvalidArgException ('text', $text, 'Value passed is most likely a URL and should use addCSSExternal()'); 643 644 if (isUri ($text)) 645 throw new InvalidArgException ('text', $text, 'Value passed is most likely a URI and should use addCSSInternal()'); 646 647 addPageHeader ('<style type="text/css">' . "\n" . trim ($text, "\r\n") . "\n</style>\n", $group); 648} 649 650function getRenderedIPNetCapacity ($range) 651{ 652 switch (strlen ($range['ip_bin'])) 653 { 654 case 4: return getRenderedIPv4NetCapacity ($range); 655 case 16: return getRenderedIPv6NetCapacity ($range); 656 default: throw new InvalidArgException ('range["ip_bin"]', $range['ip_bin'], "Invalid binary IP"); 657 } 658} 659 660function getRenderedIPv4NetCapacity ($range) 661{ 662 $class = 'net-usage'; 663 if (isset ($range['own_addrlist'])) 664 { 665 // full mode 666 // $a is "aquamarine zone", $b is "gray zone" 667 $total = ip4_range_size ($range); 668 669 // compute $a_total: own range size, without subranges 670 if (! isset ($range['kidc']) || $range['kidc'] == 0) 671 $a_total = $total; 672 else 673 { 674 $a_total = 0; 675 foreach ($range['spare_ranges'] as $mask => $spare_list) 676 $a_total = bcadd ($a_total, bcmul (count ($spare_list), ip4_mask_size ($mask)), 0); 677 } 678 $a_used = markupIPAddrList ($range['own_addrlist']); 679 $b_total = bcsub ($total, $a_total, 0); 680 $b_used = markupIPAddrList ($range['addrlist']) - $a_used; 681 682 // generate link to progress bar image 683 $width = 100; 684 if ($total != 0) 685 { 686 $px_a = round (bcdiv ($a_total, $total, 4) * $width); 687 $px1 = round (bcdiv ($a_used, $total, 4) * $width); 688 $px2 = $px_a - $px1; 689 $px3 = round (bcdiv ($b_used, $total, 4) * $width); 690 if ($px3 + $px1 + $px2 > $width) 691 $px3 = $width - $px1 - $px2; 692 } 693 else 694 $px1 = $px2 = $px3 = 0; 695 696 $title_items = array(); 697 $title2_items = array(); 698 if ($a_total != 0) 699 { 700 $title_items[] = "$a_used / $a_total"; 701 $title2_items[] = sprintf ("%d%% used", bcdiv ($a_used, $a_total, 4) * 100); 702 } 703 if ($b_total != 0) 704 { 705 $title_items[] = ($b_used ? "$b_used / " : "") . $b_total; 706 $title2_items[] = sprintf ("%d%% sub-allocated", bcdiv ($b_total, $total, 4) * 100); 707 } 708 $title = implode (', ', $title_items); 709 $title2 = implode (', ', $title2_items); 710 $text = "<img width='$width' height=10 border=0 title='$title2' src='?module=progressbar4&px1=$px1&px2=$px2&px3=$px3'>" . 711 " <small class='title'>$title</small>"; 712 } 713 else 714 { 715 // fast mode 716 $class .= ' pending'; 717 addJSInternal ('js/net-usage.js'); 718 719 $free_text = ''; 720 if (isset ($range['kidc']) && $range['kidc'] > 0) 721 { 722 $free_masks = array_keys ($range['spare_ranges']); 723 sort ($free_masks, SORT_NUMERIC); 724 if ($mask = array_shift ($free_masks)) 725 { 726 $cnt = count ($range['spare_ranges'][$mask]); 727 $free_text = ', ' . ($cnt > 1 ? "<small>${cnt}×</small>" : "") . "/$mask free"; 728 } 729 } 730 $text = ip4_range_size ($range) . $free_text; 731 } 732 733 $div_id = $range['ip'] . '/' . $range['mask']; 734 735 return "<div class=\"$class\" id=\"$div_id\">" . $text . "</div>"; 736} 737 738function getRenderedIPv6NetCapacity ($range) 739{ 740 $div_id = $range['ip'] . '/' . $range['mask']; 741 $class = 'net-usage'; 742 if (isset ($range['addrlist'])) 743 $used = markupIPAddrList ($range['addrlist']); 744 else 745 { 746 $used = NULL; 747 $class .= ' pending'; 748 addJSInternal ('js/net-usage.js'); 749 } 750 751 static $prefixes = array 752 ( 753 0 => '', 754 3 => 'k', 755 6 => 'M', 756 9 => 'G', 757 12 => 'T', 758 15 => 'P', 759 18 => 'E', 760 21 => 'Z', 761 24 => 'Y', 762 ); 763 764 if ($range['mask'] <= 64) 765 { 766 $what = 'net'; 767 $preposition = 'in'; 768 $range['mask'] += 64; 769 } 770 else 771 { 772 $what = 'IP'; 773 $preposition = 'of'; 774 } 775 $what .= (0 == $range['mask'] % 64 ? '' : 's'); 776 $addrc = isset ($used) ? "$used $preposition " : ''; 777 778 $dec_order = intval ((128 - $range['mask']) / 10) * 3; 779 $mult = isset ($prefixes[$dec_order]) ? $prefixes[$dec_order] : '??'; 780 781 $cnt = 1 << ((128 - $range['mask']) % 10); 782 if ($cnt == 1 && $mult == '') 783 $cnt = '1'; 784 785 return "<div class=\"$class\" id=\"$div_id\">" . "{$addrc}${cnt}${mult} ${what}" . "</div>"; 786} 787 788// Buffer the header only once. Disregard subsequent calls even if they 789// are made for a different group. 790function addPageHeader ($header, $group) 791{ 792 global $html_headers; 793 794 foreach ($html_headers as $group_contents) 795 if (in_array ($header, $group_contents)) 796 return; 797 $html_headers[$group][] = $header; 798} 799 800// print part of HTML HEAD block 801function printPageHeaders () 802{ 803 global $pageheaders; 804 ksort ($pageheaders); 805 foreach ($pageheaders as $s) 806 echo $s . "\n"; 807 // add tabindex to all input forms 808 addJSInternal ('js/tabindex_auto.js'); 809 810 // add JS/CSS headers 811 global $html_headers; 812 ksort($html_headers); 813 814 foreach ($html_headers as $group_name => $list) 815 foreach ($list as $index => $item) 816 echo "<!-- $group_name:$index -->\n" . $item; 817} 818 819function cmpTags ($a, $b) 820{ 821 global $taglist; 822 if (isset ($a['id']) && isset ($b['id'])) 823 { 824 $a_root = array_first ($taglist[$a['id']]['trace']); 825 $b_root = array_first ($taglist[$b['id']]['trace']); 826 if ($a_root < $b_root) 827 return -1; 828 elseif ($a_root > $b_root) 829 return 1; 830 } 831 elseif (isset ($a['id'])) 832 return -1; 833 elseif (isset ($b['id'])) 834 return 1; 835 836 return strcmp ($a['tag'], $b['tag']); 837} 838 839function getTagClassName ($tagid) 840{ 841 global $taglist; 842 843 $class = ''; 844 foreach ($taglist[$tagid]['trace'] as $parent) 845 $class .= 'tag-' . $parent . ' '; 846 $class .= 'tag-' . $tagid . ' etag-' . $tagid; 847 848 $class .= getTagClass ($taglist[$tagid]); 849 850 return $class; 851} 852 853function serializeTags ($chain, $baseurl = '') 854{ 855 global $taglist; 856 $tmp = array(); 857 usort ($chain, 'cmpTags'); 858 foreach ($chain as $taginfo) 859 { 860 $title = ''; 861 if (isset ($taginfo['user']) && isset ($taginfo['time'])) 862 $title = htmlspecialchars ($taginfo['user'] . ', ' . formatAge ($taginfo['time']), ENT_QUOTES); 863 if (isset($taginfo['parent_id'])) 864 { 865 $parent_info = array(); 866 foreach ($taglist[$taginfo['id']]['trace'] as $tag_id) 867 $parent_info[] = $taglist[$tag_id]['tag']; 868 $parent_info[] = $taginfo['tag']; 869 if ($title != '') 870 $title .= "\n"; 871 $title .= implode (" → ", $parent_info); 872 } 873 $has_descr = array_key_exists ('description', $taginfo) && $taginfo['description'] !== NULL; 874 if ($has_descr) 875 $title .= ($title == '' ? '' : "\n\n") . stringForOption ($taginfo['description'], 0); 876 if ($title != '') 877 $title = "title='$title'"; 878 879 if (! array_key_exists ('id', $taginfo)) 880 $class = ''; 881 else 882 { 883 $class = $has_descr ? 'tag-descr ' : ''; 884 $class .= getTagClassName ($taginfo['id']); 885 $class = "class='${class}'"; 886 } 887 888 $href = ''; 889 if ($baseurl == '') 890 $tag = 'span'; 891 else 892 { 893 $tag = 'a'; 894 $href = "href='${baseurl}cft[]=${taginfo['id']}'"; 895 } 896 $tmp[] = "<$tag $href $title $class>" . $taginfo['tag'] . "</$tag>"; 897 } 898 return implode (', ', $tmp); 899} 900 901function startPortlet ($title = '') 902{ 903 echo "<div class=portlet><h2>${title}</h2>"; 904} 905 906function finishPortlet () 907{ 908 echo "</div>\n"; 909} 910 911function getPageName ($page_code) 912{ 913 global $page; 914 $title = isset ($page[$page_code]['title']) ? $page[$page_code]['title'] : callHook ('dynamic_title_decoder', $page_code); 915 if (is_array ($title)) 916 $title = $title['name']; 917 return $title; 918} 919 920function printTagTRs ($cell, $baseurl = '') 921{ 922 if (getConfigVar ('SHOW_EXPLICIT_TAGS') == 'yes' && count ($cell['etags'])) 923 { 924 echo "<tr><th width='50%' class=tagchain>Explicit tags:</th><td class=tagchain>"; 925 echo serializeTags ($cell['etags'], $baseurl) . "</td></tr>\n"; 926 } 927 if (getConfigVar ('SHOW_IMPLICIT_TAGS') == 'yes' && count ($cell['itags'])) 928 { 929 echo "<tr><th width='50%' class=tagchain>Implicit tags:</th><td class=tagchain>"; 930 echo serializeTags ($cell['itags'], $baseurl) . "</td></tr>\n"; 931 } 932 if (getConfigVar ('SHOW_AUTOMATIC_TAGS') == 'yes' && count ($cell['atags'])) 933 { 934 echo "<tr><th width='50%' class=tagchain>Automatic tags:</th><td class=tagchain>"; 935 echo serializeTags ($cell['atags']) . "</td></tr>\n"; 936 } 937} 938 939// stub function to override it by chain-connected hooks 940function modifyEntitySummary ($cell, $summary) 941{ 942 return $summary; 943} 944 945// renders 'summary' portlet, which persist on default tab of every realm page. 946// $values is a tricky array. 947// 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. 948// 'tags' key has a special meaning: instead of value, the result of printTagTRs call is appended to output 949// if the value is a single-element array, its value rendered as-is instead of <tr> tag and all its contents. 950// 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 951function renderEntitySummary ($cell, $title, $values = array()) 952{ 953 global $page_by_realm; 954 // allow plugins to override summary table 955 $values = callHook ('modifyEntitySummary', $cell, $values); 956 957 startPortlet ($title); 958 echo "<table border=0 cellspacing=0 cellpadding=3 width='100%'>\n"; 959 foreach ($values as $name => $value) 960 { 961 if (is_array ($value) && count ($value) == 1) 962 { 963 $value = array_shift ($value); 964 echo $value; 965 continue; 966 } 967 if (is_array ($value)) 968 { 969 $name = array_shift ($value); 970 $value = array_shift ($value); 971 } 972 elseif (! is_array ($value)) 973 $name .= ':'; 974 $class = 'tdright'; 975 $m = array(); 976 if (preg_match('/^\{(.*?)\}(.*)/', $name, $m)) 977 { 978 $class .= ' ' . $m[1]; 979 $name = $m[2]; 980 } 981 if ($name == 'tags:') 982 { 983 $baseurl = ''; 984 if (isset ($page_by_realm[$cell['realm']])) 985 $baseurl = makeHref(array('page'=>$page_by_realm[$cell['realm']], 'tab'=>'default'))."&"; 986 printTagTRs ($cell, $baseurl); 987 } 988 else 989 echo "<tr><th width='50%' class='$class'>$name</th><td class=tdleft>$value</td></tr>"; 990 } 991 echo "</table>\n"; 992 finishPortlet(); 993} 994 995function getOpLink ($params, $title, $img_name = '', $comment = '', $class = '') 996{ 997 if (isset ($params)) 998 { 999 $ret = '<a href="' . makeHrefProcess ($params) . '"'; 1000 $class .= ' input'; 1001 } 1002 else 1003 { 1004 $ret = '<a href="#" onclick="return false;"'; 1005 $class .= ' noclick'; 1006 } 1007 if ($comment != '') 1008 $ret .= ' title="' . htmlspecialchars ($comment, ENT_QUOTES) . '"'; 1009 $class = trim ($class); 1010 if ($class != '') 1011 $ret .= ' class="' . htmlspecialchars ($class, ENT_QUOTES) . '"'; 1012 $ret .= '>'; 1013 if ($img_name != '') 1014 { 1015 $ret .= getImageHREF ($img_name, $comment); 1016 if ($title != '') 1017 $ret .= ' '; 1018 } 1019 if (FALSE !== strpos ($class, 'need-confirmation')) 1020 addJSInternal ('js/racktables.js'); 1021 $ret .= $title . '</a>'; 1022 return $ret; 1023} 1024 1025function getPopupLink ($helper, $params, $window_name = '', $img_name = '', $title = '', $comment = '', $class = '') 1026{ 1027 $ret = ''; 1028 $popup_args = 'height=700, width=700, location=no, menubar=no, resizable=yes, scrollbars=yes, status=no, titlebar=no, toolbar=no'; 1029 $ret .= '<a href="#"'; 1030 $class = trim ($class); 1031 if ($class != '') 1032 $ret .= ' class="' . htmlspecialchars ($class, ENT_QUOTES) . '"'; 1033 if ($comment != '') 1034 $ret .= 'title="' . htmlspecialchars ($comment, ENT_QUOTES) . '"'; 1035 $href = makeHref (array ('module' => 'popup', 'helper' => $helper) + makePageParams ($params)); 1036 $ret .= " onclick=\"window.open('$href', '$window_name', '$popup_args'); return false\">"; 1037 1038 if ($img_name != '') 1039 { 1040 $ret .= getImageHREF ($img_name, $comment); 1041 if ($title != '') 1042 $ret .= ' '; 1043 } 1044 $ret .= $title; 1045 $ret .= '</a>'; 1046 return $ret; 1047} 1048 1049function renderProgressBar ($percentage = 0, $theme = '', $inline = FALSE) 1050{ 1051 echo getProgressBar ($percentage, $theme, $inline); 1052} 1053 1054function getProgressBar ($percentage = 0, $theme = '', $inline = FALSE) 1055{ 1056 $done = ((int) ($percentage * 100)); 1057 if (! $inline) 1058 $src = "?module=progressbar&done=$done" . (empty ($theme) ? '' : "&theme=${theme}"); 1059 else 1060 { 1061 $bk_request = $_REQUEST; 1062 $_REQUEST['theme'] = $theme; 1063 $src = 'data:image/png;base64,' . chunk_split (base64_encode (getOutputOf ('renderProgressBarImage', $done))); 1064 $_REQUEST = $bk_request; 1065 } 1066 $ret = "<img width=100 height=10 border=0 title='${done}%' src='$src'>"; 1067 return $ret; 1068} 1069 1070function renderNetVLAN ($cell) 1071{ 1072 echo getRenderedNetVLAN ($cell); 1073} 1074 1075function getRenderedNetVLAN ($cell) 1076{ 1077 if (empty ($cell['8021q'])) 1078 return; 1079 $links = array(); 1080 foreach ($cell['8021q'] as $vi) 1081 { 1082 $vlan_info = getVlanRow ("${vi['domain_id']}-${vi['vlan_id']}"); 1083 $links[] = formatVLANAsShortLink ($vlan_info); 1084 } 1085 $noun = count ($cell['8021q']) > 1 ? 'VLANs' : 'VLAN'; 1086 return "<div class='vlan'><strong><small>${noun}</small> " . implode (', ', $links) . '</strong></div>'; 1087} 1088 1089// DEPRECATED and will be removed in 0.22.0 1090function includeJQueryUI ($do_css = TRUE) 1091{ 1092 includeJQueryUIJS(); 1093 if ($do_css) 1094 includeJQueryUICSS(); 1095} 1096 1097function includeJQueryUIJS() 1098{ 1099 addJSInternal ('js/jquery-ui-1.8.21.min.js'); 1100} 1101 1102function includeJQueryUICSS() 1103{ 1104 addCSSInternal ('css/jquery-ui-1.8.22.redmond.css'); 1105} 1106 1107function getRenderedIPPortPair ($ip, $port = NULL) 1108{ 1109 return "<a href=\"" . 1110 makeHref (array ('page' => 'ipaddress', 'tab'=>'default', 'ip' => $ip)) . 1111 "\">" . $ip . "</a>" . 1112 (isset ($port) ? ":" . $port : ""); 1113} 1114 1115// Print common operation form prologue, include bypass argument, if 1116// appropriate, and some extra hidden inputs, if requested. 1117// Use special encoding for upload forms 1118function printOpFormIntro ($opname, $extra = array(), $upload = FALSE) 1119{ 1120 global $pageno, $tabno, $page; 1121 1122 echo "<form method=post id=${opname} name=${opname} action='?module=redirect&page=${pageno}&tab=${tabno}&op=${opname}'"; 1123 if ($upload) 1124 echo " enctype='multipart/form-data'"; 1125 echo ">"; 1126 fillBypassValues ($pageno, $extra); 1127 foreach ($extra as $inputname => $inputvalue) 1128 printf ('<input type=hidden name="%s" value="%s">', htmlspecialchars ($inputname, ENT_QUOTES), htmlspecialchars ($inputvalue, ENT_QUOTES)); 1129} 1130 1131// Display hrefs for all of a file's parents. If scissors are requested, 1132// prepend cutting button to each of them. 1133function serializeFileLinks ($links, $scissors = FALSE) 1134{ 1135 $comma = ''; 1136 $ret = ''; 1137 foreach ($links as $link_id => $li) 1138 { 1139 $cell = spotEntity ($li['entity_type'], $li['entity_id']); 1140 $ret .= $comma; 1141 if ($scissors) 1142 $ret .= getOpLink (array('op'=>'unlinkFile', 'link_id'=>$link_id), '', 'cut', 'Unlink file') . ' '; 1143 $ret .= mkCellA ($cell); 1144 $comma = '<br>'; 1145 } 1146 return $ret; 1147} 1148 1149function makeFileDownloadButton ($file_id, $imgname = 'download') 1150{ 1151 $href = makeHref (array ('module' => 'download', 'file_id' => $file_id)); 1152 $img = getImageHREF ($imgname, 'download file'); 1153 return "<a href='${href}'>${img}</a>"; 1154} 1155 1156// This function is DEPRECATED and will be removed in version 0.22.0. 1157// Instead of it please use one of the stringFor...() functions below. 1158function niftyString ($string, $maxlen = 30, $usetags = TRUE) 1159{ 1160 $cutind = '…'; // length is 1 1161 if ($string == '') 1162 return ' '; 1163 // a tab counts for a space 1164 $string = preg_replace ("/\t/", ' ', $string); 1165 if (! $maxlen || mb_strlen ($string) <= $maxlen) 1166 return htmlspecialchars ($string, ENT_QUOTES, 'UTF-8'); 1167 return 1168 ($usetags ? ("<span title='" . htmlspecialchars ($string, ENT_QUOTES, 'UTF-8') . "'>") : '') . 1169 str_replace (' ', ' ', htmlspecialchars (mb_substr ($string, 0, $maxlen - 1), ENT_QUOTES, 'UTF-8')) . 1170 $cutind . 1171 ($usetags ? '</span>' : ''); 1172} 1173 1174// "Some text, %s, some more text." 1175function stringForLabel ($string, $maxlen = 30) 1176{ 1177 // A tab counts for a space. 1178 $string = preg_replace ("/\t/", ' ', $string); 1179 $full = htmlspecialchars ($string, ENT_QUOTES, 'UTF-8'); 1180 if ($maxlen == 0 || mb_strlen ($string) <= $maxlen) 1181 return $full; 1182 $trimmed = mb_substr ($string, 0, $maxlen - 1); 1183 $trimmed = htmlspecialchars ($trimmed, ENT_QUOTES, 'UTF-8'); 1184 $trimmed = str_replace (' ', ' ', $trimmed) . '…'; 1185 return "<span title='${full}'>${trimmed}</span>"; 1186} 1187 1188// "<TD>%s</TD>" 1189function stringForTD ($string, $maxlen = 30) 1190{ 1191 // The non-breaking space helps the TD to render properly. 1192 return $string == '' ? ' ' : stringForLabel ($string, $maxlen); 1193} 1194 1195// "<INPUT type=text value='%s'>" 1196function stringForTextInputValue ($string, $maxlen = 30) 1197{ 1198 if ($maxlen != 0) 1199 $string = mb_substr ($string, 0, $maxlen); 1200 return htmlspecialchars ($string, ENT_QUOTES, 'UTF-8'); 1201} 1202 1203// "<TEXTAREA>%s</TEXTAREA>" 1204function stringForTextarea ($string) 1205{ 1206 return htmlspecialchars ($string, ENT_QUOTES, 'UTF-8'); 1207} 1208 1209// <OPTION>%s</OPTION> 1210function stringForOption ($string, $maxlen = 80) 1211{ 1212 $string = preg_replace ("/\t/", ' ', $string); 1213 if ($maxlen == 0 || mb_strlen ($string) <= $maxlen) 1214 return htmlspecialchars ($string, ENT_QUOTES, 'UTF-8'); 1215 $string = mb_substr ($string, 0, $maxlen - 1); 1216 return htmlspecialchars ($string, ENT_QUOTES, 'UTF-8') . '…'; 1217} 1218 1219function printTagsPicker ($preselect=NULL) 1220{ 1221 global $taglist; 1222 if (! count ($taglist)) 1223 { 1224 printf ('(None exist yet, %s?)', mkA ('configure', 'tagtree', NULL, 'edit')); 1225 return; 1226 } 1227 printTagsPickerInput ('taglist'); 1228 printTagsPickerUl ('taglist', $preselect); 1229 enableTagsPicker (); 1230} 1231 1232function printTagsPickerInput ($input_name) 1233{ 1234 # use data-attribute as identifier for tagit 1235 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'>"; 1236 echo "<span title='show tag tree' class='icon-folder-open tagit_input_" . $input_name . "'></span>"; 1237} 1238 1239function printTagsPickerUl ($input_name, $preselect = NULL) 1240{ 1241 global $target_given_tags; 1242 if ($preselect === NULL) 1243 $preselect = $target_given_tags; 1244 foreach (array_keys ($preselect) as $key) 1245 { 1246 $preselect[$key]['time_parsed'] = formatAge ($preselect[$key]['time']); # readable time format 1247 $preselect[$key]['description'] = stringForTextarea ($preselect[$key]['description']); 1248 } 1249 usort ($preselect, 'cmpTags'); 1250 $preselect_hidden = ""; 1251 foreach ($preselect as $value) 1252 $preselect_hidden .= "<input type=hidden name=" . $input_name . "[] value=" . $value['id'] . ">"; 1253 echo $preselect_hidden; # print preselected tags id that used in case javascript problems 1254 echo "<ul data-tagit='yes' data-tagit-valuename='" . $input_name . "' data-tagit-preselect='" . json_encode($preselect) . "' class='tagit-vertical'></ul>"; 1255} 1256 1257function enableTagsPicker () 1258{ 1259 global $taglist; 1260 static $taglist_inserted; 1261 includeJQueryUIJS(); 1262 includeJQueryUICSS(); 1263 addCSSInternal ('css/tagit.css'); 1264 addJSInternal ('js/tag-it.js'); 1265 addJSInternal ('js/tag-it-local.js'); 1266 if (! $taglist_inserted) 1267 { 1268 $taglist_filtered = array(); 1269 foreach ($taglist as $key => $taginfo) # remove unused fields 1270 { 1271 $taglist_filtered[$key] = array_sub ($taginfo, array("tag", "is_assignable", "trace")); 1272 if ($taginfo['color'] != NULL) 1273 $taglist_filtered[$key]['tagclass'] = getTagClass ($taginfo); 1274 } 1275 addJSText ('var taglist = ' . json_encode ($taglist_filtered) . ';'); 1276 $taglist_inserted = TRUE; 1277 } 1278} 1279 1280function makeIPAllocLink ($ip_bin, $alloc, $display_ifname = FALSE) 1281{ 1282 $object_name = ! isset ($object_name) || $object_name == '' ? 1283 formatEntityName (spotEntity ('object', $alloc['object_id'])) : 1284 $alloc['object_name']; 1285 $title = $display_ifname ? 1286 '' : 1287 "{$alloc['name']} @ {$object_name}"; 1288 return 1289 '<a href="' . makeHref (array ('page' => 'object', 'tab' => 'default', 'object_id' => $alloc['object_id'], 'hl_ip' => ip_format ($ip_bin))) . '"' . 1290 ' title="' . htmlspecialchars ($title, ENT_QUOTES) . '"' . 1291 ">" . ($display_ifname ? $alloc['name'] . '@' : '') . $object_name . "</a>"; 1292} 1293 1294function makeHtmlTag ($tagname, $attributes = array()) 1295{ 1296 $ret = '<' . $tagname; 1297 foreach ($attributes as $key => $value) 1298 $ret .= " $key=\"" . htmlspecialchars($value, ENT_QUOTES) . '"'; 1299 $ret .= '>'; 1300 return $ret; 1301} 1302 1303function getCellClass ($cell, $context) 1304{ 1305 $ctxmap = array 1306 ( 1307 'atom_plain' => 'background:white;', 1308 'atom_selected' => 'border:3px solid #80ffff !important;background:white;', 1309 'list_plain' => '', 1310 'list_selected' => 'outline: 3px solid #0aff0a;', 1311 ); 1312 if (! array_key_exists ($context, $ctxmap)) 1313 throw new InvalidArgException ('context', $context, 'unknown value'); 1314 if (! array_key_exists ('colors', $cell) || ! count ($cell['colors'])) 1315 return ''; 1316 $style = $ctxmap[$context]; 1317 $step = intval (round (100 / count ($cell['colors']))); 1318 $percent = 0; 1319 $gradient = ''; 1320 foreach ($cell['colors'] as $color) 1321 { 1322 $rgb = colorHex2Rgb ($color); 1323 $gradient .= "rgba(${rgb},0.2) ${percent}%, rgba(${rgb},0.3) " . ($percent + $step) . "%,"; 1324 $percent += $step; 1325 } 1326 $style .= "background-image:linear-gradient(135deg," . trim ($gradient, ',') . ") !important;"; 1327 $cell_id = $cell[$cell['realm'] == 'user' ? 'user_id' : 'id']; 1328 return getCachedCSSClassForStyle ("cellcolor-${cell_id}", $style); 1329} 1330 1331function getTagClass ($taginfo) 1332{ 1333 if (! array_key_exists ('color', $taginfo) || $taginfo['color'] === NULL) 1334 return ''; 1335 $rgb = colorHex2Rgb ($taginfo['color'], TRUE); 1336 return getCachedCSSClassForStyle ("tagcolor-${taginfo['id']}", "background: rgb($rgb);"); 1337} 1338 1339// This function has a side effect: it adds inline CSS. 1340function getCachedCSSClassForStyle ($class, $style) 1341{ 1342 static $cache = array(); 1343 $cachedclass = array_search ($style, $cache); 1344 if ($cachedclass !== FALSE) 1345 return " $cachedclass"; 1346 addCSSText (".{$class} {{$style}}"); 1347 $cache[$class] = $style; 1348 return " $class"; 1349} 1350 1351function colorHex2Rgb($color, $pastel = FALSE) 1352{ 1353 $color = trim ($color, '#'); 1354 1355 if ($pastel) 1356 { 1357 $rgb = intval (round ((hexdec (substr ($color, 0, 2)) + 255) / 2)) . ','; 1358 $rgb .= intval (round ((hexdec (substr ($color, 2, 2)) + 255) / 2)) . ','; 1359 $rgb .= intval (round ((hexdec (substr ($color, 4, 2)) + 255) / 2)); 1360 } 1361 else 1362 $rgb = hexdec (substr ($color, 0, 2)) . ',' . hexdec (substr ($color, 2, 2)) . ',' . hexdec (substr ($color, 4, 2)); 1363 1364 return $rgb; 1365} 1366 1367function setEntityColors(&$entity) 1368{ 1369 $entity['colors'] = array(); 1370 foreach ($entity['etags'] as $taginfo) 1371 if ($taginfo['color'] !== NULL && ! in_array ($taginfo['color'], $entity['colors'])) 1372 { 1373 $entity['colors'][] = $taginfo['color']; 1374 getTagClass ($taginfo); // set tag CSS class 1375 } 1376} 1377