1<?php 2/* vim: set expandtab tabstop=4 shiftwidth=4: */ 3// +----------------------------------------------------------------------+ 4// | PHP Version 4 | 5// +----------------------------------------------------------------------+ 6// | Copyright (c) 1997-2002 The PHP Group | 7// +----------------------------------------------------------------------+ 8// | This source file is subject to version 2.02 of the PHP license, | 9// | that is bundled with this package in the file LICENSE, and is | 10// | available at through the world-wide-web at | 11// | http://www.php.net/license/2_02.txt. | 12// | If you did not receive a copy of the PHP license and are unable to | 13// | obtain it through the world-wide-web, please send a note to | 14// | license@php.net so we can mail you a copy immediately. | 15// +----------------------------------------------------------------------+ 16// | Authors: Alan Knowles <alan@akbkhome> | 17// +----------------------------------------------------------------------+ 18// 19// $Id: Tag.php 334846 2014-09-12 04:50:56Z alan_k $ 20/* FC/BC compatibility with php5 */ 21if ( (substr(phpversion(),0,1) < 5) && !function_exists('clone')) { 22 eval('function clone($t) { return $t; }'); 23} 24 25/** 26* Compiler That deals with standard HTML Tag output. 27* Since it's pretty complex it has it's own class. 28* I guess this class should deal with the main namespace 29* and the parent (standard compiler can redirect other namespaces to other classes. 30* 31* one instance of these exists for each namespace. 32* 33* 34* @version $Id: Tag.php 334846 2014-09-12 04:50:56Z alan_k $ 35*/ 36 37class HTML_Template_Flexy_Compiler_Flexy_Tag 38{ 39 40 41 /** 42 * Parent Compiler for 43 * 44 * @var object HTML_Template_Flexy_Compiler 45 * 46 * @access public 47 */ 48 var $compiler; 49 50 /** 51 * 52 * Factory method to create Tag Handlers 53 * 54 * $type = namespace eg. <flexy:toJavascript loads Flexy.php 55 * the default is this... (eg. Tag) 56 * 57 * 58 * @param string Namespace handler for element. 59 * @param object HTML_Template_Flexy_Compiler 60 * 61 * 62 * @return object tag compiler 63 * @access public 64 */ 65 66 static function &factory($type,&$compiler) { 67 if (!$type) { 68 $type = 'Tag'; 69 } 70 71 $class = 'HTML_Template_Flexy_Compiler_Flexy_' . $type; 72 if ($compiler->classExists($class)) { 73 $ret = new $class; 74 $ret->compiler = &$compiler; 75 return $ret; 76 } 77 78 $filename = 'HTML/Template/Flexy/Compiler/Flexy/' . ucfirst(strtolower($type)) . '.php'; 79 if (!HTML_Template_Flexy_Compiler_Flexy_Tag::fileExistsInPath($filename)) { 80 $ret = HTML_Template_Flexy_Compiler_Flexy_Tag::factory('Tag',$compiler); 81 return $ret; 82 } 83 // if we dont have a handler - just use the basic handler. 84 if (!file_exists(dirname(__FILE__) . '/'. ucfirst(strtolower($type)) . '.php')) { 85 $type = 'Tag'; 86 } 87 88 include_once 'HTML/Template/Flexy/Compiler/Flexy/' . ucfirst(strtolower($type)) . '.php'; 89 90 $class = 'HTML_Template_Flexy_Compiler_Flexy_' . $type; 91 if (!$compiler->classExists($class)) { 92 $ret = false; 93 return $ret; 94 } 95 $ret = HTML_Template_Flexy_Compiler_Flexy_Tag::factory($type,$compiler); 96 return $ret; 97 } 98 /** 99 * 100 * Check that a file exists in the "include_path" 101 * 102 * @param string Filename 103 * 104 * @return boolean true if it is in there. 105 * @access public 106 */ 107 static function fileExistsInPath($filename) { 108 if (isset($GLOBALS['_'.__CLASS__]['cache'][$filename])) { 109 return $GLOBALS['_'.__CLASS__]['cache'][$filename]; 110 } 111 $bits = explode(PATH_SEPARATOR,ini_get('include_path')); 112 foreach($bits as $b) { 113 if (file_exists("$b/$filename")) { 114 return $GLOBALS['_'.__CLASS__]['cache'][$filename] = true; 115 } 116 } 117 return $GLOBALS['_'.__CLASS__]['cache'][$filename] = false; 118 } 119 120 121 122 /** 123 * The current element to parse.. 124 * 125 * @var object 126 * @access public 127 */ 128 var $element; 129 130 /** 131 * Flag to indicate has attribute flexy:foreach (so you cant mix it with flexy:if!) 132 * 133 * @var boolean 134 * @access public 135 */ 136 var $hasForeach = false; 137 138 /** 139 * toString - display tag, attributes, postfix and any code in attributes. 140 * Note first thing it does is call any parseTag Method that exists.. 141 * 142 * 143 * @see parent::toString() 144 */ 145 function toString($element) 146 { 147 148 global $_HTML_TEMPLATE_FLEXY_TOKEN; 149 global $_HTML_TEMPLATE_FLEXY; 150 151 // store the element in a variable 152 $this->element = $element; 153 // echo "toString: Line {$this->element->line} <{$this->element->tag}>\n"; 154 155 // if the FLEXYSTARTCHILDREN flag was set, only do children 156 // normally set in BODY tag. 157 // this will probably be superseeded by the Class compiler. 158 159 if (isset($element->ucAttributes['FLEXY:STARTCHILDREN'])) { 160 161 return $element->compileChildren($this->compiler); 162 } 163 // look for flexy:ignore.. 164 $flexyignore = $this->parseAttributeIgnore(); 165 166 // rewriting should be done with a tag.../flag. 167 168 $this->reWriteURL("HREF"); 169 $this->reWriteURL("SRC"); 170 $this->reWriteURL("BACKGROUND"); 171 172 // handle elements 173 if (($ret =$this->_parseTags()) !== false) { 174 return $ret; 175 } 176 // these add to the close tag.. 177 178 $ret = $this->parseAttributeForeach(); 179 $ret .= $this->parseAttributeIf(); 180 181 // support Custom Attributes... 182 require_once 'HTML/Template/Flexy/Compiler/Flexy/CustomFlexyAttributes.php'; 183 $customFlexyAttributes = new HTML_Template_Flexy_Compiler_Flexy_CustomFlexyAttributes($this->compiler); 184 $customFlexyAttributes->doCustomAttributes($element); 185 186 187 $add = $this->toStringOpenTag($element,$ret); 188 189 if (is_object($add) && is_a($add,'PEAR_Error')) { 190 return $add; 191 } 192 193 194 195 196 197 // post stuff this is probably in the wrong place... 198 199 if ($element->postfix) { 200 foreach ($element->postfix as $e) { 201 $add = $e->compile($this->compiler); 202 if (is_object($add) && is_a($add,'PEAR_Error')) { 203 return $add; 204 } 205 $ret .= $add; 206 } 207 } else if ($this->element->postfix) { // if postfixed by self.. 208 foreach ($this->element->postfix as $e) { 209 $add = $e->compile($this->compiler); 210 if (is_object($add) && is_a($add,'PEAR_Error')) { 211 return $add; 212 } 213 214 $ret .= $add; 215 } 216 } 217 218 219 $tmp = $this->toStringChildren($element,$ret); 220 if (is_object($tmp) && is_a($tmp,'PEAR_Error')) { 221 return $tmp; 222 } 223 $tmp = $this->toStringCloseTag($element,$ret); 224 if (is_object($tmp) && is_a($tmp,'PEAR_Error')) { 225 return $tmp; 226 } 227 228 229 // reset flexyignore 230 231 $_HTML_TEMPLATE_FLEXY_TOKEN['flexyIgnore'] = $flexyignore; 232 233 if (isset($_HTML_TEMPLATE_FLEXY['currentOptions']['output.block']) && 234 ($_HTML_TEMPLATE_FLEXY['currentOptions']['output.block'] == $element->getAttribute('ID'))) { 235 236 // echo $_HTML_TEMPLATE_FLEXY['compiledTemplate']; 237 238 $fh = fopen($_HTML_TEMPLATE_FLEXY['compiledTemplate'],'w'); 239 fwrite($fh,$ret); 240 fclose($fh); 241 242 } 243 244 245 246 return $ret; 247 } 248 249 /** 250 * convert a tag into compiled version 251 * @arg object Element 252 * @arg inout output string to template 253 * @return none? or pear error. 254 * 255 */ 256 257 function toStringOpenTag(&$element,&$ret) 258 { 259 // START ADDITION... 260 if ((empty($element->tag)) || (empty($element->oTag))) { 261 return; 262 } 263 // ...END ADDITION 264 265 266 // spit ou the tag and attributes. 267 268 if ($element->oTag{0} == '?') { 269 $ret .= '<?php echo "<"; ?>'; 270 } else { 271 $ret .= "<"; 272 } 273 $ret .= $element->oTag; 274 //echo '<PRE>'.print_r($element->attributes,true); 275 foreach ($element->attributes as $k=>$v) { 276 // if it's a flexy tag ignore it. 277 278 279 if (strtoupper($k) == 'FLEXY:RAW') { 280 if (!is_array($v) || !isset($v[1]) || !is_object($v[1])) { 281 return $this->_raiseErrorWithPositionAndTag( 282 'flexy:raw only accepts a variable or method call as an argument, eg.'. 283 ' flexy:raw="{somevalue}" you provided something else.' . 284 285 null, HTML_TEMPLATE_FLEXY_ERROR_DIE); 286 } 287 $add = $v[1]->compile($this->compiler); 288 if (is_object($add) && is_a($add,'PEAR_Error')) { 289 return $add; 290 } 291 $ret .= ' ' . $add; 292 continue; 293 294 } 295 296 if (strtoupper(substr($k,0,6)) == 'FLEXY:') { 297 continue; 298 } 299 // true == an attribute without a ="xxx" 300 if ($v === true) { 301 $ret .= " $k"; 302 continue; 303 } 304 305 // if it's an input with value or placeholder... 306 // the output the translated value.. 307 if (is_string($v) && (in_array($element->tag , array('INPUT','TEXTAREA'))) && 308 (strtoupper($k) == 'VALUE' || strtoupper($k) == 'PLACEHOLDER' ) 309 ) { 310 // look up the translation.. 311 $v = '"'. htmlspecialchars( 312 $this->compiler->flexy->translateString(substr($v,1,-1)) 313 ) .'"'; 314 315 } 316 317 // if it's a string just dump it. 318 if (is_string($v)) { 319 $v = str_replace(array('{_(',')_}'),array('',''),$v); 320 $ret .= " {$k}={$v}"; 321 continue; 322 } 323 324 // normally the value is an array of string, however 325 // if it is an object - then it's a conditional key. 326 // eg. if (something) echo ' SELECTED'; 327 // the object is responsible for adding it's space.. 328 329 if (is_object($v)) { 330 $add = $v->compile($this->compiler); 331 if (is_object($add) && is_a($add,'PEAR_Error')) { 332 return $add; 333 } 334 335 $ret .= $add; 336 continue; 337 } 338 339 // otherwise its a key="sometext{andsomevars}" 340 341 $ret .= " {$k}="; 342 343 foreach($v as $item) { 344 345 if (is_string($item)) { 346 // skip translation strings in tags. 347 $item = str_replace(array('{_(',')_}'),array('',''),$item); 348 $ret .= $item; 349 continue; 350 } 351 $add = $item->compile($this->compiler); 352 if (is_object($add) && is_a($add,'PEAR_Error')) { 353 return $add; 354 } 355 $ret .= $add; 356 } 357 } 358 $ret .= ">"; 359 } 360 /** 361 * compile children to string. 362 * @arg object Element 363 * @arg inout output string to template 364 * @return none? or pear error. 365 */ 366 367 function toStringChildren(&$element,&$ret) 368 { 369 // dump contents of script raw - to prevent gettext additions.. 370 // print_r($element); 371 // make sure tag isn't empty because it wouldn't make sense to output script without script tags 372 if (((! empty($element->tag)) && ($element->tag == 'SCRIPT')) 373 || ((! empty($element->oTag)) && ($element->oTag == 'SCRIPT'))) { 374 foreach($element->children as $c) { 375 //print_R($c); 376 if (!$c) { 377 continue; 378 } 379 if ($c->token == 'Text') { 380 $ret .= $c->value; 381 continue; 382 } 383 // techically we shouldnt have anything else inside of script tags. 384 // as the tokeinzer is supposted to ignore it.. 385 } 386 return; 387 } 388 $add = $element->compileChildren($this->compiler); 389 if (is_object($add) && is_a($add,'PEAR_Error')) { 390 return $add; 391 } 392 $ret .= $add; 393 394 } 395 /** 396 * compile closing tag to string. 397 * @arg object Element 398 * @arg inout output string to template 399 * @return none? or pear error. 400 */ 401 402 function toStringCloseTag(&$element,&$ret) 403 { 404 // output the closing tag. 405 // If the tag is empty don't output closing tags, just output postfixes if any exist... 406 if ( !$element->close) { 407 return; 408 } 409 410 if ((! empty($element->tag)) && (! empty($element->oTag))) 411 { 412 $add = $element->close->compile($this->compiler); 413 if (is_object($add) && is_a($add,'PEAR_Error')) { 414 return $add; 415 } 416 $ret .= $add; 417 return; 418 } 419 // RICK - added by me 420 // element has a seperate closing tag (eg. </something>) and opening and closing tags should be removed 421 // because FLEXY:OMITTAG element attribute is set, but still need postfix stuff like for ending ifs and foreach 422 // so this is NOT OPTIONAL if foreach and if are not optional. 423 if ($element->close->postfix) { 424 foreach ($element->close->postfix as $e) { 425 $add = $e->compile($this->compiler); 426 if (is_object($add) && is_a($add,'PEAR_Error')) { 427 return $add; 428 } 429 $ret .= $add; 430 } 431 return; 432 } 433 if ($this->element->close->postfix) { // if postfixed by self.. 434 foreach ($this->element->close->postfix as $e) { 435 $add = $e->compile($this->compiler); 436 if (is_object($add) && is_a($add,'PEAR_Error')) { 437 return $add; 438 } 439 440 $ret .= $add; 441 } 442 return; 443 } 444 445 } 446 447 448 /** 449 * Reads an flexy:foreach attribute - 450 * 451 * 452 * @return string to add to output. 453 * @access public 454 */ 455 456 function parseAttributeIgnore() 457 { 458 459 global $_HTML_TEMPLATE_FLEXY_TOKEN; 460 461 $flexyignore = $_HTML_TEMPLATE_FLEXY_TOKEN['flexyIgnore']; 462 463 if ($this->element->getAttribute('FLEXY:IGNORE') !== false) { 464 $_HTML_TEMPLATE_FLEXY_TOKEN['flexyIgnore'] = true; 465 $this->element->clearAttribute('FLEXY:IGNORE'); 466 } 467 return $flexyignore; 468 469 } 470 471 /** 472 * Reads an flexy:foreach attribute - 473 * 474 * 475 * @return string to add to output. 476 * @access public 477 */ 478 479 function parseAttributeForeach() 480 { 481 global $_HTML_TEMPLATE_FLEXY; 482 $foreach = $this->element->getAttribute('FLEXY:FOREACH'); 483 if ($foreach === false) { 484 return ''; 485 } 486 //var_dump($foreach); 487 488 $this->element->hasForeach = true; 489 // create a foreach element to wrap this with. 490 $foreachTokens = explode( ",", $foreach ); //usual 491 $first = array_shift($foreachTokens); 492 // we will accept first argument as a method call.. with arguments. 493 // this however does not deal with '#' with commas and braces insed very weill.. 494 if (strpos($first, '(') !== false) { 495 while (strpos($first, ')') === false) { 496 if (!count($foreachTokens)) { 497 return $this->_raiseErrorWithPositionAndTag( 498 "Missing Closer on functin call: An flexy:foreach attribute was found. flexy:foreach="$foreach"<BR> 499 the syntax is <sometag flexy:foreach="onarray,withvariable[,withanothervar] ><BR>", 500 null, HTML_TEMPLATE_FLEXY_ERROR_DIE); 501 } 502 $first .= ',' . array_shift($foreachTokens); 503 } 504 } 505 array_unshift($foreachTokens, $first); 506 507 $foreachObj = $this->element->factory('Foreach', 508 $foreachTokens, 509 $this->element->line); 510 // failed = probably not enough variables.. 511 512 513 if ($foreachObj === false) { 514 515 return $this->_raiseErrorWithPositionAndTag( 516 "Missing Arguments: An flexy:foreach attribute was found. flexy:foreach="$foreach"<BR> 517 the syntax is <sometag flexy:foreach="onarray,withvariable[,withanothervar] ><BR>", 518 null, HTML_TEMPLATE_FLEXY_ERROR_DIE); 519 } 520 521 522 523 // does it have a closetag? 524 if (!$this->element->close) { 525 526 if ($this->element->getAttribute('/') === false) { 527 528 529 return $this->_raiseErrorWithPositionAndTag( 530 "A flexy:foreach attribute was found without a corresponding </{$this->element->tag} tag", 531 null, HTML_TEMPLATE_FLEXY_ERROR_DIE); 532 } 533 // it's an xhtml tag! 534 $this->element->postfix = array($this->element->factory("End", '', $this->element->line)); 535 } else { 536 $this->element->close->postfix = array($this->element->factory("End", '', $this->element->line)); 537 } 538 539 $this->element->clearAttribute('FLEXY:FOREACH'); 540 return $foreachObj->compile($this->compiler); 541 } 542 /** 543 * Reads an flexy:if attribute - 544 * 545 * 546 * @return string to add to output. 547 * @access public 548 */ 549 550 function parseAttributeIf() 551 { 552 // dont use the together, if is depreciated.. 553 $if = $this->element->getAttribute('FLEXY:IF'); 554 555 if ($if === false) { 556 return ''; 557 } 558 559 if (isset($this->element->hasForeach)) { 560 return $this->_raiseErrorWithPositionAndTag("You may not use FOREACH and IF tags in the same tag", 561 null, HTML_TEMPLATE_FLEXY_ERROR_DIE); 562 } 563 564 // allow if="!somevar" 565 $ifnegative = ''; 566 567 if ($if{0} == '!') { 568 $ifnegative = '!'; 569 $if = substr($if,1); 570 } 571 // if="xxxxx" 572 // if="xxxx.xxxx()" - should create a method prefixed with 'if:' 573 // these checks should really be in the if/method class..!!! 574 575 576 577 if (!preg_match('/^[_A-Z][A-Z0-9_]*(\[[0-9]+\])?((\[|%5B)[A-Z0-9_]+(\]|%5D))*'. 578 '(\.[_A-Z][A-Z0-9_]*((\[|%5B)[A-Z0-9_]+(\]|%5D))*)*(\\([^)]*\))?$/i',$if)) { 579 return $this->_raiseErrorWithPositionAndTag( 580 "IF tags only accept simple object.variable or object.method() values. {$if}", 581 null, HTML_TEMPLATE_FLEXY_ERROR_DIE); 582 583 } 584 585 if (substr($if,-1) == ')') { 586 // grab args.. 587 $args = substr($if,strpos($if,'(')+1,-1); 588 // simple explode ... 589 590 $args = strlen(trim($args)) ? explode(',',$args) : array(); 591 //print_R($args); 592 593 // this is nasty... - we need to check for quotes = eg. # at beg. & end.. 594 $args_clean = array(); 595 for ($i=0; $i<count($args); $i++) { 596 if ($args[$i]{0} != '#') { 597 $args_clean[] = $args[$i]; 598 continue; 599 } 600 // single # - so , must be inside.. 601 if ((strlen($args[$i]) > 1) && ($args[$i]{strlen($args[$i])-1}=='#')) { 602 $args_clean[] = $args[$i]; 603 continue; 604 } 605 606 $args[$i] .=',' . $args[$i+1]; 607 // remove args+1.. 608 array_splice($args,$i+1,1); 609 $i--; 610 // reparse.. 611 } 612 613 614 615 $ifObj = $this->element->factory('Method', 616 array('if:'.$ifnegative.substr($if,0,strpos($if,'(')), $args_clean), 617 $this->element->line); 618 } else { 619 $ifObj = $this->element->factory('If', $ifnegative.$if, $this->element->line); 620 } 621 622 // does it have a closetag? - you must have one - so you will have to hack in <span flexy:if=..><img></span> on tags 623 // that do not have close tags - it's done this way to try and avoid mistakes. 624 625 626 if (!$this->element->close) { 627 //echo "<PRE>";print_R($this->element); 628 629 if ($this->element->getAttribute('/') !== false) { 630 $this->element->postfix = array($this->element->factory("End",'', $this->element->line)); 631 } else { 632 633 return $this->_raiseErrorWithPositionAndTag( 634 "An flexy:if attribute was found in <{$this->element->name} tag without a corresponding </{$this->element->name} tag", 635 null, HTML_TEMPLATE_FLEXY_ERROR_DIE); 636 637 } 638 } else { 639 640 $this->element->close->postfix = array($this->element->factory("End",'', $this->element->line)); 641 } 642 $this->element->clearAttribute('FLEXY:IF'); 643 return $ifObj->compile($this->compiler); 644 } 645 646 /** 647 * Reads Tags - and relays to parseTagXXXXXXX 648 * 649 * 650 * @return string | false = html output or ignore (just output the tag) 651 * @access private 652 */ 653 654 655 function _parseTags() 656 { 657 global $_HTML_TEMPLATE_FLEXY_TOKEN; 658 // doesnt really need strtolower etc. as php functions are not case sensitive! 659 660 661 /* always render script correctly */ 662 if (0 == strcasecmp($this->element->tag,"script")) { 663 return $this->parseTagScript(); 664 } 665 666 if ($this->element->getAttribute('FLEXY:DYNAMIC')) { 667 return $this->compiler->appendPhp( 668 $this->getElementPhp( $this->element->getAttribute('ID') ) 669 ); 670 671 } 672 673 if ($this->element->getAttribute('FLEXY:IGNOREONLY') !== false) { 674 return false; 675 } 676 if ($_HTML_TEMPLATE_FLEXY_TOKEN['flexyIgnore']) { 677 return false; 678 } 679 $tag = $this->element->tag; 680 if (strpos($tag,':') !== false) { 681 $bits = explode(':',$tag); 682 $tag = $bits[1]; 683 } 684 685 if (in_array(strtolower($tag), array('menulist','textbox','checkbox'))) { 686 $method = 'parseXulTag'; 687 } else { 688 $method = 'parseTag'.$tag; 689 if (!method_exists($this,$method)) { 690 return false; 691 } 692 } 693 694 if (($this->element->getAttribute('NAME') === false) && 695 ($this->element->getAttribute('ID') === false) ) { 696 return false; 697 } 698 // do any of the attributes use flexy data... 699 //foreach ($this->element->attributes as $k=>$v) { 700 // if (is_array($v)) { 701 // return false; 702 // } 703 //} 704 705 //echo "call $method" . serialize($this->element->attributes). "\n"; 706 707 return $this->$method(); 708 // allow the parse methods to return output. 709 710 } 711 712 713 714 715 /** 716 * produces the code for dynamic elements 717 * 718 * @return string | false = html output or ignore (just output the tag) 719 * @access public 720 */ 721 722 function getElementPhp($id,$mergeWithName=false,$varsOnly = false) { 723 724 global $_HTML_TEMPLATE_FLEXY; 725 static $tmpId=0; 726 727 728 729 if (!$id) { 730 731 return $this->_raiseErrorWithPositionAndTag("Dynamic tags require an ID value", 732 null, HTML_TEMPLATE_FLEXY_ERROR_DIE); 733 734 } 735 736 // dont mix and match.. 737 if (($this->element->getAttribute('FLEXY:IF') !== false) || 738 ($this->element->getAttribute('FLEXY:FOREACH') !== false) ) 739 { 740 return $this->_raiseErrorWithPositionAndTag( 741 " You can not mix flexy:if= or flexy:foreach= with dynamic form elements " . 742 " (turn off tag to element code with flexyIgnore=0, use flexy:ignore="yes" in the tag" . 743 " or put the conditional outside in a span tag", 744 null, HTML_TEMPLATE_FLEXY_ERROR_DIE); 745 } 746 747 if ((strtolower($this->element->getAttribute('TYPE')) == 'checkbox' ) && 748 (substr($this->element->getAttribute('NAME'),-2) == '[]')) { 749 if ($this->element->getAttribute('ID') === false) { 750 $id = 'tmpId'. (++$tmpId); 751 $this->element->attributes['id'] = $id; 752 $this->element->ucAttributes['ID'] = $id; 753 } else { 754 $id = $this->element->getAttribute('ID'); 755 } 756 $mergeWithName = true; 757 } 758 759 760 761 762 763 if (isset($_HTML_TEMPLATE_FLEXY['elements'][$id])) { 764 // echo "<PRE>";print_r($this);print_r($_HTML_TEMPLATE_FLEXY['elements']);echo "</PRE>"; 765 return $this->_raiseErrorWithPositionAndTag( 766 "The Dynamic tag Name '$id' has already been used previously by tag <{$_HTML_TEMPLATE_FLEXY['elements'][$id]->tag}>", 767 null,HTML_TEMPLATE_FLEXY_ERROR_DIE); 768 } 769 770 $ret = ''; 771 $unset = ''; 772 773 //echo '<PRE>';print_r($this->element);echo '</PRE>'; 774 if (isset($this->element->ucAttributes['FLEXY:USE'])) { 775 $ar = $this->element->ucAttributes['FLEXY:USE']; 776 $str = ''; 777 778 for($i =1; $i < count($ar) -1; $i++) { 779 switch(true) { 780 case (is_object($ar[$i]) && is_a($ar[$i], 'HTML_Template_Flexy_Token_Var')): 781 $str .= '. ' . $ar[$i]->toVar($ar[$i]->value). ' '; 782 break; 783 case is_string($ar[$i]): 784 $str .= '. ' . $ar[0] . $ar[$i] . $ar[0]; 785 break; 786 default: 787 return $this->_raiseErrorWithPositionAndTag( 788 "unsupported type found in attribute, use flexy:ignore to prevent parsing or remove it. " . 789 print_r($this->element,true), 790 null,HTML_TEMPLATE_FLEXY_ERROR_DIE); 791 } 792 } 793 $str = trim(ltrim($str,'.')); 794 $_HTML_TEMPLATE_FLEXY['elements'][$id] = $this->toElement($this->element); 795 796 return $ret . 797 ' 798 if (!isset($this->elements['.$str.'])) { 799 echo "ELEMENT MISSING $str"; 800 } 801 echo $this->elements['.$str.']->toHtml();' .$unset; 802 } 803 804 805 806 if ($this->elementUsesDynamic($this->element)) { 807 $used = array(); 808 foreach ($this->element->attributes as $attribute => $attributeValue) { 809 if (!is_array($attributeValue)) { 810 continue; 811 } 812 if (strtoupper(substr($attribute,0,6)) == 'FLEXY:') { 813 continue; 814 } 815 unset($this->element->attributes[$attribute]); 816 // generate code to put data into value.. 817 $output_avar = '$this->elements[\''.$id.'\']->attributes[\''.$attribute.'\']'; 818 $used[] = "'{$attribute}'"; 819 $ret .= "\nif (!isset({$output_avar})) {\n"; 820 // get the " or ' that encapsulates the element. 821 $wrapper = array_shift($attributeValue); 822 array_pop($attributeValue); 823 $ret .= " {$output_avar} = '';\n"; 824 //echo '<PRE>';print_r($attributeValue);echo '</PRE>'; 825 foreach($attributeValue as $item) { 826 827 if (is_string($item)) { 828 $ret .= " {$output_avar} .= {$wrapper}{$item}{$wrapper};\n"; 829 continue; 830 } 831 if (!is_object($item) || !is_a($item, 'HTML_Template_Flexy_Token_Var')) { 832 return $this->_raiseErrorWithPositionAndTag( 833 "unsupported type found in attribute, use flexy:ignore to prevent parsing or remove it. " . 834 print_r($this->element,true), 835 null,HTML_TEMPLATE_FLEXY_ERROR_DIE); 836 } 837 838 $var = $item->toVar($item->value); 839 if (is_object($var) && is_a($var, 'PEAR_Error')) { 840 return $var; 841 } 842 list($prefix,$suffix) = $this->compiler->getModifierWrapper($item); 843 $prefix = substr($prefix,4); 844 845 $ret .= " {$output_avar} .= {$prefix}{$var}{$suffix};\n"; 846 } 847 848 $ret .= "}\n"; 849 } 850 $ret .= "\$_attributes_used = array(".implode(',',$used).");\n"; 851 $unset = "\n".'if (isset($_attributes_used)) { foreach($_attributes_used as $_a) {'."\n". 852 ' unset($this->elements[\''. $id .'\']->attributes[$_a]);'."\n" . 853 "}}\n"; 854 855 856 } 857 858 859 860 861 // this is for a case where you can use a sprintf as the name, and overlay it with a variable element.. 862 $_HTML_TEMPLATE_FLEXY['elements'][$id] = $this->toElement($this->element); 863 864 865 866 if ($varsOnly) { // used by form tag. 867 return array($ret,$unset); 868 } 869 870 if ($var = $this->element->getAttribute('FLEXY:NAMEUSES')) { 871 // force var to use name (as radio buttons pick up id.) 872 873 $ename = $this->element->getAttribute('NAME'); 874 $printfnamevar = $printfvar = 'sprintf(\''.$ename .'\','.$this->element->toVar($var) .')'; 875 // support id replacement as well ... 876 $idreplace = ''; 877 878 879 if (strtolower($this->element->getAttribute('TYPE')) == 'radio') { 880 $ename = $this->element->getAttribute('ID'); 881 $printfvar = 'sprintf(\''.$ename .'\','.$this->element->toVar($var) .')'; 882 } 883 884 885 if ($this->element->getAttribute('ID')) { 886 $idvar = 'sprintf(\''.$this->element->getAttribute('ID') .'\','.$this->element->toVar($var) .')'; 887 $idreplace = '$_element->attributes[\'id\'] = '.$idvar.';'; 888 } 889 return $ret . ' 890 $_element = $this->mergeElement( 891 $this->elements[\''.$id.'\'], 892 isset('.$this->element->toVar($var).') && isset($this->elements['.$printfnamevar .']) ? $this->elements['.$printfnamevar .'] : false 893 ); 894 $_element->attributes[\'name\'] = '.$printfnamevar. '; 895 ' . $idreplace . ' 896 echo $_element->toHtml();' .$unset; 897 898 } 899 900 901 if ($mergeWithName) { 902 $name = $this->element->getAttribute('NAME'); 903 //if ((strtolower($this->element->getAttribute('TYPE')) == 'checkbox') && (substr($name,-2) == '[]')) { 904 // $name = substr($name,0,-2); 905 //} 906 if (!$name) { 907 return $ret .' 908 $_element = $this->elements[\''.$id.'\']; 909 echo $_element->toHtml();' . $unset; 910 } else { 911 return $ret . 912 ' 913 $_element = $this->elements[\''.$id.'\']; 914 if (isset($this->elements[\''.$name.'\'])) { 915 $_element = $this->mergeElement($_element,$this->elements[\''.$name.'\']); 916 } 917 echo $_element->toHtml();' . $unset; 918 } 919 920 } 921 return $ret . 'echo $this->elements[\''.$id.'\']->toHtml();'. $unset; 922 923 } 924 925 /** 926 * Reads an Script tag - check if PHP is allowed. 927 * 928 * @return false|PEAR_Error 929 * @access public 930 */ 931 function parseTagScript() 932 { 933 934 935 $lang = $this->element->getAttribute('LANGUAGE'); 936 if (!$lang) { 937 return false; 938 } 939 $lang = strtoupper($lang); 940 $allow = $GLOBALS['_HTML_TEMPLATE_FLEXY']['currentOptions']['allowPHP']; 941 942 if ($allow === true) { 943 944 return false; 945 } 946 947 if ($lang == "PHP") { 948 if ($allow == 'delete') { 949 return ''; 950 } 951 return $this->_raiseErrorWithPositionAndTag('PHP code found in script (script)', 952 HTML_TEMPLATE_FLEXY_ERROR_SYNTAX,HTML_TEMPLATE_FLEXY_ERROR_RETURN 953 ); 954 } 955 return false; 956 957 } 958 /** 959 * Reads an Input tag - build a element object for it 960 * 961 * 962 * @return string | false = html output or ignore (just output the tag) 963 * @access public 964 */ 965 966 967 function parseTagInput() 968 { 969 global $_HTML_TEMPLATE_FLEXY; 970 971 $utype = strtoupper($this->element->getAttribute('TYPE')); 972 if (in_array($utype, array('SUBMIT','BUTTON','INPUT',''))) { 973 $this->compiler->addStringToGettext( 974 $this->element->getAttribute('VALUE') 975 ); 976 977 } 978 if (in_array($utype, array('TEXT','PASSWORD'))) { 979 $this->compiler->addStringToGettext( 980 $this->element->getAttribute('PLACEHOLDER') 981 ); 982 } 983 // form elements : format: 984 //value - fill out as PHP CODE 985 986 // as a general rule, this uses name, rather than ID except on 987 // radio 988 $mergeWithName = false; 989 $id = $this->element->getAttribute('NAME'); 990 991 992 if (isset($this->element->ucAttributes['FLEXY:RAW'])) { 993 return $this->_raiseErrorWithPositionAndTag( 994 "Flexy:raw can only be used with flexy:ignore, to prevent conversion of html ". 995 "elements to flexy elements", 996 null, HTML_TEMPLATE_FLEXY_ERROR_DIE 997 ); 998 } 999 // checkboxes need more work.. - at the momemnt assume one with the same value... 1000 if (!in_array(strtoupper($this->element->getAttribute('TYPE')), array('RADIO'))) { 1001 if (!$id) { 1002 return false; 1003 } 1004 return $this->compiler->appendPhp($this->getElementPhp( $id,$mergeWithName)); 1005 1006 } 1007 // now we are only dealing with radio buttons.. which are a bit odd... 1008 1009 // we need to create a generic holder for this based on the name.. 1010 // this is only really available for use with setting stuff... 1011 1012 if (!isset($_HTML_TEMPLATE_FLEXY['elements'][$id])) { 1013 $_HTML_TEMPLATE_FLEXY['elements'][$id] = new HTML_Template_Flexy_Element("input", 1014 array('type'=>'radio')); 1015 1016 } 1017 // we dont really care if it is getting reused loads of times.. (expected as each radio button will use it. 1018 $name = $id; 1019 $id = $this->element->getAttribute('ID'); 1020 if (!$id) { 1021 $id = $name . '_' . $this->element->getAttribute('VALUE'); 1022 } 1023 // this get's tricky as we could end up with elements with the same name... (if value was not set.., 1024 // or two elements have the same name.. 1025 1026 $mergeWithName = true; 1027 1028 return $this->compiler->appendPhp($this->getElementPhp( $id,$mergeWithName)); 1029 1030 } 1031 1032 /** 1033 * Deal with a TextArea tag - build a element object for it 1034 * 1035 * @return string | false = html output or ignore (just output the tag) 1036 * @access public 1037 */ 1038 1039 function parseTagTextArea() 1040 { 1041 // create a translatable string for placeholder addString should handle if it does not exist.. 1042 $this->compiler->addStringToGettext( 1043 $this->element->getAttribute('PLACEHOLDER') 1044 ); 1045 1046 return $this->compiler->appendPhp( 1047 $this->getElementPhp( $this->element->getAttribute('NAME'))); 1048 1049 1050 1051 } 1052 /** 1053 * Deal with Selects - build a element object for it (unless flexyignore is set) 1054 * 1055 * 1056 * @return string | false = html output or ignore (just output the tag) 1057 * @access public 1058 */ 1059 1060 function parseTagSelect() 1061 { 1062 return $this->compiler->appendPhp( 1063 $this->getElementPhp( $this->element->getAttribute('NAME'))); 1064 } 1065 1066 1067 1068 1069 /** 1070 * Reads an Form tag - and set up the element object header etc. 1071 * 1072 * @return string | false = html output or ignore (just output the tag) 1073 * @access public 1074 */ 1075 1076 function parseTagForm() 1077 { 1078 global $_HTML_TEMPLATE_FLEXY; 1079 $copy = clone($this->element); 1080 $copy->children = array(); 1081 $id = $this->element->getAttribute('NAME'); 1082 // dont make forms dynamic if they dont have a name.. 1083 if (!$id) { 1084 return false; 1085 } 1086 1087 // this adds the element to the elements array. 1088 $old = clone($this->element); 1089 $this->element = $copy; 1090 list($prefix,$suffix) = $this->getElementPhp($id,false,true); 1091 $this->element= $old; 1092 1093 1094 return 1095 $this->compiler->appendPhp($prefix .'echo $this->elements[\''.$id.'\']->toHtmlnoClose();'.$suffix) . 1096 $this->element->compileChildren($this->compiler) . 1097 $this->compiler->appendHtml( "</{$copy->oTag}>"); 1098 1099 } 1100 1101 /** 1102 * Deal with Label - build a element object for it (unless flexyignore is set) 1103 * 1104 * 1105 * @return string | false = html output or ignore (just output the tag) 1106 * @access public 1107 */ 1108 1109 function parseTagLabel() 1110 { 1111 1112 if (empty($GLOBALS['_HTML_TEMPLATE_FLEXY']['currentOptions']['useElementLabels'])) { 1113 return false; 1114 } 1115 // this may need some protection for general usage..... 1116 1117 $for = $this->element->getAttribute('FOR'); 1118 $ret = ''; 1119 $tmp = $this->toStringChildren($this->element, $ret); 1120 if (is_object($tmp) && is_a($tmp,'PEAR_Error')) { 1121 return $tmp; 1122 } 1123 1124 return $this->compiler->appendPhp( 1125 'echo "<label for=\"' . $for . '\">";' . 1126 'if (!empty($this->elements[\'' . $for . '\']->label)) ' . 1127 ' { echo htmlspecialchars($this->elements[\'' . $for . '\']->label); } else { ?>' . 1128 htmlspecialchars($ret) . '<? } ' . 1129 'echo "</label>";' 1130 ); 1131 } 1132 1133 1134 1135 /** 1136 * reWriteURL - can using the config option 'url_rewrite' 1137 * format "from:to,from:to" 1138 * only handle left rewrite. 1139 * so 1140 * "/images:/myroot/images" 1141 * would change 1142 * /images/xyz.gif to /myroot/images/xyz.gif 1143 * /images/stylesheet/imagestyles.css to /myroot/images/stylesheet/imagestyles.css 1144 * note /imagestyles did not get altered. 1145 * will only work on strings (forget about doing /images/{someimage} 1146 * 1147 * 1148 * @param string attribute to rewrite 1149 * @return none 1150 * @access public 1151 */ 1152 function reWriteURL($which) 1153 { 1154 global $_HTML_TEMPLATE_FLEXY; 1155 1156 1157 if (!is_string($original = $this->element->getAttribute($which))) { 1158 return; 1159 } 1160 1161 if ($original == '') { 1162 return; 1163 } 1164 1165 if (empty($_HTML_TEMPLATE_FLEXY['currentOptions']['url_rewrite'])) { 1166 return; 1167 } 1168 1169 $bits = explode(",",$_HTML_TEMPLATE_FLEXY['currentOptions']['url_rewrite']); 1170 $new = $original; 1171 1172 foreach ($bits as $bit) { 1173 if (!strlen(trim($bit))) { 1174 continue; 1175 } 1176 $parts = explode (':', $bit); 1177 if (!isset($parts[1])) { 1178 return $this->_raiseErrorWithPositionAndTag('HTML_Template_Flexy: url_rewrite syntax incorrect'. 1179 print_r(array($bits,$bits),true),null,HTML_TEMPLATE_FLEXY_ERROR_DIE); 1180 } 1181 $new = preg_replace('#^'.$parts[0].'#',$parts[1], $new); 1182 } 1183 1184 1185 if ($original == $new) { 1186 return; 1187 } 1188 $this->element->ucAttributes[$which] = '"'. $new . '"'; 1189 } 1190 1191 /** 1192 * Convert flexy tokens to HTML_Template_Flexy_Elements. 1193 * 1194 * @param object token to convert into a element. 1195 * @return object HTML_Template_Flexy_Element 1196 * @access public 1197 */ 1198 function toElement($element,$stripspaces = false) 1199 { 1200 require_once 'HTML/Template/Flexy/Element.php'; 1201 $ret = new HTML_Template_Flexy_Element; 1202 1203 1204 if (strtolower(get_class($element)) != 'html_template_flexy_token_tag') { 1205 $this->compiler->addStringToGettext($element->value); 1206 return $element->value; 1207 } 1208 1209 1210 $ret->tag = strtolower($element->tag); 1211 1212 if ($ret->tag == 'menulist') { // for XUL menulist, remove the white space between tags.. 1213 $stripspaces = true; 1214 } 1215 1216 $ats = $element->getAttributes(); 1217 1218 if (isset($element->attributes['flexy:xhtml'])) { 1219 $ats['flexy:xhtml'] = true; 1220 } 1221 1222 foreach(array_keys($ats) as $a) { 1223 $ret->attributes[$a] = $this->unHtmlEntities($ats[$a]); 1224 } 1225 //print_r($ats); 1226 if (!$element->children) { 1227 return $ret; 1228 } 1229 1230 // children - normally to deal with <element> 1231 1232 //print_r($this->children); 1233 foreach(array_keys($element->children) as $i) { 1234 // not quite sure why this happens - but it does. 1235 if (!is_object($element->children[$i])) { 1236 continue; 1237 } 1238 if ($stripspaces && (strtolower(get_class($element->children[$i])) != 'html_template_flexy_token_tag')) { 1239 continue; 1240 } 1241 $ret->children[] = $this->toElement($element->children[$i],$stripspaces); 1242 } 1243 return $ret; 1244 } 1245 1246 /** 1247 * do the reverse of htmlspecialchars on an attribute.. 1248 * 1249 * copied from get-html-translation-table man page 1250 * 1251 * @param mixed from attribute values 1252 * 1253 * @return string return 1254 * @access public 1255 * @see see also methods..... 1256 */ 1257 1258 function unHtmlEntities ($in) 1259 { 1260 if (!is_string($in)) { 1261 return $in; 1262 } 1263 $trans_tbl = get_html_translation_table (HTML_ENTITIES); 1264 $trans_tbl = array_flip ($trans_tbl); 1265 $ret = strtr ($in, $trans_tbl); 1266 return preg_replace_callback('/&#(\d+);/m', array($this,'unHtmlEntitiesChar'),$ret); 1267 } 1268 1269 function unHtmlEntitiesChar($ar) { 1270 1271 return chr($ar[1]); 1272 } 1273 1274 /** 1275 * Deal with XUL tags 1276 * 1277 * @return string | false = html output or ignore (just output the tag) 1278 * @access public 1279 */ 1280 1281 function parseXulTag() 1282 { 1283 1284 // does it contain any flexy tags?? 1285 if ($this->elementUsesDynamic($this->element)) { 1286 return false; 1287 } 1288 1289 return $this->compiler->appendPhp( 1290 $this->getElementPhp( $this->element->getAttribute('ID'))); 1291 } 1292 1293 /** 1294 * Recursively search for any flexy:if flexy:foreach or {xxxx} tags inside tags.. 1295 * 1296 * @param HTML_Template_Flexy_Token element to check. 1297 * @return boolean true if it finds a dynamic tag. 1298 * @access public 1299 */ 1300 1301 1302 function elementUsesDynamic($e) 1303 { 1304 if (!is_object($e)) { 1305 return false; 1306 } 1307 if (is_a($e,'HTML_Template_Flexy_Token_Var')) { 1308 return true; 1309 } 1310 if (is_a($e,'HTML_Template_Flexy_Token_Foreach')) { 1311 return true; 1312 } 1313 if (is_a($e,'HTML_Template_Flexy_Token_If')) { 1314 return true; 1315 } 1316 if (is_a($e,'HTML_Template_Flexy_Token_Method')) { 1317 return true; 1318 } 1319 if (!is_a($e,'HTML_Template_Flexy_Token_Tag')) { 1320 return false; 1321 } 1322 if ($e->getAttribute('FLEXY:IF') !== false) { 1323 return true; 1324 } 1325 if ($e->getAttribute('FLEXY:FOREACH') !== false) { 1326 return true; 1327 } 1328 foreach($e->attributes as $k=>$v) { 1329 if (is_array($v) || is_object($v)) { 1330 return true; 1331 } 1332 } 1333 foreach($e->children as $c) { 1334 if ($this->elementUsesDynamic($c)) { 1335 return true; 1336 } 1337 } 1338 return false; 1339 1340 1341 1342 } 1343 1344 1345 /** 1346 * calls HTML_Template_Flexy::raiseError() with the current file, line and tag 1347 * @param string Message to display 1348 * @param type (see HTML_Template_Flexy::raiseError()) 1349 * @param boolean isFatal. 1350 * 1351 * @access private 1352 */ 1353 function _raiseErrorWithPositionAndTag($message, $type = null, $fatal = HTML_TEMPLATE_FLEXY_ERROR_RETURN ) { 1354 global $_HTML_TEMPLATE_FLEXY; 1355 $message = "Error:{$_HTML_TEMPLATE_FLEXY['filename']} on Line {$this->element->line}" . 1356 " in Tag <{$this->element->tag}>:<BR>\n" . $message; 1357 return HTML_Template_Flexy::staticRaiseError($message, $type, $fatal); 1358 } 1359 1360 1361} 1362