PageRenderTime 55ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/src/lib/HTML/Template/Flexy/Compiler/Flexy/Tag.php

https://github.com/clayhinson/sgl2
PHP | 1283 lines | 709 code | 254 blank | 320 comment | 163 complexity | c3393ef57239e257c814b329d09a20d4 MD5 | raw file
  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,v 1.37 2009/03/05 05:37:33 alan_k Exp $
  20. /* FC/BC compatibility with php5 */
  21. if ( (substr(phpversion(),0,1) < 5) && !function_exists('clone')) {
  22. eval('function clone($t) { return $t; }');
  23. }
  24. /**
  25. * Compiler That deals with standard HTML Tag output.
  26. * Since it's pretty complex it has it's own class.
  27. * I guess this class should deal with the main namespace
  28. * and the parent (standard compiler can redirect other namespaces to other classes.
  29. *
  30. * one instance of these exists for each namespace.
  31. *
  32. *
  33. * @version $Id: Tag.php,v 1.37 2009/03/05 05:37:33 alan_k Exp $
  34. */
  35. class HTML_Template_Flexy_Compiler_Flexy_Tag
  36. {
  37. /**
  38. * Parent Compiler for
  39. *
  40. * @var object HTML_Template_Flexy_Compiler
  41. *
  42. * @access public
  43. */
  44. var $compiler;
  45. /**
  46. *
  47. * Factory method to create Tag Handlers
  48. *
  49. * $type = namespace eg. <flexy:toJavascript loads Flexy.php
  50. * the default is this... (eg. Tag)
  51. *
  52. *
  53. * @param string Namespace handler for element.
  54. * @param object HTML_Template_Flexy_Compiler
  55. *
  56. *
  57. * @return object tag compiler
  58. * @access public
  59. */
  60. function &factory($type,&$compiler) {
  61. if (!$type) {
  62. $type = 'Tag';
  63. }
  64. $class = 'HTML_Template_Flexy_Compiler_Flexy_' . $type;
  65. if ($compiler->classExists($class)) {
  66. $ret = new $class;
  67. $ret->compiler = &$compiler;
  68. return $ret;
  69. }
  70. $filename = 'HTML/Template/Flexy/Compiler/Flexy/' . ucfirst(strtolower($type)) . '.php';
  71. if (!HTML_Template_Flexy_Compiler_Flexy_Tag::fileExistsInPath($filename)) {
  72. $ret = HTML_Template_Flexy_Compiler_Flexy_Tag::factory('Tag',$compiler);
  73. return $ret;
  74. }
  75. // if we dont have a handler - just use the basic handler.
  76. if (!file_exists(dirname(__FILE__) . '/'. ucfirst(strtolower($type)) . '.php')) {
  77. $type = 'Tag';
  78. }
  79. include_once 'HTML/Template/Flexy/Compiler/Flexy/' . ucfirst(strtolower($type)) . '.php';
  80. $class = 'HTML_Template_Flexy_Compiler_Flexy_' . $type;
  81. if (!$compiler->classExists($class)) {
  82. $ret = false;
  83. return $ret;
  84. }
  85. $ret = HTML_Template_Flexy_Compiler_Flexy_Tag::factory($type,$compiler);
  86. return $ret;
  87. }
  88. /**
  89. *
  90. * Check that a file exists in the "include_path"
  91. *
  92. * @param string Filename
  93. *
  94. * @return boolean true if it is in there.
  95. * @access public
  96. */
  97. function fileExistsInPath($filename) {
  98. if (isset($GLOBALS['_'.__CLASS__]['cache'][$filename])) {
  99. return $GLOBALS['_'.__CLASS__]['cache'][$filename];
  100. }
  101. $bits = explode(PATH_SEPARATOR,ini_get('include_path'));
  102. foreach($bits as $b) {
  103. if (file_exists("$b/$filename")) {
  104. return $GLOBALS['_'.__CLASS__]['cache'][$filename] = true;
  105. }
  106. }
  107. return $GLOBALS['_'.__CLASS__]['cache'][$filename] = false;
  108. }
  109. /**
  110. * The current element to parse..
  111. *
  112. * @var object
  113. * @access public
  114. */
  115. var $element;
  116. /**
  117. * Flag to indicate has attribute flexy:foreach (so you cant mix it with flexy:if!)
  118. *
  119. * @var boolean
  120. * @access public
  121. */
  122. var $hasForeach = false;
  123. /**
  124. * toString - display tag, attributes, postfix and any code in attributes.
  125. * Note first thing it does is call any parseTag Method that exists..
  126. *
  127. *
  128. * @see parent::toString()
  129. */
  130. function toString($element)
  131. {
  132. global $_HTML_TEMPLATE_FLEXY_TOKEN;
  133. global $_HTML_TEMPLATE_FLEXY;
  134. // store the element in a variable
  135. $this->element = $element;
  136. // echo "toString: Line {$this->element->line} &lt;{$this->element->tag}&gt;\n";
  137. // if the FLEXYSTARTCHILDREN flag was set, only do children
  138. // normally set in BODY tag.
  139. // this will probably be superseeded by the Class compiler.
  140. if (isset($element->ucAttributes['FLEXY:STARTCHILDREN'])) {
  141. return $element->compileChildren($this->compiler);
  142. }
  143. // look for flexy:ignore..
  144. $flexyignore = $this->parseAttributeIgnore();
  145. // rewriting should be done with a tag.../flag.
  146. $this->reWriteURL("HREF");
  147. $this->reWriteURL("SRC");
  148. $this->reWriteURL("BACKGROUND");
  149. // handle elements
  150. if (($ret =$this->_parseTags()) !== false) {
  151. return $ret;
  152. }
  153. // these add to the close tag..
  154. $ret = $this->parseAttributeForeach();
  155. $ret .= $this->parseAttributeIf();
  156. // support Custom Attributes...
  157. require_once 'HTML/Template/Flexy/Compiler/Flexy/CustomFlexyAttributes.php';
  158. $customFlexyAttributes = new HTML_Template_Flexy_Compiler_Flexy_CustomFlexyAttributes();
  159. $customFlexyAttributes->doCustomAttributes($element);
  160. $add = $this->toStringOpenTag($element,$ret);
  161. if (is_a($add,'PEAR_Error')) {
  162. return $add;
  163. }
  164. // post stuff this is probably in the wrong place...
  165. if ($element->postfix) {
  166. foreach ($element->postfix as $e) {
  167. $add = $e->compile($this->compiler);
  168. if (is_a($add,'PEAR_Error')) {
  169. return $add;
  170. }
  171. $ret .= $add;
  172. }
  173. } else if ($this->element->postfix) { // if postfixed by self..
  174. foreach ($this->element->postfix as $e) {
  175. $add = $e->compile($this->compiler);
  176. if (is_a($add,'PEAR_Error')) {
  177. return $add;
  178. }
  179. $ret .= $add;
  180. }
  181. }
  182. $tmp = $this->toStringChildren($element,$ret);
  183. if (is_a($tmp,'PEAR_Error')) {
  184. return $tmp;
  185. }
  186. $tmp = $this->toStringCloseTag($element,$ret);
  187. if (is_a($tmp,'PEAR_Error')) {
  188. return $tmp;
  189. }
  190. // reset flexyignore
  191. $_HTML_TEMPLATE_FLEXY_TOKEN['flexyIgnore'] = $flexyignore;
  192. if (isset($_HTML_TEMPLATE_FLEXY['currentOptions']['output.block']) &&
  193. ($_HTML_TEMPLATE_FLEXY['currentOptions']['output.block'] == $element->getAttribute('ID'))) {
  194. // echo $_HTML_TEMPLATE_FLEXY['compiledTemplate'];
  195. $fh = fopen($_HTML_TEMPLATE_FLEXY['compiledTemplate'],'w');
  196. fwrite($fh,$ret);
  197. fclose($fh);
  198. }
  199. return $ret;
  200. }
  201. /**
  202. * convert a tag into compiled version
  203. * @arg object Element
  204. * @arg inout output string to template
  205. * @return none? or pear error.
  206. *
  207. */
  208. function toStringOpenTag(&$element,&$ret)
  209. {
  210. // START ADDITION...
  211. if ((empty($element->tag)) || (empty($element->oTag))) {
  212. return;
  213. }
  214. // ...END ADDITION
  215. // spit ou the tag and attributes.
  216. if ($element->oTag{0} == '?') {
  217. $ret .= '<?php echo "<"; ?>';
  218. } else {
  219. $ret .= "<";
  220. }
  221. $ret .= $element->oTag;
  222. //echo '<PRE>'.print_r($element->attributes,true);
  223. foreach ($element->attributes as $k=>$v) {
  224. // if it's a flexy tag ignore it.
  225. if (strtoupper($k) == 'FLEXY:RAW') {
  226. if (!is_array($v) || !isset($v[1]) || !is_object($v[1])) {
  227. return $this->_raiseErrorWithPositionAndTag(
  228. 'flexy:raw only accepts a variable or method call as an argument, eg.'.
  229. ' flexy:raw="{somevalue}" you provided something else.' .
  230. null, HTML_TEMPLATE_FLEXY_ERROR_DIE);
  231. }
  232. $add = $v[1]->compile($this->compiler);
  233. if (is_a($add,'PEAR_Error')) {
  234. return $add;
  235. }
  236. $ret .= ' ' . $add;
  237. continue;
  238. }
  239. if (strtoupper(substr($k,0,6)) == 'FLEXY:') {
  240. continue;
  241. }
  242. // true == an attribute without a ="xxx"
  243. if ($v === true) {
  244. $ret .= " $k";
  245. continue;
  246. }
  247. // if it's a string just dump it.
  248. if (is_string($v)) {
  249. $v = str_replace(array('{_(',')_}'),array('',''),$v);
  250. $ret .= " {$k}={$v}";
  251. continue;
  252. }
  253. // normally the value is an array of string, however
  254. // if it is an object - then it's a conditional key.
  255. // eg. if (something) echo ' SELECTED';
  256. // the object is responsible for adding it's space..
  257. if (is_object($v)) {
  258. $add = $v->compile($this->compiler);
  259. if (is_a($add,'PEAR_Error')) {
  260. return $add;
  261. }
  262. $ret .= $add;
  263. continue;
  264. }
  265. // otherwise its a key="sometext{andsomevars}"
  266. $ret .= " {$k}=";
  267. foreach($v as $item) {
  268. if (is_string($item)) {
  269. // skip translation strings in tags.
  270. $item = str_replace(array('{_(',')_}'),array('',''),$item);
  271. $ret .= $item;
  272. continue;
  273. }
  274. $add = $item->compile($this->compiler);
  275. if (is_a($add,'PEAR_Error')) {
  276. return $add;
  277. }
  278. $ret .= $add;
  279. }
  280. }
  281. $ret .= ">";
  282. }
  283. /**
  284. * compile children to string.
  285. * @arg object Element
  286. * @arg inout output string to template
  287. * @return none? or pear error.
  288. */
  289. function toStringChildren(&$element,&$ret)
  290. {
  291. // dump contents of script raw - to prevent gettext additions..
  292. // print_r($element);
  293. // make sure tag isn't empty because it wouldn't make sense to output script without script tags
  294. if (((! empty($element->tag)) && ($element->tag == 'SCRIPT'))
  295. || ((! empty($element->oTag)) && ($element->oTag == 'SCRIPT'))) {
  296. foreach($element->children as $c) {
  297. //print_R($c);
  298. if (!$c) {
  299. continue;
  300. }
  301. if ($c->token == 'Text') {
  302. $ret .= $c->value;
  303. continue;
  304. }
  305. // techically we shouldnt have anything else inside of script tags.
  306. // as the tokeinzer is supposted to ignore it..
  307. }
  308. return;
  309. }
  310. $add = $element->compileChildren($this->compiler);
  311. if (is_a($add,'PEAR_Error')) {
  312. return $add;
  313. }
  314. $ret .= $add;
  315. }
  316. /**
  317. * compile closing tag to string.
  318. * @arg object Element
  319. * @arg inout output string to template
  320. * @return none? or pear error.
  321. */
  322. function toStringCloseTag(&$element,&$ret)
  323. {
  324. // output the closing tag.
  325. // If the tag is empty don't output closing tags, just output postfixes if any exist...
  326. if ( !$element->close) {
  327. return;
  328. }
  329. if ((! empty($element->tag)) && (! empty($element->oTag)))
  330. {
  331. $add = $element->close->compile($this->compiler);
  332. if (is_a($add,'PEAR_Error')) {
  333. return $add;
  334. }
  335. $ret .= $add;
  336. return;
  337. }
  338. // RICK - added by me
  339. // element has a seperate closing tag (eg. </something>) and opening and closing tags should be removed
  340. // because FLEXY:OMITTAG element attribute is set, but still need postfix stuff like for ending ifs and foreach
  341. // so this is NOT OPTIONAL if foreach and if are not optional.
  342. if ($element->close->postfix) {
  343. foreach ($element->close->postfix as $e) {
  344. $add = $e->compile($this->compiler);
  345. if (is_a($add,'PEAR_Error')) {
  346. return $add;
  347. }
  348. $ret .= $add;
  349. }
  350. return;
  351. }
  352. if ($this->element->close->postfix) { // if postfixed by self..
  353. foreach ($this->element->close->postfix as $e) {
  354. $add = $e->compile($this->compiler);
  355. if (is_a($add,'PEAR_Error')) {
  356. return $add;
  357. }
  358. $ret .= $add;
  359. }
  360. return;
  361. }
  362. }
  363. /**
  364. * Reads an flexy:foreach attribute -
  365. *
  366. *
  367. * @return string to add to output.
  368. * @access public
  369. */
  370. function parseAttributeIgnore()
  371. {
  372. global $_HTML_TEMPLATE_FLEXY_TOKEN;
  373. $flexyignore = $_HTML_TEMPLATE_FLEXY_TOKEN['flexyIgnore'];
  374. if ($this->element->getAttribute('FLEXY:IGNORE') !== false) {
  375. $_HTML_TEMPLATE_FLEXY_TOKEN['flexyIgnore'] = true;
  376. $this->element->clearAttribute('FLEXY:IGNORE');
  377. }
  378. return $flexyignore;
  379. }
  380. /**
  381. * Reads an flexy:foreach attribute -
  382. *
  383. *
  384. * @return string to add to output.
  385. * @access public
  386. */
  387. function parseAttributeForeach()
  388. {
  389. global $_HTML_TEMPLATE_FLEXY;
  390. $foreach = $this->element->getAttribute('FLEXY:FOREACH');
  391. if ($foreach === false) {
  392. return '';
  393. }
  394. //var_dump($foreach);
  395. $this->element->hasForeach = true;
  396. // create a foreach element to wrap this with.
  397. $foreachObj = $this->element->factory('Foreach',
  398. explode(',',$foreach),
  399. $this->element->line);
  400. // failed = probably not enough variables..
  401. if ($foreachObj === false) {
  402. return $this->_raiseErrorWithPositionAndTag(
  403. "Missing Arguments: An flexy:foreach attribute was found. flexy:foreach=&quot;$foreach&quot;<BR>
  404. the syntax is &lt;sometag flexy:foreach=&quot;onarray,withvariable[,withanothervar] &gt;<BR>",
  405. null, HTML_TEMPLATE_FLEXY_ERROR_DIE);
  406. }
  407. // does it have a closetag?
  408. if (!$this->element->close) {
  409. if ($this->element->getAttribute('/') === false) {
  410. return $this->_raiseErrorWithPositionAndTag(
  411. "A flexy:foreach attribute was found without a corresponding &lt;/{$this->element->tag} tag",
  412. null, HTML_TEMPLATE_FLEXY_ERROR_DIE);
  413. }
  414. // it's an xhtml tag!
  415. $this->element->postfix = array($this->element->factory("End", '', $this->element->line));
  416. } else {
  417. $this->element->close->postfix = array($this->element->factory("End", '', $this->element->line));
  418. }
  419. $this->element->clearAttribute('FLEXY:FOREACH');
  420. return $foreachObj->compile($this->compiler);
  421. }
  422. /**
  423. * Reads an flexy:if attribute -
  424. *
  425. *
  426. * @return string to add to output.
  427. * @access public
  428. */
  429. function parseAttributeIf()
  430. {
  431. // dont use the together, if is depreciated..
  432. $if = $this->element->getAttribute('FLEXY:IF');
  433. if ($if === false) {
  434. return '';
  435. }
  436. if (isset($this->element->hasForeach)) {
  437. return $this->_raiseErrorWithPositionAndTag("You may not use FOREACH and IF tags in the same tag",
  438. null, HTML_TEMPLATE_FLEXY_ERROR_DIE);
  439. }
  440. // allow if="!somevar"
  441. $ifnegative = '';
  442. if ($if{0} == '!') {
  443. $ifnegative = '!';
  444. $if = substr($if,1);
  445. }
  446. // if="xxxxx"
  447. // if="xxxx.xxxx()" - should create a method prefixed with 'if:'
  448. // these checks should really be in the if/method class..!!!
  449. if (!preg_match('/^[_A-Z][A-Z0-9_]*(\[[0-9]+\])?((\[|%5B)[A-Z0-9_]+(\]|%5D))*'.
  450. '(\.[_A-Z][A-Z0-9_]*((\[|%5B)[A-Z0-9_]+(\]|%5D))*)*(\\([^)]*\))?$/i',$if)) {
  451. return $this->_raiseErrorWithPositionAndTag(
  452. "IF tags only accept simple object.variable or object.method() values. {$if}",
  453. null, HTML_TEMPLATE_FLEXY_ERROR_DIE);
  454. }
  455. if (substr($if,-1) == ')') {
  456. // grab args..
  457. $args = substr($if,strpos($if,'(')+1,-1);
  458. // simple explode ...
  459. $args = strlen(trim($args)) ? explode(',',$args) : array();
  460. //print_R($args);
  461. // this is nasty... - we need to check for quotes = eg. # at beg. & end..
  462. $args_clean = array();
  463. for ($i=0; $i<count($args); $i++) {
  464. if ($args[$i]{0} != '#') {
  465. $args_clean[] = $args[$i];
  466. continue;
  467. }
  468. // single # - so , must be inside..
  469. if ((strlen($args[$i]) > 1) && ($args[$i]{strlen($args[$i])-1}=='#')) {
  470. $args_clean[] = $args[$i];
  471. continue;
  472. }
  473. $args[$i] .=',' . $args[$i+1];
  474. // remove args+1..
  475. array_splice($args,$i+1,1);
  476. $i--;
  477. // reparse..
  478. }
  479. $ifObj = $this->element->factory('Method',
  480. array('if:'.$ifnegative.substr($if,0,strpos($if,'(')), $args_clean),
  481. $this->element->line);
  482. } else {
  483. $ifObj = $this->element->factory('If', $ifnegative.$if, $this->element->line);
  484. }
  485. // does it have a closetag? - you must have one - so you will have to hack in <span flexy:if=..><img></span> on tags
  486. // that do not have close tags - it's done this way to try and avoid mistakes.
  487. if (!$this->element->close) {
  488. //echo "<PRE>";print_R($this->element);
  489. if ($this->element->getAttribute('/') !== false) {
  490. $this->element->postfix = array($this->element->factory("End",'', $this->element->line));
  491. } else {
  492. return $this->_raiseErrorWithPositionAndTag(
  493. "An flexy:if attribute was found in &lt;{$this->element->name} tag without a corresponding &lt;/{$this->element->name} tag",
  494. null, HTML_TEMPLATE_FLEXY_ERROR_DIE);
  495. }
  496. } else {
  497. $this->element->close->postfix = array($this->element->factory("End",'', $this->element->line));
  498. }
  499. $this->element->clearAttribute('FLEXY:IF');
  500. return $ifObj->compile($this->compiler);
  501. }
  502. /**
  503. * Reads Tags - and relays to parseTagXXXXXXX
  504. *
  505. *
  506. * @return string | false = html output or ignore (just output the tag)
  507. * @access private
  508. */
  509. function _parseTags()
  510. {
  511. global $_HTML_TEMPLATE_FLEXY_TOKEN;
  512. // doesnt really need strtolower etc. as php functions are not case sensitive!
  513. /* always render script correctly */
  514. if (0 == strcasecmp($this->element->tag,"script")) {
  515. return $this->parseTagScript();
  516. }
  517. if ($this->element->getAttribute('FLEXY:DYNAMIC')) {
  518. return $this->compiler->appendPhp(
  519. $this->getElementPhp( $this->element->getAttribute('ID') )
  520. );
  521. }
  522. if ($this->element->getAttribute('FLEXY:IGNOREONLY') !== false) {
  523. return false;
  524. }
  525. if ($_HTML_TEMPLATE_FLEXY_TOKEN['flexyIgnore']) {
  526. return false;
  527. }
  528. $tag = $this->element->tag;
  529. if (strpos($tag,':') !== false) {
  530. $bits = explode(':',$tag);
  531. $tag = $bits[1];
  532. }
  533. if (in_array(strtolower($tag), array('menulist','textbox','checkbox'))) {
  534. $method = 'parseXulTag';
  535. } else {
  536. $method = 'parseTag'.$tag;
  537. if (!method_exists($this,$method)) {
  538. return false;
  539. }
  540. }
  541. if (($this->element->getAttribute('NAME') === false) &&
  542. ($this->element->getAttribute('ID') === false) ) {
  543. return false;
  544. }
  545. // do any of the attributes use flexy data...
  546. //foreach ($this->element->attributes as $k=>$v) {
  547. // if (is_array($v)) {
  548. // return false;
  549. // }
  550. //}
  551. //echo "call $method" . serialize($this->element->attributes). "\n";
  552. return $this->$method();
  553. // allow the parse methods to return output.
  554. }
  555. /**
  556. * produces the code for dynamic elements
  557. *
  558. * @return string | false = html output or ignore (just output the tag)
  559. * @access public
  560. */
  561. function getElementPhp($id,$mergeWithName=false,$varsOnly = false) {
  562. global $_HTML_TEMPLATE_FLEXY;
  563. static $tmpId=0;
  564. if (!$id) {
  565. return $this->_raiseErrorWithPositionAndTag("Dynamic tags require an ID value",
  566. null, HTML_TEMPLATE_FLEXY_ERROR_DIE);
  567. }
  568. // dont mix and match..
  569. if (($this->element->getAttribute('FLEXY:IF') !== false) ||
  570. ($this->element->getAttribute('FLEXY:FOREACH') !== false) )
  571. {
  572. return $this->_raiseErrorWithPositionAndTag(
  573. " You can not mix flexy:if= or flexy:foreach= with dynamic form elements " .
  574. " (turn off tag to element code with flexyIgnore=0, use flexy:ignore=&quot;yes&quot; in the tag" .
  575. " or put the conditional outside in a span tag",
  576. null, HTML_TEMPLATE_FLEXY_ERROR_DIE);
  577. }
  578. if ((strtolower($this->element->getAttribute('TYPE')) == 'checkbox' ) &&
  579. (substr($this->element->getAttribute('NAME'),-2) == '[]')) {
  580. if ($this->element->getAttribute('ID') === false) {
  581. $id = 'tmpId'. (++$tmpId);
  582. $this->element->attributes['id'] = $id;
  583. $this->element->ucAttributes['ID'] = $id;
  584. } else {
  585. $id = $this->element->getAttribute('ID');
  586. }
  587. $mergeWithName = true;
  588. }
  589. if (isset($_HTML_TEMPLATE_FLEXY['elements'][$id])) {
  590. // echo "<PRE>";print_r($this);print_r($_HTML_TEMPLATE_FLEXY['elements']);echo "</PRE>";
  591. return $this->_raiseErrorWithPositionAndTag(
  592. "The Dynamic tag Name '$id' has already been used previously by tag &lt;{$_HTML_TEMPLATE_FLEXY['elements'][$id]->tag}&gt;",
  593. null,HTML_TEMPLATE_FLEXY_ERROR_DIE);
  594. }
  595. $ret = '';
  596. $unset = '';
  597. //echo '<PRE>';print_r($this->element);echo '</PRE>';
  598. if (isset($this->element->ucAttributes['FLEXY:USE'])) {
  599. $ar = $this->element->ucAttributes['FLEXY:USE'];
  600. $str = '';
  601. for($i =1; $i < count($ar) -1; $i++) {
  602. switch(true) {
  603. case is_a($ar[$i], 'HTML_Template_Flexy_Token_Var'):
  604. $str .= '. ' . $ar[$i]->toVar($ar[$i]->value). ' ';
  605. break;
  606. case is_string($ar[$i]):
  607. $str .= '. ' . $ar[0] . $ar[$i] . $ar[0];
  608. break;
  609. default:
  610. return $this->_raiseErrorWithPositionAndTag(
  611. "unsupported type found in attribute, use flexy:ignore to prevent parsing or remove it. " .
  612. print_r($this->element,true),
  613. null,HTML_TEMPLATE_FLEXY_ERROR_DIE);
  614. }
  615. }
  616. $str = trim(ltrim($str,'.'));
  617. $_HTML_TEMPLATE_FLEXY['elements'][$id] = $this->toElement($this->element);
  618. return $ret .
  619. '
  620. if (!isset($this->elements['.$str.'])) {
  621. echo "ELEMENT MISSING $str";
  622. }
  623. echo $this->elements['.$str.']->toHtml();' .$unset;
  624. }
  625. if ($this->elementUsesDynamic($this->element)) {
  626. $used = array();
  627. foreach ($this->element->attributes as $attribute => $attributeValue) {
  628. if (!is_array($attributeValue)) {
  629. continue;
  630. }
  631. if (strtoupper(substr($attribute,0,6)) == 'FLEXY:') {
  632. continue;
  633. }
  634. unset($this->element->attributes[$attribute]);
  635. // generate code to put data into value..
  636. $output_avar = '$this->elements[\''.$id.'\']->attributes[\''.$attribute.'\']';
  637. $used[] = "'{$attribute}'";
  638. $ret .= "\nif (!isset({$output_avar})) {\n";
  639. // get the " or ' that encapsulates the element.
  640. $wrapper = array_shift($attributeValue);
  641. array_pop($attributeValue);
  642. $ret .= " {$output_avar} = '';\n";
  643. //echo '<PRE>';print_r($attributeValue);echo '</PRE>';
  644. foreach($attributeValue as $item) {
  645. if (is_string($item)) {
  646. $ret .= " {$output_avar} .= {$wrapper}{$item}{$wrapper};\n";
  647. continue;
  648. }
  649. if (!is_object($item) || !is_a($item, 'HTML_Template_Flexy_Token_Var')) {
  650. return $this->_raiseErrorWithPositionAndTag(
  651. "unsupported type found in attribute, use flexy:ignore to prevent parsing or remove it. " .
  652. print_r($this->element,true),
  653. null,HTML_TEMPLATE_FLEXY_ERROR_DIE);
  654. }
  655. $var = $item->toVar($item->value);
  656. if (is_a($var, 'PEAR_Error')) {
  657. return $var;
  658. }
  659. list($prefix,$suffix) = $this->compiler->getModifierWrapper($item);
  660. $prefix = substr($prefix,4);
  661. $ret .= " {$output_avar} .= {$prefix}{$var}{$suffix};\n";
  662. }
  663. $ret .= "}\n";
  664. }
  665. $ret .= "\$_attributes_used = array(".implode(',',$used).");\n";
  666. $unset = "\n".'if (isset($_attributes_used)) { foreach($_attributes_used as $_a) {'."\n".
  667. ' unset($this->elements[\''. $id .'\']->attributes[$_a]);'."\n" .
  668. "}}\n";
  669. }
  670. // this is for a case where you can use a sprintf as the name, and overlay it with a variable element..
  671. $_HTML_TEMPLATE_FLEXY['elements'][$id] = $this->toElement($this->element);
  672. if ($varsOnly) { // used by form tag.
  673. return array($ret,$unset);
  674. }
  675. if ($var = $this->element->getAttribute('FLEXY:NAMEUSES')) {
  676. // force var to use name (as radio buttons pick up id.)
  677. $ename = $this->element->getAttribute('NAME');
  678. $printfnamevar = $printfvar = 'sprintf(\''.$ename .'\','.$this->element->toVar($var) .')';
  679. // support id replacement as well ...
  680. $idreplace = '';
  681. if (strtolower($this->element->getAttribute('TYPE')) == 'radio') {
  682. $ename = $this->element->getAttribute('ID');
  683. $printfvar = 'sprintf(\''.$ename .'\','.$this->element->toVar($var) .')';
  684. }
  685. if ($this->element->getAttribute('ID')) {
  686. $idvar = 'sprintf(\''.$this->element->getAttribute('ID') .'\','.$this->element->toVar($var) .')';
  687. $idreplace = '$this->elements['.$printfvar.']->attributes[\'id\'] = '.$idvar.';';
  688. }
  689. return $ret . '
  690. $_element = $this->mergeElement(
  691. $this->elements[\''.$id.'\'],
  692. isset('.$this->element->toVar($var).') && isset($this->elements['.$printfnamevar .']) ? $this->elements['.$printfnamevar .'] : false
  693. );
  694. $_element->attributes[\'name\'] = '.$printfnamevar. ';
  695. ' . $idreplace . '
  696. echo $_element->toHtml();' .$unset;
  697. }
  698. if ($mergeWithName) {
  699. $name = $this->element->getAttribute('NAME');
  700. //if ((strtolower($this->element->getAttribute('TYPE')) == 'checkbox') && (substr($name,-2) == '[]')) {
  701. // $name = substr($name,0,-2);
  702. //}
  703. if (!$name) {
  704. return $ret .'
  705. $_element = $this->elements[\''.$id.'\'];
  706. echo $_element->toHtml();' . $unset;
  707. } else {
  708. return $ret .
  709. '
  710. $_element = $this->elements[\''.$id.'\'];
  711. if (isset($this->elements[\''.$name.'\'])) {
  712. $_element = $this->mergeElement($_element,$this->elements[\''.$name.'\']);
  713. }
  714. echo $_element->toHtml();' . $unset;
  715. }
  716. }
  717. return $ret . 'echo $this->elements[\''.$id.'\']->toHtml();'. $unset;
  718. }
  719. /**
  720. * Reads an Script tag - check if PHP is allowed.
  721. *
  722. * @return false|PEAR_Error
  723. * @access public
  724. */
  725. function parseTagScript()
  726. {
  727. $lang = $this->element->getAttribute('LANGUAGE');
  728. if (!$lang) {
  729. return false;
  730. }
  731. $lang = strtoupper($lang);
  732. $allow = $GLOBALS['_HTML_TEMPLATE_FLEXY']['currentOptions']['allowPHP'];
  733. if ($allow === true) {
  734. return false;
  735. }
  736. if ($lang == "PHP") {
  737. if ($allow == 'delete') {
  738. return '';
  739. }
  740. return $this->_raiseErrorWithPositionAndTag('PHP code found in script (script)',
  741. HTML_TEMPLATE_FLEXY_ERROR_SYNTAX,HTML_TEMPLATE_FLEXY_ERROR_RETURN
  742. );
  743. }
  744. return false;
  745. }
  746. /**
  747. * Reads an Input tag - build a element object for it
  748. *
  749. *
  750. * @return string | false = html output or ignore (just output the tag)
  751. * @access public
  752. */
  753. function parseTagInput()
  754. {
  755. global $_HTML_TEMPLATE_FLEXY;
  756. if (in_array(strtoupper($this->element->getAttribute('TYPE')), array('SUBMIT','BUTTON','INPUT',''))) {
  757. $this->compiler->addStringToGettext($this->element->getAttribute('VALUE'));
  758. }
  759. // form elements : format:
  760. //value - fill out as PHP CODE
  761. // as a general rule, this uses name, rather than ID except on
  762. // radio
  763. $mergeWithName = false;
  764. $id = $this->element->getAttribute('NAME');
  765. if (isset($this->element->ucAttributes['FLEXY:RAW'])) {
  766. return $this->_raiseErrorWithPositionAndTag(
  767. "Flexy:raw can only be used with flexy:ignore, to prevent conversion of html ".
  768. "elements to flexy elements",
  769. null, HTML_TEMPLATE_FLEXY_ERROR_DIE
  770. );
  771. }
  772. // checkboxes need more work.. - at the momemnt assume one with the same value...
  773. if (!in_array(strtoupper($this->element->getAttribute('TYPE')), array('RADIO'))) {
  774. if (!$id) {
  775. return false;
  776. }
  777. return $this->compiler->appendPhp($this->getElementPhp( $id,$mergeWithName));
  778. }
  779. // now we are only dealing with radio buttons.. which are a bit odd...
  780. // we need to create a generic holder for this based on the name..
  781. // this is only really available for use with setting stuff...
  782. if (!isset($_HTML_TEMPLATE_FLEXY['elements'][$id])) {
  783. $_HTML_TEMPLATE_FLEXY['elements'][$id] = new HTML_Template_Flexy_Element("input",
  784. array('type'=>'radio'));
  785. }
  786. // we dont really care if it is getting reused loads of times.. (expected as each radio button will use it.
  787. $name = $id;
  788. $id = $this->element->getAttribute('ID');
  789. if (!$id) {
  790. $id = $name . '_' . $this->element->getAttribute('VALUE');
  791. }
  792. // this get's tricky as we could end up with elements with the same name... (if value was not set..,
  793. // or two elements have the same name..
  794. $mergeWithName = true;
  795. return $this->compiler->appendPhp($this->getElementPhp( $id,$mergeWithName));
  796. }
  797. /**
  798. * Deal with a TextArea tag - build a element object for it
  799. *
  800. * @return string | false = html output or ignore (just output the tag)
  801. * @access public
  802. */
  803. function parseTagTextArea()
  804. {
  805. return $this->compiler->appendPhp(
  806. $this->getElementPhp( $this->element->getAttribute('NAME')));
  807. }
  808. /**
  809. * Deal with Selects - build a element object for it (unless flexyignore is set)
  810. *
  811. *
  812. * @return string | false = html output or ignore (just output the tag)
  813. * @access public
  814. */
  815. function parseTagSelect()
  816. {
  817. return $this->compiler->appendPhp(
  818. $this->getElementPhp( $this->element->getAttribute('NAME')));
  819. }
  820. /**
  821. * Reads an Form tag - and set up the element object header etc.
  822. *
  823. * @return string | false = html output or ignore (just output the tag)
  824. * @access public
  825. */
  826. function parseTagForm()
  827. {
  828. global $_HTML_TEMPLATE_FLEXY;
  829. $copy = clone($this->element);
  830. $copy->children = array();
  831. $id = $this->element->getAttribute('NAME');
  832. // dont make forms dynamic if they dont have a name..
  833. if (!$id) {
  834. return false;
  835. }
  836. // this adds the element to the elements array.
  837. $old = clone($this->element);
  838. $this->element = $copy;
  839. list($prefix,$suffix) = $this->getElementPhp($id,false,true);
  840. $this->element= $old;
  841. return
  842. $this->compiler->appendPhp($prefix .'echo $this->elements[\''.$id.'\']->toHtmlnoClose();'.$suffix) .
  843. $this->element->compileChildren($this->compiler) .
  844. $this->compiler->appendHtml( "</{$copy->oTag}>");
  845. }
  846. /**
  847. * reWriteURL - can using the config option 'url_rewrite'
  848. * format "from:to,from:to"
  849. * only handle left rewrite.
  850. * so
  851. * "/images:/myroot/images"
  852. * would change
  853. * /images/xyz.gif to /myroot/images/xyz.gif
  854. * /images/stylesheet/imagestyles.css to /myroot/images/stylesheet/imagestyles.css
  855. * note /imagestyles did not get altered.
  856. * will only work on strings (forget about doing /images/{someimage}
  857. *
  858. *
  859. * @param string attribute to rewrite
  860. * @return none
  861. * @access public
  862. */
  863. function reWriteURL($which)
  864. {
  865. global $_HTML_TEMPLATE_FLEXY;
  866. if (!is_string($original = $this->element->getAttribute($which))) {
  867. return;
  868. }
  869. if ($original == '') {
  870. return;
  871. }
  872. if (empty($_HTML_TEMPLATE_FLEXY['currentOptions']['url_rewrite'])) {
  873. return;
  874. }
  875. $bits = explode(",",$_HTML_TEMPLATE_FLEXY['currentOptions']['url_rewrite']);
  876. $new = $original;
  877. foreach ($bits as $bit) {
  878. if (!strlen(trim($bit))) {
  879. continue;
  880. }
  881. $parts = explode (':', $bit);
  882. if (!isset($parts[1])) {
  883. return $this->_raiseErrorWithPositionAndTag('HTML_Template_Flexy: url_rewrite syntax incorrect'.
  884. print_r(array($bits,$bits),true),null,HTML_TEMPLATE_FLEXY_ERROR_DIE);
  885. }
  886. $new = preg_replace('#^'.$parts[0].'#',$parts[1], $new);
  887. }
  888. if ($original == $new) {
  889. return;
  890. }
  891. $this->element->ucAttributes[$which] = '"'. $new . '"';
  892. }
  893. /**
  894. * Convert flexy tokens to HTML_Template_Flexy_Elements.
  895. *
  896. * @param object token to convert into a element.
  897. * @return object HTML_Template_Flexy_Element
  898. * @access public
  899. */
  900. function toElement($element,$stripspaces = false)
  901. {
  902. require_once 'HTML/Template/Flexy/Element.php';
  903. $ret = new HTML_Template_Flexy_Element;
  904. if (strtolower(get_class($element)) != 'html_template_flexy_token_tag') {
  905. $this->compiler->addStringToGettext($element->value);
  906. return $element->value;
  907. }
  908. $ret->tag = strtolower($element->tag);
  909. if ($ret->tag == 'menulist') { // for XUL menulist, remove the white space between tags..
  910. $stripspaces = true;
  911. }
  912. $ats = $element->getAttributes();
  913. if (isset($element->attributes['flexy:xhtml'])) {
  914. $ats['flexy:xhtml'] = true;
  915. }
  916. foreach(array_keys($ats) as $a) {
  917. $ret->attributes[$a] = $this->unHtmlEntities($ats[$a]);
  918. }
  919. //print_r($ats);
  920. if (!$element->children) {
  921. return $ret;
  922. }
  923. // children - normally to deal with <element>
  924. //print_r($this->children);
  925. foreach(array_keys($element->children) as $i) {
  926. // not quite sure why this happens - but it does.
  927. if (!is_object($element->children[$i])) {
  928. continue;
  929. }
  930. if ($stripspaces && (strtolower(get_class($element->children[$i])) != 'html_template_flexy_token_tag')) {
  931. continue;
  932. }
  933. $ret->children[] = $this->toElement($element->children[$i],$stripspaces);
  934. }
  935. return $ret;
  936. }
  937. /**
  938. * do the reverse of htmlspecialchars on an attribute..
  939. *
  940. * copied from get-html-translation-table man page
  941. *
  942. * @param mixed from attribute values
  943. *
  944. * @return string return
  945. * @access public
  946. * @see see also methods.....
  947. */
  948. function unHtmlEntities ($in)
  949. {
  950. if (!is_string($in)) {
  951. return $in;
  952. }
  953. $trans_tbl = get_html_translation_table (HTML_ENTITIES);
  954. $trans_tbl = array_flip ($trans_tbl);
  955. $ret = strtr ($in, $trans_tbl);
  956. return preg_replace('/&#(\d+);/me', "chr('\\1')",$ret);
  957. }
  958. /**
  959. * Deal with XUL tags
  960. *
  961. * @return string | false = html output or ignore (just output the tag)
  962. * @access public
  963. */
  964. function parseXulTag()
  965. {
  966. // does it contain any flexy tags??
  967. if ($this->elementUsesDynamic($this->element)) {
  968. return false;
  969. }
  970. return $this->compiler->appendPhp(
  971. $this->getElementPhp( $this->element->getAttribute('ID')));
  972. }
  973. /**
  974. * Recursively search for any flexy:if flexy:foreach or {xxxx} tags inside tags..
  975. *
  976. * @param HTML_Template_Flexy_Token element to check.
  977. * @return boolean true if it finds a dynamic tag.
  978. * @access public
  979. */
  980. function elementUsesDynamic($e)
  981. {
  982. if (is_a($e,'HTML_Template_Flexy_Token_Var')) {
  983. return true;
  984. }
  985. if (is_a($e,'HTML_Template_Flexy_Token_Foreach')) {
  986. return true;
  987. }
  988. if (is_a($e,'HTML_Template_Flexy_Token_If')) {
  989. return true;
  990. }
  991. if (is_a($e,'HTML_Template_Flexy_Token_Method')) {
  992. return true;
  993. }
  994. if (!is_a($e,'HTML_Template_Flexy_Token_Tag')) {
  995. return false;
  996. }
  997. if ($e->getAttribute('FLEXY:IF') !== false) {
  998. return true;
  999. }
  1000. if ($e->getAttribute('FLEXY:FOREACH') !== false) {
  1001. return true;
  1002. }
  1003. foreach($e->attributes as $k=>$v) {
  1004. if (is_array($v) || is_object($v)) {
  1005. return true;
  1006. }
  1007. }
  1008. foreach($e->children as $c) {
  1009. if ($this->elementUsesDynamic($c)) {
  1010. return true;
  1011. }
  1012. }
  1013. return false;
  1014. }
  1015. /**
  1016. * calls HTML_Template_Flexy::raiseError() with the current file, line and tag
  1017. * @param string Message to display
  1018. * @param type (see HTML_Template_Flexy::raiseError())
  1019. * @param boolean isFatal.
  1020. *
  1021. * @access private
  1022. */
  1023. function _raiseErrorWithPositionAndTag($message, $type = null, $fatal = HTML_TEMPLATE_FLEXY_ERROR_RETURN ) {
  1024. global $_HTML_TEMPLATE_FLEXY;
  1025. $message = "Error:{$_HTML_TEMPLATE_FLEXY['filename']} on Line {$this->element->line}" .
  1026. " in Tag &lt;{$this->element->tag}&gt;:<BR>\n" . $message;
  1027. return HTML_Template_Flexy::raiseError($message, $type, $fatal);
  1028. }
  1029. }