1<?php 2// PHP Weathermap 0.97a 3// Copyright Howard Jones, 2005-2010 howie@thingy.com 4// http://www.network-weathermap.com/ 5// Released under the GNU Public License 6 7require_once "HTML_ImageMap.class.php"; 8 9require_once "WeatherMap.functions.php"; 10require_once "WeatherMapNode.class.php"; 11require_once "WeatherMapLink.class.php"; 12 13$WEATHERMAP_VERSION="0.97a"; 14$weathermap_debugging=FALSE; 15$weathermap_map=""; 16$weathermap_warncount=0; 17$weathermap_debug_suppress = array("processstring","mysprintf"); 18$weathemap_lazycounter=0; 19 20// Turn on ALL error reporting for now. 21// error_reporting (E_ALL|E_STRICT); 22 23// parameterise the in/out stuff a bit 24define("IN",0); 25define("OUT",1); 26define("WMCHANNELS",2); 27 28define('CONFIG_TYPE_LITERAL',0); 29define('CONFIG_TYPE_COLOR',1); 30 31// some strings that are used in more than one place 32define('FMT_BITS_IN',"{link:this:bandwidth_in:%2k}"); 33define('FMT_BITS_OUT',"{link:this:bandwidth_out:%2k}"); 34define('FMT_UNFORM_IN',"{link:this:bandwidth_in}"); 35define('FMT_UNFORM_OUT',"{link:this:bandwidth_out}"); 36define('FMT_PERC_IN',"{link:this:inpercent:%.2f}%"); 37define('FMT_PERC_OUT',"{link:this:outpercent:%.2f}%"); 38 39// the fields within a spine triple 40define("X",0); 41define("Y",1); 42define("DISTANCE",2); 43 44// *********************************************** 45 46// template class for data sources. All data sources extend this class. 47// I really wish PHP4 would just die overnight 48class WeatherMapDataSource 49{ 50 // Initialize - called after config has been read (so SETs are processed) 51 // but just before ReadData. Used to allow plugins to verify their dependencies 52 // (if any) and bow out gracefully. Return FALSE to signal that the plugin is not 53 // in a fit state to run at the moment. 54 function Init(&$map) { return TRUE; } 55 56 // called with the TARGET string. Returns TRUE or FALSE, depending on whether it wants to handle this TARGET 57 // called by map->ReadData() 58 function Recognise( $targetstring ) { return FALSE; } 59 60 // the actual ReadData 61 // returns an array of two values (in,out). -1,-1 if it couldn't get valid data 62 // configline is passed in, to allow for better error messages 63 // itemtype and itemname may be used as part of the target (e.g. for TSV source line) 64 // function ReadData($targetstring, $configline, $itemtype, $itemname, $map) { return (array(-1,-1)); } 65 function ReadData($targetstring, &$map, &$item) 66 { 67 return(array(-1,-1)); 68 } 69 70 // pre-register a target + context, to allow a plugin to batch up queries to a slow database, or snmp for example 71 function Register($targetstring, &$map, &$item) 72 { 73 74 } 75 76 // called before ReadData, to allow plugins to DO the prefetch of targets known from Register 77 function Prefetch() 78 { 79 80 } 81} 82 83// template classes for the pre- and post-processor plugins 84class WeatherMapPreProcessor 85{ 86 function run($map) { return FALSE; } 87} 88 89class WeatherMapPostProcessor 90{ 91 function run($map) { return FALSE; } 92} 93 94// *********************************************** 95 96// Links, Nodes and the Map object inherit from this class ultimately. 97// Just to make some common code common. 98class WeatherMapBase 99{ 100 var $notes = array(); 101 var $hints = array(); 102 var $inherit_fieldlist; 103 104 function add_note($name,$value) 105 { 106 debug("Adding note $name='$value' to ".$this->name."\n"); 107 $this->notes[$name] = $value; 108 } 109 110 function get_note($name) 111 { 112 if(isset($this->notes[$name])) 113 { 114 // debug("Found note $name in ".$this->name." with value of ".$this->notes[$name].".\n"); 115 return($this->notes[$name]); 116 } 117 else 118 { 119 // debug("Looked for note $name in ".$this->name." which doesn't exist.\n"); 120 return(NULL); 121 } 122 } 123 124 function add_hint($name,$value) 125 { 126 debug("Adding hint $name='$value' to ".$this->name."\n"); 127 $this->hints[$name] = $value; 128 # warn("Adding hint $name to ".$this->my_type()."/".$this->name."\n"); 129 } 130 131 132 function get_hint($name) 133 { 134 if(isset($this->hints[$name])) 135 { 136 // debug("Found hint $name in ".$this->name." with value of ".$this->hints[$name].".\n"); 137 return($this->hints[$name]); 138 } 139 else 140 { 141 // debug("Looked for hint $name in ".$this->name." which doesn't exist.\n"); 142 return(NULL); 143 } 144 } 145} 146 147class WeatherMapConfigItem 148{ 149 var $defined_in; 150 var $name; 151 var $value; 152 var $type; 153} 154 155// The 'things on the map' class. More common code (mainly variables, actually) 156class WeatherMapItem extends WeatherMapBase 157{ 158 var $owner; 159 160 var $configline; 161 var $infourl; 162 var $overliburl; 163 var $overlibwidth, $overlibheight; 164 var $overlibcaption; 165 var $my_default; 166 var $defined_in; 167 var $config_override; # used by the editor to allow text-editing 168 169 function my_type() { return "ITEM"; } 170} 171 172class WeatherMap extends WeatherMapBase 173{ 174 var $nodes = array(); // an array of WeatherMapNodes 175 var $links = array(); // an array of WeatherMapLinks 176 var $texts = array(); // an array containing all the extraneous text bits 177 var $used_images = array(); // an array of image filenames referred to (used by editor) 178 var $seen_zlayers = array(0=>array(),1000=>array()); // 0 is the background, 1000 is the legends, title, etc 179 180 var $config; 181 var $next_id; 182 var $min_ds_time; 183 var $max_ds_time; 184 var $background; 185 var $htmlstyle; 186 var $imap; 187 var $colours; 188 var $configfile; 189 var $imagefile, 190 $imageuri; 191 var $rrdtool; 192 var $title, 193 $titlefont; 194 var $kilo; 195 var $sizedebug, 196 $widthmod, 197 $debugging; 198 var $linkfont, 199 $nodefont, 200 $keyfont, 201 $timefont; 202 // var $bg_r, $bg_g, $bg_b; 203 var $timex, 204 $timey; 205 var $width, 206 $height; 207 var $keyx, 208 $keyy, $keyimage; 209 var $titlex, 210 $titley; 211 var $keytext, 212 $stamptext, $datestamp; 213 var $min_data_time, $max_data_time; 214 var $htmloutputfile, 215 $imageoutputfile; 216 var $htmlstylesheet; 217 var $defaultlink, 218 $defaultnode; 219 var $need_size_precalc; 220 var $keystyle,$keysize; 221 var $rrdtool_check; 222 var $inherit_fieldlist; 223 var $mintimex, $maxtimex; 224 var $mintimey, $maxtimey; 225 var $minstamptext, $maxstamptext; 226 var $context; 227 var $cachefolder,$mapcache,$cachefile_version; 228 var $name; 229 var $black, 230 $white, 231 $grey, 232 $selected; 233 234 var $datasourceclasses; 235 var $preprocessclasses; 236 var $postprocessclasses; 237 var $activedatasourceclasses; 238 var $thumb_width, $thumb_height; 239 var $has_includes; 240 var $has_overlibs; 241 var $node_template_tree; 242 var $link_template_tree; 243 244 var $plugins = array(); 245 var $included_files = array(); 246 var $usage_stats = array(); 247 248 function WeatherMap() 249 { 250 $this->inherit_fieldlist=array 251 ( 252 'width' => 800, 253 'height' => 600, 254 'kilo' => 1000, 255 'numscales' => array('DEFAULT' => 0), 256 'datasourceclasses' => array(), 257 'preprocessclasses' => array(), 258 'postprocessclasses' => array(), 259 'included_files' => array(), 260 'context' => '', 261 'dumpconfig' => FALSE, 262 'rrdtool_check' => '', 263 'background' => '', 264 'imageoutputfile' => '', 265 'imageuri' => '', 266 'htmloutputfile' => '', 267 'htmlstylesheet' => '', 268 'labelstyle' => 'percent', // redundant? 269 'htmlstyle' => 'static', 270 'keystyle' => array('DEFAULT' => 'classic'), 271 'title' => 'Network Weathermap', 272 'keytext' => array('DEFAULT' => 'Traffic Load'), 273 'keyx' => array('DEFAULT' => -1), 274 'keyy' => array('DEFAULT' => -1), 275 'keyimage' => array(), 276 'keysize' => array('DEFAULT' => 400), 277 'stamptext' => 'Created: %b %d %Y %H:%M:%S', 278 'keyfont' => 4, 279 'titlefont' => 2, 280 'timefont' => 2, 281 'timex' => 0, 282 'timey' => 0, 283 284 'mintimex' => -10000, 285 'mintimey' => -10000, 286 'maxtimex' => -10000, 287 'maxtimey' => -10000, 288 'minstamptext' => 'Oldest Data: %b %d %Y %H:%M:%S', 289 'maxstamptext' => 'Newest Data: %b %d %Y %H:%M:%S', 290 291 'thumb_width' => 0, 292 'thumb_height' => 0, 293 'titlex' => -1, 294 'titley' => -1, 295 'cachefolder' => 'cached', 296 'mapcache' => '', 297 'sizedebug' => FALSE, 298 'debugging' => FALSE, 299 'widthmod' => FALSE, 300 'has_includes' => FALSE, 301 'has_overlibs' => FALSE, 302 'name' => 'MAP' 303 ); 304 305 $this->Reset(); 306 } 307 308 function my_type() { return "MAP"; } 309 310 function Reset() 311 { 312 $this->next_id = 100; 313 foreach (array_keys($this->inherit_fieldlist)as $fld) { $this->$fld=$this->inherit_fieldlist[$fld]; } 314 315 $this->min_ds_time = NULL; 316 $this->max_ds_time = NULL; 317 318 $this->need_size_precalc=FALSE; 319 320 $this->nodes=array(); // an array of WeatherMapNodes 321 $this->links=array(); // an array of WeatherMapLinks 322 323 // these are the default defaults 324 // by putting them into a normal object, we can use the 325 // same code for writing out LINK DEFAULT as any other link. 326 debug("Creating ':: DEFAULT ::' DEFAULT LINK\n"); 327 // these two are used for default settings 328 $deflink = new WeatherMapLink; 329 $deflink->name=":: DEFAULT ::"; 330 $deflink->template=":: DEFAULT ::"; 331 $deflink->Reset($this); 332 333 $this->links[':: DEFAULT ::'] = &$deflink; 334 335 debug("Creating ':: DEFAULT ::' DEFAULT NODE\n"); 336 $defnode = new WeatherMapNode; 337 $defnode->name=":: DEFAULT ::"; 338 $defnode->template=":: DEFAULT ::"; 339 $defnode->Reset($this); 340 341 $this->nodes[':: DEFAULT ::'] = &$defnode; 342 343 $this->node_template_tree = array(); 344 $this->link_template_tree = array(); 345 346 $this->node_template_tree['DEFAULT'] = array(); 347 $this->link_template_tree['DEFAULT'] = array(); 348 349 350// ************************************ 351 // now create the DEFAULT link and node, based on those. 352 // these can be modified by the user, but their template (and therefore comparison in WriteConfig) is ':: DEFAULT ::' 353 debug("Creating actual DEFAULT NODE from :: DEFAULT ::\n"); 354 $defnode2 = new WeatherMapNode; 355 $defnode2->name = "DEFAULT"; 356 $defnode2->template = ":: DEFAULT ::"; 357 $defnode2->Reset($this); 358 359 $this->nodes['DEFAULT'] = &$defnode2; 360 361 debug("Creating actual DEFAULT LINK from :: DEFAULT ::\n"); 362 $deflink2 = new WeatherMapLink; 363 $deflink2->name = "DEFAULT"; 364 $deflink2->template = ":: DEFAULT ::"; 365 $deflink2->Reset($this); 366 367 $this->links['DEFAULT'] = &$deflink2; 368 369// for now, make the old defaultlink and defaultnode work too. 370// $this->defaultlink = $this->links['DEFAULT']; 371// $this->defaultnode = $this->nodes['DEFAULT']; 372 373 assert('is_object($this->nodes[":: DEFAULT ::"])'); 374 assert('is_object($this->links[":: DEFAULT ::"])'); 375 assert('is_object($this->nodes["DEFAULT"])'); 376 assert('is_object($this->links["DEFAULT"])'); 377 378// ************************************ 379 380 381 $this->imap=new HTML_ImageMap('weathermap'); 382 $this->colours=array 383 ( 384 ); 385 386 debug ("Adding default map colour set.\n"); 387 $defaults=array 388 ( 389 'KEYTEXT' => array('bottom' => -2, 'top' => -1, 'red1' => 0, 'green1' => 0, 'blue1' => 0, 'special' => 1), 390 'KEYOUTLINE' => array('bottom' => -2, 'top' => -1, 'red1' => 0, 'green1' => 0, 'blue1' => 0, 'special' => 1), 391 'KEYBG' => array('bottom' => -2, 'top' => -1, 'red1' => 255, 'green1' => 255, 'blue1' => 255, 'special' => 1), 392 'BG' => array('bottom' => -2, 'top' => -1, 'red1' => 255, 'green1' => 255, 'blue1' => 255, 'special' => 1), 393 'TITLE' => array('bottom' => -2, 'top' => -1, 'red1' => 0, 'green1' => 0, 'blue1' => 0, 'special' => 1), 394 'TIME' => array('bottom' => -2, 'top' => -1, 'red1' => 0, 'green1' => 0, 'blue1' => 0, 'special' => 1) 395 ); 396 397 foreach ($defaults as $key => $def) { $this->colours['DEFAULT'][$key]=$def; } 398 399 $this->configfile=''; 400 $this->imagefile=''; 401 $this->imageuri=''; 402 403 $this->fonts=array(); 404 405 // Adding these makes the editor's job a little easier, mainly 406 for($i=1; $i<=5; $i++) 407 { 408 $this->fonts[$i]->type="GD builtin"; 409 $this->fonts[$i]->file=''; 410 $this->fonts[$i]->size=0; 411 } 412 413 $this->LoadPlugins('data', 'lib' . DIRECTORY_SEPARATOR . 'datasources'); 414 $this->LoadPlugins('pre', 'lib' . DIRECTORY_SEPARATOR . 'pre'); 415 $this->LoadPlugins('post', 'lib' . DIRECTORY_SEPARATOR . 'post'); 416 417 debug("WeatherMap class Reset() complete\n"); 418 } 419 420 function myimagestring($image, $fontnumber, $x, $y, $string, $colour, $angle=0) 421 { 422 // if it's supposed to be a special font, and it hasn't been defined, then fall through 423 if ($fontnumber > 5 && !isset($this->fonts[$fontnumber])) 424 { 425 warn ("Using a non-existent special font ($fontnumber) - falling back to internal GD fonts [WMWARN03]\n"); 426 if($angle != 0) warn("Angled text doesn't work with non-FreeType fonts [WMWARN02]\n"); 427 $fontnumber=5; 428 } 429 430 if (($fontnumber > 0) && ($fontnumber < 6)) 431 { 432 imagestring($image, $fontnumber, $x, $y - imagefontheight($fontnumber), $string, $colour); 433 if($angle != 0) warn("Angled text doesn't work with non-FreeType fonts [WMWARN02]\n"); 434 } 435 else 436 { 437 // look up what font is defined for this slot number 438 if ($this->fonts[$fontnumber]->type == 'truetype') 439 { 440 wimagettftext($image, $this->fonts[$fontnumber]->size, $angle, $x, $y, 441 $colour, $this->fonts[$fontnumber]->file, $string); 442 } 443 444 if ($this->fonts[$fontnumber]->type == 'gd') 445 { 446 imagestring($image, $this->fonts[$fontnumber]->gdnumber, 447 $x, $y - imagefontheight($this->fonts[$fontnumber]->gdnumber), 448 $string, $colour); 449 if($angle != 0) warn("Angled text doesn't work with non-FreeType fonts [WMWARN04]\n"); 450 } 451 } 452 } 453 454 function myimagestringsize($fontnumber, $string) 455 { 456 $linecount = 1; 457 458 $lines = split("\n",$string); 459 $linecount = sizeof($lines); 460 $maxlinelength=0; 461 foreach($lines as $line) 462 { 463 $l = strlen($line); 464 if($l > $maxlinelength) $maxlinelength = $l; 465 } 466 467 if (($fontnumber > 0) && ($fontnumber < 6)) 468 { return array(imagefontwidth($fontnumber) * $maxlinelength, $linecount * imagefontheight($fontnumber)); } 469 else 470 { 471 // look up what font is defined for this slot number 472 if (!isset($this->fonts[$fontnumber])) 473 { 474 warn ("Using a non-existent special font ($fontnumber) - falling back to internal GD fonts [WMWARN36]\n"); 475 $fontnumber=5; 476 return array(imagefontwidth($fontnumber) * $maxlinelength, $linecount * imagefontheight($fontnumber)); 477 } 478 else 479 { 480 if ($this->fonts[$fontnumber]->type == 'truetype') 481 { 482 $ysize = 0; 483 $xsize = 0; 484 foreach($lines as $line) 485 { 486 $bounds=imagettfbbox($this->fonts[$fontnumber]->size, 0, $this->fonts[$fontnumber]->file, $line); 487 $cx = $bounds[4] - $bounds[0]; 488 $cy = $bounds[1] - $bounds[5]; 489 if($cx > $xsize) $xsize = $cx; 490 $ysize += ($cy*1.2); 491 # warn("Adding $cy (x was $cx)\n"); 492 } 493 #$bounds=imagettfbbox($this->fonts[$fontnumber]->size, 0, $this->fonts[$fontnumber]->file, 494 # $string); 495 # return (array($bounds[4] - $bounds[0], $bounds[1] - $bounds[5])); 496 # warn("Size of $string is $xsize x $ysize over $linecount lines\n"); 497 498 return(array($xsize,$ysize)); 499 } 500 501 if ($this->fonts[$fontnumber]->type == 'gd') 502 { return array(imagefontwidth($this->fonts[$fontnumber]->gdnumber) * $maxlinelength, 503 $linecount * imagefontheight($this->fonts[$fontnumber]->gdnumber)); } 504 } 505 } 506 } 507 508 function ProcessString($input,&$context, $include_notes=TRUE,$multiline=FALSE) 509 { 510 # debug("ProcessString: input is $input\n"); 511 512 assert('is_scalar($input)'); 513 514 $context_description = strtolower( $context->my_type() ); 515 if($context_description != "map") $context_description .= ":" . $context->name; 516 517 debug("Trace: ProcessString($input, $context_description)\n"); 518 519 if($multiline==TRUE) 520 { 521 $i = $input; 522 $input = str_replace("\\n","\n",$i); 523 # if($i != $input) warn("$i into $input\n"); 524 } 525 526 $output = $input; 527 528 # while( preg_match("/(\{[^}]+\})/",$input,$matches) ) 529 while( preg_match("/(\{(?:node|map|link)[^}]+\})/",$input,$matches) ) 530 { 531 $value = "[UNKNOWN]"; 532 $format = ""; 533 $key = $matches[1]; 534 debug("ProcessString: working on ".$key."\n"); 535 536 if ( preg_match("/\{(node|map|link):([^}]+)\}/",$key,$matches) ) 537 { 538 $type = $matches[1]; 539 $args = $matches[2]; 540 # debug("ProcessString: type is ".$type.", arguments are ".$args."\n"); 541 542 if($type == 'map') 543 { 544 $the_item = $this; 545 if(preg_match("/map:([^:]+):*([^:]*)/",$args,$matches)) 546 { 547 $args = $matches[1]; 548 $format = $matches[2]; 549 } 550 } 551 552 if(($type == 'link') || ($type == 'node')) 553 { 554 if(preg_match("/([^:]+):([^:]+):*([^:]*)/",$args,$matches)) 555 { 556 $itemname = $matches[1]; 557 $args = $matches[2]; 558 $format = $matches[3]; 559 560 # debug("ProcessString: item is $itemname, and args are now $args\n"); 561 562 $the_item = NULL; 563 if( ($itemname == "this") && ($type == strtolower($context->my_type())) ) 564 { 565 $the_item = $context; 566 } 567 elseif( strtolower($context->my_type()) == "link" && $type == 'node' && ($itemname == '_linkstart_' || $itemname == '_linkend_') ) 568 { 569 // this refers to the two nodes at either end of this link 570 if($itemname == '_linkstart_') 571 { 572 $the_item = $context->a; 573 } 574 575 if($itemname == '_linkend_') 576 { 577 $the_item = $context->b; 578 } 579 } 580 elseif( ($itemname == "parent") && ($type == strtolower($context->my_type())) && ($type=='node') && ($context->relative_to != '') ) 581 { 582 $the_item = $this->nodes[$context->relative_to]; 583 } 584 else 585 { 586 if( ($type == 'link') && isset($this->links[$itemname]) ) 587 { 588 $the_item = $this->links[$itemname]; 589 } 590 if( ($type == 'node') && isset($this->nodes[$itemname]) ) 591 { 592 $the_item = $this->nodes[$itemname]; 593 } 594 } 595 } 596 } 597 598 if(is_null($the_item)) 599 { 600 warn("ProcessString: $key refers to unknown item (context is $context_description) [WMWARN05]\n"); 601 } 602 else 603 { 604 # warn($the_item->name.": ".var_dump($the_item->hints)."\n"); 605 debug("ProcessString: Found appropriate item: ".get_class($the_item)." ".$the_item->name."\n"); 606 607 # warn($the_item->name."/hints: ".var_dump($the_item->hints)."\n"); 608 # warn($the_item->name."/notes: ".var_dump($the_item->notes)."\n"); 609 610 // SET and notes have precedent over internal properties 611 // this is my laziness - it saves me having a list of reserved words 612 // which are currently used for internal props. You can just 'overwrite' any of them. 613 if(isset($the_item->hints[$args])) 614 { 615 $value = $the_item->hints[$args]; 616 debug("ProcessString: used hint\n"); 617 } 618 // for some things, we don't want to allow notes to be considered. 619 // mainly - TARGET (which can define command-lines), shouldn't be 620 // able to get data from uncontrolled sources (i.e. data sources rather than SET in config files). 621 elseif($include_notes && isset($the_item->notes[$args])) 622 { 623 $value = $the_item->notes[$args]; 624 debug("ProcessString: used note\n"); 625 626 } 627 elseif(isset($the_item->$args)) 628 { 629 $value = $the_item->$args; 630 debug("ProcessString: used internal property\n"); 631 } 632 } 633 } 634 635 // format, and sanitise the value string here, before returning it 636 637 if($value===NULL) $value='NULL'; 638 debug("ProcessString: replacing ".$key." with $value\n"); 639 640 # if($format != '') $value = sprintf($format,$value); 641 if($format != '') 642 { 643 644 # debug("Formatting with mysprintf($format,$value)\n"); 645 $value = mysprintf($format,$value); 646 } 647 648 # debug("ProcessString: formatted to $value\n"); 649 $input = str_replace($key,'',$input); 650 $output = str_replace($key,$value,$output); 651 } 652 #debug("ProcessString: output is $output\n"); 653 return ($output); 654} 655 656function RandomData() 657{ 658 foreach ($this->links as $link) 659 { 660 $this->links[$link->name]->bandwidth_in=rand(0, $link->max_bandwidth_in); 661 $this->links[$link->name]->bandwidth_out=rand(0, $link->max_bandwidth_out); 662 } 663} 664 665function LoadPlugins( $type="data", $dir="lib/datasources" ) 666{ 667 debug("Beginning to load $type plugins from $dir\n"); 668 669 if ( ! file_exists($dir)) { 670 $dir = dirname(__FILE__) . DIRECTORY_SEPARATOR . $dir; 671 debug("Relative path didn't exist. Trying $dir\n"); 672 } 673 # $this->datasourceclasses = array(); 674 $dh=@opendir($dir); 675 676 if(!$dh) { // try to find it with the script, if the relative path fails 677 $srcdir = substr($_SERVER['argv'][0], 0, strrpos($_SERVER['argv'][0], DIRECTORY_SEPARATOR)); 678 $dh = opendir($srcdir.DIRECTORY_SEPARATOR.$dir); 679 if ($dh) $dir = $srcdir.DIRECTORY_SEPARATOR.$dir; 680 } 681 682 if ($dh) 683 { 684 while ($file=readdir($dh)) 685 { 686 $realfile = $dir . DIRECTORY_SEPARATOR . $file; 687 688 if( is_file($realfile) && preg_match( '/\.php$/', $realfile ) ) 689 { 690 debug("Loading $type Plugin class from $file\n"); 691 692 include_once( $realfile ); 693 $class = preg_replace( "/\.php$/", "", $file ); 694 if($type == 'data') 695 { 696 $this->datasourceclasses [$class]= $class; 697 $this->activedatasourceclasses[$class]=1; 698 } 699 if($type == 'pre') $this->preprocessclasses [$class]= $class; 700 if($type == 'post') $this->postprocessclasses [$class]= $class; 701 702 debug("Loaded $type Plugin class $class from $file\n"); 703 $this->plugins[$type][$class] = new $class; 704 if(! isset($this->plugins[$type][$class])) 705 { 706 debug("** Failed to create an object for plugin $type/$class\n"); 707 } 708 else 709 { 710 debug("Instantiated $class.\n"); 711 } 712 } 713 else 714 { 715 debug("Skipping $file\n"); 716 } 717 } 718 } 719 else 720 { 721 warn("Couldn't open $type Plugin directory ($dir). Things will probably go wrong. [WMWARN06]\n"); 722 } 723} 724 725function DatasourceInit() 726{ 727 debug("Running Init() for Data Source Plugins...\n"); 728 foreach ($this->datasourceclasses as $ds_class) 729 { 730 // make an instance of the class 731 $dsplugins[$ds_class] = new $ds_class; 732 debug("Running $ds_class"."->Init()\n"); 733 # $ret = call_user_func(array($ds_class, 'Init'), $this); 734 assert('isset($this->plugins["data"][$ds_class])'); 735 736 $ret = $this->plugins['data'][$ds_class]->Init($this); 737 738 if(! $ret) 739 { 740 debug("Removing $ds_class from Data Source list, since Init() failed\n"); 741 $this->activedatasourceclasses[$ds_class]=0; 742 # unset($this->datasourceclasses[$ds_class]); 743 } 744 } 745 debug("Finished Initialising Plugins...\n"); 746} 747 748function ProcessTargets() 749{ 750 debug("Preprocessing targets\n"); 751 752 $allitems = array(&$this->links, &$this->nodes); 753 reset($allitems); 754 755 debug("Preprocessing targets\n"); 756 757 while( list($kk,) = each($allitems)) 758 { 759 unset($objects); 760 $objects = &$allitems[$kk]; 761 762 reset($objects); 763 while (list($k,) = each($objects)) 764 { 765 unset($myobj); 766 $myobj = &$objects[$k]; 767 768 $type = $myobj->my_type(); 769 $name=$myobj->name; 770 771 772 if( ($type=='LINK' && isset($myobj->a)) || ($type=='NODE' && !is_null($myobj->x) ) ) 773 { 774 if (count($myobj->targets)>0) 775 { 776 $tindex = 0; 777 foreach ($myobj->targets as $target) 778 { 779 debug ("ProcessTargets: New Target: $target[4]\n"); 780 // processstring won't use notes (only hints) for this string 781 782 $targetstring = $this->ProcessString($target[4], $myobj, FALSE, FALSE); 783 if($target[4] != $targetstring) debug("Targetstring is now $targetstring\n"); 784 785 // if the targetstring starts with a -, then we're taking this value OFF the aggregate 786 $multiply = 1; 787 if(preg_match("/^-(.*)/",$targetstring,$matches)) 788 { 789 $targetstring = $matches[1]; 790 $multiply = -1 * $multiply; 791 } 792 793 // if the remaining targetstring starts with a number and a *-, then this is a scale factor 794 if(preg_match("/^(\d+\.?\d*)\*(.*)/",$targetstring,$matches)) 795 { 796 $targetstring = $matches[2]; 797 $multiply = $multiply * floatval($matches[1]); 798 } 799 800 $matched = FALSE; 801 $matched_by = ''; 802 foreach ($this->datasourceclasses as $ds_class) 803 { 804 if(!$matched) 805 { 806 // $recognised = call_user_func(array($ds_class, 'Recognise'), $targetstring); 807 $recognised = $this->plugins['data'][$ds_class]->Recognise($targetstring); 808 809 if( $recognised ) 810 { 811 $matched = TRUE; 812 $matched_by = $ds_class; 813 814 if($this->activedatasourceclasses[$ds_class]) 815 { 816 $this->plugins['data'][$ds_class]->Register($targetstring, $this, $myobj); 817 if($type == 'NODE') 818 { 819 $this->nodes[$name]->targets[$tindex][1] = $multiply; 820 $this->nodes[$name]->targets[$tindex][0] = $targetstring; 821 $this->nodes[$name]->targets[$tindex][5] = $matched_by; 822 } 823 if($type == 'LINK') 824 { 825 $this->links[$name]->targets[$tindex][1] = $multiply; 826 $this->links[$name]->targets[$tindex][0] = $targetstring; 827 $this->links[$name]->targets[$tindex][5] = $matched_by; 828 } 829 } 830 else 831 { 832 warn("ProcessTargets: $type $name, target: $targetstring on config line $target[3] of $target[2] was recognised as a valid TARGET by a plugin that is unable to run ($ds_class) [WMWARN07]\n"); 833 } 834 } 835 } 836 } 837 if(! $matched) 838 { 839 warn("ProcessTargets: $type $name, target: $target[4] on config line $target[3] of $target[2] was not recognised as a valid TARGET [WMWARN08]\n"); 840 } 841 842 $tindex++; 843 } 844 } 845 } 846 } 847 } 848} 849 850function ReadData() 851{ 852 $this->DatasourceInit(); 853 854 debug ("======================================\n"); 855 debug("ReadData: Updating link data for all links and nodes\n"); 856 857 // we skip readdata completely in sizedebug mode 858 if ($this->sizedebug == 0) 859 { 860 $this->ProcessTargets(); 861 862 debug ("======================================\n"); 863 debug("Starting prefetch\n"); 864 foreach ($this->datasourceclasses as $ds_class) 865 { 866 $this->plugins['data'][$ds_class]->Prefetch(); 867 } 868 869 debug ("======================================\n"); 870 debug("Starting main collection loop\n"); 871 872 $allitems = array(&$this->links, &$this->nodes); 873 reset($allitems); 874 875 while( list($kk,) = each($allitems)) 876 { 877 unset($objects); 878 $objects = &$allitems[$kk]; 879 880 reset($objects); 881 while (list($k,) = each($objects)) 882 { 883 unset($myobj); 884 $myobj = &$objects[$k]; 885 886 $type = $myobj->my_type(); 887 888 $total_in=0; 889 $total_out=0; 890 $name=$myobj->name; 891 debug ("\n"); 892 debug ("ReadData for $type $name: \n"); 893 894 if( ($type=='LINK' && isset($myobj->a)) || ($type=='NODE' && !is_null($myobj->x) ) ) 895 { 896 if (count($myobj->targets)>0) 897 { 898 $tindex = 0; 899 foreach ($myobj->targets as $target) 900 { 901 debug ("ReadData: New Target: $target[4]\n"); 902 # debug ( var_dump($target)); 903 904 $targetstring = $target[0]; 905 $multiply = $target[1]; 906 907 # exit(); 908 909 $in = 0; 910 $out = 0; 911 $datatime = 0; 912 if ($target[4] != '') 913 { 914 // processstring won't use notes (only hints) for this string 915 916 $targetstring = $this->ProcessString($target[0], $myobj, FALSE, FALSE); 917 if($target[0] != $targetstring) debug("Targetstring is now $targetstring\n"); 918 if($multiply != 1) debug("Will multiply result by $multiply\n"); 919 920 if($target[0] != "") 921 { 922 $matched_by = $target[5]; 923 list($in,$out,$datatime) = $this->plugins['data'][ $target[5] ]->ReadData($targetstring, $this, $myobj); 924 } 925 926 if (($in === NULL) && ($out === NULL)) 927 { 928 $in=0; 929 $out=0; 930 warn 931 ("ReadData: $type $name, target: $targetstring on config line $target[3] of $target[2] had no valid data, according to $matched_by\n"); 932 } 933 else 934 { 935 if($in === NULL) $in = 0; 936 if($out === NULL) $out = 0; 937 } 938 939 if($multiply != 1) { 940 debug("Pre-multiply: $in $out\n"); 941 942 $in = $multiply*$in; 943 $out = $multiply*$out; 944 945 debug("Post-multiply: $in $out\n"); 946 } 947 948 $total_in=$total_in + $in; 949 $total_out=$total_out + $out; 950 debug("Aggregate so far: $total_in $total_out\n"); 951 # keep a track of the range of dates for data sources (mainly for MRTG/textfile based DS) 952 if($datatime > 0) 953 { 954 if($this->max_data_time==NULL || $datatime > $this->max_data_time) $this->max_data_time = $datatime; 955 if($this->min_data_time==NULL || $datatime < $this->min_data_time) $this->min_data_time = $datatime; 956 957 debug("DataTime MINMAX: ".$this->min_data_time." -> ".$this->max_data_time."\n"); 958 } 959 960 } 961 $tindex++; 962 } 963 964 debug ("ReadData complete for $type $name: $total_in $total_out\n"); 965 } 966 else 967 { 968 debug("ReadData: No targets for $type $name\n"); 969 } 970 } 971 else 972 { 973 debug("ReadData: Skipping $type $name that looks like a template\n."); 974 } 975 976 # $this->links[$name]->bandwidth_in=$total_in; 977 # $this->links[$name]->bandwidth_out=$total_out; 978 $myobj->bandwidth_in = $total_in; 979 $myobj->bandwidth_out = $total_out; 980 981 if($type == 'LINK' && $myobj->duplex=='half') 982 { 983 // in a half duplex link, in and out share a common bandwidth pool, so percentages need to include both 984 debug("Calculating percentage using half-duplex\n"); 985 $myobj->outpercent = (($total_in + $total_out) / ($myobj->max_bandwidth_out)) * 100; 986 $myobj->inpercent = (($total_out + $total_in) / ($myobj->max_bandwidth_in)) * 100; 987 if($myobj->max_bandwidth_out != $myobj->max_bandwidth_in) 988 { 989 warn("ReadData: $type $name: You're using asymmetric bandwidth AND half-duplex in the same link. That makes no sense. [WMWARN44]\n"); 990 } 991 } 992 else 993 { 994 $myobj->outpercent = (($total_out) / ($myobj->max_bandwidth_out)) * 100; 995 $myobj->inpercent = (($total_in) / ($myobj->max_bandwidth_in)) * 100; 996 } 997 998 # print $myobj->name."=>".$myobj->inpercent."%/".$myobj->outpercent."\n"; 999 1000 $warn_in = true; 1001 $warn_out = true; 1002 if($type=='NODE' && $myobj->scalevar =='in') $warn_out = false; 1003 if($type=='NODE' && $myobj->scalevar =='out') $warn_in = false; 1004 1005 if($myobj->scaletype == 'percent') 1006 { 1007 list($incol,$inscalekey,$inscaletag) = $this->NewColourFromPercent($myobj->inpercent,$myobj->usescale,$myobj->name, TRUE, $warn_in); 1008 list($outcol,$outscalekey, $outscaletag) = $this->NewColourFromPercent($myobj->outpercent,$myobj->usescale,$myobj->name, TRUE, $warn_out); 1009 } 1010 else 1011 { 1012 // use absolute values, if that's what is requested 1013 list($incol,$inscalekey,$inscaletag) = $this->NewColourFromPercent($myobj->bandwidth_in,$myobj->usescale,$myobj->name, FALSE, $warn_in); 1014 list($outcol,$outscalekey, $outscaletag) = $this->NewColourFromPercent($myobj->bandwidth_out,$myobj->usescale,$myobj->name, FALSE, $warn_out); 1015 } 1016 1017 $myobj->add_note("inscalekey",$inscalekey); 1018 $myobj->add_note("outscalekey",$outscalekey); 1019 1020 $myobj->add_note("inscaletag",$inscaletag); 1021 $myobj->add_note("outscaletag",$outscaletag); 1022 1023 $myobj->add_note("inscalecolor",$incol->as_html()); 1024 $myobj->add_note("outscalecolor",$outcol->as_html()); 1025 1026 $myobj->colours[IN] = $incol; 1027 $myobj->colours[OUT] = $outcol; 1028 1029 ### warn("TAGS (setting) |$inscaletag| |$outscaletag| \n"); 1030 1031 debug ("ReadData: Setting $total_in,$total_out\n"); 1032 unset($myobj); 1033 } 1034 } 1035 debug ("ReadData Completed.\n"); 1036 debug("------------------------------\n"); 1037 } 1038} 1039 1040// nodename is a vestigal parameter, from the days when nodes were just big labels 1041function DrawLabelRotated($im, $x, $y, $angle, $text, $font, $padding, $linkname, $textcolour, $bgcolour, $outlinecolour, &$map, $direction) 1042{ 1043 list($strwidth, $strheight)=$this->myimagestringsize($font, $text); 1044 1045 if(abs($angle)>90) $angle -= 180; 1046 if($angle < -180) $angle +=360; 1047 1048 $rangle = -deg2rad($angle); 1049 1050 $extra=3; 1051 1052 $x1= $x - ($strwidth / 2) - $padding - $extra; 1053 $x2= $x + ($strwidth / 2) + $padding + $extra; 1054 $y1= $y - ($strheight / 2) - $padding - $extra; 1055 $y2= $y + ($strheight / 2) + $padding + $extra; 1056 1057 // a box. the last point is the start point for the text. 1058 $points = array($x1,$y1, $x1,$y2, $x2,$y2, $x2,$y1, $x-$strwidth/2, $y+$strheight/2 + 1); 1059 $npoints = count($points)/2; 1060 1061 RotateAboutPoint($points, $x,$y, $rangle); 1062 1063 if ($bgcolour != array 1064 ( 1065 -1, 1066 -1, 1067 -1 1068 )) 1069 { 1070 $bgcol=myimagecolorallocate($im, $bgcolour[0], $bgcolour[1], $bgcolour[2]); 1071 # imagefilledrectangle($im, $x1, $y1, $x2, $y2, $bgcol); 1072 wimagefilledpolygon($im,$points,4,$bgcol); 1073 } 1074 1075 if ($outlinecolour != array 1076 ( 1077 -1, 1078 -1, 1079 -1 1080 )) 1081 { 1082 $outlinecol=myimagecolorallocate($im, $outlinecolour[0], $outlinecolour[1], $outlinecolour[2]); 1083 # imagerectangle($im, $x1, $y1, $x2, $y2, $outlinecol); 1084 wimagepolygon($im,$points,4,$outlinecol); 1085 } 1086 1087 $textcol=myimagecolorallocate($im, $textcolour[0], $textcolour[1], $textcolour[2]); 1088 $this->myimagestring($im, $font, $points[8], $points[9], $text, $textcol,$angle); 1089 1090 $areaname = "LINK:L".$map->links[$linkname]->id.':'.($direction+2); 1091 1092 // the rectangle is about half the size in the HTML, and easier to optimise/detect in the browser 1093 if($angle==0) 1094 { 1095 $map->imap->addArea("Rectangle", $areaname, '', array($x1, $y1, $x2, $y2)); 1096 debug ("Adding Rectangle imagemap for $areaname\n"); 1097 } 1098 else 1099 { 1100 $map->imap->addArea("Polygon", $areaname, '', $points); 1101 debug ("Adding Poly imagemap for $areaname\n"); 1102 } 1103 1104} 1105 1106function ColourFromPercent($image, $percent,$scalename="DEFAULT",$name="") 1107{ 1108 $col = NULL; 1109 $tag = ''; 1110 1111 $nowarn_clipping = intval($this->get_hint("nowarn_clipping")); 1112 $nowarn_scalemisses = intval($this->get_hint("nowarn_scalemisses")); 1113 1114 $bt = debug_backtrace(); 1115 $function = (isset($bt[1]['function']) ? $bt[1]['function'] : ''); 1116 print "$function calls ColourFromPercent\n"; 1117 1118 exit(); 1119 1120 if(isset($this->colours[$scalename])) 1121 { 1122 $colours=$this->colours[$scalename]; 1123 1124 if ($percent > 100) 1125 { 1126 if($nowarn_clipping==0) warn ("ColourFromPercent: Clipped $name $percent% to 100% [WMWARN33]\n"); 1127 $percent=100; 1128 } 1129 1130 foreach ($colours as $key => $colour) 1131 { 1132 if (($percent >= $colour['bottom']) and ($percent <= $colour['top'])) 1133 { 1134 if(isset($colour['tag'])) $tag = $colour['tag']; 1135 1136 // we get called early now, so might not need to actually allocate a colour 1137 if(isset($image)) 1138 { 1139 if (isset($colour['red2'])) 1140 { 1141 if($colour["bottom"] == $colour["top"]) 1142 { 1143 $ratio = 0; 1144 } 1145 else 1146 { 1147 $ratio=($percent - $colour["bottom"]) / ($colour["top"] - $colour["bottom"]); 1148 } 1149 1150 $r=$colour["red1"] + ($colour["red2"] - $colour["red1"]) * $ratio; 1151 $g=$colour["green1"] + ($colour["green2"] - $colour["green1"]) * $ratio; 1152 $b=$colour["blue1"] + ($colour["blue2"] - $colour["blue1"]) * $ratio; 1153 1154 $col = myimagecolorallocate($image, $r, $g, $b); 1155 } 1156 else { 1157 $r=$colour["red1"]; 1158 $g=$colour["green1"]; 1159 $b=$colour["blue1"]; 1160 1161 $col = myimagecolorallocate($image, $r, $g, $b); 1162 # $col = $colour['gdref1']; 1163 } 1164 debug("CFPC $name $tag $key $r $g $b\n"); 1165 } 1166 1167 ### warn(">>TAGS CFPC $tag\n"); 1168 1169 return(array($col,$key,$tag)); 1170 } 1171 } 1172 } 1173 else 1174 { 1175 if($scalename != 'none') 1176 { 1177 warn("ColourFromPercent: Attempted to use non-existent scale: $scalename for $name [WMWARN09]\n"); 1178 } 1179 else 1180 { 1181 return array($this->white,'',''); 1182 } 1183 } 1184 1185 // you'll only get grey for a COMPLETELY quiet link if there's no 0 in the SCALE lines 1186 if ($percent == 0) { return array($this->grey,'',''); } 1187 1188 // and you'll only get white for a link with no colour assigned 1189 if($nowarn_scalemisses==0) warn("ColourFromPercent: Scale $scalename doesn't cover $percent% for $name [WMWARN29]\n"); 1190 return array($this->white,'',''); 1191} 1192 1193function NewColourFromPercent($value,$scalename="DEFAULT",$name="",$is_percent=TRUE, $scale_warning=TRUE) 1194{ 1195 $col = new Colour(0,0,0); 1196 $tag = ''; 1197 $matchsize = NULL; 1198 1199 $nowarn_clipping = intval($this->get_hint("nowarn_clipping")); 1200 $nowarn_scalemisses = (!$scale_warning) || intval($this->get_hint("nowarn_scalemisses")); 1201 1202 if(isset($this->colours[$scalename])) 1203 { 1204 $colours=$this->colours[$scalename]; 1205 1206 if ($is_percent && $value > 100) 1207 { 1208 if($nowarn_clipping==0) warn ("NewColourFromPercent: Clipped $value% to 100% for item $name [WMWARN33]\n"); 1209 $value = 100; 1210 } 1211 1212 if ($is_percent && $value < 0) 1213 { 1214 if($nowarn_clipping==0) warn ("NewColourFromPercent: Clipped $value% to 0% for item $name [WMWARN34]\n"); 1215 $value = 0; 1216 } 1217 1218 foreach ($colours as $key => $colour) 1219 { 1220 if ( (!isset($colour['special']) || $colour['special'] == 0) and ($value >= $colour['bottom']) and ($value <= $colour['top'])) 1221 { 1222 $range = $colour['top'] - $colour['bottom']; 1223 if (isset($colour['red2'])) 1224 { 1225 if($colour["bottom"] == $colour["top"]) 1226 { 1227 $ratio = 0; 1228 } 1229 else 1230 { 1231 $ratio=($value - $colour["bottom"]) / ($colour["top"] - $colour["bottom"]); 1232 } 1233 1234 $r=$colour["red1"] + ($colour["red2"] - $colour["red1"]) * $ratio; 1235 $g=$colour["green1"] + ($colour["green2"] - $colour["green1"]) * $ratio; 1236 $b=$colour["blue1"] + ($colour["blue2"] - $colour["blue1"]) * $ratio; 1237 } 1238 else { 1239 $r=$colour["red1"]; 1240 $g=$colour["green1"]; 1241 $b=$colour["blue1"]; 1242 1243 # $col = new Colour($r, $g, $b); 1244 # $col = $colour['gdref1']; 1245 } 1246 1247 // change in behaviour - with multiple matching ranges for a value, the smallest range wins 1248 if( is_null($matchsize) || ($range < $matchsize) ) 1249 { 1250 $col = new Colour($r, $g, $b); 1251 $matchsize = $range; 1252 } 1253 1254 if(isset($colour['tag'])) $tag = $colour['tag']; 1255 #### warn(">>NCFPC TAGS $tag\n"); 1256 debug("NCFPC $name $scalename $value '$tag' $key $r $g $b\n"); 1257 1258 return(array($col,$key,$tag)); 1259 } 1260 } 1261 } 1262 else 1263 { 1264 if($scalename != 'none') 1265 { 1266 warn("ColourFromPercent: Attempted to use non-existent scale: $scalename for item $name [WMWARN09]\n"); 1267 } 1268 else 1269 { 1270 return array(new Colour(255,255,255),'',''); 1271 } 1272 } 1273 1274 // shouldn't really get down to here if there's a complete SCALE 1275 1276 // you'll only get grey for a COMPLETELY quiet link if there's no 0 in the SCALE lines 1277 if ($value == 0) { return array(new Colour(192,192,192),'',''); } 1278 1279 if($nowarn_scalemisses==0) warn("NewColourFromPercent: Scale $scalename doesn't include a line for $value".($is_percent ? "%" : "")." while drawing item $name [WMWARN29]\n"); 1280 1281 // and you'll only get white for a link with no colour assigned 1282 return array(new Colour(255,255,255),'',''); 1283} 1284 1285 1286function coloursort($a, $b) 1287{ 1288 if ($a['bottom'] == $b['bottom']) 1289 { 1290 if($a['top'] < $b['top']) { return -1; }; 1291 if($a['top'] > $b['top']) { return 1; }; 1292 return 0; 1293 } 1294 1295 if ($a['bottom'] < $b['bottom']) { return -1; } 1296 1297 return 1; 1298} 1299 1300function FindScaleExtent($scalename="DEFAULT") 1301{ 1302 $max = -999999999999999999999; 1303 $min = - $max; 1304 1305 if(isset($this->colours[$scalename])) 1306 { 1307 $colours=$this->colours[$scalename]; 1308 1309 foreach ($colours as $key => $colour) 1310 { 1311 if(! $colour['special']) 1312 { 1313 $min = min($colour['bottom'], $min); 1314 $max = max($colour['top'], $max); 1315 } 1316 } 1317 } 1318 else 1319 { 1320 warn("FindScaleExtent: non-existent SCALE $scalename [WMWARN43]\n"); 1321 } 1322 return array($min, $max); 1323} 1324 1325function DrawLegend_Horizontal($im,$scalename="DEFAULT",$width=400) 1326{ 1327 $title=$this->keytext[$scalename]; 1328 1329 $colours=$this->colours[$scalename]; 1330 $nscales=$this->numscales[$scalename]; 1331 1332 debug("Drawing $nscales colours into SCALE\n"); 1333 1334 $font=$this->keyfont; 1335 1336 # $x=$this->keyx[$scalename]; 1337 # $y=$this->keyy[$scalename]; 1338 $x = 0; 1339 $y = 0; 1340 1341 # $width = 400; 1342 $scalefactor = $width/100; 1343 1344 list($tilewidth, $tileheight)=$this->myimagestringsize($font, "100%"); 1345 $box_left = $x; 1346 # $box_left = 0; 1347 $scale_left = $box_left + 4 + $scalefactor/2; 1348 $box_right = $scale_left + $width + $tilewidth + 4 + $scalefactor/2; 1349 $scale_right = $scale_left + $width; 1350 1351 $box_top = $y; 1352 # $box_top = 0; 1353 $scale_top = $box_top + $tileheight + 6; 1354 $scale_bottom = $scale_top + $tileheight * 1.5; 1355 $box_bottom = $scale_bottom + $tileheight * 2 + 6; 1356 1357 $scale_im = imagecreatetruecolor($box_right+1, $box_bottom+1); 1358 $scale_ref = 'gdref_legend_'.$scalename; 1359 $this->AllocateScaleColours($scale_im,$scale_ref); 1360 1361 wimagefilledrectangle($scale_im, $box_left, $box_top, $box_right, $box_bottom, 1362 $this->colours['DEFAULT']['KEYBG'][$scale_ref]); 1363 wimagerectangle($scale_im, $box_left, $box_top, $box_right, $box_bottom, 1364 $this->colours['DEFAULT']['KEYOUTLINE'][$scale_ref]); 1365 1366 $this->myimagestring($scale_im, $font, $scale_left, $scale_bottom + $tileheight * 2 + 2 , $title, 1367 $this->colours['DEFAULT']['KEYTEXT'][$scale_ref]); 1368 1369 for($p=0;$p<=100;$p++) 1370 { 1371 $dx = $p*$scalefactor; 1372 1373 if( ($p % 25) == 0) 1374 { 1375 imageline($scale_im, $scale_left + $dx, $scale_top - $tileheight, 1376 $scale_left + $dx, $scale_bottom + $tileheight, 1377 $this->colours['DEFAULT']['KEYTEXT'][$scale_ref]); 1378 $labelstring=sprintf("%d%%", $p); 1379 $this->myimagestring($scale_im, $font, $scale_left + $dx + 2, $scale_top - 2, $labelstring, 1380 $this->colours['DEFAULT']['KEYTEXT'][$scale_ref]); 1381 } 1382 1383 list($col,$junk) = $this->NewColourFromPercent($p,$scalename); 1384 if($col->is_real()) 1385 { 1386 $cc = $col->gdallocate($scale_im); 1387 wimagefilledrectangle($scale_im, $scale_left + $dx - $scalefactor/2, $scale_top, 1388 $scale_left + $dx + $scalefactor/2, $scale_bottom, 1389 $cc); 1390 } 1391 } 1392 1393 imagecopy($im,$scale_im,$this->keyx[$scalename],$this->keyy[$scalename],0,0,imagesx($scale_im),imagesy($scale_im)); 1394 $this->keyimage[$scalename] = $scale_im; 1395 1396 $rx = $this->keyx[$scalename]; 1397 $ry = $this->keyy[$scalename]; 1398 1399 $this->imap->addArea("Rectangle", "LEGEND:$scalename", '', 1400 array($rx+$box_left, $ry+$box_top, $rx+$box_right, $ry+$box_bottom)); 1401} 1402 1403function DrawLegend_Vertical($im,$scalename="DEFAULT",$height=400,$inverted=false) 1404{ 1405 $title=$this->keytext[$scalename]; 1406 1407 $colours=$this->colours[$scalename]; 1408 $nscales=$this->numscales[$scalename]; 1409 1410 debug("Drawing $nscales colours into SCALE\n"); 1411 1412 $font=$this->keyfont; 1413 1414 $x=$this->keyx[$scalename]; 1415 $y=$this->keyy[$scalename]; 1416 1417 # $height = 400; 1418 $scalefactor = $height/100; 1419 1420 list($tilewidth, $tileheight)=$this->myimagestringsize($font, "100%"); 1421 1422 # $box_left = $x; 1423 # $box_top = $y; 1424 $box_left = 0; 1425 $box_top = 0; 1426 1427 $scale_left = $box_left+$scalefactor*2 +4 ; 1428 $scale_right = $scale_left + $tileheight*2; 1429 $box_right = $scale_right + $tilewidth + $scalefactor*2 + 4; 1430 1431 list($titlewidth,$titleheight) = $this->myimagestringsize($font,$title); 1432 if( ($box_left + $titlewidth + $scalefactor*3) > $box_right) 1433 { 1434 $box_right = $box_left + $scalefactor*4 + $titlewidth; 1435 } 1436 1437 $scale_top = $box_top + 4 + $scalefactor + $tileheight*2; 1438 $scale_bottom = $scale_top + $height; 1439 $box_bottom = $scale_bottom + $scalefactor + $tileheight/2 + 4; 1440 1441 $scale_im = imagecreatetruecolor($box_right+1, $box_bottom+1); 1442 $scale_ref = 'gdref_legend_'.$scalename; 1443 $this->AllocateScaleColours($scale_im,$scale_ref); 1444 1445 wimagefilledrectangle($scale_im, $box_left, $box_top, $box_right, $box_bottom, 1446 $this->colours['DEFAULT']['KEYBG']['gdref1']); 1447 wimagerectangle($scale_im, $box_left, $box_top, $box_right, $box_bottom, 1448 $this->colours['DEFAULT']['KEYOUTLINE']['gdref1']); 1449 1450 $this->myimagestring($scale_im, $font, $scale_left-$scalefactor, $scale_top - $tileheight , $title, 1451 $this->colours['DEFAULT']['KEYTEXT']['gdref1']); 1452 1453 $updown = 1; 1454 if($inverted) $updown = -1; 1455 1456 1457 for($p=0;$p<=100;$p++) 1458 { 1459 if($inverted) 1460 { 1461 $dy = (100-$p) * $scalefactor; 1462 } 1463 else 1464 { 1465 $dy = $p*$scalefactor; 1466 } 1467 1468 if( ($p % 25) == 0) 1469 { 1470 imageline($scale_im, $scale_left - $scalefactor, $scale_top + $dy, 1471 $scale_right + $scalefactor, $scale_top + $dy, 1472 $this->colours['DEFAULT']['KEYTEXT'][$scale_ref]); 1473 $labelstring=sprintf("%d%%", $p); 1474 $this->myimagestring($scale_im, $font, $scale_right + $scalefactor*2 , $scale_top + $dy + $tileheight/2, 1475 $labelstring, $this->colours['DEFAULT']['KEYTEXT'][$scale_ref]); 1476 } 1477 1478 list($col,$junk) = $this->NewColourFromPercent($p,$scalename); 1479 if( $col->is_real()) 1480 { 1481 $cc = $col->gdallocate($scale_im); 1482 wimagefilledrectangle($scale_im, $scale_left, $scale_top + $dy - $scalefactor/2, 1483 $scale_right, $scale_top + $dy + $scalefactor/2, 1484 $cc); 1485 } 1486 } 1487 1488 imagecopy($im,$scale_im,$this->keyx[$scalename],$this->keyy[$scalename],0,0,imagesx($scale_im),imagesy($scale_im)); 1489 $this->keyimage[$scalename] = $scale_im; 1490 1491 $rx = $this->keyx[$scalename]; 1492 $ry = $this->keyy[$scalename]; 1493 $this->imap->addArea("Rectangle", "LEGEND:$scalename", '', 1494 array($rx+$box_left, $ry+$box_top, $rx+$box_right, $ry+$box_bottom)); 1495} 1496 1497function DrawLegend_Classic($im,$scalename="DEFAULT",$use_tags=FALSE) 1498{ 1499 $title=$this->keytext[$scalename]; 1500 1501 $colours=$this->colours[$scalename]; 1502 usort($colours, array("Weathermap", "coloursort")); 1503 1504 $nscales=$this->numscales[$scalename]; 1505 1506 debug("Drawing $nscales colours into SCALE\n"); 1507 1508 $hide_zero = intval($this->get_hint("key_hidezero_".$scalename)); 1509 $hide_percent = intval($this->get_hint("key_hidepercent_".$scalename)); 1510 1511 // did we actually hide anything? 1512 $hid_zero = FALSE; 1513 if( ($hide_zero == 1) && isset($colours['0_0']) ) 1514 { 1515 $nscales--; 1516 $hid_zero = TRUE; 1517 } 1518 1519 $font=$this->keyfont; 1520 1521 $x=$this->keyx[$scalename]; 1522 $y=$this->keyy[$scalename]; 1523 1524 list($tilewidth, $tileheight)=$this->myimagestringsize($font, "MMMM"); 1525 $tileheight=$tileheight * 1.1; 1526 $tilespacing=$tileheight + 2; 1527 1528 if (($this->keyx[$scalename] >= 0) && ($this->keyy[$scalename] >= 0)) 1529 { 1530 1531 # $minwidth = imagefontwidth($font) * strlen('XX 100%-100%')+10; 1532 # $boxwidth = imagefontwidth($font) * strlen($title) + 10; 1533 list($minwidth, $junk)=$this->myimagestringsize($font, 'MMMM 100%-100%'); 1534 list($minminwidth, $junk)=$this->myimagestringsize($font, 'MMMM '); 1535 list($boxwidth, $junk)=$this->myimagestringsize($font, $title); 1536 1537 if($use_tags) 1538 { 1539 $max_tag = 0; 1540 foreach ($colours as $colour) 1541 { 1542 if ( isset($colour['tag']) ) 1543 { 1544 list($w, $junk)=$this->myimagestringsize($font, $colour['tag']); 1545 # print $colour['tag']." $w \n"; 1546 if($w > $max_tag) $max_tag = $w; 1547 } 1548 } 1549 1550 // now we can tweak the widths, appropriately to allow for the tag strings 1551 # print "$max_tag > $minwidth?\n"; 1552 if( ($max_tag + $minminwidth) > $minwidth) $minwidth = $minminwidth + $max_tag; 1553 # print "minwidth is now $minwidth\n"; 1554 } 1555 1556 $minwidth+=10; 1557 $boxwidth+=10; 1558 1559 if ($boxwidth < $minwidth) { $boxwidth=$minwidth; } 1560 1561 $boxheight=$tilespacing * ($nscales + 1) + 10; 1562 1563 $boxx=$x; $boxy=$y; 1564 $boxx=0; 1565 $boxy=0; 1566 1567 // allow for X11-style negative positioning 1568 if ($boxx < 0) { $boxx+=$this->width; } 1569 1570 if ($boxy < 0) { $boxy+=$this->height; } 1571 1572 $scale_im = imagecreatetruecolor($boxwidth+1, $boxheight+1); 1573 $scale_ref = 'gdref_legend_'.$scalename; 1574 $this->AllocateScaleColours($scale_im,$scale_ref); 1575 1576 wimagefilledrectangle($scale_im, $boxx, $boxy, $boxx + $boxwidth, $boxy + $boxheight, 1577 $this->colours['DEFAULT']['KEYBG'][$scale_ref]); 1578 wimagerectangle($scale_im, $boxx, $boxy, $boxx + $boxwidth, $boxy + $boxheight, 1579 $this->colours['DEFAULT']['KEYOUTLINE'][$scale_ref]); 1580 $this->myimagestring($scale_im, $font, $boxx + 4, $boxy + 4 + $tileheight, $title, 1581 $this->colours['DEFAULT']['KEYTEXT'][$scale_ref]); 1582 1583 $i=1; 1584 1585 foreach ($colours as $colour) 1586 { 1587 if (!isset($colour['special']) || $colour['special'] == 0) 1588 // if ( 1==1 || $colour['bottom'] >= 0) 1589 { 1590 // pick a value in the middle... 1591 $value = ($colour['bottom'] + $colour['top']) / 2; 1592 debug(sprintf("%f-%f (%f) %d %d %d\n", $colour['bottom'], $colour['top'], $value, $colour['red1'], $colour['green1'], $colour['blue1'])); 1593 1594 # debug("$i: drawing\n"); 1595 if( ($hide_zero == 0) || $colour['key'] != '0_0') 1596 { 1597 $y=$boxy + $tilespacing * $i + 8; 1598 $x=$boxx + 6; 1599 1600 $fudgefactor = 0; 1601 if( $hid_zero && $colour['bottom']==0 ) 1602 { 1603 // calculate a small offset that can be added, which will hide the zero-value in a 1604 // gradient, but not make the scale incorrect. A quarter of a pixel should do it. 1605 $fudgefactor = ($colour['top'] - $colour['bottom'])/($tilewidth*4); 1606 # warn("FUDGING $fudgefactor\n"); 1607 } 1608 1609 // if it's a gradient, red2 is defined, and we need to sweep the values 1610 if (isset($colour['red2'])) 1611 { 1612 for ($n=0; $n <= $tilewidth; $n++) 1613 { 1614 $value 1615 = $fudgefactor + $colour['bottom'] + ($n / $tilewidth) * ($colour['top'] - $colour['bottom']); 1616 list($ccol,$junk) = $this->NewColourFromPercent($value, $scalename, "", FALSE); 1617 $col = $ccol->gdallocate($scale_im); 1618 wimagefilledrectangle($scale_im, $x + $n, $y, $x + $n, $y + $tileheight, 1619 $col); 1620 } 1621 } 1622 else 1623 { 1624 // pick a value in the middle... 1625 //$value = ($colour['bottom'] + $colour['top']) / 2; 1626 list($ccol,$junk) = $this->NewColourFromPercent($value, $scalename, "", FALSE); 1627 $col = $ccol->gdallocate($scale_im); 1628 wimagefilledrectangle($scale_im, $x, $y, $x + $tilewidth, $y + $tileheight, 1629 $col); 1630 } 1631 1632 if($use_tags) 1633 { 1634 $labelstring = ""; 1635 if(isset($colour['tag'])) $labelstring = $colour['tag']; 1636 } 1637 else 1638 { 1639 $labelstring=sprintf("%s-%s", $colour['bottom'], $colour['top']); 1640 if($hide_percent==0) { $labelstring.="%"; } 1641 } 1642 1643 $this->myimagestring($scale_im, $font, $x + 4 + $tilewidth, $y + $tileheight, $labelstring, 1644 $this->colours['DEFAULT']['KEYTEXT'][$scale_ref]); 1645 $i++; 1646 } 1647 imagecopy($im,$scale_im,$this->keyx[$scalename],$this->keyy[$scalename],0,0,imagesx($scale_im),imagesy($scale_im)); 1648 $this->keyimage[$scalename] = $scale_im; 1649 1650 } 1651 } 1652 1653 $this->imap->addArea("Rectangle", "LEGEND:$scalename", '', 1654 array($this->keyx[$scalename], $this->keyy[$scalename], $this->keyx[$scalename] + $boxwidth, $this->keyy[$scalename] + $boxheight)); 1655 # $this->imap->setProp("href","#","LEGEND"); 1656 # $this->imap->setProp("extrahtml","onclick=\"position_legend();\"","LEGEND"); 1657 1658 } 1659} 1660 1661function DrawTimestamp($im, $font, $colour, $which="") 1662{ 1663 // add a timestamp to the corner, so we can tell if it's all being updated 1664 # $datestring = "Created: ".date("M d Y H:i:s",time()); 1665 # $this->datestamp=strftime($this->stamptext, time()); 1666 1667 switch($which) 1668 { 1669 case "MIN": 1670 $stamp = strftime($this->minstamptext, $this->min_data_time); 1671 $pos_x = $this->mintimex; 1672 $pos_y = $this->mintimey; 1673 break; 1674 case "MAX": 1675 $stamp = strftime($this->maxstamptext, $this->max_data_time); 1676 $pos_x = $this->maxtimex; 1677 $pos_y = $this->maxtimey; 1678 break; 1679 default: 1680 $stamp = $this->datestamp; 1681 $pos_x = $this->timex; 1682 $pos_y = $this->timey; 1683 break; 1684 } 1685 1686 list($boxwidth, $boxheight)=$this->myimagestringsize($font, $stamp); 1687 1688 $x=$this->width - $boxwidth; 1689 $y=$boxheight; 1690 1691 if (($pos_x != 0) && ($pos_y != 0)) 1692 { 1693 $x = $pos_x; 1694 $y = $pos_y; 1695 } 1696 1697 $this->myimagestring($im, $font, $x, $y, $stamp, $colour); 1698 $this->imap->addArea("Rectangle", $which."TIMESTAMP", '', array($x, $y, $x + $boxwidth, $y - $boxheight)); 1699} 1700 1701function DrawTitle($im, $font, $colour) 1702{ 1703 $string = $this->ProcessString($this->title,$this); 1704 1705 if($this->get_hint('screenshot_mode')==1) $string= screenshotify($string); 1706 1707 list($boxwidth, $boxheight)=$this->myimagestringsize($font, $string); 1708 1709 $x=10; 1710 $y=$this->titley - $boxheight; 1711 1712 if (($this->titlex >= 0) && ($this->titley >= 0)) 1713 { 1714 $x=$this->titlex; 1715 $y=$this->titley; 1716 } 1717 1718 $this->myimagestring($im, $font, $x, $y, $string, $colour); 1719 1720 $this->imap->addArea("Rectangle", "TITLE", '', array($x, $y, $x + $boxwidth, $y - $boxheight)); 1721} 1722 1723function ReadConfigNG($input, $is_include=FALSE, $initial_context="GLOBAL") 1724{ 1725 $valid_commands = array( 1726 "GLOBAL.set", "LINK.set", "NODE.set", 1727 "GLOBAL.#","LINK.#","NODE.#", 1728 "GLOBAL.include", "NODE.include", "LINK.include", 1729 1730 "GLOBAL.width", "GLOBAL.height", "GLOBAL.background", 1731 "GLOBAL.scale", "GLOBAL.title", "GLOBAL.titlepos", 1732 "GLOBAL.fontdefine", 1733 "GLOBAL.keystyle", "GLOBAL.titlecolor", "GLOBAL.timecolor", 1734 "GLOBAL.titlefont", "GLOBAL.timefont", "GLOBAL.htmloutputfile", 1735 "GLOBAL.htmlstyle", "GLOBAL.imageoutputfile", "GLOBAL.keyfont", 1736 "GLOBAL.keytextcolor", "GLOBAL.keyoutlinecolor", "GLOBAL.keybgcolor", 1737 "GLOBAL.bgcolor", 1738 1739 "SCALE.keypos", "SCALE.keystyle", "SCALE.scale", 1740 1741 "LINK.width", "LINK.link", "LINK.nodes", 1742 "LINK.target", "LINK.usescale", "LINK.infourl", 1743 "LINK.linkstyle", "LINK.overlibcaption", "LINK.inoverlibcaption", "LINK.outoverlibcaption", 1744 "LINK.inoverlibgraph", "LINK.outoverlibgraph", 1745 "LINK.overlibgraph", "LINK.overlibwidth", "LINK.overlibheight", 1746 "LINK.bwlabel", "LINK.via", "LINK.zorder", "LINK.outlinecolor", 1747 "LINK.notes", "LINK.innotes", "LINK.outnotes","LINK.ininfourl", "LINK.outinfourl", 1748 "LINK.bwstyle", "LINK.template", "LINK.splitpos", "LINK.bwlabelpos", "LINK.incomment", "LINK.outcomment", 1749 "LINK.viastyle", "LINK.bandwidth", "LINK.inbwformat", "LINK.outbwformat", 1750 "LINK.commentstyle", "LINK.commentfont", "LINK.commentfontcolor", "LINK.bwfont", 1751 1752 "NODE.icon", "NODE.target", "NODE.position", "NODE.infourl", "NODE.overlibgraph", 1753 "NODE.zorder", "NODE.label", "NODE.template", "NODE.labelbgcolor", 1754 "NODE.maxvalue", 1755 "NODE.labeloutlinecolor", "NODE.aiconoutlinecolor", "NODE.aiconfillcolor", "NODE.usescale", 1756 "NODE.labelfontcolor", "NODE.labelfont", "NODE.labelangle", "NODE.labelfontshadowcolor", 1757 "NODE.node", "NODE.overlibwidth", "NODE.overlibheight", "NODE.labeloffset" 1758 ); 1759 1760 1761 if( (strchr($input,"\n")!=FALSE) || (strchr($input,"\r")!=FALSE ) ) 1762 { 1763 debug("ReadConfig Detected that this is a config fragment.\n"); 1764 // strip out any Windows line-endings that have gotten in here 1765 $input=str_replace("\r", "", $input); 1766 $lines = split("/n",$input); 1767 $filename = "{text insert}"; 1768 } 1769 else 1770 { 1771 debug("ReadConfig Detected that this is a config filename.\n"); 1772 $filename = $input; 1773 1774 $fd=fopen($filename, "r"); 1775 1776 if ($fd) 1777 { 1778 while (!feof($fd)) 1779 { 1780 $buffer=fgets($fd, 4096); 1781 // strip out any Windows line-endings that have gotten in here 1782 $buffer=str_replace("\r", "", $buffer); 1783 $lines[] = $buffer; 1784 } 1785 fclose($fd); 1786 } 1787 } 1788 1789 $linecount = 0; 1790 $context = $initial_context; 1791 1792 foreach($lines as $buffer) 1793 { 1794 $linematched=0; 1795 $linecount++; 1796 $nextcontext = ""; 1797 $key = ""; 1798 1799 $buffer = trim($buffer); 1800 // alternative for use later where quoted strings are more useful 1801 $args = ParseString($buffer); 1802 1803 if(sizeof($args) > 0) 1804 { 1805 $linematched++; 1806 $cmd = strtolower(array_shift($args)); 1807 1808 if($cmd == 'include') 1809 { 1810 $this->ReadConfigNG($args[0],TRUE, $context); 1811 } 1812 elseif($cmd == 'node') 1813 { 1814 $context = "NODE.".$args[0]; 1815 } 1816 elseif($cmd == 'link') 1817 { 1818 $context = "LINK.".$args[0]; 1819 $vcount = 0; # reset the via-number counter, it's a new link 1820 } 1821 elseif($cmd == 'scale' || $cmd == 'keystyle' || $cmd == 'keypos') 1822 { 1823 if( preg_match("/^[0-9\-]+/i",$args[0]) ) 1824 { 1825 $scalename = "DEFAULT"; 1826 } 1827 else 1828 { 1829 $scalename = array_shift($args); 1830 } 1831 if($cmd=="scale") $key = $args[0]."_".$args[1]; 1832 $nextcontext = $context; 1833 $context = "SCALE.".$scalename; 1834 } 1835 1836 array_unshift($args,$cmd); 1837 1838 if($context == 'GLOBAL') 1839 { 1840 $ctype='GLOBAL'; 1841 } 1842 else 1843 { 1844 list($ctype,$junk) = split("\\.", $context, 2); 1845 } 1846 1847 $lookup = $ctype.".".$cmd; 1848 1849 // Some things (scales, mainly) might define special keys 1850 // the key should be unique for that object 1851 // most (all?) things for a link or node are one-offs. 1852 if($key == "") $key = $cmd; 1853 if($cmd == 'set' || $cmd == 'fontdefine') $key .= "_".$args[1]; 1854 if($cmd == 'via') 1855 { 1856 $key .= "_".$vcount; 1857 $vcount++; 1858 } 1859 1860 # everything else 1861 if( substr($cmd, 0, 1) != '#') 1862 { 1863 if(! in_array($lookup, $valid_commands)) 1864 { 1865 print "INVALID COMMAND: $lookup\n"; 1866 } 1867 1868 if(isset($config[$context][$key])) 1869 { 1870 print "REDEFINED $key in $context\n"; 1871 } 1872 else 1873 { 1874 array_unshift($args,$linecount); 1875 array_unshift($args,$filename); 1876 $this->config[$context][$key] = $args; 1877 } 1878 } 1879 print "$context\\$key $filename:$linecount ".join("|",$args)."\n"; 1880 1881 if($nextcontext != "") $context = $nextcontext; 1882 } 1883 1884 if ($linematched == 0 && trim($buffer) != '') { warn ("Unrecognised config on line $linecount: $buffer\n"); } 1885 1886 } 1887 1888 if(! $is_include) 1889 { 1890 print_r($this->config); 1891 1892 foreach ($this->config as $context=>$values) 1893 { 1894 print "> $context\n"; 1895 } 1896 } 1897 1898 1899} 1900 1901function ReadConfigNNG($input, $is_include=FALSE, $initial_context="GLOBAL") 1902{ 1903 global $valid_commands; 1904 1905 if( (strchr($input,"\n")!=FALSE) || (strchr($input,"\r")!=FALSE ) ) 1906 { 1907 debug("ReadConfig Detected that this is a config fragment.\n"); 1908 // strip out any Windows line-endings that have gotten in here 1909 $input=str_replace("\r", "", $input); 1910 $lines = split("/n",$input); 1911 $filename = "{text insert}"; 1912 } 1913 else 1914 { 1915 debug("ReadConfig Detected that this is a config filename.\n"); 1916 $filename = $input; 1917 1918 $fd=fopen($filename, "r"); 1919 1920 if ($fd) 1921 { 1922 while (!feof($fd)) 1923 { 1924 $buffer=fgets($fd, 4096); 1925 // strip out any Windows line-endings that have gotten in here 1926 $buffer=str_replace("\r", "", $buffer); 1927 $lines[] = $buffer; 1928 } 1929 fclose($fd); 1930 } 1931 } 1932 1933 $linecount = 0; 1934 $context = $initial_context; 1935 1936 foreach($lines as $buffer) 1937 { 1938 $linematched=0; 1939 $linecount++; 1940 $nextcontext = ""; 1941 $key = ""; 1942 1943 $buffer = trim($buffer); 1944 // alternative for use later where quoted strings are more useful 1945 $args = ParseString($buffer); 1946 1947 if(sizeof($args) > 0) 1948 { 1949 $linematched++; 1950 $cmd = strtolower(array_shift($args)); 1951 1952 if($cmd == 'include') 1953 { 1954 $context = $this->ReadConfigNNG($args[0],TRUE, $context); 1955 } 1956 elseif($cmd == 'node') 1957 { 1958 $context = "NODE.".$args[0]; 1959 } 1960 elseif($cmd == 'link') 1961 { 1962 $context = "LINK.".$args[0]; 1963 $vcount = 0; # reset the via-number counter, it's a new link 1964 } 1965 elseif($cmd == 'scale' || $cmd == 'keystyle' || $cmd == 'keypos') 1966 { 1967 if( preg_match("/^[0-9\-]+/i",$args[0]) ) 1968 { 1969 $scalename = "DEFAULT"; 1970 } 1971 else 1972 { 1973 $scalename = array_shift($args); 1974 } 1975 if($cmd=="scale") $key = $args[0]."_".$args[1]; 1976 $nextcontext = $context; 1977 $context = "SCALE.".$scalename; 1978 } 1979 1980 array_unshift($args,$cmd); 1981 1982 if($context == 'GLOBAL') 1983 { 1984 $ctype='GLOBAL'; 1985 } 1986 else 1987 { 1988 list($ctype,$junk) = split("\\.", $context, 2); 1989 } 1990 1991 $lookup = $ctype.".".$cmd; 1992 1993 // Some things (scales, mainly) might define special keys 1994 // the key should be unique for that object 1995 // most (all?) things for a link or node are one-offs. 1996 if($key == "") $key = $cmd; 1997 if($cmd == 'set' || $cmd == 'fontdefine') $key .= "_".$args[1]; 1998 if($cmd == 'via') 1999 { 2000 $key .= "_".$vcount; 2001 $vcount++; 2002 } 2003 2004 # everything else 2005 if( substr($cmd, 0, 1) != '#') 2006 { 2007 if(! array_key_exists($lookup, $valid_commands)) 2008 { 2009 print "INVALID COMMAND: $lookup\n"; 2010 } 2011 2012 if(isset($config[$context][$key])) 2013 { 2014 print "REDEFINED $key in $context\n"; 2015 } 2016 else 2017 { 2018 array_unshift($args,$linecount); 2019 array_unshift($args,$filename); 2020 $this->config[$context][$key] = $args; 2021 } 2022 } 2023 print "$context\\$key $filename:$linecount ".join("|",$args)."\n"; 2024 2025 if($nextcontext != "") $context = $nextcontext; 2026 } 2027 2028 if ($linematched == 0 && trim($buffer) != '') { warn ("Unrecognised config on line $linecount: $buffer\n"); } 2029 2030 } 2031 2032 if(! $is_include) 2033 { 2034 2035 # print_r($this->config); 2036 2037 foreach ($this->config as $context=>$values) 2038 { 2039 # print "> $context\n"; 2040 } 2041 } 2042 2043 return($context); 2044} 2045 2046 2047function WriteConfigNG($filename) 2048{ 2049 global $WEATHERMAP_VERSION; 2050 2051 $fd = fopen($filename); 2052 2053 2054 2055 fclose($fd); 2056} 2057 2058function ReadConfig($input, $is_include=FALSE) 2059{ 2060 $curnode=null; 2061 $curlink=null; 2062 $matches=0; 2063 $nodesseen=0; 2064 $linksseen=0; 2065 $scalesseen=0; 2066 $last_seen="GLOBAL"; 2067 $filename = ""; 2068 $objectlinecount=0; 2069 2070 // check if $input is more than one line. if it is, it's a text of a config file 2071 // if it isn't, it's the filename 2072 2073 $lines = array(); 2074 2075 if( (strchr($input,"\n")!=FALSE) || (strchr($input,"\r")!=FALSE ) ) 2076 { 2077 debug("ReadConfig Detected that this is a config fragment.\n"); 2078 // strip out any Windows line-endings that have gotten in here 2079 $input=str_replace("\r", "", $input); 2080 $lines = split("/n",$input); 2081 $filename = "{text insert}"; 2082 } 2083 else 2084 { 2085 debug("ReadConfig Detected that this is a config filename.\n"); 2086 $filename = $input; 2087 2088 if($is_include){ 2089 debug("ReadConfig Detected that this is an INCLUDED config filename.\n"); 2090 if($is_include && in_array($filename, $this->included_files)) 2091 { 2092 warn("Attempt to include '$filename' twice! Skipping it.\n"); 2093 return(FALSE); 2094 } 2095 else 2096 { 2097 $this->included_files[] = $filename; 2098 $this->has_includes = TRUE; 2099 } 2100 } 2101 2102 $fd=fopen($filename, "r"); 2103 2104 if ($fd) 2105 { 2106 while (!feof($fd)) 2107 { 2108 $buffer=fgets($fd, 4096); 2109 // strip out any Windows line-endings that have gotten in here 2110 $buffer=str_replace("\r", "", $buffer); 2111 $lines[] = $buffer; 2112 } 2113 fclose($fd); 2114 } 2115 } 2116 2117 $linecount = 0; 2118 $objectlinecount = 0; 2119 2120 foreach($lines as $buffer) 2121 { 2122 $linematched=0; 2123 $linecount++; 2124 2125 if (preg_match("/^\s*#/", $buffer)) { 2126 // this is a comment line 2127 } 2128 else 2129 { 2130 $buffer = trim($buffer); 2131 2132 // for any other config elements that are shared between nodes and links, they can use this 2133 unset($curobj); 2134 $curobj = NULL; 2135 if($last_seen == "LINK") $curobj = &$curlink; 2136 if($last_seen == "NODE") $curobj = &$curnode; 2137 if($last_seen == "GLOBAL") $curobj = &$this; 2138 2139 $objectlinecount++; 2140 2141 #if (preg_match("/^\s*(LINK|NODE)\s+([A-Za-z][A-Za-z0-9_\.\-\:]*)\s*$/i", $buffer, $matches)) 2142 if (preg_match("/^\s*(LINK|NODE)\s+(\S+)\s*$/i", $buffer, $matches)) 2143 { 2144 $objectlinecount = 0; 2145 if(1==1) 2146 { 2147 $this->ReadConfig_Commit($curobj); 2148 } 2149 else 2150 { 2151 // first, save the previous item, before starting work on the new one 2152 if ($last_seen == "NODE") 2153 { 2154 $this->nodes[$curnode->name]=$curnode; 2155 if($curnode->template == 'DEFAULT') $this->node_template_tree[ "DEFAULT" ][]= $curnode->name; 2156 2157 debug ("Saving Node: " . $curnode->name . "\n"); 2158 } 2159 2160 if ($last_seen == "LINK") 2161 { 2162 if (isset($curlink->a) && isset($curlink->b)) 2163 { 2164 $this->links[$curlink->name]=$curlink; 2165 debug ("Saving Link: " . $curlink->name . "\n"); 2166 } 2167 else 2168 { 2169 $this->links[$curlink->name]=$curlink; 2170 debug ("Saving Template-Only Link: " . $curlink->name . "\n"); 2171 } 2172 if($curlink->template == 'DEFAULT') $this->link_template_tree[ "DEFAULT" ][]= $curlink->name; 2173 } 2174 } 2175 2176 if ($matches[1] == 'LINK') 2177 { 2178 if ($matches[2] == 'DEFAULT') 2179 { 2180 if ($linksseen > 0) { warn 2181 ("LINK DEFAULT is not the first LINK. Defaults will not apply to earlier LINKs. [WMWARN26]\n"); 2182 } 2183 unset($curlink); 2184 debug("Loaded LINK DEFAULT\n"); 2185 $curlink = $this->links['DEFAULT']; 2186 } 2187 else 2188 { 2189 unset($curlink); 2190 2191 if(isset($this->links[$matches[2]])) 2192 { 2193 warn("Duplicate link name ".$matches[2]." at line $linecount - only the last one defined is used. [WMWARN25]\n"); 2194 } 2195 2196 debug("New LINK ".$matches[2]."\n"); 2197 $curlink=new WeatherMapLink; 2198 $curlink->name=$matches[2]; 2199 $curlink->Reset($this); 2200 2201 $linksseen++; 2202 } 2203 2204 $last_seen="LINK"; 2205 $curlink->configline = $linecount; 2206 $linematched++; 2207 $curobj = &$curlink; 2208 } 2209 2210 if ($matches[1] == 'NODE') 2211 { 2212 if ($matches[2] == 'DEFAULT') 2213 { 2214 if ($nodesseen > 0) { warn 2215 ("NODE DEFAULT is not the first NODE. Defaults will not apply to earlier NODEs. [WMWARN27]\n"); 2216 } 2217 2218 unset($curnode); 2219 debug("Loaded NODE DEFAULT\n"); 2220 $curnode = $this->nodes['DEFAULT']; 2221 } 2222 else 2223 { 2224 unset($curnode); 2225 2226 if(isset($this->nodes[$matches[2]])) 2227 { 2228 warn("Duplicate node name ".$matches[2]." at line $linecount - only the last one defined is used. [WMWARN24]\n"); 2229 } 2230 2231 $curnode=new WeatherMapNode; 2232 $curnode->name=$matches[2]; 2233 $curnode->Reset($this); 2234 2235 $nodesseen++; 2236 } 2237 2238 $curnode->configline = $linecount; 2239 $last_seen="NODE"; 2240 $linematched++; 2241 $curobj = &$curnode; 2242 } 2243 2244 # record where we first heard about this object 2245 $curobj->defined_in = $filename; 2246 } 2247 2248 // most of the config keywords just copy stuff into object properties. 2249 // these are all dealt with from this one array. The special-cases 2250 // follow on from that 2251 $config_keywords = array( 2252 array('LINK','/^\s*(MAXVALUE|BANDWIDTH)\s+(\d+\.?\d*[KMGT]?)\s+(\d+\.?\d*[KMGT]?)\s*$/i',array('max_bandwidth_in_cfg'=>2,'max_bandwidth_out_cfg'=>3)), 2253 array('LINK','/^\s*(MAXVALUE|BANDWIDTH)\s+(\d+\.?\d*[KMGT]?)\s*$/i',array('max_bandwidth_in_cfg'=>2,'max_bandwidth_out_cfg'=>2)), 2254 array('NODE','/^\s*(MAXVALUE)\s+(\d+\.?\d*[KMGT]?)\s+(\d+\.?\d*[KMGT]?)\s*$/i',array('max_bandwidth_in_cfg'=>2,'max_bandwidth_out_cfg'=>3)), 2255 array('NODE','/^\s*(MAXVALUE)\s+(\d+\.?\d*[KMGT]?)\s*$/i',array('max_bandwidth_in_cfg'=>2,'max_bandwidth_out_cfg'=>2)), 2256 array('GLOBAL','/^\s*BACKGROUND\s+(.*)\s*$/i',array('background'=>1)), 2257 array('GLOBAL','/^\s*HTMLOUTPUTFILE\s+(.*)\s*$/i',array('htmloutputfile'=>1)), 2258 array('GLOBAL','/^\s*HTMLSTYLESHEET\s+(.*)\s*$/i',array('htmlstylesheet'=>1)), 2259 array('GLOBAL','/^\s*IMAGEOUTPUTFILE\s+(.*)\s*$/i',array('imageoutputfile'=>1)), 2260 array('GLOBAL','/^\s*IMAGEURI\s+(.*)\s*$/i',array('imageuri'=>1)), 2261 array('GLOBAL','/^\s*TITLE\s+(.*)\s*$/i',array('title'=>1)), 2262 array('GLOBAL','/^\s*HTMLSTYLE\s+(static|overlib)\s*$/i',array('htmlstyle'=>1)), 2263 array('GLOBAL','/^\s*KEYFONT\s+(\d+)\s*$/i',array('keyfont'=>1)), 2264 array('GLOBAL','/^\s*TITLEFONT\s+(\d+)\s*$/i',array('titlefont'=>1)), 2265 array('GLOBAL','/^\s*TIMEFONT\s+(\d+)\s*$/i',array('timefont'=>1)), 2266 array('GLOBAL','/^\s*TITLEPOS\s+(-?\d+)\s+(-?\d+)\s*$/i',array('titlex'=>1, 'titley'=>2)), 2267 array('GLOBAL','/^\s*TITLEPOS\s+(-?\d+)\s+(-?\d+)\s+(.*)\s*$/i',array('titlex'=>1, 'titley'=>2, 'title'=>3)), 2268 array('GLOBAL','/^\s*TIMEPOS\s+(-?\d+)\s+(-?\d+)\s*$/i',array('timex'=>1, 'timey'=>2)), 2269 array('GLOBAL','/^\s*TIMEPOS\s+(-?\d+)\s+(-?\d+)\s+(.*)\s*$/i',array('timex'=>1, 'timey'=>2, 'stamptext'=>3)), 2270 array('GLOBAL','/^\s*MINTIMEPOS\s+(-?\d+)\s+(-?\d+)\s*$/i',array('mintimex'=>1, 'mintimey'=>2)), 2271 array('GLOBAL','/^\s*MINTIMEPOS\s+(-?\d+)\s+(-?\d+)\s+(.*)\s*$/i',array('mintimex'=>1, 'mintimey'=>2, 'minstamptext'=>3)), 2272 array('GLOBAL','/^\s*MAXTIMEPOS\s+(-?\d+)\s+(-?\d+)\s*$/i',array('maxtimex'=>1, 'maxtimey'=>2)), 2273 array('GLOBAL','/^\s*MAXTIMEPOS\s+(-?\d+)\s+(-?\d+)\s+(.*)\s*$/i',array('maxtimex'=>1, 'maxtimey'=>2, 'maxstamptext'=>3)), 2274 array('NODE', "/^\s*LABEL\s*$/i", array('label'=>'')), # special case for blank labels 2275 array('NODE', "/^\s*LABEL\s+(.*)\s*$/i", array('label'=>1)), 2276 array('(LINK|GLOBAL)', "/^\s*WIDTH\s+(\d+)\s*$/i", array('width'=>1)), 2277 array('(LINK|GLOBAL)', "/^\s*HEIGHT\s+(\d+)\s*$/i", array('height'=>1)), 2278 array('LINK', "/^\s*WIDTH\s+(\d+\.\d+)\s*$/i", array('width'=>1)), 2279 array('LINK', '/^\s*ARROWSTYLE\s+(classic|compact)\s*$/i', array('arrowstyle'=>1)), 2280 array('LINK', '/^\s*VIASTYLE\s+(curved|angled)\s*$/i', array('viastyle'=>1)), 2281 array('LINK', '/^\s*INCOMMENT\s+(.*)\s*$/i', array('comments[IN]'=>1)), 2282 array('LINK', '/^\s*OUTCOMMENT\s+(.*)\s*$/i', array('comments[OUT]'=>1)), 2283 array('LINK', '/^\s*BWFONT\s+(\d+)\s*$/i', array('bwfont'=>1)), 2284 array('LINK', '/^\s*COMMENTFONT\s+(\d+)\s*$/i', array('commentfont'=>1)), 2285 array('LINK', '/^\s*COMMENTSTYLE\s+(edge|center)\s*$/i', array('commentstyle'=>1)), 2286 array('LINK', '/^\s*DUPLEX\s+(full|half)\s*$/i', array('duplex'=>1)), 2287 array('LINK', '/^\s*BWSTYLE\s+(classic|angled)\s*$/i', array('labelboxstyle'=>1)), 2288 array('LINK', '/^\s*LINKSTYLE\s+(twoway|oneway)\s*$/i', array('linkstyle'=>1)), 2289 array('LINK', '/^\s*BWLABELPOS\s+(\d+)\s(\d+)\s*$/i', array('labeloffset_in'=>1,'labeloffset_out'=>2)), 2290 array('LINK', '/^\s*COMMENTPOS\s+(\d+)\s(\d+)\s*$/i', array('commentoffset_in'=>1, 'commentoffset_out'=>2)), 2291 array('LINK', '/^\s*USESCALE\s+([A-Za-z][A-Za-z0-9_]*)\s*$/i', array('usescale'=>1)), 2292 array('LINK', '/^\s*USESCALE\s+([A-Za-z][A-Za-z0-9_]*)\s+(absolute|percent)\s*$/i', array('usescale'=>1,'scaletype'=>2)), 2293 2294 array('LINK', '/^\s*SPLITPOS\s+(\d+)\s*$/i', array('splitpos'=>1)), 2295 2296 array('NODE', '/^\s*LABELOFFSET\s+([-+]?\d+)\s+([-+]?\d+)\s*$/i', array('labeloffsetx'=>1,'labeloffsety'=>2)), 2297 array('NODE', '/^\s*LABELOFFSET\s+(C|NE|SE|NW|SW|N|S|E|W)\s*$/i', array('labeloffset'=>1)), 2298 array('NODE', '/^\s*LABELOFFSET\s+((C|NE|SE|NW|SW|N|S|E|W)\d+)\s*$/i', array('labeloffset'=>1)), 2299 array('NODE', '/^\s*LABELOFFSET\s+(-?\d+r\d+)\s*$/i', array('labeloffset'=>1)), 2300 2301 array('NODE', '/^\s*LABELFONT\s+(\d+)\s*$/i', array('labelfont'=>1)), 2302 array('NODE', '/^\s*LABELANGLE\s+(0|90|180|270)\s*$/i', array('labelangle'=>1)), 2303 # array('(NODE|LINK)', '/^\s*TEMPLATE\s+(\S+)\s*$/i', array('template'=>1)), 2304 2305 array('LINK', '/^\s*OUTBWFORMAT\s+(.*)\s*$/i', array('bwlabelformats[OUT]'=>1,'labelstyle'=>'--')), 2306 array('LINK', '/^\s*INBWFORMAT\s+(.*)\s*$/i', array('bwlabelformats[IN]'=>1,'labelstyle'=>'--')), 2307 # array('NODE','/^\s*ICON\s+none\s*$/i',array('iconfile'=>'')), 2308 array('NODE','/^\s*ICON\s+(\S+)\s*$/i',array('iconfile'=>1, 'iconscalew'=>'#0', 'iconscaleh'=>'#0')), 2309 array('NODE','/^\s*ICON\s+(\S+)\s*$/i',array('iconfile'=>1)), 2310 array('NODE','/^\s*ICON\s+(\d+)\s+(\d+)\s+(inpie|outpie|box|rbox|round|gauge|nink)\s*$/i',array('iconfile'=>3, 'iconscalew'=>1, 'iconscaleh'=>2)), 2311 array('NODE','/^\s*ICON\s+(\d+)\s+(\d+)\s+(\S+)\s*$/i',array('iconfile'=>3, 'iconscalew'=>1, 'iconscaleh'=>2)), 2312 2313 array('NODE','/^\s*NOTES\s+(.*)\s*$/i',array('notestext[IN]'=>1,'notestext[OUT]'=>1)), 2314 array('LINK','/^\s*NOTES\s+(.*)\s*$/i',array('notestext[IN]'=>1,'notestext[OUT]'=>1)), 2315 array('LINK','/^\s*INNOTES\s+(.*)\s*$/i',array('notestext[IN]'=>1)), 2316 array('LINK','/^\s*OUTNOTES\s+(.*)\s*$/i',array('notestext[OUT]'=>1)), 2317 2318 array('NODE','/^\s*INFOURL\s+(.*)\s*$/i',array('infourl[IN]'=>1,'infourl[OUT]'=>1)), 2319 array('LINK','/^\s*INFOURL\s+(.*)\s*$/i',array('infourl[IN]'=>1,'infourl[OUT]'=>1)), 2320 array('LINK','/^\s*ININFOURL\s+(.*)\s*$/i',array('infourl[IN]'=>1)), 2321 array('LINK','/^\s*OUTINFOURL\s+(.*)\s*$/i',array('infourl[OUT]'=>1)), 2322 2323 array('NODE','/^\s*OVERLIBCAPTION\s+(.*)\s*$/i',array('overlibcaption[IN]'=>1,'overlibcaption[OUT]'=>1)), 2324 array('LINK','/^\s*OVERLIBCAPTION\s+(.*)\s*$/i',array('overlibcaption[IN]'=>1,'overlibcaption[OUT]'=>1)), 2325 array('LINK','/^\s*INOVERLIBCAPTION\s+(.*)\s*$/i',array('overlibcaption[IN]'=>1)), 2326 array('LINK','/^\s*OUTOVERLIBCAPTION\s+(.*)\s*$/i',array('overlibcaption[OUT]'=>1)), 2327 2328 array('(NODE|LINK)', "/^\s*ZORDER\s+([-+]?\d+)\s*$/i", array('zorder'=>1)), 2329 array('(NODE|LINK)', "/^\s*OVERLIBWIDTH\s+(\d+)\s*$/i", array('overlibwidth'=>1)), 2330 array('(NODE|LINK)', "/^\s*OVERLIBHEIGHT\s+(\d+)\s*$/i", array('overlibheight'=>1)), 2331 array('NODE', "/^\s*POSITION\s+([-+]?\d+)\s+([-+]?\d+)\s*$/i", array('x'=>1,'y'=>2)), 2332 array('NODE', "/^\s*POSITION\s+(\S+)\s+([-+]?\d+)\s+([-+]?\d+)\s*$/i", array('x'=>2,'y'=>3,'original_x'=>2,'original_y'=>3,'relative_to'=>1,'relative_resolved'=>FALSE)), 2333 array('NODE', "/^\s*POSITION\s+(\S+)\s+([-+]?\d+)r(\d+)\s*$/i", array('x'=>2,'y'=>3,'original_x'=>2,'original_y'=>3,'relative_to'=>1,'polar'=>TRUE,'relative_resolved'=>FALSE)) 2334 ); 2335 2336 // alternative for use later where quoted strings are more useful 2337 $args = ParseString($buffer); 2338 2339 // this loop replaces a whole pile of duplicated ifs with something with consistent handling 2340 foreach ($config_keywords as $keyword) 2341 { 2342 if(preg_match("/".$keyword[0]."/",$last_seen)) 2343 { 2344 $statskey = $last_seen."-".$keyword[1]; 2345 $statskey = str_replace( array('/^\s*','\s*$/i'),array('',''), $statskey); 2346 if(!isset($this->usage_stats[$statskey])) $this->usage_stats[$statskey] = 0; 2347 2348 if(preg_match($keyword[1],$buffer,$matches)) 2349 { 2350 # print "CONFIG MATCHED: ".$keyword[1]."\n"; 2351 2352 $this->usage_stats[$statskey]++; 2353 2354 foreach ($keyword[2] as $key=>$val) 2355 { 2356 // so we can poke in numbers too, if the value starts with # 2357 // then take the # off, and treat the rest as a number literal 2358 if(preg_match("/^#(.*)/",$val,$m)) 2359 { 2360 $val = $m[1]; 2361 } 2362 elseif(is_numeric($val)) 2363 { 2364 // if it's a number, then it;s a match number, 2365 // otherwise it's a literal to be put into a variable 2366 $val = $matches[$val]; 2367 } 2368 2369 assert('is_object($curobj)'); 2370 2371 if(preg_match('/^(.*)\[([^\]]+)\]$/',$key,$m)) 2372 { 2373 $index = constant($m[2]); 2374 $key = $m[1]; 2375 $curobj->{$key}[$index] = $val; 2376 } 2377 else 2378 { 2379 $curobj->$key = $val; 2380 } 2381 } 2382 $linematched++; 2383 # print "\n\n"; 2384 break; 2385 } 2386 } 2387 } 2388 2389 if (preg_match("/^\s*NODES\s+(\S+)\s+(\S+)\s*$/i", $buffer, $matches)) 2390 { 2391 if ($last_seen == 'LINK') 2392 { 2393 $valid_nodes=2; 2394 2395 foreach (array(1, 2)as $i) 2396 { 2397 $endoffset[$i]='C'; 2398 $nodenames[$i]=$matches[$i]; 2399 2400 // percentage of compass - must be first 2401 if (preg_match("/:(NE|SE|NW|SW|N|S|E|W|C)(\d+)$/i", $matches[$i], $submatches)) 2402 { 2403 $endoffset[$i]=$submatches[1].$submatches[2]; 2404 $nodenames[$i]=preg_replace("/:(NE|SE|NW|SW|N|S|E|W|C)\d+$/i", '', $matches[$i]); 2405 $this->need_size_precalc=TRUE; 2406 } 2407 2408 if (preg_match("/:(NE|SE|NW|SW|N|S|E|W|C)$/i", $matches[$i], $submatches)) 2409 { 2410 $endoffset[$i]=$submatches[1]; 2411 $nodenames[$i]=preg_replace("/:(NE|SE|NW|SW|N|S|E|W|C)$/i", '', $matches[$i]); 2412 $this->need_size_precalc=TRUE; 2413 } 2414 2415 if( preg_match("/:(-?\d+r\d+)$/i", $matches[$i], $submatches) ) 2416 { 2417 $endoffset[$i]=$submatches[1]; 2418 $nodenames[$i]=preg_replace("/:(-?\d+r\d+)$/i", '', $matches[$i]); 2419 $this->need_size_precalc=TRUE; 2420 } 2421 2422 if (preg_match("/:([-+]?\d+):([-+]?\d+)$/i", $matches[$i], $submatches)) 2423 { 2424 $xoff = $submatches[1]; 2425 $yoff = $submatches[2]; 2426 $endoffset[$i]=$xoff.":".$yoff; 2427 $nodenames[$i]=preg_replace("/:$xoff:$yoff$/i", '', $matches[$i]); 2428 $this->need_size_precalc=TRUE; 2429 } 2430 2431 if (!array_key_exists($nodenames[$i], $this->nodes)) 2432 { 2433 warn ("Unknown node '" . $nodenames[$i] . "' on line $linecount of config\n"); 2434 $valid_nodes--; 2435 } 2436 } 2437 2438 // TODO - really, this should kill the whole link, and reset for the next one 2439 if ($valid_nodes == 2) 2440 { 2441 $curlink->a=$this->nodes[$nodenames[1]]; 2442 $curlink->b=$this->nodes[$nodenames[2]]; 2443 $curlink->a_offset=$endoffset[1]; 2444 $curlink->b_offset=$endoffset[2]; 2445 } 2446 else { 2447 // this'll stop the current link being added 2448 $last_seen="broken"; } 2449 2450 $linematched++; 2451 } 2452 } 2453 2454 if ( $last_seen=='GLOBAL' && preg_match("/^\s*INCLUDE\s+(.*)\s*$/i", $buffer, $matches)) 2455 { 2456 if(file_exists($matches[1])){ 2457 debug("Including '{$matches[1]}'\n"); 2458 $this->ReadConfig($matches[1], TRUE); 2459 $last_seen = "GLOBAL"; 2460 }else{ 2461 warn("INCLUDE File '{$matches[1]}' not found!\n"); 2462 } 2463 $linematched++; 2464 } 2465 2466 if ( ( $last_seen=='NODE' || $last_seen=='LINK' ) && preg_match("/^\s*TARGET\s+(.*)\s*$/i", $buffer, $matches)) 2467 { 2468 $linematched++; 2469 # $targets=preg_split('/\s+/', $matches[1], -1, PREG_SPLIT_NO_EMPTY); 2470 $rawtargetlist = $matches[1]." "; 2471 2472 if($args[0]=='TARGET') 2473 { 2474 // wipe any existing targets, otherwise things in the DEFAULT accumulate with the new ones 2475 $curobj->targets = array(); 2476 array_shift($args); // take off the actual TARGET keyword 2477 2478 foreach($args as $arg) 2479 { 2480 // we store the original TARGET string, and line number, along with the breakdown, to make nicer error messages later 2481 // array of 7 things: 2482 // - only 0,1,2,3,4 are used at the moment (more used to be before DS plugins) 2483 // 0 => final target string (filled in by ReadData) 2484 // 1 => multiplier (filled in by ReadData) 2485 // 2 => config filename where this line appears 2486 // 3 => linenumber in that file 2487 // 4 => the original target string 2488 // 5 => the plugin to use to pull data 2489 $newtarget=array('','',$filename,$linecount,$arg,"",""); 2490 if ($curobj) 2491 { 2492 debug(" TARGET: $arg\n"); 2493 $curobj->targets[]=$newtarget; 2494 } 2495 } 2496 } 2497 } 2498 2499 if ($last_seen == 'LINK' && preg_match( 2500 "/^\s*BWLABEL\s+(bits|percent|unformatted|none)\s*$/i", $buffer, 2501 $matches)) 2502 { 2503 $format_in = ''; 2504 $format_out = ''; 2505 $style = strtolower($matches[1]); 2506 if($style=='percent') 2507 { 2508 $format_in = FMT_PERC_IN; 2509 $format_out = FMT_PERC_OUT; 2510 } 2511 if($style=='bits') 2512 { 2513 $format_in = FMT_BITS_IN; 2514 $format_out = FMT_BITS_OUT; 2515 } 2516 if($style=='unformatted') 2517 { 2518 $format_in = FMT_UNFORM_IN; 2519 $format_out = FMT_UNFORM_OUT; 2520 } 2521 2522 $curobj->labelstyle=$style; 2523 $curobj->bwlabelformats[IN] = $format_in; 2524 $curobj->bwlabelformats[OUT] = $format_out; 2525 $linematched++; 2526 } 2527 2528 if (preg_match("/^\s*SET\s+(\S+)\s+(.*)\s*$/i", $buffer, $matches)) 2529 { 2530 $curobj->add_hint($matches[1],trim($matches[2])); 2531 $linematched++; 2532 } 2533 2534 // allow setting a variable to "" 2535 if (preg_match("/^\s*SET\s+(\S+)\s*$/i", $buffer, $matches)) 2536 { 2537 $curobj->add_hint($matches[1],''); 2538 $linematched++; 2539 } 2540 2541 if (preg_match("/^\s*(IN|OUT)?OVERLIBGRAPH\s+(.+)$/i", $buffer, $matches)) 2542 { 2543 $this->has_overlibs = TRUE; 2544 if($last_seen == 'NODE' && $matches[1] != '') { 2545 warn("IN/OUTOVERLIBGRAPH make no sense for a NODE! [WMWARN42]\n"); 2546 } else if($last_seen == 'LINK' || $last_seen=='NODE' ) { 2547 2548 $urls = preg_split('/\s+/', $matches[2], -1, PREG_SPLIT_NO_EMPTY); 2549 2550 if($matches[1] == 'IN') $index = IN; 2551 if($matches[1] == 'OUT') $index = OUT; 2552 if($matches[1] == '') { 2553 $curobj->overliburl[IN]=$urls; 2554 $curobj->overliburl[OUT]=$urls; 2555 } else { 2556 $curobj->overliburl[$index]=$urls; 2557 } 2558 $linematched++; 2559 } 2560 } 2561 2562 // array('(NODE|LINK)', '/^\s*TEMPLATE\s+(\S+)\s*$/i', array('template'=>1)), 2563 2564 if ( ( $last_seen=='NODE' || $last_seen=='LINK' ) && preg_match("/^\s*TEMPLATE\s+(\S+)\s*$/i", $buffer, $matches)) 2565 { 2566 $tname = $matches[1]; 2567 if( ($last_seen=='NODE' && isset($this->nodes[$tname])) || ($last_seen=='LINK' && isset($this->links[$tname])) ) 2568 { 2569 $curobj->template = $matches[1]; 2570 debug("Resetting to template $last_seen ".$curobj->template."\n"); 2571 $curobj->Reset($this); 2572 if( $objectlinecount > 1 ) warn("line $linecount: TEMPLATE is not first line of object. Some data may be lost. [WMWARN39]\n"); 2573 // build up a list of templates - this will be useful later for the tree view 2574 2575 if($last_seen == 'NODE') $this->node_template_tree[ $tname ][]= $curobj->name; 2576 if($last_seen == 'LINK') $this->link_template_tree[ $tname ][]= $curobj->name; 2577 } 2578 else 2579 { 2580 warn("line $linecount: $last_seen TEMPLATE '$tname' doesn't exist! (if it does exist, check it's defined first) [WMWARN40]\n"); 2581 } 2582 $linematched++; 2583 2584 } 2585 2586 if ($last_seen == 'LINK' && preg_match("/^\s*VIA\s+([-+]?\d+)\s+([-+]?\d+)\s*$/i", $buffer, $matches)) 2587 { 2588 $curlink->vialist[]=array 2589 ( 2590 $matches[1], 2591 $matches[2] 2592 ); 2593 2594 $linematched++; 2595 } 2596 2597 if ($last_seen == 'LINK' && preg_match("/^\s*VIA\s+(\S+)\s+([-+]?\d+)\s+([-+]?\d+)\s*$/i", $buffer, $matches)) 2598 { 2599 $curlink->vialist[]=array 2600 ( 2601 $matches[2], 2602 $matches[3], 2603 $matches[1] 2604 ); 2605 2606 $linematched++; 2607 } 2608 2609 if( ($last_seen == 'NODE') && preg_match("/^\s*USE(ICON)?SCALE\s+([A-Za-z][A-Za-z0-9_]*)(\s+(in|out))?(\s+(absolute|percent))?\s*$/i",$buffer,$matches)) 2610 { 2611 $svar = ''; 2612 $stype = 'percent'; 2613 if(isset($matches[3])) 2614 { 2615 $svar = trim($matches[3]); 2616 } 2617 if(isset($matches[6])) 2618 { 2619 $stype = strtolower(trim($matches[6])); 2620 } 2621 // opens the door for other scaley things... 2622 switch($matches[1]) 2623 { 2624 case 'ICON': 2625 $varname = 'iconscalevar'; 2626 $uvarname = 'useiconscale'; 2627 $tvarname = 'iconscaletype'; 2628 2629 // if(!function_exists("imagefilter")) 2630 // { 2631 // warn("ICON SCALEs require imagefilter, which is not present in your PHP [WMWARN040]\n"); 2632 // } 2633 break; 2634 default: 2635 $varname = 'scalevar'; 2636 $uvarname = 'usescale'; 2637 $tvarname = 'scaletype'; 2638 break; 2639 } 2640 2641 if($svar != '') 2642 { 2643 $curnode->$varname = $svar; 2644 } 2645 $curnode->$tvarname = $stype; 2646 $curnode->$uvarname = $matches[2]; 2647 2648 // warn("Set $varname and $uvarname\n"); 2649 2650 // print ">> $stype $svar ".$matches[2]." ".$curnode->name." \n"; 2651 2652 $linematched++; 2653 } 2654 2655 // one REGEXP to rule them all: 2656// if(preg_match("/^\s*SCALE\s+([A-Za-z][A-Za-z0-9_]*\s+)?(\d+\.?\d*)\s+(\d+\.?\d*)\s+(\d+)\s+(\d+)\s+(\d+)(?:\s+(\d+)\s+(\d+)\s+(\d+))?\s*$/i", 2657// 0.95b if(preg_match("/^\s*SCALE\s+([A-Za-z][A-Za-z0-9_]*\s+)?(\d+\.?\d*)\s+(\d+\.?\d*)\s+(\d+)\s+(\d+)\s+(\d+)(?:\s+(\d+)\s+(\d+)\s+(\d+))?\s*(.*)$/i", 2658 if(preg_match("/^\s*SCALE\s+([A-Za-z][A-Za-z0-9_]*\s+)?(\-?\d+\.?\d*[munMGT]?)\s+(\-?\d+\.?\d*[munMGT]?)\s+(?:(\d+)\s+(\d+)\s+(\d+)(?:\s+(\d+)\s+(\d+)\s+(\d+))?|(none))\s*(.*)$/i", 2659 $buffer, $matches)) 2660 { 2661 // The default scale name is DEFAULT 2662 if($matches[1]=='') $matches[1] = 'DEFAULT'; 2663 else $matches[1] = trim($matches[1]); 2664 2665 $key=$matches[2] . '_' . $matches[3]; 2666 2667 $this->colours[$matches[1]][$key]['key']=$key; 2668 2669 $tag = $matches[11]; 2670 2671 $this->colours[$matches[1]][$key]['tag']=$tag; 2672 2673 $this->colours[$matches[1]][$key]['bottom'] = unformat_number($matches[2], $this->kilo); 2674 $this->colours[$matches[1]][$key]['top'] = unformat_number($matches[3], $this->kilo); 2675 $this->colours[$matches[1]][$key]['special'] = 0; 2676 2677 if(isset($matches[10]) && $matches[10] == 'none') 2678 { 2679 $this->colours[$matches[1]][$key]['red1'] = -1; 2680 $this->colours[$matches[1]][$key]['green1'] = -1; 2681 $this->colours[$matches[1]][$key]['blue1'] = -1; 2682 } 2683 else 2684 { 2685 $this->colours[$matches[1]][$key]['red1'] = (int)($matches[4]); 2686 $this->colours[$matches[1]][$key]['green1'] = (int)($matches[5]); 2687 $this->colours[$matches[1]][$key]['blue1'] = (int)($matches[6]); 2688 } 2689 2690 // this is the second colour, if there is one 2691 if(isset($matches[7]) && $matches[7] != '') 2692 { 2693 $this->colours[$matches[1]][$key]['red2'] = (int) ($matches[7]); 2694 $this->colours[$matches[1]][$key]['green2'] = (int) ($matches[8]); 2695 $this->colours[$matches[1]][$key]['blue2'] = (int) ($matches[9]); 2696 } 2697 2698 2699 if(! isset($this->numscales[$matches[1]])) 2700 { 2701 $this->numscales[$matches[1]]=1; 2702 } 2703 else 2704 { 2705 $this->numscales[$matches[1]]++; 2706 } 2707 // we count if we've seen any default scale, otherwise, we have to add 2708 // one at the end. 2709 if($matches[1]=='DEFAULT') 2710 { 2711 $scalesseen++; 2712 } 2713 2714 $linematched++; 2715 } 2716 2717 if (preg_match("/^\s*KEYPOS\s+([A-Za-z][A-Za-z0-9_]*\s+)?(-?\d+)\s+(-?\d+)(.*)/i", $buffer, $matches)) 2718 { 2719 $whichkey = trim($matches[1]); 2720 if($whichkey == '') $whichkey = 'DEFAULT'; 2721 2722 $this->keyx[$whichkey]=$matches[2]; 2723 $this->keyy[$whichkey]=$matches[3]; 2724 $extra=trim($matches[4]); 2725 2726 if ($extra != '') 2727 $this->keytext[$whichkey] = $extra; 2728 if(!isset($this->keytext[$whichkey])) 2729 $this->keytext[$whichkey] = "DEFAULT TITLE"; 2730 if(!isset($this->keystyle[$whichkey])) 2731 $this->keystyle[$whichkey] = "classic"; 2732 2733 $linematched++; 2734 } 2735 2736 2737 // truetype font definition (actually, we don't really check if it's truetype) - filename + size 2738 if (preg_match("/^\s*FONTDEFINE\s+(\d+)\s+(\S+)\s+(\d+)\s*$/i", $buffer, $matches)) 2739 { 2740 if (function_exists("imagettfbbox")) 2741 { 2742 // test if this font is valid, before adding it to the font table... 2743 $bounds=@imagettfbbox($matches[3], 0, $matches[2], "Ignore me"); 2744 2745 if (isset($bounds[0])) 2746 { 2747 $this->fonts[$matches[1]]->type="truetype"; 2748 $this->fonts[$matches[1]]->file=$matches[2]; 2749 $this->fonts[$matches[1]]->size=$matches[3]; 2750 } 2751 else { warn 2752 ("Failed to load ttf font " . $matches[2] . " - at config line $linecount\n [WMWARN30]"); } 2753 } 2754 else { warn 2755 ("imagettfbbox() is not a defined function. You don't seem to have FreeType compiled into your gd module. [WMWARN31]\n"); 2756 } 2757 2758 $linematched++; 2759 } 2760 2761 // GD font definition (no size here) 2762 if (preg_match("/^\s*FONTDEFINE\s+(\d+)\s+(\S+)\s*$/i", $buffer, $matches)) 2763 { 2764 $newfont=imageloadfont($matches[2]); 2765 2766 if ($newfont) 2767 { 2768 $this->fonts[$matches[1]]->type="gd"; 2769 $this->fonts[$matches[1]]->file=$matches[2]; 2770 $this->fonts[$matches[1]]->gdnumber=$newfont; 2771 } 2772 else { warn ("Failed to load GD font: " . $matches[2] 2773 . " ($newfont) at config line $linecount [WMWARN32]\n"); } 2774 2775 $linematched++; 2776 } 2777 2778 if(preg_match("/^\s*KEYSTYLE\s+([A-Za-z][A-Za-z0-9_]+\s+)?(classic|horizontal|vertical|inverted|tags)\s?(\d+)?\s*$/i",$buffer, $matches)) 2779 { 2780 $whichkey = trim($matches[1]); 2781 if($whichkey == '') $whichkey = 'DEFAULT'; 2782 $this->keystyle[$whichkey] = strtolower($matches[2]); 2783 2784 if(isset($matches[3]) && $matches[3] != '') 2785 { 2786 $this->keysize[$whichkey] = $matches[3]; 2787 } 2788 else 2789 { 2790 $this->keysize[$whichkey] = $this->keysize['DEFAULT']; 2791 } 2792 2793 $linematched++; 2794 } 2795 2796 2797 if (preg_match("/^\s*KILO\s+(\d+)\s*$/i", $buffer, $matches)) 2798 { 2799 $this->kilo=$matches[1]; 2800 # $this->defaultlink->owner->kilo=$matches[1]; 2801 # $this->links['DEFAULT']=$matches[1]; 2802 $linematched++; 2803 } 2804 2805 if (preg_match( 2806 "/^\s*(TIME|TITLE|KEYBG|KEYTEXT|KEYOUTLINE|BG)COLOR\s+(\d+)\s+(\d+)\s+(\d+)\s*$/i", 2807 $buffer, 2808 $matches)) 2809 { 2810 $key=$matches[1]; 2811 # "Found colour line for $key\n"; 2812 $this->colours['DEFAULT'][$key]['red1']=$matches[2]; 2813 $this->colours['DEFAULT'][$key]['green1']=$matches[3]; 2814 $this->colours['DEFAULT'][$key]['blue1']=$matches[4]; 2815 $this->colours['DEFAULT'][$key]['bottom']=-2; 2816 $this->colours['DEFAULT'][$key]['top']=-1; 2817 $this->colours['DEFAULT'][$key]['special']=1; 2818 2819 $linematched++; 2820 } 2821 2822 if (($last_seen == 'NODE') && (preg_match( 2823 "/^\s*(AICONOUTLINE|AICONFILL|LABELFONT|LABELFONTSHADOW|LABELBG|LABELOUTLINE)COLOR\s+((\d+)\s+(\d+)\s+(\d+)|none|contrast|copy)\s*$/i", 2824 $buffer, 2825 $matches))) 2826 { 2827 $key=$matches[1]; 2828 $field=strtolower($matches[1]) . 'colour'; 2829 $val = strtolower($matches[2]); 2830 2831 if(isset($matches[3])) // this is a regular colour setting thing 2832 { 2833 $curnode->$field=array( $matches[3],$matches[4],$matches[5]); 2834 $linematched++; 2835 } 2836 2837 if($val == 'none' && ($matches[1]=='LABELFONTSHADOW' || $matches[1]=='LABELBG' || $matches[1]=='LABELOUTLINE' || $matches[1]=='AICONOUTLINE')) 2838 { 2839 $curnode->$field=array(-1,-1,-1); 2840 $linematched++; 2841 } 2842 2843 if($val == 'contrast' && $matches[1]=='LABELFONT') 2844 { 2845 $curnode->$field=array(-3,-3,-3); 2846 $linematched++; 2847 } 2848 2849 if($matches[2] == 'copy' && $matches[1]=='AICONFILL') 2850 { 2851 $curnode->$field=array(-2,-2,-2); 2852 $linematched++; 2853 } 2854 } 2855 2856 if (($last_seen == 'LINK') && (preg_match( 2857 "/^\s*(COMMENTFONT|BWBOX|BWFONT|BWOUTLINE|OUTLINE)COLOR\s+((\d+)\s+(\d+)\s+(\d+)|none|contrast|copy)\s*$/i", 2858 $buffer, 2859 $matches))) 2860 { 2861 $key=$matches[1]; 2862 $field=strtolower($matches[1]) . 'colour'; 2863 $val = strtolower($matches[2]); 2864 2865 if(isset($matches[3])) // this is a regular colour setting thing 2866 { 2867 $curlink->$field=array( $matches[3],$matches[4],$matches[5]); 2868 $linematched++; 2869 } 2870 2871 if($val == 'none' && ($key=='BWBOX' || $key=='BWOUTLINE' || $key=='OUTLINE')) 2872 { 2873 // print "***********************************\n"; 2874 $curlink->$field=array(-1,-1,-1); 2875 $linematched++; 2876 } 2877 2878 if($val == 'contrast' && $key=='COMMENTFONT') 2879 { 2880 // print "***********************************\n"; 2881 $curlink->$field=array(-3,-3,-3); 2882 $linematched++; 2883 } 2884 } 2885 2886 if ($last_seen == 'LINK' && preg_match( 2887 "/^\s*ARROWSTYLE\s+(\d+)\s+(\d+)\s*$/i", $buffer, $matches)) 2888 { 2889 $curlink->arrowstyle=$matches[1] . ' ' . $matches[2]; 2890 $linematched++; 2891 } 2892 2893 2894 if ($linematched == 0 && trim($buffer) != '') { warn 2895 ("Unrecognised config on line $linecount: $buffer\n"); } 2896 2897 if ($linematched > 1) { warn 2898 ("Same line ($linecount) interpreted twice. This is a program error. Please report to Howie with your config!\nThe line was: $buffer"); 2899 } 2900 } // if blankline 2901 } // while 2902 2903 if(1==1) 2904 { 2905 $this->ReadConfig_Commit($curobj); 2906 } 2907 else 2908 { 2909 if ($last_seen == "NODE") 2910 { 2911 $this->nodes[$curnode->name]=$curnode; 2912 debug ("Saving Node: " . $curnode->name . "\n"); 2913 if($curnode->template == 'DEFAULT') $this->node_template_tree[ "DEFAULT" ][]= $curnode->name; 2914 } 2915 2916 if ($last_seen == "LINK") 2917 { 2918 if (isset($curlink->a) && isset($curlink->b)) 2919 { 2920 $this->links[$curlink->name]=$curlink; 2921 debug ("Saving Link: " . $curlink->name . "\n"); 2922 if($curlink->template == 'DEFAULT') $this->link_template_tree[ "DEFAULT" ][]= $curlink->name; 2923 } 2924 else { warn ("Dropping LINK " . $curlink->name . " - it hasn't got 2 NODES!"); } 2925 } 2926 } 2927 2928 2929 debug("ReadConfig has finished reading the config ($linecount lines)\n"); 2930 debug("------------------------------------------\n"); 2931 2932 // load some default colouring, otherwise it all goes wrong 2933 if ($scalesseen == 0) 2934 { 2935 debug ("Adding default SCALE colour set (no SCALE lines seen).\n"); 2936 $defaults=array 2937 ( 2938 '0_0' => array('bottom' => 0, 'top' => 0, 'red1' => 192, 'green1' => 192, 'blue1' => 192, 'special'=>0), 2939 '0_1' => array('bottom' => 0, 'top' => 1, 'red1' => 255, 'green1' => 255, 'blue1' => 255, 'special'=>0), 2940 '1_10' => array('bottom' => 1, 'top' => 10, 'red1' => 140, 'green1' => 0, 'blue1' => 255, 'special'=>0), 2941 '10_25' => array('bottom' => 10, 'top' => 25, 'red1' => 32, 'green1' => 32, 'blue1' => 255, 'special'=>0), 2942 '25_40' => array('bottom' => 25, 'top' => 40, 'red1' => 0, 'green1' => 192, 'blue1' => 255, 'special'=>0), 2943 '40_55' => array('bottom' => 40, 'top' => 55, 'red1' => 0, 'green1' => 240, 'blue1' => 0, 'special'=>0), 2944 '55_70' => array('bottom' => 55, 'top' => 70, 'red1' => 240, 'green1' => 240, 'blue1' => 0, 'special'=>0), 2945 '70_85' => array('bottom' => 70, 'top' => 85, 'red1' => 255, 'green1' => 192, 'blue1' => 0, 'special'=>0), 2946 '85_100' => array('bottom' => 85, 'top' => 100, 'red1' => 255, 'green1' => 0, 'blue1' => 0, 'special'=>0) 2947 ); 2948 2949 foreach ($defaults as $key => $def) 2950 { 2951 $this->colours['DEFAULT'][$key]=$def; 2952 $this->colours['DEFAULT'][$key]['key']=$key; 2953 $scalesseen++; 2954 } 2955 // we have a 0-0 line now, so we need to hide that. 2956 $this->add_hint("key_hidezero_DEFAULT",1); 2957 } 2958 else { debug ("Already have $scalesseen scales, no defaults added.\n"); } 2959 2960 $this->numscales['DEFAULT']=$scalesseen; 2961 $this->configfile="$filename"; 2962 2963 if($this->has_overlibs && $this->htmlstyle == 'static') 2964 { 2965 warn("OVERLIBGRAPH is used, but HTMLSTYLE is static. This is probably wrong. [WMWARN41]\n"); 2966 } 2967 2968 debug("Building cache of z-layers and finalising bandwidth.\n"); 2969 2970// $allitems = array_merge($this->links, $this->nodes); 2971 2972 $allitems = array(); 2973 foreach ($this->nodes as $node) 2974 { 2975 $allitems[] = $node; 2976 } 2977 foreach ($this->links as $link) 2978 { 2979 $allitems[] = $link; 2980 } 2981 2982 # foreach ($allitems as &$item) 2983 foreach ($allitems as $ky=>$vl) 2984 { 2985 $item =& $allitems[$ky]; 2986 $z = $item->zorder; 2987 if(!isset($this->seen_zlayers[$z]) || !is_array($this->seen_zlayers[$z])) 2988 { 2989 $this->seen_zlayers[$z]=array(); 2990 } 2991 array_push($this->seen_zlayers[$z], $item); 2992 2993 // while we're looping through, let's set the real bandwidths 2994 if($item->my_type() == "LINK") 2995 { 2996 $this->links[$item->name]->max_bandwidth_in = unformat_number($item->max_bandwidth_in_cfg, $this->kilo); 2997 $this->links[$item->name]->max_bandwidth_out = unformat_number($item->max_bandwidth_out_cfg, $this->kilo); 2998 } 2999 elseif($item->my_type() == "NODE") 3000 { 3001 $this->nodes[$item->name]->max_bandwidth_in = unformat_number($item->max_bandwidth_in_cfg, $this->kilo); 3002 $this->nodes[$item->name]->max_bandwidth_out = unformat_number($item->max_bandwidth_out_cfg, $this->kilo); 3003 } 3004 else 3005 { 3006 warn("Internal bug - found an item of type: ".$item->my_type()."\n"); 3007 } 3008 // $item->max_bandwidth_in=unformat_number($item->max_bandwidth_in_cfg, $this->kilo); 3009 // $item->max_bandwidth_out=unformat_number($item->max_bandwidth_out_cfg, $this->kilo); 3010 3011 debug (sprintf(" Setting bandwidth on ".$item->my_type()." $item->name (%s -> %d bps, %s -> %d bps, KILO = %d)\n", $item->max_bandwidth_in_cfg, $item->max_bandwidth_in, $item->max_bandwidth_out_cfg, $item->max_bandwidth_out, $this->kilo)); 3012 } 3013 3014 debug("Found ".sizeof($this->seen_zlayers)." z-layers including builtins (0,100).\n"); 3015 3016 // calculate any relative positions here - that way, nothing else 3017 // really needs to know about them 3018 3019 debug("Resolving relative positions for NODEs...\n"); 3020 // safety net for cyclic dependencies 3021 $i=100; 3022 do 3023 { 3024 $skipped = 0; $set=0; 3025 foreach ($this->nodes as $node) 3026 { 3027 if( ($node->relative_to != '') && (!$node->relative_resolved)) 3028 { 3029 debug("Resolving relative position for NODE ".$node->name." to ".$node->relative_to."\n"); 3030 if(array_key_exists($node->relative_to,$this->nodes)) 3031 { 3032 3033 // check if we are relative to another node which is in turn relative to something 3034 // we need to resolve that one before we can resolve this one! 3035 if( ($this->nodes[$node->relative_to]->relative_to != '') && (!$this->nodes[$node->relative_to]->relative_resolved) ) 3036 { 3037 debug("Skipping unresolved relative_to. Let's hope it's not a circular one\n"); 3038 $skipped++; 3039 } 3040 else 3041 { 3042 $rx = $this->nodes[$node->relative_to]->x; 3043 $ry = $this->nodes[$node->relative_to]->y; 3044 3045 if($node->polar) 3046 { 3047 // treat this one as a POLAR relative coordinate. 3048 // - draw rings around a node! 3049 $angle = $node->x; 3050 $distance = $node->y; 3051 $newpos_x = $rx + $distance * sin(deg2rad($angle)); 3052 $newpos_y = $ry - $distance * cos(deg2rad($angle)); 3053 debug("->$newpos_x,$newpos_y\n"); 3054 $this->nodes[$node->name]->x = $newpos_x; 3055 $this->nodes[$node->name]->y = $newpos_y; 3056 $this->nodes[$node->name]->relative_resolved=TRUE; 3057 $set++; 3058 } 3059 else 3060 { 3061 3062 // save the relative coords, so that WriteConfig can work 3063 // resolve the relative stuff 3064 3065 $newpos_x = $rx + $this->nodes[$node->name]->x; 3066 $newpos_y = $ry + $this->nodes[$node->name]->y; 3067 debug("->$newpos_x,$newpos_y\n"); 3068 $this->nodes[$node->name]->x = $newpos_x; 3069 $this->nodes[$node->name]->y = $newpos_y; 3070 $this->nodes[$node->name]->relative_resolved=TRUE; 3071 $set++; 3072 } 3073 } 3074 } 3075 else 3076 { 3077 warn("NODE ".$node->name." has a relative position to an unknown node! [WMWARN10]\n"); 3078 } 3079 } 3080 } 3081 debug("Relative Positions Cycle $i - set $set and Skipped $skipped for unresolved dependencies\n"); 3082 $i--; 3083 } while( ($set>0) && ($i!=0) ); 3084 3085 if($skipped>0) 3086 { 3087 warn("There are Circular dependencies in relative POSITION lines for $skipped nodes. [WMWARN11]\n"); 3088 } 3089 3090 debug("-----------------------------------\n"); 3091 3092 3093 debug("Running Pre-Processing Plugins...\n"); 3094 foreach ($this->preprocessclasses as $pre_class) 3095 { 3096 debug("Running $pre_class"."->run()\n"); 3097 $this->plugins['pre'][$pre_class]->run($this); 3098 } 3099 debug("Finished Pre-Processing Plugins...\n"); 3100 3101 return (TRUE); 3102} 3103 3104function ReadConfig_Commit(&$curobj) 3105{ 3106 if(is_null($curobj)) return; 3107 3108 $last_seen = $curobj->my_type(); 3109 3110 // first, save the previous item, before starting work on the new one 3111 if ($last_seen == "NODE") 3112 { 3113 $this->nodes[$curobj->name]=$curobj; 3114 debug ("Saving Node: " . $curobj->name . "\n"); 3115 if($curobj->template == 'DEFAULT') $this->node_template_tree[ "DEFAULT" ][]= $curobj->name; 3116 } 3117 3118 if ($last_seen == "LINK") 3119 { 3120 if (isset($curobj->a) && isset($curobj->b)) 3121 { 3122 $this->links[$curobj->name]=$curobj; 3123 debug ("Saving Link: " . $curobj->name . "\n"); 3124 } 3125 else 3126 { 3127 $this->links[$curobj->name]=$curobj; 3128 debug ("Saving Template-Only Link: " . $curobj->name . "\n"); 3129 } 3130 if($curobj->template == 'DEFAULT') $this->link_template_tree[ "DEFAULT" ][]= $curobj->name; 3131 } 3132} 3133 3134function WriteConfig($filename) 3135{ 3136 global $WEATHERMAP_VERSION; 3137 3138 $fd=fopen($filename, "w"); 3139 $output=""; 3140 3141 if ($fd) 3142 { 3143 $output.="# Automatically generated by php-weathermap v$WEATHERMAP_VERSION\n\n"; 3144 3145 if (count($this->fonts) > 0) 3146 { 3147 foreach ($this->fonts as $fontnumber => $font) 3148 { 3149 if ($font->type == 'truetype') 3150 $output.=sprintf("FONTDEFINE %d %s %d\n", $fontnumber, $font->file, $font->size); 3151 3152 if ($font->type == 'gd') 3153 $output.=sprintf("FONTDEFINE %d %s\n", $fontnumber, $font->file); 3154 } 3155 3156 $output.="\n"; 3157 } 3158 3159 $basic_params = array( 3160 array('background','BACKGROUND',CONFIG_TYPE_LITERAL), 3161 array('width','WIDTH',CONFIG_TYPE_LITERAL), 3162 array('height','HEIGHT',CONFIG_TYPE_LITERAL), 3163 array('htmlstyle','HTMLSTYLE',CONFIG_TYPE_LITERAL), 3164 array('kilo','KILO',CONFIG_TYPE_LITERAL), 3165 array('keyfont','KEYFONT',CONFIG_TYPE_LITERAL), 3166 array('timefont','TIMEFONT',CONFIG_TYPE_LITERAL), 3167 array('titlefont','TITLEFONT',CONFIG_TYPE_LITERAL), 3168 array('title','TITLE',CONFIG_TYPE_LITERAL), 3169 array('htmloutputfile','HTMLOUTPUTFILE',CONFIG_TYPE_LITERAL), 3170 array('htmlstylesheet','HTMLSTYLESHEET',CONFIG_TYPE_LITERAL), 3171 array('imageuri','IMAGEURI',CONFIG_TYPE_LITERAL), 3172 array('imageoutputfile','IMAGEOUTPUTFILE',CONFIG_TYPE_LITERAL) 3173 ); 3174 3175 foreach ($basic_params as $param) 3176 { 3177 $field = $param[0]; 3178 $keyword = $param[1]; 3179 3180 if ($this->$field != $this->inherit_fieldlist[$field]) 3181 { 3182 if($param[2] == CONFIG_TYPE_COLOR) $output.="$keyword " . render_colour($this->$field) . "\n"; 3183 if($param[2] == CONFIG_TYPE_LITERAL) $output.="$keyword " . $this->$field . "\n"; 3184 } 3185 } 3186 3187 if (($this->timex != $this->inherit_fieldlist['timex']) 3188 || ($this->timey != $this->inherit_fieldlist['timey']) 3189 || ($this->stamptext != $this->inherit_fieldlist['stamptext'])) 3190 $output.="TIMEPOS " . $this->timex . " " . $this->timey . " " . $this->stamptext . "\n"; 3191 3192 if (($this->mintimex != $this->inherit_fieldlist['mintimex']) 3193 || ($this->mintimey != $this->inherit_fieldlist['mintimey']) 3194 || ($this->minstamptext != $this->inherit_fieldlist['minstamptext'])) 3195 $output.="MINTIMEPOS " . $this->mintimex . " " . $this->mintimey . " " . $this->minstamptext . "\n"; 3196 3197 if (($this->maxtimex != $this->inherit_fieldlist['maxtimex']) 3198 || ($this->maxtimey != $this->inherit_fieldlist['maxtimey']) 3199 || ($this->maxstamptext != $this->inherit_fieldlist['maxstamptext'])) 3200 $output.="MAXTIMEPOS " . $this->maxtimex . " " . $this->maxtimey . " " . $this->maxstamptext . "\n"; 3201 3202 if (($this->titlex != $this->inherit_fieldlist['titlex']) 3203 || ($this->titley != $this->inherit_fieldlist['titley'])) 3204 $output.="TITLEPOS " . $this->titlex . " " . $this->titley . "\n"; 3205 3206 $output.="\n"; 3207 3208 foreach ($this->colours as $scalename=>$colours) 3209 { 3210 // not all keys will have keypos but if they do, then all three vars should be defined 3211 if ( (isset($this->keyx[$scalename])) && (isset($this->keyy[$scalename])) && (isset($this->keytext[$scalename])) 3212 && (($this->keytext[$scalename] != $this->inherit_fieldlist['keytext']) 3213 || ($this->keyx[$scalename] != $this->inherit_fieldlist['keyx']) 3214 || ($this->keyy[$scalename] != $this->inherit_fieldlist['keyy']))) 3215 { 3216 // sometimes a scale exists but without defaults. A proper scale object would sort this out... 3217 if($this->keyx[$scalename] == '') { $this->keyx[$scalename] = -1; } 3218 if($this->keyy[$scalename] == '') { $this->keyy[$scalename] = -1; } 3219 3220 $output.="KEYPOS " . $scalename." ". $this->keyx[$scalename] . " " . $this->keyy[$scalename] . " " . $this->keytext[$scalename] . "\n"; 3221 } 3222 3223 if ( (isset($this->keystyle[$scalename])) && ($this->keystyle[$scalename] != $this->inherit_fieldlist['keystyle']['DEFAULT']) ) 3224 { 3225 $extra=''; 3226 if ( (isset($this->keysize[$scalename])) && ($this->keysize[$scalename] != $this->inherit_fieldlist['keysize']['DEFAULT']) ) 3227 { 3228 $extra = " ".$this->keysize[$scalename]; 3229 } 3230 $output.="KEYSTYLE " . $scalename." ". $this->keystyle[$scalename] . $extra . "\n"; 3231 } 3232 $locale = localeconv(); 3233 $decimal_point = $locale['decimal_point']; 3234 3235 foreach ($colours as $k => $colour) 3236 { 3237 if (!isset($colour['special']) || ! $colour['special'] ) 3238 { 3239 $top = rtrim(rtrim(sprintf("%f",$colour['top']),"0"),$decimal_point); 3240 $bottom= rtrim(rtrim(sprintf("%f",$colour['bottom']),"0"),$decimal_point); 3241 3242 if ($bottom > 1000) { 3243 $bottom = nice_bandwidth($colour['bottom'], $this->kilo); 3244 } 3245 3246 if ($top > 1000) { 3247 $top = nice_bandwidth($colour['top'], $this->kilo); 3248 } 3249 3250 $tag = (isset($colour['tag'])? $colour['tag']:''); 3251 3252 if( ($colour['red1'] == -1) && ($colour['green1'] == -1) && ($colour['blue1'] == -1)) 3253 { 3254 $output.=sprintf("SCALE %s %-4s %-4s none %s\n", $scalename, 3255 $bottom, $top, $tag); 3256 } 3257 elseif (!isset($colour['red2'])) 3258 { 3259 $output.=sprintf("SCALE %s %-4s %-4s %3d %3d %3d %s\n", $scalename, 3260 $bottom, $top, 3261 $colour['red1'], $colour['green1'], $colour['blue1'],$tag); 3262 } 3263 else 3264 { 3265 $output.=sprintf("SCALE %s %-4s %-4s %3d %3d %3d %3d %3d %3d %s\n", $scalename, 3266 $bottom, $top, 3267 $colour['red1'], 3268 $colour['green1'], $colour['blue1'], 3269 $colour['red2'], $colour['green2'], 3270 $colour['blue2'], $tag); 3271 } 3272 } 3273 else { $output.=sprintf("%sCOLOR %d %d %d\n", $k, $colour['red1'], $colour['green1'], 3274 $colour['blue1']); } 3275 } 3276 $output .= "\n"; 3277 } 3278 3279 foreach ($this->hints as $hintname=>$hint) 3280 { 3281 $output .= "SET $hintname $hint\n"; 3282 } 3283 3284 // this doesn't really work right, but let's try anyway 3285 if($this->has_includes) 3286 { 3287 $output .= "\n# Included files\n"; 3288 foreach ($this->included_files as $ifile) 3289 { 3290 $output .= "INCLUDE $ifile\n"; 3291 } 3292 } 3293 3294 $output.="\n# End of global section\n\n"; 3295 3296 fwrite($fd, $output); 3297 3298 ## fwrite($fd,$this->nodes['DEFAULT']->WriteConfig()); 3299 ## fwrite($fd,$this->links['DEFAULT']->WriteConfig()); 3300 3301 # fwrite($fd, "\n\n# Node definitions:\n"); 3302 3303 foreach (array("template","normal") as $which) 3304 { 3305 if($which == "template") fwrite($fd,"\n# TEMPLATE-only NODEs:\n"); 3306 if($which == "normal") fwrite($fd,"\n# regular NODEs:\n"); 3307 3308 foreach ($this->nodes as $node) 3309 { 3310 if(!preg_match("/^::\s/",$node->name)) 3311 { 3312 if($node->defined_in == $this->configfile) 3313 { 3314 3315 if($which=="template" && $node->x === NULL) { debug("TEMPLATE\n"); fwrite($fd,$node->WriteConfig()); } 3316 if($which=="normal" && $node->x !== NULL) { fwrite($fd,$node->WriteConfig()); } 3317 } 3318 } 3319 } 3320 3321 if($which == "template") fwrite($fd,"\n# TEMPLATE-only LINKs:\n"); 3322 if($which == "normal") fwrite($fd,"\n# regular LINKs:\n"); 3323 3324 foreach ($this->links as $link) 3325 { 3326 if(!preg_match("/^::\s/",$link->name)) 3327 { 3328 if($link->defined_in == $this->configfile) 3329 { 3330 if($which=="template" && $link->a === NULL) fwrite($fd,$link->WriteConfig()); 3331 if($which=="normal" && $link->a !== NULL) fwrite($fd,$link->WriteConfig()); 3332 } 3333 } 3334 } 3335 } 3336 3337 fwrite($fd, "\n\n# That's All Folks!\n"); 3338 3339 fclose($fd); 3340 } 3341 else 3342 { 3343 warn ("Couldn't open config file $filename for writing"); 3344 return (FALSE); 3345 } 3346 3347 return (TRUE); 3348} 3349 3350// pre-allocate colour slots for the colours used by the arrows 3351// this way, it's the pretty icons that suffer if there aren't enough colours, and 3352// not the actual useful data 3353// we skip any gradient scales 3354function AllocateScaleColours($im,$refname='gdref1') 3355{ 3356 # $colours=$this->colours['DEFAULT']; 3357 foreach ($this->colours as $scalename=>$colours) 3358 { 3359 foreach ($colours as $key => $colour) 3360 { 3361 if ( (!isset($this->colours[$scalename][$key]['red2']) ) && (!isset( $this->colours[$scalename][$key][$refname] )) ) 3362 { 3363 $r=$colour['red1']; 3364 $g=$colour['green1']; 3365 $b=$colour['blue1']; 3366 debug ("AllocateScaleColours: $scalename/$refname $key ($r,$g,$b)\n"); 3367 $this->colours[$scalename][$key][$refname]=myimagecolorallocate($im, $r, $g, $b); 3368 } 3369 } 3370 } 3371} 3372 3373function DrawMap($filename = '', $thumbnailfile = '', $thumbnailmax = 250, $withnodes = TRUE, $use_via_overlay = FALSE, $use_rel_overlay=FALSE) 3374{ 3375 debug("Trace: DrawMap()\n"); 3376 metadump("# start",true); 3377 $bgimage=NULL; 3378 if($this->configfile != "") 3379 { 3380 $this->cachefile_version = crc32(file_get_contents($this->configfile)); 3381 } 3382 else 3383 { 3384 $this->cachefile_version = crc32("........"); 3385 } 3386 3387 debug("Running Post-Processing Plugins...\n"); 3388 foreach ($this->postprocessclasses as $post_class) 3389 { 3390 debug("Running $post_class"."->run()\n"); 3391 //call_user_func_array(array($post_class, 'run'), array(&$this)); 3392 $this->plugins['post'][$post_class]->run($this); 3393 3394 } 3395 debug("Finished Post-Processing Plugins...\n"); 3396 3397 debug("=====================================\n"); 3398 debug("Start of Map Drawing\n"); 3399 3400 $this->datestamp = strftime($this->stamptext, time()); 3401 3402 // do the basic prep work 3403 if ($this->background != '') 3404 { 3405 if (is_readable($this->background)) 3406 { 3407 $bgimage=imagecreatefromfile($this->background); 3408 3409 if (!$bgimage) { warn 3410 ("Failed to open background image. One possible reason: Is your BACKGROUND really a PNG?\n"); 3411 } 3412 else 3413 { 3414 $this->width=imagesx($bgimage); 3415 $this->height=imagesy($bgimage); 3416 } 3417 } 3418 else { warn 3419 ("Your background image file could not be read. Check the filename, and permissions, for " 3420 . $this->background . "\n"); } 3421 } 3422 3423 $image=wimagecreatetruecolor($this->width, $this->height); 3424 3425 # $image = imagecreate($this->width, $this->height); 3426 if (!$image) { warn 3427 ("Couldn't create output image in memory (" . $this->width . "x" . $this->height . ")."); } 3428 else 3429 { 3430 ImageAlphaBlending($image, true); 3431 # imageantialias($image,true); 3432 3433 // by here, we should have a valid image handle 3434 3435 // save this away, now 3436 $this->image=$image; 3437 3438 $this->white=myimagecolorallocate($image, 255, 255, 255); 3439 $this->black=myimagecolorallocate($image, 0, 0, 0); 3440 $this->grey=myimagecolorallocate($image, 192, 192, 192); 3441 $this->selected=myimagecolorallocate($image, 255, 0, 0); // for selections in the editor 3442 3443 $this->AllocateScaleColours($image); 3444 3445 // fill with background colour anyway, in case the background image failed to load 3446 wimagefilledrectangle($image, 0, 0, $this->width, $this->height, $this->colours['DEFAULT']['BG']['gdref1']); 3447 3448 if ($bgimage) 3449 { 3450 imagecopy($image, $bgimage, 0, 0, 0, 0, $this->width, $this->height); 3451 imagedestroy ($bgimage); 3452 } 3453 3454 // Now it's time to draw a map 3455 3456 // do the node rendering stuff first, regardless of where they are actually drawn. 3457 // this is so we can get the size of the nodes, which links will need if they use offsets 3458 foreach ($this->nodes as $node) 3459 { 3460 // don't try and draw template nodes 3461 debug("Pre-rendering ".$node->name." to get bounding boxes.\n"); 3462 if(!is_null($node->x)) $this->nodes[$node->name]->pre_render($image, $this); 3463 } 3464 3465 $all_layers = array_keys($this->seen_zlayers); 3466 sort($all_layers); 3467 3468 foreach ($all_layers as $z) 3469 { 3470 $z_items = $this->seen_zlayers[$z]; 3471 debug("Drawing layer $z\n"); 3472 // all the map 'furniture' is fixed at z=1000 3473 if($z==1000) 3474 { 3475 foreach ($this->colours as $scalename=>$colours) 3476 { 3477 debug("Drawing KEY for $scalename if necessary.\n"); 3478 3479 if( (isset($this->numscales[$scalename])) && (isset($this->keyx[$scalename])) && ($this->keyx[$scalename] >= 0) && ($this->keyy[$scalename] >= 0) ) 3480 { 3481 if($this->keystyle[$scalename]=='classic') $this->DrawLegend_Classic($image,$scalename,FALSE); 3482 if($this->keystyle[$scalename]=='horizontal') $this->DrawLegend_Horizontal($image,$scalename,$this->keysize[$scalename]); 3483 if($this->keystyle[$scalename]=='vertical') $this->DrawLegend_Vertical($image,$scalename,$this->keysize[$scalename]); 3484 if($this->keystyle[$scalename]=='inverted') $this->DrawLegend_Vertical($image,$scalename,$this->keysize[$scalename],true); 3485 if($this->keystyle[$scalename]=='tags') $this->DrawLegend_Classic($image,$scalename,TRUE); 3486 } 3487 } 3488 3489 $this->DrawTimestamp($image, $this->timefont, $this->colours['DEFAULT']['TIME']['gdref1']); 3490 if(! is_null($this->min_data_time)) 3491 { 3492 $this->DrawTimestamp($image, $this->timefont, $this->colours['DEFAULT']['TIME']['gdref1'],"MIN"); 3493 $this->DrawTimestamp($image, $this->timefont, $this->colours['DEFAULT']['TIME']['gdref1'],"MAX"); 3494 } 3495 $this->DrawTitle($image, $this->titlefont, $this->colours['DEFAULT']['TITLE']['gdref1']); 3496 } 3497 3498 if(is_array($z_items)) 3499 { 3500 foreach($z_items as $it) 3501 { 3502 if(strtolower(get_class($it))=='weathermaplink') 3503 { 3504 // only draw LINKs if they have NODES defined (not templates) 3505 // (also, check if the link still exists - if this is in the editor, it may have been deleted by now) 3506 if ( isset($this->links[$it->name]) && isset($it->a) && isset($it->b)) 3507 { 3508 debug("Drawing LINK ".$it->name."\n"); 3509 $this->links[$it->name]->Draw($image, $this); 3510 } 3511 } 3512 if(strtolower(get_class($it))=='weathermapnode') 3513 { 3514 // if(!is_null($it->x)) $it->pre_render($image, $this); 3515 if($withnodes) 3516 { 3517 // don't try and draw template nodes 3518 if( isset($this->nodes[$it->name]) && !is_null($it->x)) 3519 { 3520 # print "::".get_class($it)."\n"; 3521 debug("Drawing NODE ".$it->name."\n"); 3522 $this->nodes[$it->name]->NewDraw($image, $this); 3523 $ii=0; 3524 foreach($this->nodes[$it->name]->boundingboxes as $bbox) 3525 { 3526 # $areaname = "NODE:" . $it->name . ':'.$ii; 3527 $areaname = "NODE:N". $it->id . ":" . $ii; 3528 $this->imap->addArea("Rectangle", $areaname, '', $bbox); 3529 debug("Adding imagemap area"); 3530 $ii++; 3531 } 3532 debug("Added $ii bounding boxes too\n"); 3533 } 3534 } 3535 } 3536 } 3537 } 3538 } 3539 3540 $overlay = myimagecolorallocate($image, 200, 0, 0); 3541 3542 // for the editor, we can optionally overlay some other stuff 3543 if($this->context == 'editor') 3544 { 3545 if($use_rel_overlay) 3546 { 3547 # $overlay = myimagecolorallocate($image, 200, 0, 0); 3548 3549 // first, we can show relatively positioned NODEs 3550 foreach ($this->nodes as $node) { 3551 if($node->relative_to != '') 3552 { 3553 $rel_x = $this->nodes[$node->relative_to]->x; 3554 $rel_y = $this->nodes[$node->relative_to]->y; 3555 imagearc($image,$node->x, $node->y, 3556 15,15,0,360,$overlay); 3557 imagearc($image,$node->x, $node->y, 3558 16,16,0,360,$overlay); 3559 3560 imageline($image,$node->x, $node->y, 3561 $rel_x, $rel_y, $overlay); 3562 } 3563 } 3564 } 3565 3566 if($use_via_overlay) 3567 { 3568 // then overlay VIAs, so they can be seen 3569 foreach($this->links as $link) 3570 { 3571 foreach ($link->vialist as $via) 3572 { 3573 if(isset($via[2])) 3574 { 3575 $x = $this->nodes[$via[2]]->x + $via[0]; 3576 $y = $this->nodes[$via[2]]->y + $via[1]; 3577 } 3578 else 3579 { 3580 $x = $via[0]; 3581 $y = $via[1]; 3582 } 3583 imagearc($image, $x,$y, 10,10,0,360,$overlay); 3584 imagearc($image, $x,$y, 12,12,0,360,$overlay); 3585 } 3586 } 3587 } 3588 } 3589 3590 #$this->myimagestring($image, 3, 200, 100, "Test 1\nLine 2", $overlay,0); 3591 3592# $this->myimagestring($image, 30, 100, 100, "Test 1\nLine 2", $overlay,0); 3593 #$this->myimagestring($image, 30, 200, 200, "Test 1\nLine 2", $overlay,45); 3594 3595 // Ready to output the results... 3596 3597 if($filename == 'null') 3598 { 3599 // do nothing at all - we just wanted the HTML AREAs for the editor or HTML output 3600 } 3601 else 3602 { 3603 if ($filename == '') { imagepng ($image); } 3604 else { 3605 $result = FALSE; 3606 $functions = TRUE; 3607 if(function_exists('imagejpeg') && preg_match("/\.jpg/i",$filename)) 3608 { 3609 debug("Writing JPEG file to $filename\n"); 3610 $result = imagejpeg($image, $filename); 3611 } 3612 elseif(function_exists('imagegif') && preg_match("/\.gif/i",$filename)) 3613 { 3614 debug("Writing GIF file to $filename\n"); 3615 $result = imagegif($image, $filename); 3616 } 3617 elseif(function_exists('imagepng') && preg_match("/\.png/i",$filename)) 3618 { 3619 debug("Writing PNG file to $filename\n"); 3620 $result = imagepng($image, $filename); 3621 } 3622 else 3623 { 3624 warn("Failed to write map image. No function existed for the image format you requested. [WMWARN12]\n"); 3625 $functions = FALSE; 3626 } 3627 3628 if(($result==FALSE) && ($functions==TRUE)) 3629 { 3630 if(file_exists($filename)) 3631 { 3632 warn("Failed to overwrite existing image file $filename - permissions of existing file are wrong? [WMWARN13]"); 3633 } 3634 else 3635 { 3636 warn("Failed to create image file $filename - permissions of output directory are wrong? [WMWARN14]"); 3637 } 3638 } 3639 } 3640 } 3641 3642 if($this->context == 'editor2') 3643 { 3644 $cachefile = $this->cachefolder.DIRECTORY_SEPARATOR.dechex(crc32($this->configfile))."_bg.".$this->cachefile_version.".png"; 3645 imagepng($image, $cachefile); 3646 $cacheuri = $this->cachefolder.'/'.dechex(crc32($this->configfile))."_bg.".$this->cachefile_version.".png"; 3647 $this->mapcache = $cacheuri; 3648 } 3649 3650 if (function_exists('imagecopyresampled')) 3651 { 3652 // if one is specified, and we can, write a thumbnail too 3653 if ($thumbnailfile != '') 3654 { 3655 $result = FALSE; 3656 if ($this->width > $this->height) { $factor=($thumbnailmax / $this->width); } 3657 else { $factor=($thumbnailmax / $this->height); } 3658 3659 $this->thumb_width = $this->width * $factor; 3660 $this->thumb_height = $this->height * $factor; 3661 3662 $imagethumb=imagecreatetruecolor($this->thumb_width, $this->thumb_height); 3663 imagecopyresampled($imagethumb, $image, 0, 0, 0, 0, $this->thumb_width, $this->thumb_height, 3664 $this->width, $this->height); 3665 $result = imagepng($imagethumb, $thumbnailfile); 3666 imagedestroy($imagethumb); 3667 3668 3669 3670 if(($result==FALSE)) 3671 { 3672 if(file_exists($filename)) 3673 { 3674 warn("Failed to overwrite existing image file $filename - permissions of existing file are wrong? [WMWARN15]"); 3675 } 3676 else 3677 { 3678 warn("Failed to create image file $filename - permissions of output directory are wrong? [WMWARN16]"); 3679 } 3680 } 3681 } 3682 } 3683 else 3684 { 3685 warn("Skipping thumbnail creation, since we don't have the necessary function. [WMWARN17]"); 3686 } 3687 imagedestroy ($image); 3688 } 3689} 3690 3691function CleanUp() 3692{ 3693 // destroy all the images we created, to prevent memory leaks 3694 foreach ($this->nodes as $node) { if(isset($node->image)) imagedestroy($node->image); } 3695 #foreach ($this->nodes as $node) { unset($node); } 3696 #foreach ($this->links as $link) { unset($link); } 3697 3698} 3699 3700function PreloadMapHTML() 3701{ 3702 debug("Trace: PreloadMapHTML()\n"); 3703 // onmouseover="return overlib('<img src=graph.png>',DELAY,250,CAPTION,'$caption');" onmouseout="return nd();" 3704 3705 // find the middle of the map 3706 $center_x=$this->width / 2; 3707 $center_y=$this->height / 2; 3708 3709 // loop through everything. Figure out along the way if it's a node or a link 3710 $allitems = array(&$this->nodes, &$this->links); 3711 reset($allitems); 3712 3713 while( list($kk,) = each($allitems)) 3714 { 3715 unset($objects); 3716 # $objects = &$this->links; 3717 $objects = &$allitems[$kk]; 3718 3719 reset($objects); 3720 while (list($k,) = each($objects)) 3721 { 3722 unset($myobj); 3723 $myobj = &$objects[$k]; 3724 3725 $type = $myobj->my_type(); 3726 $prefix = substr($type,0,1); 3727 3728 $dirs = array(); 3729 //print "\n\nConsidering a $type - ".$myobj->name.".\n"; 3730 if($type == 'LINK') $dirs = array(IN=>array(0,2), OUT=>array(1,3)); 3731 if($type == 'NODE') $dirs = array(IN=>array(0,1,2,3)); 3732 3733 // check to see if any of the relevant things have a value 3734 $change = ""; 3735 foreach ($dirs as $d=>$parts) 3736 { 3737 //print "$d - ".join(" ",$parts)."\n"; 3738 $change .= join('',$myobj->overliburl[$d]); 3739 $change .= $myobj->notestext[$d]; 3740 } 3741 3742 if ($this->htmlstyle == "overlib") 3743 { 3744 //print "CHANGE: $change\n"; 3745 3746 // skip all this if it's a template node 3747 if($type=='LINK' && ! isset($myobj->a->name)) { $change = ''; } 3748 if($type=='NODE' && ! isset($myobj->x)) { $change = ''; } 3749 3750 if($change != '') 3751 { 3752 //print "Something to be done.\n"; 3753 if($type=='NODE') 3754 { 3755 $mid_x = $myobj->x; 3756 $mid_y = $myobj->y; 3757 } 3758 if($type=='LINK') 3759 { 3760 $a_x = $this->nodes[$myobj->a->name]->x; 3761 $a_y = $this->nodes[$myobj->a->name]->y; 3762 3763 $b_x = $this->nodes[$myobj->b->name]->x; 3764 $b_y = $this->nodes[$myobj->b->name]->y; 3765 3766 $mid_x=($a_x + $b_x) / 2; 3767 $mid_y=($a_y + $b_y) / 2; 3768 } 3769 $left=""; $above=""; 3770 $img_extra = ""; 3771 3772 if ($myobj->overlibwidth != 0) 3773 { 3774 $left="WIDTH," . $myobj->overlibwidth . ","; 3775 $img_extra .= " WIDTH=$myobj->overlibwidth"; 3776 3777 if ($mid_x > $center_x) $left.="LEFT,"; 3778 } 3779 3780 if ($myobj->overlibheight != 0) 3781 { 3782 $above="HEIGHT," . $myobj->overlibheight . ","; 3783 $img_extra .= " HEIGHT=$myobj->overlibheight"; 3784 3785 if ($mid_y > $center_y) $above.="ABOVE,"; 3786 } 3787 3788 foreach ($dirs as $dir=>$parts) 3789 { 3790 $caption = ($myobj->overlibcaption[$dir] != '' ? $myobj->overlibcaption[$dir] : $myobj->name); 3791 $caption = $this->ProcessString($caption,$myobj); 3792 3793 $overlibhtml = "onmouseover=\"return overlib('"; 3794 3795 $n = 0; 3796 if(sizeof($myobj->overliburl[$dir]) > 0) 3797 { 3798 // print "ARRAY:".is_array($link->overliburl[$dir])."\n"; 3799 foreach ($myobj->overliburl[$dir] as $url) 3800 { 3801 if($n>0) { $overlibhtml .= '<br />'; } 3802 $overlibhtml .= "<img $img_extra src=" . $this->ProcessString($url,$myobj) . ">"; 3803 $n++; 3804 } 3805 } 3806 # print "Added $n for $dir\n"; 3807 if(trim($myobj->notestext[$dir]) != '') 3808 { 3809 # put in a linebreak if there was an image AND notes 3810 if($n>0) $overlibhtml .= '<br />'; 3811 $note = $this->ProcessString($myobj->notestext[$dir],$myobj); 3812 $note = htmlspecialchars($note, ENT_NOQUOTES); 3813 $note=str_replace("'", "\\'", $note); 3814 $note=str_replace('"', """, $note); 3815 $overlibhtml .= $note; 3816 } 3817 $overlibhtml .= "',DELAY,250,${left}${above}CAPTION,'" . $caption 3818 . "');\" onmouseout=\"return nd();\""; 3819 3820 foreach ($parts as $part) 3821 { 3822 $areaname = $type.":" . $prefix . $myobj->id. ":" . $part; 3823 //print "INFOURL for $areaname - "; 3824 3825 $this->imap->setProp("extrahtml", $overlibhtml, $areaname); 3826 } 3827 } 3828 } // if change 3829 } // overlib? 3830 3831 // now look at inforurls 3832 foreach ($dirs as $dir=>$parts) 3833 { 3834 foreach ($parts as $part) 3835 { 3836 # $areaname = $type.":" . $myobj->name . ":" . $part; 3837 $areaname = $type.":" . $prefix . $myobj->id. ":" . $part; 3838 //print "INFOURL for $areaname - "; 3839 3840 if ( ($this->htmlstyle != 'editor') && ($myobj->infourl[$dir] != '') ) { 3841 $this->imap->setProp("href", $this->ProcessString($myobj->infourl[$dir],$myobj), $areaname); 3842 //print "Setting.\n"; 3843 } 3844 else 3845 { 3846 //print "NOT Setting.\n"; 3847 } 3848 } 3849 } 3850 3851 } 3852 } 3853 3854} 3855 3856function asJS() 3857{ 3858 $js=''; 3859 3860 $js .= "var Links = new Array();\n"; 3861 $js .= "var LinkIDs = new Array();\n"; 3862 # $js.=$this->defaultlink->asJS(); 3863 3864 foreach ($this->links as $link) { $js.=$link->asJS(); } 3865 3866 $js .= "var Nodes = new Array();\n"; 3867 $js .= "var NodeIDs = new Array();\n"; 3868 # $js.=$this->defaultnode->asJS(); 3869 3870 foreach ($this->nodes as $node) { $js.=$node->asJS(); } 3871 3872 return $js; 3873} 3874 3875function asJSON() 3876{ 3877 $json = ''; 3878 3879 $json .= "{ \n"; 3880 3881 $json .= "\"map\": { \n"; 3882 foreach (array_keys($this->inherit_fieldlist)as $fld) 3883 { 3884 $json .= js_escape($fld).": "; 3885 $json .= js_escape($this->$fld); 3886 $json .= ",\n"; 3887 } 3888 $json = rtrim($json,", \n"); 3889 $json .= "\n},\n"; 3890 3891 $json .= "\"nodes\": {\n"; 3892 $json .= $this->defaultnode->asJSON(); 3893 foreach ($this->nodes as $node) { $json .= $node->asJSON(); } 3894 $json = rtrim($json,", \n"); 3895 $json .= "\n},\n"; 3896 3897 3898 3899 $json .= "\"links\": {\n"; 3900 $json .= $this->defaultlink->asJSON(); 3901 foreach ($this->links as $link) { $json .= $link->asJSON(); } 3902 $json = rtrim($json,", \n"); 3903 $json .= "\n},\n"; 3904 3905 $json .= "'imap': [\n"; 3906 $json .= $this->imap->subJSON("NODE:"); 3907 // should check if there WERE nodes... 3908 $json .= ",\n"; 3909 $json .= $this->imap->subJSON("LINK:"); 3910 $json .= "\n]\n"; 3911 $json .= "\n"; 3912 3913 $json .= ", 'valid': 1}\n"; 3914 3915 return($json); 3916} 3917 3918// This method MUST run *after* DrawMap. It relies on DrawMap to call the map-drawing bits 3919// which will populate the ImageMap with regions. 3920// 3921// imagemapname is a parameter, so we can stack up several maps in the Cacti plugin with their own imagemaps 3922function MakeHTML($imagemapname = "weathermap_imap") 3923{ 3924 debug("Trace: MakeHTML()\n"); 3925 // PreloadMapHTML fills in the ImageMap info, ready for the HTML to be created. 3926 $this->PreloadMapHTML(); 3927 3928 $html=''; 3929 3930 $html .= '<div class="weathermapimage" style="margin-left: auto; margin-right: auto; width: '.$this->width.'px;" >'; 3931 if ( $this->imageuri != '') { 3932 $html.=sprintf( 3933 '<img id="wmapimage" src="%s" width="%d" height="%d" border="0" usemap="#%s"', 3934 $this->imageuri, 3935 $this->width, 3936 $this->height, 3937 $imagemapname 3938 ); 3939 //$html .= 'alt="network weathermap" '; 3940 $html .= '/>'; 3941 } 3942 else { 3943 $html.=sprintf( 3944 '<img id="wmapimage" src="%s" width="%d" height="%d" border="0" usemap="#%s"', 3945 $this->imagefile, 3946 $this->width, 3947 $this->height, 3948 $imagemapname 3949 ); 3950 //$html .= 'alt="network weathermap" '; 3951 $html .= '/>'; 3952 } 3953 $html .= '</div>'; 3954 3955 $html .= $this->SortedImagemap($imagemapname); 3956 3957 return ($html); 3958} 3959 3960function SortedImagemap($imagemapname) 3961{ 3962 $html='<map name="' . $imagemapname . '" id="' . $imagemapname . '">'; 3963 3964 # $html.=$this->imap->subHTML("NODE:",true); 3965 # $html.=$this->imap->subHTML("LINK:",true); 3966 3967 $all_layers = array_keys($this->seen_zlayers); 3968 rsort($all_layers); 3969 3970 debug("Starting to dump imagemap in reverse Z-order...\n"); 3971 // this is not precisely efficient, but it'll get us going 3972 // XXX - get Imagemap to store Z order, or map items to store the imagemap 3973 foreach ($all_layers as $z) 3974 { 3975 debug("Writing HTML for layer $z\n"); 3976 $z_items = $this->seen_zlayers[$z]; 3977 if(is_array($z_items)) 3978 { 3979 debug(" Found things for layer $z\n"); 3980 3981 // at z=1000, the legends and timestamps live 3982 if($z == 1000) { 3983 debug(" Builtins fit here.\n"); 3984 $html .= $this->imap->subHTML("LEGEND:",true,($this->context != 'editor')); 3985 $html .= $this->imap->subHTML("TIMESTAMP",true,($this->context != 'editor')); 3986 } 3987 3988 foreach($z_items as $it) 3989 { 3990 # print " " . $it->name . "\n"; 3991 if($it->name != 'DEFAULT' && $it->name != ":: DEFAULT ::") 3992 { 3993 $name = ""; 3994 if(strtolower(get_class($it))=='weathermaplink') $name = "LINK:L"; 3995 if(strtolower(get_class($it))=='weathermapnode') $name = "NODE:N"; 3996 $name .= $it->id . ":"; 3997 debug(" Writing $name from imagemap\n"); 3998 // skip the linkless areas if we are in the editor - they're redundant 3999 $html .= $this->imap->subHTML($name,true,($this->context != 'editor')); 4000 } 4001 } 4002 } 4003 } 4004 4005 $html.='</map>'; 4006 4007 return($html); 4008} 4009 4010// update any editor cache files. 4011// if the config file is newer than the cache files, or $agelimit seconds have passed, 4012// then write new stuff, otherwise just return. 4013// ALWAYS deletes files in the cache folder older than $agelimit, also! 4014function CacheUpdate($agelimit=600) 4015{ 4016 global $weathermap_lazycounter; 4017 4018 $cachefolder = $this->cachefolder; 4019 $configchanged = filemtime($this->configfile ); 4020 // make a unique, but safe, prefix for all cachefiles related to this map config 4021 // we use CRC32 because it makes for a shorter filename, and collisions aren't the end of the world. 4022 $cacheprefix = dechex(crc32($this->configfile)); 4023 4024 debug("Comparing files in $cachefolder starting with $cacheprefix, with date of $configchanged\n"); 4025 4026 $dh=opendir($cachefolder); 4027 4028 if ($dh) 4029 { 4030 while ($file=readdir($dh)) 4031 { 4032 $realfile = $cachefolder . DIRECTORY_SEPARATOR . $file; 4033 4034 if(is_file($realfile) && ( preg_match('/^'.$cacheprefix.'/',$file) )) 4035 // if (is_file($realfile) ) 4036 { 4037 debug("$realfile\n"); 4038 if( (filemtime($realfile) < $configchanged) || ((time() - filemtime($realfile)) > $agelimit) ) 4039 { 4040 debug("Cache: deleting $realfile\n"); 4041 unlink($realfile); 4042 } 4043 } 4044 } 4045 closedir ($dh); 4046 4047 foreach ($this->nodes as $node) 4048 { 4049 if(isset($node->image)) 4050 { 4051 $nodefile = $cacheprefix."_".dechex(crc32($node->name)).".png"; 4052 $this->nodes[$node->name]->cachefile = $nodefile; 4053 imagepng($node->image,$cachefolder.DIRECTORY_SEPARATOR.$nodefile); 4054 } 4055 } 4056 4057 foreach ($this->keyimage as $key=>$image) 4058 { 4059 $scalefile = $cacheprefix."_scale_".dechex(crc32($key)).".png"; 4060 $this->keycache[$key] = $scalefile; 4061 imagepng($image,$cachefolder.DIRECTORY_SEPARATOR.$scalefile); 4062 } 4063 4064 4065 $json = ""; 4066 $fd = fopen($cachefolder.DIRECTORY_SEPARATOR.$cacheprefix."_map.json","w"); 4067 foreach (array_keys($this->inherit_fieldlist)as $fld) 4068 { 4069 $json .= js_escape($fld).": "; 4070 $json .= js_escape($this->$fld); 4071 $json .= ",\n"; 4072 } 4073 $json = rtrim($json,", \n"); 4074 fputs($fd,$json); 4075 fclose($fd); 4076 4077 $json = ""; 4078 $fd = fopen($cachefolder.DIRECTORY_SEPARATOR.$cacheprefix."_tree.json","w"); 4079 $id = 10; // first ID for user-supplied thing 4080 4081 $json .= "{ id: 1, text: 'SCALEs'\n, children: [\n"; 4082 foreach ($this->colours as $scalename=>$colours) 4083 { 4084 $json .= "{ id: " . $id++ . ", text:" . js_escape($scalename) . ", leaf: true }, \n"; 4085 } 4086 $json = rtrim($json,", \n"); 4087 $json .= "]},\n"; 4088 4089 $json .= "{ id: 2, text: 'FONTs',\n children: [\n"; 4090 foreach ($this->fonts as $fontnumber => $font) 4091 { 4092 if ($font->type == 'truetype') 4093 $json .= sprintf("{ id: %d, text: %s, leaf: true}, \n", $id++, js_escape("Font $fontnumber (TT)")); 4094 4095 if ($font->type == 'gd') 4096 $json .= sprintf("{ id: %d, text: %s, leaf: true}, \n", $id++, js_escape("Font $fontnumber (GD)")); 4097 } 4098 $json = rtrim($json,", \n"); 4099 $json .= "]},\n"; 4100 4101 $json .= "{ id: 3, text: 'NODEs',\n children: [\n"; 4102 $json .= "{ id: ". $id++ . ", text: 'DEFAULT', children: [\n"; 4103 4104 $weathemap_lazycounter = $id; 4105 // pass the list of subordinate nodes to the recursive tree function 4106 $json .= $this->MakeTemplateTree( $this->node_template_tree ); 4107 $id = $weathermap_lazycounter; 4108 4109 $json = rtrim($json,", \n"); 4110 $json .= "]} ]},\n"; 4111 4112 $json .= "{ id: 4, text: 'LINKs',\n children: [\n"; 4113 $json .= "{ id: ". $id++ . ", text: 'DEFAULT', children: [\n"; 4114 $weathemap_lazycounter = $id; 4115 $json .= $this->MakeTemplateTree( $this->link_template_tree ); 4116 $id = $weathermap_lazycounter; 4117 $json = rtrim($json,", \n"); 4118 $json .= "]} ]}\n"; 4119 4120 fputs($fd,"[". $json . "]"); 4121 fclose($fd); 4122 4123 $fd = fopen($cachefolder.DIRECTORY_SEPARATOR.$cacheprefix."_nodes.json","w"); 4124 $json = ""; 4125// $json = $this->defaultnode->asJSON(TRUE); 4126 foreach ($this->nodes as $node) { $json .= $node->asJSON(TRUE); } 4127 $json = rtrim($json,", \n"); 4128 fputs($fd,$json); 4129 fclose($fd); 4130 4131 $fd = fopen($cachefolder.DIRECTORY_SEPARATOR.$cacheprefix."_nodes_lite.json","w"); 4132 $json = ""; 4133// $json = $this->defaultnode->asJSON(FALSE); 4134 foreach ($this->nodes as $node) { $json .= $node->asJSON(FALSE); } 4135 $json = rtrim($json,", \n"); 4136 fputs($fd,$json); 4137 fclose($fd); 4138 4139 4140 4141 $fd = fopen($cachefolder.DIRECTORY_SEPARATOR.$cacheprefix."_links.json","w"); 4142 $json = ""; 4143// $json = $this->defaultlink->asJSON(TRUE); 4144 foreach ($this->links as $link) { $json .= $link->asJSON(TRUE); } 4145 $json = rtrim($json,", \n"); 4146 fputs($fd,$json); 4147 fclose($fd); 4148 4149 $fd = fopen($cachefolder.DIRECTORY_SEPARATOR.$cacheprefix."_links_lite.json","w"); 4150 $json = ""; 4151// $json = $this->defaultlink->asJSON(FALSE); 4152 foreach ($this->links as $link) { $json .= $link->asJSON(FALSE); } 4153 $json = rtrim($json,", \n"); 4154 fputs($fd,$json); 4155 fclose($fd); 4156 4157 $fd = fopen($cachefolder.DIRECTORY_SEPARATOR.$cacheprefix."_imaphtml.json","w"); 4158 $json = $this->imap->subHTML("LINK:"); 4159 fputs($fd,$json); 4160 fclose($fd); 4161 4162 4163 $fd = fopen($cachefolder.DIRECTORY_SEPARATOR.$cacheprefix."_imap.json","w"); 4164 $json = ''; 4165 $nodejson = trim($this->imap->subJSON("NODE:")); 4166 if($nodejson != '') 4167 { 4168 $json .= $nodejson; 4169 // should check if there WERE nodes... 4170 $json .= ",\n"; 4171 } 4172 $json .= $this->imap->subJSON("LINK:"); 4173 fputs($fd,$json); 4174 fclose($fd); 4175 4176 } 4177 else { debug("Couldn't read cache folder.\n"); } 4178} 4179 4180function MakeTemplateTree( &$tree_list, $startpoint="DEFAULT") 4181{ 4182 global $weathermap_lazycounter; 4183 4184 $output = ""; 4185 foreach ($tree_list[$startpoint] as $subnode) 4186 { 4187 $output .= "{ id: " . $weathermap_lazycounter++ . ", text: " . js_escape($subnode); 4188 if( isset($tree_list[$subnode])) 4189 { 4190 $output .= ", children: [ \n"; 4191 $output .= $this->MakeTemplateTree($tree_list, $subnode); 4192 $output = rtrim($output,", \n"); 4193 $output .= "] \n"; 4194 } 4195 else 4196 { 4197 $output .= ", leaf: true "; 4198 } 4199 $output .= "}, \n"; 4200 } 4201 4202 return($output); 4203} 4204 4205function DumpStats($filename="") 4206{ 4207 $report = "Feature Statistics:\n\n"; 4208 foreach ($this->usage_stats as $key=>$val) 4209 { 4210 $report .= sprintf("%70s => %d\n",$key,$val); 4211 } 4212 4213 if($filename == "") print $report; 4214} 4215 4216}; 4217// vim:ts=4:sw=4: 4218?> 4219