PageRenderTime 99ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/saf/lib/XML/XT.php

https://github.com/lux/sitellite
PHP | 3429 lines | 2621 code | 119 blank | 689 comment | 154 complexity | f3aad1f09e949f8670da114e394b3339 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, Apache-2.0, GPL-3.0
  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | Sitellite Content Management System |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 2010 Sitellite.org Community |
  7. // +----------------------------------------------------------------------+
  8. // | This software is released under the GNU GPL License. |
  9. // | Please see the accompanying file docs/LICENSE for licensing details. |
  10. // | |
  11. // | You should have received a copy of the GNU GPL License |
  12. // | along with this program; if not, visit www.sitellite.org. |
  13. // | The license text is also available at the following web site |
  14. // | address: <http://www.sitellite.org/index/license |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: John Luxford <john.luxford@gmail.com> |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // XT is an XML-based template engine.
  20. //
  21. $GLOBALS['loader']->import ('saf.Template.Transformation');
  22. $GLOBALS['loader']->import ('saf.XML.XT.Expression');
  23. define ('XT_DEFAULT_PREFIX', 'xt:');
  24. define ('XT_POST_PREFIX', 'xa:');
  25. /**
  26. * XT is an XML-based template engine. For reference information,
  27. * see the {@link http://www.sitellite.org/index/news-app/section.Templates Templates
  28. * category of the Sitellite.org articles}.
  29. *
  30. * Change History
  31. *
  32. * New in 1.2:
  33. * - Added support for xmlchar tags as a means of displaying HTML entities that
  34. * are not supported by the XML spec, without having to declare an HTML doctype.
  35. * For more information see http://xmlchar.sf.net/ and
  36. * http://www.w3.org/TR/REC-html40/sgml/entities.html for a list of HTML
  37. * entities.
  38. * - Added global functions as aliases of the main public methods. These simply
  39. * call the methods on a global XT object named $tpl. They are: template_xt(),
  40. * template_messy(), template_validate(), and template_wrap().
  41. * - Passed the default object to the $intl->get() calls so that {tag}-style
  42. * substitutions can be made.
  43. * - Added aliases xt:translate and xt:i18n that point to xt:intl.
  44. * - Added aliases xt:elsif that points to xt:elseif.
  45. * - Fixed a few bugs regarding xt:content and xt:replace attributes, and the
  46. * xt:condition, xt:if, xt:elseif, and xt:else tags.
  47. *
  48. * New in 1.4:
  49. * - Added an xt:cache tag which allows you to cache pieces of a template for
  50. * improved performance.
  51. * - Added an ignoreUntilLevel() method which controls the private
  52. * $_ignoreUntilLevel property.
  53. * - Added the $cacheLocation, $cacheDuration, and $cacheCount properties to
  54. * work with the new xt:cache tag.
  55. * - Added the $isHtml property to tell the xt:intl tag whether or not to add
  56. * an HTML span tag to its output.
  57. *
  58. * New in 1.6:
  59. * - Added xt:comment and xt:note tags (xt:note is an alias to xt:comment), so
  60. * that comments can be added to templates that will be retained in the
  61. * rendered output (since normal xml comments are stripped by the
  62. * xml_parse_into_struct() function). For comments that should not be
  63. * retained, please use ordinary xml comments.
  64. * - Added start, end, and length as attributes of loop iterators.
  65. * - Added _header_handler(), makeToc(), and support for automatic generation
  66. * of tables of contents for HTML content. Also added the $toc and $buildToc
  67. * properties.
  68. *
  69. * New in 1.8:
  70. * - Added the ability to call XT tags (attributes too? not sure) using
  71. * <xt-tagname /> as well as with the <xt:tagname /> namespace. This feature
  72. * helps when you want to render XT tags with CSS, since namespaces in CSS are
  73. * not supported by any browser (except Opera 7 apparently) at present.
  74. *
  75. * New in 2.0:
  76. * - Added the ability to use inline expressions in any tag attribute, for example:
  77. * <a href="${site/prefix}/index/news"><xt:intl>News</xt:intl></a>
  78. * This drastically reduces the amount of code needed to implement common
  79. * expressions, and really increases the flexibility of the language.
  80. * - Fixed a bug where XT string expressions that began with an inline expression
  81. * (for example: <h1 xt:content="string: ${site/domain} - welcome">welcome</h1>)
  82. * would cause the inline expression to disappear.
  83. * - Changes to PHPShorthand have improved the stability of the php expression type,
  84. * especially in the area of respecting quoted strings.
  85. *
  86. * New in 2.2:
  87. * - Fixed a bug where tags that contained no children and used the xt:condition
  88. * attribute would improperly set the $ignoreUntilLevel variable, causing
  89. * unpredictable rendering below.
  90. *
  91. * New in 2.4:
  92. * - Added a new include type "virtual" which includes a relative URL from the
  93. * web site document root, allowing the inclusion of CGI scripts and other
  94. * types of dynamic content directly into the template. This would be the
  95. * equivalent of the PHP code:
  96. *
  97. * include ('http://www.example.com/cgi-bin/script_name.cgi');
  98. *
  99. * <code>
  100. * <?php
  101. *
  102. * loader_import ('saf.XML.XT');
  103. *
  104. * $tpl = new XT ('inc/html', XT_DEFAULT_PREFIX);
  105. *
  106. * $tpldata = '';
  107. *
  108. * if ($tpl->validate ($tpldata)) {
  109. * echo $tpl->fill (
  110. * $tpldata,
  111. * array (
  112. * 'foo' => 'Testing...',
  113. * )
  114. * );
  115. * } else {
  116. * echo 'Error: ' $tpl->error . ' on line ' . $tpl->err_line;
  117. * }
  118. *
  119. * ? >
  120. * </code>
  121. *
  122. * @package XML
  123. * @author John Luxford <john.luxford@gmail.com>
  124. * @license http://www.sitellite.org/index/license GNU GPL License
  125. * @version 2.2, 2003-09-28, $Id: XT.php,v 1.17 2008/03/09 18:46:06 lux Exp $
  126. * @access public
  127. *
  128. */
  129. class XT {
  130. /**
  131. * The path to the template directory.
  132. *
  133. * @access public
  134. *
  135. */
  136. var $path = '';
  137. /**
  138. * The output of the current fill() call.
  139. *
  140. * @access public
  141. *
  142. */
  143. var $output = '';
  144. //var $object;
  145. /**
  146. * A cache for templates read from files, so if they are
  147. * called a second or third time XT doesn't have to read them from
  148. * the file system again.
  149. *
  150. * @access private
  151. *
  152. */
  153. var $cache = array ();
  154. /**
  155. * A cache of the node array of parsed templates. Used to
  156. * reduce the number of XML parsers that need to be executed during
  157. * a template with loops and complex structures in it.
  158. *
  159. * @access private
  160. *
  161. */
  162. var $nodeCache = array ();
  163. /**
  164. * An internal structure built to buffer SQL command blocks
  165. * prior to executing them.
  166. *
  167. * @access private
  168. *
  169. */
  170. var $sql = array ();
  171. //var $log = array ();
  172. /**
  173. * An internal structure built to buffer loop command blocks
  174. * prior to executing them.
  175. *
  176. * @access private
  177. *
  178. */
  179. var $loop = array ();
  180. /**
  181. * An internal structure built to buffer condition command blocks
  182. * prior to executing them.
  183. *
  184. * @access private
  185. *
  186. */
  187. var $if = array ();
  188. //var $switch = array ();
  189. /**
  190. * An internal structure built to store template blocks
  191. * so that they can be reused later in the same or even another
  192. * script (via an include call).
  193. *
  194. * @access private
  195. *
  196. */
  197. var $block = array ();
  198. //var $grids = array ();
  199. /**
  200. * Tell XT to stop output until the closing of a certain
  201. * tag has been found. This is managed via the ignoreUntilLevel()
  202. * method.
  203. *
  204. * @access private
  205. *
  206. */
  207. var $_ignoreUntilLevel = array ();
  208. /**
  209. * Used by the condition loop and condition blocks to tell
  210. * XT to continue buffering the loop or condition body until the
  211. * proper closing tag has been found.
  212. *
  213. * @access private
  214. *
  215. */
  216. var $openUntilLevel = false;
  217. /**
  218. * Used to distinguish between xt:loop and xt:condition
  219. * attributes by the $openUntilLevel logic.
  220. *
  221. * @access private
  222. *
  223. */
  224. var $isLoop = false;
  225. /**
  226. * The current XML namespace XT is looking for to find
  227. * command tags and attributes.
  228. *
  229. * @access public
  230. *
  231. */
  232. var $prefix = 'xt:';
  233. //var $postfix = 'xa:';
  234. /**
  235. * Determines whether the template should be treated as
  236. * HTML or a different kind of markup. This affects tags such
  237. * as xt:intl where an HTML span tag can be added to surround
  238. * the string.
  239. *
  240. * @access public
  241. *
  242. */
  243. var $isHtml = true;
  244. /**
  245. * Contains a list of tags that are self-closing (ie.
  246. * they do not contain any data, such as a br tag).
  247. * This list is only referenced if $isHtml is true.
  248. *
  249. * @access public
  250. *
  251. */
  252. var $selfClosing = array (
  253. 'img',
  254. 'br',
  255. 'hr',
  256. 'meta',
  257. 'link',
  258. 'area',
  259. );
  260. /**
  261. * Determines whether the template should build a
  262. * table of contents (TOC) based on HTML header tags found within.
  263. * Defaults to false, and must be set to true in order to
  264. * generate TOCs.
  265. *
  266. * @access public
  267. *
  268. */
  269. var $buildToc = false;
  270. /**
  271. * The list of HTML headers found in the document.
  272. *
  273. * @access public
  274. *
  275. */
  276. var $toc = array ();
  277. var $_addToHeader = false;
  278. var $_bind_list = array ();
  279. var $_bind_attrs = array ();
  280. var $_bind_parts = array ();
  281. /**
  282. * Location to store cached contents in. Defaults to
  283. * 'store:cache/templates/'. Note that the scope will be
  284. * appended to the $cacheLocation for each cacheable element.
  285. *
  286. * @access public
  287. *
  288. */
  289. var $cacheLocation = 'store:cache/templates/';
  290. /**
  291. * How long in seconds to store the cached elements
  292. * before regenerating them. May be overridden with the duration
  293. * attribute of the cache tag. Defaults to 3600 which is one hour.
  294. *
  295. * @access public
  296. *
  297. */
  298. var $cacheDuration = 3600;
  299. /**
  300. * Used to generate an auto-incrementing ID value for
  301. * cache elements that are missing an "id" attribute (so as
  302. * to make the attribute optional).
  303. *
  304. * @access public
  305. *
  306. */
  307. var $cacheCount = 0;
  308. /**
  309. * Contains the name of the file currently being processed.
  310. * This is set in the getDoc() method, so technically it will set
  311. * the current file even when you're just retrieving its contents.
  312. * This shouldn't affect its validity for most uses, but when you
  313. * want to retrieve the last parsed file, it means you have to do
  314. * so prior to calling getDoc() again, either directly or indirectly.
  315. *
  316. * @access public
  317. *
  318. */
  319. var $file = false;
  320. /**
  321. * The XTE object used to evaluate expressions in XT
  322. * tags.
  323. *
  324. * @access public
  325. *
  326. */
  327. var $exp;
  328. /**
  329. * The XML parser resource.
  330. *
  331. * @access private
  332. *
  333. */
  334. var $parser;
  335. /**
  336. * The error message, if an error occurs.
  337. *
  338. * @access public
  339. *
  340. */
  341. var $error;
  342. /**
  343. * The error code, if an error occurs.
  344. *
  345. * @access public
  346. *
  347. */
  348. var $err_code;
  349. /**
  350. * The error byte number, if an error occurs.
  351. *
  352. * @access public
  353. *
  354. */
  355. var $err_byte;
  356. /**
  357. * The error line number, if an error occurs.
  358. *
  359. * @access public
  360. *
  361. */
  362. var $err_line;
  363. /**
  364. * The error column number, if an error occurs.
  365. *
  366. * @access public
  367. *
  368. */
  369. var $err_colnum;
  370. /**
  371. * Rows from an xt:sql statement.
  372. *
  373. * @access public
  374. *
  375. */
  376. var $rows = 0;
  377. /**
  378. * Transformations to perform on a variable.
  379. *
  380. * @access public
  381. *
  382. */
  383. var $transformations = array ();
  384. /**
  385. * Constructor method. $prefix is either XT_DEFAULT_PREFIX,
  386. * XT_POST_PREFIX, or a custom prefix. The prefix is essentially
  387. * the XML namespace XT is to recognize. The use of multiple
  388. * namespaces can allow you to partially parse a template, cache
  389. * that, then parse the rest which might contain user-specific content
  390. * such as personal information. XT_DEFAULT_PREFIX and XT_POST_PREFIX
  391. * are constants defined by this package.
  392. *
  393. * @access public
  394. * @param string $path
  395. * @param string $prefix
  396. *
  397. */
  398. function XT ($path = '', $prefix = XT_DEFAULT_PREFIX) {
  399. $this->path = $path;
  400. $this->prefix = $prefix;
  401. $this->exp = new XTExpression (false);
  402. }
  403. /**
  404. * Retrieves a template from the appropriate location,
  405. * such as the $cache array, $nodeCache array, a file, or if
  406. * the string is the template itself, it returns that. Also
  407. * handles caching to $cache and $nodeCache of the appropriate
  408. * templates.
  409. *
  410. * @access private
  411. * @param string $data
  412. * @return string
  413. *
  414. */
  415. function getDoc ($data) {
  416. if (@is_array ($this->nodeCache[$data])) {
  417. return $this->nodeCache[$data];
  418. }
  419. $doc = $data;
  420. /*
  421. if (! empty ($this->path)) {
  422. $path = $this->path . '/';
  423. }
  424. */
  425. $path = $this->path () . '/';
  426. // get real data if data is from a file
  427. if (@is_file ($path . $data)) {
  428. if (isset ($this->cache[$data])) {
  429. $data = $this->cache[$data];
  430. } else {
  431. $file = $data;
  432. $data = @join ('', @file ($path . $file));
  433. $this->cache[$file] = $data;
  434. $this->file = $file;
  435. } // else do nothing, data is a string already
  436. }
  437. return $data;
  438. }
  439. /**
  440. * Returns either the contents of the $path property,
  441. * or the current working directory, which should be used as
  442. * the path instead.
  443. *
  444. * @access public
  445. * @return string
  446. *
  447. */
  448. function path () {
  449. if (! empty ($this->path)) {
  450. return $this->path;
  451. } else {
  452. return getcwd ();
  453. }
  454. }
  455. function ignoreUntilLevel ($level = false) {
  456. if ($level === -1) {
  457. array_pop ($this->_ignoreUntilLevel);
  458. } elseif ($level !== false) {
  459. $this->_ignoreUntilLevel[] = $level;
  460. } else {
  461. $c = count ($this->_ignoreUntilLevel);
  462. if ($c > 0) {
  463. return $this->_ignoreUntilLevel[$c - 1];
  464. } else {
  465. return false;
  466. }
  467. }
  468. }
  469. /**
  470. * Validates a template to see if it is a valid XML
  471. * document.
  472. *
  473. * @access public
  474. * @param string $data
  475. * @return boolean
  476. *
  477. */
  478. function validate ($data) {
  479. $data = $this->getDoc ($data);
  480. if (is_array ($data)) { // if data came from nodeCache it must be valid
  481. return true;
  482. }
  483. // create the xml parser now, and declare the handler methods
  484. $this->parser = xml_parser_create ($this->encoding);
  485. if (! $this->parser) {
  486. $this->error = 'Template Error: Failed to create an XML parser!';
  487. return false;
  488. }
  489. if (! xml_parser_set_option ($this->parser, XML_OPTION_CASE_FOLDING, false)) {
  490. xml_parser_free ($this->parser);
  491. $this->error = 'Template Error: Failed to disable case folding!';
  492. return false;
  493. }
  494. if ($this->parser) {
  495. if (xml_parse ($this->parser, $data, true)) {
  496. xml_parser_free ($this->parser);
  497. return true;
  498. } else {
  499. $this->err_code = xml_get_error_code ($this->parser);
  500. $this->err_line = xml_get_current_line_number ($this->parser);
  501. $this->err_byte = xml_get_current_byte_index ($this->parser);
  502. $this->err_colnum = xml_get_current_column_number ($this->parser);
  503. $this->error = 'Template Error: ' . xml_error_string ($this->err_code);
  504. xml_parser_free ($this->parser);
  505. return false;
  506. }
  507. } else {
  508. $this->error = 'Template Error: No parser available!';
  509. return false;
  510. }
  511. }
  512. /**
  513. * Executes a template. $obj is an optional object you
  514. * can pass to the template, which makes its properties immediately
  515. * available to the template. $carry is used internally to determine
  516. * whether to reset the object register before executing.
  517. *
  518. * @access public
  519. * @param string $data
  520. * @param object $obj
  521. * @param boolean $carry
  522. * @return string
  523. *
  524. */
  525. function fill ($data, $obj = '', $carry = false) {
  526. $this->error = false;
  527. // duplicate object for parser isolation
  528. $tpl = clone ($this); // deliberate copy, we want two separate objects here
  529. $tpl->exp = clone ($this->exp);
  530. if (! $carry) {
  531. //$tpl->register = array ();
  532. $tpl->exp->resetRegister ();
  533. $tpl->carry = false;
  534. } else {
  535. $tpl->carry = true;
  536. //$tpl->register = array ();
  537. //$tpl->register =& $this->register;
  538. //$this->exp->resetRegister ();
  539. }
  540. $tpl->output = '';
  541. if ($obj !== '') {
  542. //$tpl->register['object'] = $obj;
  543. $tpl->exp->setObject ($obj);
  544. } else {
  545. //$tpl->register['object'] = new StdClass;
  546. $tpl->exp->setObject (new StdClass);
  547. }
  548. $tpl->sql = array ();
  549. $tpl->loop = array ();
  550. $tpl->if = array ();
  551. $tpl->switch = array ();
  552. $tpl->buffer = array ();
  553. $tpl->open = false;
  554. $tpl->open_var = false;
  555. $tpl->toc = array ();
  556. $tpl->rows = 0;
  557. $tpl->error = false;
  558. $tpl->err_code = false;
  559. $tpl->err_byte = false;
  560. $tpl->err_line = false;
  561. $tpl->err_colnum = false;
  562. $doc = $data;
  563. $data = $tpl->getDoc ($data);
  564. if (is_array ($data)) { // use nodeCache instead of new xml parser
  565. foreach ($data as $node) {
  566. $node = $this->reverseEntities ($node);
  567. $tpl->_output ($tpl->{$tpl->makeMethod ($node['tag'], $node['type'], $node['level'])} ($node));
  568. }
  569. // gather blocks from included templates
  570. foreach ($tpl->block as $key => $block) {
  571. if (! isset ($this->block[$key])) {
  572. $this->block[$key] =& $tpl->block[$key];
  573. }
  574. }
  575. $this->rows = $tpl->rows;
  576. $this->toc = $tpl->toc;
  577. //$tpl->output = $this->reverseEntities ($tpl->output);
  578. return $tpl->output;
  579. }
  580. // create the xml parser now, and declare the handler methods
  581. $this->parser = xml_parser_create ($this->encoding);
  582. if (! $this->parser) {
  583. $this->error = 'Template Error: Failed to create an XML parser!';
  584. return false;
  585. }
  586. if (! xml_parser_set_option ($this->parser, XML_OPTION_CASE_FOLDING, false)) {
  587. xml_parser_free ($this->parser);
  588. $this->error = 'Template Error: Failed to disable case folding!';
  589. return false;
  590. }
  591. if ($this->parser) {
  592. // turning inline PHP off for the time being
  593. // actually, i don't think we need it at all
  594. //$data = $tpl->inline ($data);
  595. $data = $this->convertEntities ($data);
  596. if (xml_parse_into_struct ($this->parser, $data, $tpl->vals, $tpl->tags)) {
  597. xml_parser_free ($this->parser);
  598. //echo '<pre>';
  599. //print_r ($tpl->vals);
  600. //echo '</pre>';
  601. // cache the node structure
  602. $this->nodeCache[$data] = $tpl->vals;
  603. // list of paths for the current tag and its parents
  604. // takes the form [level] = [path 1, path 2]
  605. $this->_path_list = array ();
  606. // the current level
  607. $this->_path_level = 0;
  608. /*
  609. $colours = array (
  610. '000',
  611. '600',
  612. '060',
  613. '006',
  614. '900',
  615. '396',
  616. '369',
  617. 'f00',
  618. '0f0',
  619. '00f',
  620. 'f90',
  621. '666',
  622. '999',
  623. 'bbb',
  624. );
  625. */
  626. // mainloop
  627. foreach ($tpl->vals as $node) {
  628. $node = $this->reverseEntities ($node);
  629. $norm_tag = str_replace (':', '-', $node['tag']);
  630. if ($node['type'] == 'cdata' || strpos ($norm_tag, 'ch-') === 0 || strpos ($norm_tag, 'xt-') === 0 || ! in_array ($norm_tag, $tpl->_bind_parts)) {
  631. $tpl->_output ($tpl->{$tpl->makeMethod ($node['tag'], $node['type'], $node['level'])} ($node));
  632. continue;
  633. }
  634. // echo '<span style="color: #' . $colours[$node['level']] . '">' . str_repeat (' ', $node['level']) . $node['tag'] . ' (' . $node['type'] . ")</span>\n";
  635. $node['paths'] = array ();
  636. if ($node['type'] == 'open' || $node['type'] == 'complete') {
  637. if ($node['level'] > $this->_path_level) {
  638. // moving up a new level (ie. a sub-node)
  639. $this->_path_level++;
  640. $this->_path_list[$this->_path_level] = $node;
  641. } elseif ($this->_path_level > 0 && $node['level'] == $this->_path_level) {
  642. // next sibling at the same level
  643. array_pop ($this->_path_list);
  644. $this->_path_list[$this->_path_level] = $node;
  645. } elseif ($node['level'] < $this->_path_level) {
  646. // do nothing...
  647. } else {
  648. // moving up a new level
  649. $this->_path_level++;
  650. $this->_path_list[$this->_path_level] = $node;
  651. }
  652. // compile all variations of this tag's xpath for a match in $this->_bind_list
  653. $paths = array ('//' . $node['tag']);
  654. $list = $this->_path_list[$this->_path_level - 2]['paths'];
  655. if (is_array ($list)) {
  656. foreach ($list as $p) {
  657. $paths[] = $p . '/' . $node['tag'];
  658. $paths[] = $p . '//' . $node['tag'];
  659. }
  660. } else {
  661. $paths[] = '/' . $node['tag'];
  662. }
  663. $count = count ($paths);
  664. $cpl = count ($this->_path_list) - 1;
  665. if (is_array ($this->_path_list[$cpl]['attributes'])) {
  666. foreach ($this->_path_list[$cpl]['attributes'] as $k => $v) {
  667. if (strpos ($k, 'xt:') !== 0) {
  668. for ($i = 0; $i < $count; $i++) {
  669. $paths[] = $paths[$i] . '[@' . $k . '="' . $v . '"]';
  670. }
  671. }
  672. }
  673. }
  674. // echo '<div style="padding: 10px; margin: 10px; border: 1px solid #aaa">' . join ("\n", $paths) . '</div>';
  675. $this->_path_list[$cpl]['paths'] = $paths;
  676. $node['paths'] = $paths;
  677. if ($node['type'] == 'complete') {
  678. foreach (array_intersect (array_keys ($this->_bind_list), $paths) as $key) {
  679. $node['value'] .= $this->_bind_list[$key];
  680. }
  681. }
  682. foreach (array_intersect (array_keys ($this->_bind_attrs), $paths) as $key) {
  683. //info ($node['attributes']);
  684. foreach ($this->_bind_attrs[$key] as $k => $v) {
  685. $node['attributes'][$k] = $v;
  686. }
  687. //info ($node['attributes']);
  688. }
  689. if ($node['type'] == 'complete') {
  690. $this->_path_level--;
  691. array_pop ($this->_path_list);
  692. }
  693. } elseif ($node['type'] == 'close') {
  694. if (count ($this->_path_list) > 0 && $this->_path_list[count ($this->_path_list) - 1] != null) {
  695. foreach (array_intersect (array_keys ($this->_bind_list), $this->_path_list[count ($this->_path_list) - 1]['paths']) as $key) {
  696. $tpl->_output ($this->_bind_list[$key]);
  697. }
  698. $this->_path_level--;
  699. array_pop ($this->_path_list);
  700. }
  701. }
  702. $tpl->_output ($tpl->{$tpl->makeMethod ($node['tag'], $node['type'], $node['level'])} ($node));
  703. }
  704. // gather blocks from included templates
  705. foreach ($tpl->block as $key => $block) {
  706. if (! isset ($this->block[$key])) {
  707. $this->block[$key] =& $tpl->block[$key];
  708. }
  709. }
  710. $this->rows = $tpl->rows;
  711. $this->toc = $tpl->toc;
  712. //$tpl->output = $this->reverseEntities ($tpl->output);
  713. return $tpl->output;
  714. } else {
  715. $this->err_code = xml_get_error_code ($this->parser);
  716. $this->err_line = xml_get_current_line_number ($this->parser);
  717. $this->err_byte = xml_get_current_byte_index ($this->parser);
  718. $this->err_colnum = xml_get_current_column_number ($this->parser);
  719. $this->error = 'Template Error: ' . xml_error_string ($this->err_code);
  720. xml_parser_free ($this->parser);
  721. return false;
  722. }
  723. } else {
  724. $this->error = 'Template Error: No parser available!';
  725. return false;
  726. }
  727. }
  728. /**
  729. * Bind some content to the specified tag.
  730. *
  731. * @access public
  732. * @param string
  733. * @param string
  734. *
  735. */
  736. function bind ($path, $data) {
  737. $this->_bind_list[$path] .= $data;
  738. $path = preg_split ('|/+|', $path, -1, PREG_SPLIT_NO_EMPTY);
  739. foreach ($path as $k => $v) {
  740. if (strpos ($v, '[') !== false) {
  741. $path[$k] = preg_replace ('|\[[^\]]+\]|', '', $v);
  742. }
  743. }
  744. $this->_bind_parts = array_unique (array_merge ($this->_bind_parts, $path));
  745. }
  746. /**
  747. * Bind an attribute to the specified tag.
  748. *
  749. * @access public
  750. * @param string
  751. * @param string
  752. * @param string
  753. *
  754. */
  755. function bindAttr ($path, $attr, $value) {
  756. $this->_bind_attrs[$path][$attr] = $value;
  757. $path = preg_split ('|/+|', $path, -1, PREG_SPLIT_NO_EMPTY);
  758. foreach ($path as $k => $v) {
  759. if (strpos ($v, '[') !== false) {
  760. $path[$k] = preg_replace ('|\[[^\]]+\]|', '', $v);
  761. }
  762. }
  763. $this->_bind_parts = array_unique (array_merge ($this->_bind_parts, $path));
  764. }
  765. /**
  766. * Uses the saf.HTML.Messy package to implement a "messy"
  767. * parser in XT, allowing for invalid markup in templates (ie.
  768. * HTML instead of XHTML). Of course since the markup accepted is
  769. * invalid, your mileage may vary. This method is discouraged
  770. * unless you have a good reason for using it.
  771. *
  772. * @access public
  773. * @param string $data
  774. * @param object $obj
  775. * @param boolean $carry
  776. * @return string
  777. *
  778. */
  779. function messy ($data, $obj = '', $carry = false) {
  780. if (! is_object ($this->messy)) {
  781. global $loader;
  782. $loader->import ('saf.HTML.Messy');
  783. $this->messy = new Messy ();
  784. }
  785. /*
  786. if (! empty ($this->path)) {
  787. $path = $this->path . '/';
  788. }
  789. */
  790. $path = $this->path () . '/';
  791. if (@is_file ($path . $data)) {
  792. $data = $this->getDoc ($data);
  793. }
  794. $this->nodeCache[$data] = $this->messy->parse ($data);
  795. return $this->fill ($data, $obj, $carry);
  796. }
  797. /**
  798. * Executes the specified box using the Sitellite box API,
  799. * which is essentially just an include. Note: This is now an alias
  800. * for the loader_box() function.
  801. *
  802. * @access public
  803. * @param string $name
  804. * @param associative array $parameters
  805. * @return string
  806. *
  807. */
  808. function box ($name, $parameters = array ()) {
  809. if ($this->file) {
  810. $GLOBALS['_xte'] =& $this->exp;
  811. }
  812. $out = loader_box ($name, $parameters);
  813. unset ($GLOBALS['_xte']);
  814. if (empty ($out)) {
  815. return html_marker ('Empty Box: ' . $name);
  816. }
  817. return html_marker ('Box: ' . $name) . $out;
  818. }
  819. /**
  820. * Executes the specified form using the Sitellite form API,
  821. * which is essentially just an include of a file that defines a
  822. * subclass of saf.MailForm. Note: This is now an alias
  823. * for the loader_form() function.
  824. *
  825. * @access public
  826. * @param string $name
  827. * @return string
  828. *
  829. */
  830. function form ($name) {
  831. $out = loader_form ($name);
  832. if (empty ($out)) {
  833. return html_marker ('Empty Form: ' . $name);
  834. }
  835. return html_marker ('Form: ' . $name) . $out;
  836. }
  837. /**
  838. * Evaluates PHP code embedded into a template. Currently
  839. * not used, because there's really no reason why embedded PHP
  840. * should be needed.
  841. *
  842. * @access private
  843. * @param string $data
  844. * @return string
  845. *
  846. */
  847. function inline ($data) {
  848. ob_start ();
  849. eval (CLOSE_TAG . $data);
  850. $newdata = ob_get_contents ();
  851. ob_end_clean ();
  852. return $newdata;
  853. }
  854. /**
  855. * Determines which callback function to call for the
  856. * specified node.
  857. *
  858. * @access private
  859. * @param string $name
  860. * @param string $type
  861. * @return string
  862. *
  863. */
  864. function makeMethod ($name, $type, $level) {
  865. $iul = $this->ignoreUntilLevel ();
  866. if ($iul && $iul < $level) {
  867. switch ($type) {
  868. case 'close':
  869. return '_default_end';
  870. case 'cdata':
  871. return '_default_cdata';
  872. default:
  873. return '_default';
  874. }
  875. }
  876. if (strpos ($name, 'xt-') === 0) {
  877. $name = str_replace ('xt-', 'xt:', $name);
  878. }
  879. switch ($type) {
  880. case 'complete':
  881. if (strpos ($name, 'ch:') === 0) {
  882. return '_ch_handler';
  883. }
  884. case 'open':
  885. if ($this->buildToc && in_array ($name, array ('h1', 'h2', 'h3', 'h4', 'h5', 'h6'))) {
  886. return '_header_handler';
  887. }
  888. $m = str_replace (array ($this->prefix, '-'), array ('_', '_'), $name);
  889. $d = '_default';
  890. break;
  891. case 'close':
  892. if ($this->buildToc && in_array ($name, array ('h1', 'h2', 'h3', 'h4', 'h5', 'h6'))) {
  893. return '_header_end';
  894. }
  895. $m = str_replace (array ($this->prefix, '-'), array ('_', '_'), $name) . '_end';
  896. $d = '_default_end';
  897. break;
  898. default:
  899. $m = str_replace (array ($this->prefix, '-'), array ('_', '_'), $name) . '_cdata';
  900. $d = '_default_cdata';
  901. }
  902. if (strpos ($m, '_') === 0 && method_exists ($this, $m)) {
  903. return $m;
  904. }
  905. return $d;
  906. }
  907. /**
  908. * Determines where to send the output of a tag callback.
  909. *
  910. * @access private
  911. * @param string $str
  912. *
  913. */
  914. function _output ($str) {
  915. if ($this->open) {
  916. $this->open_var .= $str;
  917. } else {
  918. $this->output .= $str;
  919. }
  920. }
  921. /**
  922. * Wraps $str in xt:tpl tags and returns it.
  923. *
  924. * @access public
  925. * @param string $str
  926. * @return string
  927. *
  928. */
  929. function wrap ($str) {
  930. return '<' . $this->prefix . 'tpl>' . $str . '</' . $this->prefix . 'tpl>';
  931. }
  932. /**
  933. * Sets the value of a property of the default object
  934. * in the register. Always returns an empty string.
  935. *
  936. * @access private
  937. * @param string $name
  938. * @param string $value
  939. * @return string
  940. *
  941. */
  942. function setVal ($name, $value) {
  943. if (is_object ($this->exp->register['object'])) {
  944. $this->exp->register['object']->{$name} = $value;
  945. } elseif (is_array ($this->exp->register['object'])) {
  946. $this->exp->register['object'][$name] = $value;
  947. } else {
  948. $this->exp->setObject (new StdClass);
  949. $this->exp->register['object']->{$name} = $value;
  950. }
  951. return '';
  952. }
  953. /**
  954. * Gets the value of a property in the register.
  955. * Returns that value, which may be transformed, if a
  956. * transformation has been defined for that property name.
  957. *
  958. * @access private
  959. * @param string $val
  960. * @param associative array $node
  961. * @param string $type
  962. * @param boolean $transform
  963. * @return string
  964. *
  965. */
  966. function getVal ($val, $node, $type = 'path', $transform = true) {
  967. if (preg_match ('/\/([a-zA-Z0-9_-]+)$/', $val, $regs)) {
  968. $var = $regs[1];
  969. } else {
  970. $var = $val;
  971. }
  972. $val = $this->exp->evaluate ($val, $node, $type, $this->carry);
  973. if ($transform && isset ($this->transformations[$var]) && is_object ($this->transformations[$var])) {
  974. return $this->transformations[$var]->transform ($val);
  975. } else {
  976. return $val;
  977. }
  978. }
  979. /**
  980. * Reverses the conversion of HTML entities to XT-compatible ch:entity
  981. * tags.
  982. *
  983. * @access public
  984. * @param string $data
  985. * @return string
  986. *
  987. */
  988. function reverseEntities ($data) {
  989. if (is_array ($data)) {
  990. if (isset ($data['value'])) {
  991. $data['value'] = $this->reverseEntities ($data['value']);
  992. }
  993. if (isset ($data['attributes'])) {
  994. foreach ($data['attributes'] as $key => $value) {
  995. $data['attributes'][$key] = $this->reverseEntities ($value);
  996. }
  997. }
  998. return $data;
  999. }
  1000. $data = preg_replace (
  1001. '/\[ch:n([0-9]+)\]/',
  1002. '&#\1;',
  1003. $data
  1004. );
  1005. return preg_replace (
  1006. '/\[ch:([a-zA-Z0-9]+)\]/',
  1007. '&\1;',
  1008. $data
  1009. );
  1010. }
  1011. /**
  1012. * Converts HTML entities into XT-compatible ch:entity
  1013. * tags.
  1014. *
  1015. * @access public
  1016. * @param string $data
  1017. * @return string
  1018. *
  1019. */
  1020. function convertEntities ($data) {
  1021. $data = preg_replace (
  1022. '/&#([0-9]+);/',
  1023. '[ch:n\1]',
  1024. $data
  1025. );
  1026. return preg_replace (
  1027. '/&([a-zA-Z0-9]+);/',
  1028. '[ch:\1]',
  1029. $data
  1030. );
  1031. $data = preg_replace (
  1032. '/&#([0-9]+);/',
  1033. '<ch:n\1 />',
  1034. $data
  1035. );
  1036. return preg_replace (
  1037. '/&([a-zA-Z0-9]+);/',
  1038. '<ch:\1 />',
  1039. $data
  1040. );
  1041. return str_replace (
  1042. array (
  1043. '&nbsp;',
  1044. '&iexcl;',
  1045. '&cent;',
  1046. '&pound;',
  1047. '&curren;',
  1048. '&yen;',
  1049. '&brvbar;',
  1050. '&sect;',
  1051. '&uml;',
  1052. '&copy;',
  1053. '&ordf;',
  1054. '&laquo;',
  1055. '&not;',
  1056. '&shy;',
  1057. '&reg;',
  1058. '&macr;',
  1059. '&deg;',
  1060. '&plusmn;',
  1061. '&sup2;',
  1062. '&sup3;',
  1063. '&acute;',
  1064. '&micro;',
  1065. '&para;',
  1066. '&middot;',
  1067. '&cedil;',
  1068. '&sup1;',
  1069. '&ordm;',
  1070. '&raquo;',
  1071. '&frac14;',
  1072. '&frac12;',
  1073. '&frac34;',
  1074. '&iquest;',
  1075. '&Agrave;',
  1076. '&Aacute;',
  1077. '&Acirc;',
  1078. '&Atilde;',
  1079. '&Auml;',
  1080. '&Aring;',
  1081. '&AElig;',
  1082. '&Ccedil;',
  1083. '&Egrave;',
  1084. '&Eacute;',
  1085. '&Ecirc;',
  1086. '&Euml;',
  1087. '&Igrave;',
  1088. '&Iacute;',
  1089. '&Icirc;',
  1090. '&Iuml;',
  1091. '&ETH;',
  1092. '&Ntilde;',
  1093. '&Ograve;',
  1094. '&Oacute;',
  1095. '&Ocirc;',
  1096. '&Otilde;',
  1097. '&Ouml;',
  1098. '&times;',
  1099. '&Oslash;',
  1100. '&Ugrave;',
  1101. '&Uacute;',
  1102. '&Ucirc;',
  1103. '&Uuml;',
  1104. '&Yacute;',
  1105. '&THORN;',
  1106. '&szlig;',
  1107. '&agrave;',
  1108. '&aacute;',
  1109. '&acirc;',
  1110. '&atilde;',
  1111. '&auml;',
  1112. '&aring;',
  1113. '&aelig;',
  1114. '&ccedil;',
  1115. '&egrave;',
  1116. '&eacute;',
  1117. '&ecirc;',
  1118. '&euml;',
  1119. '&igrave;',
  1120. '&iacute;',
  1121. '&icirc;',
  1122. '&iuml;',
  1123. '&eth;',
  1124. '&ntilde;',
  1125. '&ograve;',
  1126. '&oacute;',
  1127. '&ocirc;',
  1128. '&otilde;',
  1129. '&ouml;',
  1130. '&divide;',
  1131. '&oslash;',
  1132. '&ugrave;',
  1133. '&uacute;',
  1134. '&ucirc;',
  1135. '&uuml;',
  1136. '&yacute;',
  1137. '&thorn;',
  1138. '&yuml;',
  1139. '&fnof;',
  1140. '&Alpha;',
  1141. '&Beta;',
  1142. '&Gamma;',
  1143. '&Delta;',
  1144. '&Epsilon;',
  1145. '&Zeta;',
  1146. '&Eta;',
  1147. '&Theta;',
  1148. '&Iota;',
  1149. '&Kappa;',
  1150. '&Lambda;',
  1151. '&Mu;',
  1152. '&Nu;',
  1153. '&Xi;',
  1154. '&Omicron;',
  1155. '&Pi;',
  1156. '&Rho;',
  1157. '&Sigma;',
  1158. '&Tau;',
  1159. '&Upsilon;',
  1160. '&Phi;',
  1161. '&Chi;',
  1162. '&Psi;',
  1163. '&Omega;',
  1164. '&alpha;',
  1165. '&beta;',
  1166. '&gamma;',
  1167. '&delta;',
  1168. '&epsilon;',
  1169. '&zeta;',
  1170. '&eta;',
  1171. '&theta;',
  1172. '&iota;',
  1173. '&kappa;',
  1174. '&lambda;',
  1175. '&mu;',
  1176. '&nu;',
  1177. '&xi;',
  1178. '&omicron;',
  1179. '&pi;',
  1180. '&rho;',
  1181. '&sigmaf;',
  1182. '&sigma;',
  1183. '&tau;',
  1184. '&upsilon;',
  1185. '&phi;',
  1186. '&chi;',
  1187. '&psi;',
  1188. '&omega;',
  1189. '&thetasym;',
  1190. '&upsih;',
  1191. '&piv;',
  1192. '&bull;',
  1193. '&hellip;',
  1194. '&prime;',
  1195. '&Prime;',
  1196. '&oline;',
  1197. '&frasl;',
  1198. '&weierp;',
  1199. '&image;',
  1200. '&real;',
  1201. '&trade;',
  1202. '&alefsym;',
  1203. '&larr;',
  1204. '&uarr;',
  1205. '&rarr;',
  1206. '&darr;',
  1207. '&harr;',
  1208. '&crarr;',
  1209. '&lArr;',
  1210. '&uArr;',
  1211. '&rArr;',
  1212. '&dArr;',
  1213. '&hArr;',
  1214. '&forall;',
  1215. '&part;',
  1216. '&exist;',
  1217. '&empty;',
  1218. '&nabla;',
  1219. '&isin;',
  1220. '&notin;',
  1221. '&ni;',
  1222. '&prod;',
  1223. '&sum;',
  1224. '&minus;',
  1225. '&lowast;',
  1226. '&radic;',
  1227. '&prop;',
  1228. '&infin;',
  1229. '&ang;',
  1230. '&and;',
  1231. '&or;',
  1232. '&cap;',
  1233. '&cup;',
  1234. '&int;',
  1235. '&there4;',
  1236. '&sim;',
  1237. '&cong;',
  1238. '&asymp;',
  1239. '&ne;',
  1240. '&equiv;',
  1241. '&le;',
  1242. '&ge;',
  1243. '&sub;',
  1244. '&sup;',
  1245. '&nsub;',
  1246. '&sube;',
  1247. '&supe;',
  1248. '&oplus;',
  1249. '&otimes;',
  1250. '&perp;',
  1251. '&sdot;',
  1252. '&lceil;',
  1253. '&rceil;',
  1254. '&lfloor;',
  1255. '&rfloor;',
  1256. '&lang;',
  1257. '&rang;',
  1258. '&loz;',
  1259. '&spades;',
  1260. '&clubs;',
  1261. '&hearts;',
  1262. '&diams;',
  1263. '&quot;',
  1264. '&amp;',
  1265. '&lt;',
  1266. '&gt;',
  1267. '&OElig;',
  1268. '&oelig;',
  1269. '&Scaron;',
  1270. '&scaron;',
  1271. '&Yuml;',
  1272. '&circ;',
  1273. '&tilde;',
  1274. '&ensp;',
  1275. '&emsp;',
  1276. '&thinsp;',
  1277. '&zwnj;',
  1278. '&zwj;',
  1279. '&lrm;',
  1280. '&rlm;',
  1281. '&ndash;',
  1282. '&mdash;',
  1283. '&lsquo;',
  1284. '&rsquo;',
  1285. '&sbquo;',
  1286. '&ldquo;',
  1287. '&rdquo;',
  1288. '&bdquo;',
  1289. '&dagger;',
  1290. '&Dagger;',
  1291. '&permil;',
  1292. '&lsaquo;',
  1293. '&rsaquo;',
  1294. '&euro;',
  1295. ),
  1296. array (
  1297. '<ch:nbsp />',
  1298. '<ch:iexcl />',
  1299. '<ch:cent />',
  1300. '<ch:pound />',
  1301. '<ch:curren />',
  1302. '<ch:yen />',
  1303. '<ch:brvbar />',
  1304. '<ch:sect />',
  1305. '<ch:uml />',
  1306. '<ch:copy />',
  1307. '<ch:ordf />',
  1308. '<ch:laquo />',
  1309. '<ch:not />',
  1310. '<ch:shy />',
  1311. '<ch:reg />',
  1312. '<ch:macr />',
  1313. '<ch:deg />',
  1314. '<ch:plusmn />',
  1315. '<ch:sup2 />',
  1316. '<ch:sup3 />',
  1317. '<ch:acute />',
  1318. '<ch:micro />',
  1319. '<ch:para />',
  1320. '<ch:middot />',
  1321. '<ch:cedil />',
  1322. '<ch:sup1 />',
  1323. '<ch:ordm />',
  1324. '<ch:raquo />',
  1325. '<ch:frac14 />',
  1326. '<ch:frac12 />',
  1327. '<ch:frac34 />',
  1328. '<ch:iquest />',
  1329. '<ch:Agrave />',
  1330. '<ch:Aacute />',
  1331. '<ch:Acirc />',
  1332. '<ch:Atilde />',
  1333. '<ch:Auml />',
  1334. '<ch:Aring />',
  1335. '<ch:AElig />',
  1336. '<ch:Ccedil />',
  1337. '<ch:Egrave />',
  1338. '<ch:Eacute />',
  1339. '<ch:Ecirc />',
  1340. '<ch:Euml />',
  1341. '<ch:Igrave />',
  1342. '<ch:Iacute />',
  1343. '<ch:Icirc />',
  1344. '<ch:Iuml />',
  1345. '<ch:ETH />',
  1346. '<ch:Ntilde />',
  1347. '<ch:Ograve />',
  1348. '<ch:Oacute />',
  1349. '<ch:Ocirc />',
  1350. '<ch:Otilde />',
  1351. '<ch:Ouml />',
  1352. '<ch:times />',
  1353. '<ch:Oslash />',
  1354. '<ch:Ugrave />',
  1355. '<ch:Uacute />',
  1356. '<ch:Ucirc />',
  1357. '<ch:Uuml />',
  1358. '<ch:Yacute />',
  1359. '<ch:THORN />',
  1360. '<ch:szlig />',
  1361. '<ch:agrave />',
  1362. '<ch:aacute />',
  1363. '<ch:acirc />',
  1364. '<ch:atilde />',
  1365. '<ch:auml />',
  1366. '<ch:aring />',
  1367. '<ch:aelig />',
  1368. '<ch:ccedil />',
  1369. '<ch:egrave />',
  1370. '<ch:eacute />',
  1371. '<ch:ecirc />',
  1372. '<ch:euml />',
  1373. '<ch:igrave />',
  1374. '<ch:iacute />',
  1375. '<ch:icirc />',
  1376. '<ch:iuml />',
  1377. '<ch:eth />',
  1378. '<ch:ntilde />',
  1379. '<ch:ograve />',
  1380. '<ch:oacute />',
  1381. '<ch:ocirc />',
  1382. '<ch:otilde />',
  1383. '<ch:ouml />',
  1384. '<ch:divide />',
  1385. '<ch:oslash />',
  1386. '<ch:ugrave />',
  1387. '<ch:uacute />',
  1388. '<ch:ucirc />',
  1389. '<ch:uuml />',
  1390. '<ch:yacute />',
  1391. '<ch:thorn />',
  1392. '<ch:yuml />',
  1393. '<ch:fnof />',
  1394. '<ch:Alpha />',
  1395. '<ch:Beta />',
  1396. '<ch:Gamma />',
  1397. '<ch:Delta />',
  1398. '<ch:Epsilon />',
  1399. '<ch:Zeta />',
  1400. '<ch:Eta />',
  1401. '<ch:Theta />',
  1402. '<ch:Iota />',
  1403. '<ch:Kappa />',
  1404. '<ch:Lambda />',
  1405. '<ch:Mu />',
  1406. '<ch:Nu />',
  1407. '<ch:Xi />',
  1408. '<ch:Omicron />',
  1409. '<ch:Pi />',
  1410. '<ch:Rho />',
  1411. '<ch:Sigma />',
  1412. '<ch:Tau />',
  1413. '<ch:Upsilon />',
  1414. '<ch:Phi />',
  1415. '<ch:Chi />',
  1416. '<ch:Psi />',
  1417. '<ch:Omega />',
  1418. '<ch:alpha />',
  1419. '<ch:beta />',
  1420. '<ch:gamma />',
  1421. '<ch:delta />',
  1422. '<ch:epsilon />',
  1423. '<ch:zeta />',
  1424. '<ch:eta />',
  1425. '<ch:theta />',
  1426. '<ch:iota />',
  1427. '<ch:kappa />',
  1428. '<ch:lambda />',
  1429. '<ch:mu />',
  1430. '<ch:nu />',
  1431. '<ch:xi />',
  1432. '<ch:omicron />',
  1433. '<ch:pi />',
  1434. '<ch:rho />',
  1435. '<ch:sigmaf />',
  1436. '<ch:sigma />',
  1437. '<ch:tau />',
  1438. '<ch:upsilon />',
  1439. '<ch:phi />',
  1440. '<ch:chi />',
  1441. '<ch:psi />',
  1442. '<ch:omega />',
  1443. '<ch:thetasym />',
  1444. '<ch:upsih />',
  1445. '<ch:piv />',
  1446. '<ch:bull />',
  1447. '<ch:hellip />',
  1448. '<ch:prime />',
  1449. '<ch:Prime />',
  1450. '<ch:oline />',
  1451. '<ch:frasl />',
  1452. '<ch:weierp />',
  1453. '<ch:image />',
  1454. '<ch:real />',
  1455. '<ch:trade />',
  1456. '<ch:alefsym />',
  1457. '<ch:larr />',
  1458. '<ch:uarr />',
  1459. '<ch:rarr />',
  1460. '<ch:darr />',
  1461. '<ch:harr />',
  1462. '<ch:crarr />',
  1463. '<ch:lArr />',
  1464. '<ch:uArr />',
  1465. '<ch:rArr />',
  1466. '<ch:dArr />',
  1467. '<ch:hArr />',
  1468. '<ch:forall />',
  1469. '<ch:part />',
  1470. '<ch:exist />',
  1471. '<ch:empty />',
  1472. '<ch:nabla />',
  1473. '<ch:isin />',
  1474. '<ch:notin />',
  1475. '<ch:ni />',
  1476. '<ch:prod />',
  1477. '<ch:sum />',
  1478. '<ch:minus />',
  1479. '<ch:lowast />',
  1480. '<ch:radic />',
  1481. '<ch:prop />',
  1482. '<ch:infin />',
  1483. '<ch:ang />',
  1484. '<ch:and />',
  1485. '<ch:or />',
  1486. '<ch:cap />',
  1487. '<ch:cup />',
  1488. '<ch:int />',
  1489. '<ch:there4 />',
  1490. '<ch:sim />',
  1491. '<ch:cong />',
  1492. '<ch:asymp />',
  1493. '<ch:ne />',
  1494. '<ch:equiv />',
  1495. '<ch:le />',
  1496. '<ch:ge />',
  1497. '<ch:sub />',
  1498. '<ch:sup />',
  1499. '<ch:nsub />',
  1500. '<ch:sube />',
  1501. '<ch:supe />',
  1502. '<ch:oplus />',
  1503. '<ch:otimes />',
  1504. '<ch:perp />',
  1505. '<ch:sdot />',
  1506. '<ch:lceil />',
  1507. '<ch:rceil />',
  1508. '<ch:lfloor />',
  1509. '<ch:rfloor />',
  1510. '<ch:lang />',
  1511. '<ch:rang />',
  1512. '<ch:loz />',
  1513. '<ch:spades />',
  1514. '<ch:clubs />',
  1515. '<ch:hearts />',
  1516. '<ch:diams />',
  1517. '<ch:quot />',
  1518. '<ch:amp />',
  1519. '<ch:lt />',
  1520. '<ch:gt />',
  1521. '<ch:OElig />',
  1522. '<ch:oelig />',
  1523. '<ch:Scaron />',
  1524. '<ch:scaron />',
  1525. '<ch:Yuml />',
  1526. '<ch:circ />',
  1527. '<ch:tilde />',
  1528. '<ch:ensp />',
  1529. '<ch:emsp />',
  1530. '<ch:thinsp />',
  1531. '<ch:zwnj />',
  1532. '<ch:zwj />',
  1533. '<ch:lrm />',
  1534. '<ch:rlm />',
  1535. '<ch:ndash />',
  1536. '<ch:mdash />',
  1537. '<ch:lsquo />',
  1538. '<ch:rsquo />',
  1539. '<ch:sbquo />',
  1540. '<ch:ldquo />',
  1541. '<ch:rdquo />',
  1542. '<ch:bdquo />',
  1543. '<ch:dagger />',
  1544. '<ch:Dagger />',
  1545. '<ch:permil />',
  1546. '<ch:lsaquo />',
  1547. '<ch:rsaquo />',
  1548. '<ch:euro />',
  1549. ),
  1550. $data
  1551. );
  1552. }
  1553. // ------------------------------
  1554. // ----- TAG HANDLERS BELOW -----
  1555. // ------------------------------
  1556. /**
  1557. * Default open and complete tag handler.
  1558. *
  1559. * @access private
  1560. * @param associative array $node
  1561. * @return string
  1562. *
  1563. */
  1564. function _default ($node) {
  1565. $iul = $this->ignoreUntilLevel ();
  1566. if ($iul && $iul < $node['level']) {
  1567. return '';
  1568. }
  1569. $out = '<' . $node['tag'];
  1570. if ($this->_addToHeader) {
  1571. $this->toc[count ($this->toc) - 1]['value'] .= $node['value'];
  1572. }
  1573. if (isset ($node['attributes'])) {
  1574. if (! $this->open) {
  1575. if (isset ($node['attributes'][$this->prefix . 'replace'])) {
  1576. $replace = $node['attributes'][$this->prefix . 'replace'];
  1577. unset ($node['attributes'][$this->prefix . 'replace']);
  1578. if ($node['type'] != 'complete') {
  1579. $this->ignoreUntilLevel ($node['level']);
  1580. }
  1581. return $this->getVal ($replace, $node, 'path', true);
  1582. } else {
  1583. if (isset ($node['attributes'][$this->prefix . 'attributes'])) {
  1584. $statements = $this->exp->splitStatement ($node['attributes'][$this->prefix . 'attributes']);
  1585. foreach ($statements as $statement) {
  1586. list ($attr, $expr) = $this->exp->splitAssignment ($statement);
  1587. $node['attributes'][$attr] = $this->exp->evaluate ($expr, $node, 'path', true);
  1588. }
  1589. unset ($node['attributes'][$this->prefix . 'attributes']);
  1590. } elseif (isset ($node['attributes'][$this->prefix . 'attrs'])) {
  1591. $statements = $this->exp->splitStatement ($node['attributes'][$this->prefix . 'attrs']);
  1592. foreach ($statements as $statement) {
  1593. list ($attr, $expr) = $this->exp->splitAssignment ($statement);
  1594. $node['attributes'][$attr] = $this->exp->evaluate ($expr, $node, 'path', true);
  1595. }
  1596. unset ($node['attributes'][$this->prefix . 'attrs']);
  1597. }
  1598. if (isset ($node['attributes'][$this->prefix . 'content'])) {
  1599. $node['value'] = $this->getVal ($node['attributes'][$this->prefix . 'content'], $node, 'path', true);
  1600. unset ($node['attributes'][$this->prefix . 'content']);
  1601. if ($node['type'] != 'complete') {
  1602. $this->ignoreUntilLevel ($node['level']);
  1603. }
  1604. }
  1605. if (isset ($node['attributes'][$this->prefix . 'loop'])) {
  1606. list ($varname, $expr) = $this->exp->splitAssignment ($node['attributes'][$this->prefix . 'loop']);
  1607. $this->loop[] = array (
  1608. 'varname' => $varname,
  1609. 'expr' => $expr,
  1610. 'struct' => '',
  1611. );
  1612. $this->open = true;
  1613. $this->open_var =& $this->loop[count ($this->loop) - 1]['struct'];
  1614. $this->openUntilLevel = $node['level'];
  1615. $this->isLoop = true;
  1616. unset ($node['attributes'][$this->prefix . 'loop']);
  1617. } elseif (isset ($node['attributes'][$this->prefix . 'repeat'])) {
  1618. list ($varname, $expr) = $this->exp->splitAssignment ($node['attributes'][$this->prefix . 'repeat']);
  1619. $this->loop[] = array (
  1620. 'varname' => $varname,
  1621. 'expr' => $expr,
  1622. 'struct' => '',
  1623. );
  1624. $this->open = true;
  1625. $this->open_var =& $this->loop[count ($this->loop) - 1]['struct'];
  1626. $this->openUntilLevel = $node['level'];
  1627. $this->isLoop = true;
  1628. unset ($node['attributes'][$this->prefix . 'repeat']);
  1629. }
  1630. if (isset ($node['attributes'][$this->prefix . 'condition'])) {
  1631. list ($one, $two) = $this->exp->splitAssignment ($node['attributes'][$this->prefix . 'condition']);
  1632. if ($one === 'not') {
  1633. $negate = true;
  1634. $expr = $two;
  1635. } else {
  1636. $negate = false;
  1637. $expr = $node['attributes'][$this->prefix . 'condition'];
  1638. }
  1639. //echo '<pre>evaluating expression "' . "\t" . $expr . "\t" . '"</pre>';
  1640. if ($negate && $this->exp->evaluate ($expr, $node, 'path', true)) {
  1641. if ($node['type'] == 'complete') {
  1642. return '';
  1643. }
  1644. $this->ignoreUntilLevel ($node['level']);
  1645. return '';
  1646. } elseif (! $negate && ! $this->exp->evaluate ($expr, $node, 'path', true)) {
  1647. if ($node['type'] == 'complete') {
  1648. return '';
  1649. }
  1650. $this->ignoreUntilLevel ($node['level']);
  1651. return '';
  1652. } else {
  1653. unset ($node['attributes'][$this->prefix . 'condition']);
  1654. }
  1655. }
  1656. }
  1657. }
  1658. foreach ($node['attributes'] as $key => $value) {
  1659. if (strstr ($value, '${')) {
  1660. $value = $this->exp->evaluate ('string: ' . $value, $node, 'path', true);
  1661. }
  1662. $out .= ' ' . $key . '="' . $value . '"';
  1663. }
  1664. }
  1665. if ($node['type'] == 'complete') {
  1666. if (isset ($node['value']) || ($this->isHtml && ! in_array ($node['tag'], $this->selfClosing))) {
  1667. $out .= '>' . $node['value'] . '</' . $node['tag'] . '>';
  1668. } else {
  1669. $out .= ' />';
  1670. }
  1671. } else {
  1672. $out .= '>';
  1673. if (isset ($node['value'])) {
  1674. $out .= $node['value'];
  1675. }
  1676. }
  1677. return $out;
  1678. }
  1679. /**
  1680. * Default close tag handler.
  1681. *
  1682. * @access private
  1683. * @param associative array $node
  1684. * @return string
  1685. *
  1686. */
  1687. function _default_end ($node) {
  1688. $out = '';
  1689. $iul = $this->ignoreUntilLevel ();
  1690. if ($iul && $iul < $node['level']) {
  1691. return '';
  1692. } elseif ($this->open && $node['level'] == $this->openUntilLevel && $this->isLoop) {
  1693. // xt:loop ends here
  1694. $this->open_var .= '</' . $node['tag'] . '>';
  1695. $loop = array_shift ($this->loop);
  1696. $this->open = false;
  1697. unset ($this->open_var);
  1698. $this->openUntilLevel = false;
  1699. $this->isLoop = false;
  1700. $list =& $this->exp->repeat ($loop['expr'], $node);
  1701. $total = count ($list);
  1702. foreach ($list as $key => $item) {
  1703. $this->exp->setCurrent ($item, $loop['varname'], $key, $total);
  1704. $out .= $this->fill ($this->wrap ($loop['struct']), $this->exp->getObject ('object'), true);
  1705. }
  1706. //$this->exp->setCurrent (new StdClass, $loop['varname'], 0, 0);
  1707. return $out;
  1708. } elseif ($iul && $node['level'] == $iul) {
  1709. $this->ignoreUntilLevel (-1);
  1710. return '';
  1711. }
  1712. return '</' . $node['tag'] . '>';
  1713. }
  1714. /**
  1715. * Default cdata node handler.
  1716. *
  1717. * @access private
  1718. * @param associative array $node
  1719. * @return string
  1720. *
  1721. */
  1722. function _default_cdata ($node) {
  1723. $iul = $this->ignoreUntilLevel ();
  1724. if ($iul && $iul < $node['level']) {
  1725. return '';
  1726. }
  1727. if ($this->_addToHeader) {
  1728. $this->toc[count ($this->toc) - 1]['value'] .= $node['value'];
  1729. }
  1730. return $node['value'];
  1731. }
  1732. /**
  1733. * Handler for xmlchar tags. See
  1734. * http://xmlchar.sf.net/ for more information.
  1735. *
  1736. * @access private
  1737. * @param associative array $node
  1738. * @return string
  1739. *
  1740. */
  1741. function _ch_handler ($node) {
  1742. $iul = $this->ignoreUntilLevel ();
  1743. if ($iul && $iul < $node['level']) {
  1744. return '';
  1745. }
  1746. if (preg_match ('/^ch:n[0-9]+$/', $node['tag'])) {
  1747. $node['tag'] = str_replace ('n', '#', $node['tag']);
  1748. }
  1749. return '&' . str_replace ('ch:', '', $node['tag']) . ';';
  1750. }
  1751. /**
  1752. * Handler for header tags, if $makeToc is true.
  1753. *
  1754. * @access private
  1755. * @param associative array $node
  1756. * @return string
  1757. *
  1758. */
  1759. function _header_handler ($node) {
  1760. $iul = $this->ignoreUntilLevel ();
  1761. if ($iul && $iul < $node['level']) {
  1762. return '';
  1763. }
  1764. $node['href'] = md5 ($node['tag'] . count ($this->toc) . $node['value']);
  1765. $this->toc[] = $node;
  1766. if ($node['type'] == 'complete') {
  1767. $end = $node['value'] . '</' . $node['tag'] . '>';
  1768. } else {
  1769. $this->_addToHeader = true;
  1770. $end = $node['value'];
  1771. }
  1772. $attrs = '';
  1773. if (is_array ($node['attributes'])) {
  1774. foreach ($node['attributes'] as $key => $value) {
  1775. if (strstr ($value, '${')) {
  1776. $value = $this->exp->evaluate ('string: ' . $value, $node, 'path', true);
  1777. }
  1778. $attrs .= ' ' . $key . '="' . $value . '"';
  1779. }
  1780. }
  1781. return '<a name="' . $node['href'] . '" style="margin: 0px; padding: 0px; display: inline"></a><' . $node['tag'] . $attrs . '>' . $end;
  1782. }
  1783. /**
  1784. * Handler for end header tags, if $makeToc is true.
  1785. *
  1786. * @access private
  1787. * @param associative array $node
  1788. * @return string
  1789. *
  1790. */
  1791. function _header_end ($node) {
  1792. $iul = $this->ignoreUntilLevel ();
  1793. if ($iul && $iul < $node['level']) {
  1794. return '';
  1795. }
  1796. $this->_addToHeader = false;
  1797. return '</' . $node['tag'] . '>';
  1798. }
  1799. /**
  1800. * Generates a table of contents as an HTML unordered
  1801. * list, based on the $toc property. Note: Also requires
  1802. * $buildToc to be set to true.
  1803. *
  1804. * @access public
  1805. * @param string $title
  1806. * @return string
  1807. *
  1808. */
  1809. function makeToc ($title = '') {
  1810. if (count ($this->toc) == 0 || ! $this->buildToc) {
  1811. return '';
  1812. }
  1813. $out = '';
  1814. if (! empty ($title)) {
  1815. $out .= '<h2>' . $title . '</h2>' . NEWLINEx2;
  1816. }
  1817. $prev = '';
  1818. $out .= '<ul>' . NEWLINE;
  1819. $c = 0;
  1820. foreach ($this->toc as $node) {
  1821. if ($prev == $node['tag'] || empty ($prev)) {
  1822. // same level
  1823. $out .= TAB . '<li><a href="#' . $node['href'] . '">' . $node['value'] . '</a></li>' . NEWLINE;
  1824. } elseif ($prev < $node['tag']) {
  1825. // this tag under
  1826. $c++;
  1827. $out .= '<ul>' . NEWLINE;
  1828. $out .= TAB . '<li><a href="#' . $node['href'] . '">' . $node['value'] . '</a></li>' . NEWLINE;
  1829. } elseif ($prev > $node['tag']) {
  1830. // close list and move down one
  1831. $c--;
  1832. $out .= '</ul>' . NEWLINE;
  1833. $out .= TAB . '<li><a href="#' . $node['href'] . '">' . $node['value'] . '</a></li>' . NEWLINE;
  1834. }
  1835. $prev = $node['tag'];
  1836. }
  1837. while ($c > 0) {
  1838. $out .= '</ul>' . NEWLINE;
  1839. $c--;
  1840. }
  1841. return $out . '</ul>' . NEWLINEx2;
  1842. }
  1843. // <xt:tpl>
  1844. /**
  1845. * Open tpl tag handler.
  1846. *
  1847. * @access private
  1848. * @param associative array $node
  1849. * @return string
  1850. *
  1851. */
  1852. function _tpl ($node) {
  1853. return $node['value'];
  1854. }
  1855. // </xt:tpl>
  1856. /**
  1857. * Close tpl tag handler.
  1858. *
  1859. * @access private
  1860. * @param associative array $node
  1861. * @return string
  1862. *
  1863. */
  1864. function _tpl_end ($node) {
  1865. if (! isset ($node['value'])) {
  1866. return '';
  1867. }
  1868. return $node['value'];
  1869. }
  1870. /**
  1871. * Cdata tpl tag handler.
  1872. *
  1873. * @access private
  1874. * @param associative array $node
  1875. * @return string
  1876. *
  1877. */
  1878. function _tpl_cdata ($node) {
  1879. return $node['value'];
  1880. }
  1881. // <xt:doctype root="html" access="public" name="" uri="" />
  1882. /**
  1883. * Creates a doctype declaration from an xt:doctype
  1884. * tag.
  1885. *
  1886. * @access private
  1887. * @param associative array $node
  1888. * @return string
  1889. *
  1890. */
  1891. function _doctype ($node) {
  1892. $out = '<!DOCTYPE ' . $node['attributes']['root'];
  1893. $out .= ' ' . strtoupper ($node['attributes']['access']) . ' "';
  1894. if (isset ($node['attributes']['name'])) {
  1895. $out .= $node['attributes']['name'] . '" "';
  1896. }
  1897. $out .= $node['attributes']['uri'] . "\">\n";
  1898. return $out;
  1899. }
  1900. // <xt:xmldecl version="1.0" encoding="utf-8" />
  1901. /**
  1902. * Creates an xml declaration tag from an xt:xmldecl
  1903. * tag.
  1904. *
  1905. * @access private
  1906. * @param associative array $node
  1907. * @return string
  1908. *
  1909. */
  1910. function _xmldecl ($node) {
  1911. $out = '<?xml';
  1912. foreach ($node['attributes'] as $key => $value) {
  1913. $out .= ' ' . $key . '="' . $value . '"';
  1914. }
  1915. $out .= CLOSE_TAG;
  1916. return $out;
  1917. }
  1918. // <xt:xmlstyle type="text/css" href="foo.css" />
  1919. /**
  1920. * Creates an xml stylesheet declaration tag from an
  1921. * xt:xmlstyle tag.
  1922. *
  1923. * @access private
  1924. * @param associative array $node
  1925. * @return string
  1926. *
  1927. */
  1928. function _xmlstyle ($node) {
  1929. $out = '<?xml-stylesheet';
  1930. foreach ($node['attributes'] as $key => $value) {
  1931. $out .= ' ' . $key . '="' . $value . '"';
  1932. }
  1933. $out .= CLOSE_TAG;
  1934. return $out;
  1935. }
  1936. // <xt:comment>Message here</xt:comment>
  1937. /**
  1938. * Creates an xml comment tag from an xt:comment
  1939. * tag.
  1940. *
  1941. * @access private
  1942. * @param associative array $node
  1943. * @return string
  1944. *
  1945. */
  1946. function _comment ($node) {
  1947. if ($node['type'] == 'complete') {
  1948. return '<!-- ' . $node['value'] . ' -->';
  1949. }
  1950. return '<!-- ' . $node['value'];
  1951. }
  1952. // <xt:comment>Message here</xt:comment>
  1953. /**
  1954. * Creates an xml comment tag from an xt:comment
  1955. * tag.
  1956. *
  1957. * @access private
  1958. * @param associative array $node
  1959. * @return string
  1960. *
  1961. */
  1962. function _comment_end ($node) {
  1963. return $node['value'] . ' -->';
  1964. }
  1965. // <xt:comment>Message here</xt:comment>
  1966. /**
  1967. * Creates an xml comment tag from an xt:comment
  1968. * tag.
  1969. *
  1970. * @access private
  1971. * @param associative array $node
  1972. * @return string
  1973. *
  1974. */
  1975. function _comment_cdata ($node) {
  1976. return $node['value'];
  1977. }
  1978. // <xt:note>Message here</xt:note> is an alias of <xt:comment></xt:comment>
  1979. /**
  1980. * Creates an xml comment tag from an xt:note
  1981. * tag.
  1982. *
  1983. * @access private
  1984. * @param associative array $node
  1985. * @return string
  1986. *
  1987. */
  1988. function _note ($node) {
  1989. return $this->_comment ($node);
  1990. }
  1991. /**
  1992. * Creates an xml comment tag from an xt:note
  1993. * tag.
  1994. *
  1995. * @access private
  1996. * @param associative array $node
  1997. * @return string
  1998. *
  1999. */
  2000. function _note_end ($node) {
  2001. return $this->_comment_end ($node);
  2002. }
  2003. /**
  2004. * Creates an xml comment tag from an xt:note
  2005. * tag.
  2006. *
  2007. * @access private
  2008. * @param associative array $node
  2009. * @return string
  2010. *
  2011. */
  2012. function _note_cdata ($node) {
  2013. return $this->_comment_cdata ($node);
  2014. }
  2015. // <xt:import pkg="saf.Misc.Alt" />
  2016. // pkg is string
  2017. /**
  2018. * Import tag handler.
  2019. *
  2020. * @access private
  2021. * @param associative array $node
  2022. * @return string
  2023. *
  2024. */
  2025. function _import ($node) {
  2026. foreach (preg_split ('/, ?/', ltrim (rtrim ($node['attributes']['pkg']))) as $var) {
  2027. global $loader;
  2028. $loader->import ($var);
  2029. }
  2030. return '';
  2031. }
  2032. // <xt:set-obj name="c" new="Alt ('odd', 'even')" />
  2033. // name is string, new is string
  2034. /**
  2035. * Set-obj tag handler.
  2036. *
  2037. * @access private
  2038. * @param associative array $node
  2039. * @return string
  2040. *
  2041. */
  2042. function _set_obj ($node) {
  2043. eval (CLOSE_TAG . OPEN_TAG . ' $this->exp->setObject (new ' . $node['attributes']['new'] . ', \'' . $node['attributes']['name'] . '\'); ' . CLOSE_TAG);
  2044. return '';
  2045. }
  2046. // <xt:set name="varname" value="foobar" />
  2047. // name is string, value is expression but string by default
  2048. /**
  2049. * Set tag handler.
  2050. *
  2051. * @access private
  2052. * @param associative array $node
  2053. * @return string
  2054. *
  2055. */
  2056. function _set ($node) {
  2057. if ($this->open) {
  2058. return $this->_default ($node);
  2059. }
  2060. return $this->setVal ($node['attributes']['name'], $this->exp->evaluate ($node['attributes']['value'], $node, 'string', true));
  2061. }
  2062. // <xt:exec value="foobar" />
  2063. // value is expression but php by default
  2064. /**
  2065. * Exec tag handler.
  2066. *
  2067. * @access private
  2068. * @param associative array $node
  2069. * @return string
  2070. *
  2071. */
  2072. function _exec ($node) {
  2073. $this->exp->evaluate ($node['attributes']['value'], $node, 'string', true);
  2074. return '';
  2075. }
  2076. // <xt:register name="cgi" />
  2077. // name is string
  2078. /**
  2079. * Register tag handler.
  2080. *
  2081. * @access private
  2082. * @param associative array $node
  2083. * @return string
  2084. *
  2085. */
  2086. function _register ($node) {
  2087. $this->exp->register ($node['attributes']['name']);
  2088. }
  2089. // <xt:inc name="other-template.tpl" />
  2090. // all attributes are strings
  2091. /**
  2092. * Open inc tag handler.
  2093. *
  2094. * @access private
  2095. * @param associative array $node
  2096. * @return string
  2097. *
  2098. */
  2099. function _inc ($node) {
  2100. // evaluate the node name
  2101. $node['attributes']['name'] = $this->exp->evaluate ($node['attributes']['name'], $node, 'string', true);
  2102. if ($node['type'] != 'complete') {
  2103. $this->ignoreUntilLevel ($node['level']);
  2104. }
  2105. if ($node['attributes']['type'] == 'csv') {
  2106. if (! empty ($node['attributes']['delimiter'])) {
  2107. $delimiters = array (
  2108. 'tab' => "\t",
  2109. 'comma' => ',',
  2110. 'colon' => ':',
  2111. 'pipe' => '|',
  2112. 'semicolon' => ';',
  2113. );
  2114. if (isset ($delimiters[$node['attributes']['delimiter']])) {
  2115. $delimiter = $delimiters[$node['attributes']['delimiter']];
  2116. } else {
  2117. $delimiter = $node['attributes']['delimiter'];
  2118. }
  2119. } else {
  2120. $delimiter = $delimiters['comma'];
  2121. }
  2122. $out = "<table>\n";
  2123. $data = @file ($this->path () . '/' . $node['attributes']['name']);
  2124. if (! is_array ($data)) {
  2125. return '';
  2126. }
  2127. if ($node['attributes']['header'] == 'yes') {
  2128. $headers = array_shift ($data);
  2129. $out .= "\t<tr>\n";
  2130. foreach (preg_split ('/' . $delimiter . '/', $headers) as $header) {
  2131. $out .= "\t\t<th>" . $header . "</th>\n";
  2132. }
  2133. $out .= "\t</tr>\n";
  2134. }
  2135. foreach ($data as $line) {
  2136. $out .= "\t<tr>\n";
  2137. foreach (preg_split ('/' . $delimiter . '/', $line) as $item) {
  2138. $out .= "\t\t<td>" . $item . "</td>\n";
  2139. }
  2140. $out .= "\t</tr>\n";
  2141. }
  2142. return $out . "<table>\n";
  2143. } elseif ($node['attributes']['type'] == 'messy') {
  2144. return $this->messy ($node['attributes']['name'], $this->exp->register['object']);
  2145. } elseif ($node['attributes']['type'] == 'simple') {
  2146. return template_simple ($node['attributes']['name'], $this->exp->register['object']);
  2147. } elseif ($node['attributes']['type'] == 'virtual') {
  2148. //ob_start ();
  2149. if (strpos ($node['attributes']['name'], '/') === 0 || strpos ($node['attributes']['name'], '://') === false) {
  2150. $url = site_url () . $node['attributes']['name'];
  2151. } else {
  2152. $url = $node['attributes']['name'];
  2153. }
  2154. //include ($url);
  2155. //$o = ob_get_contents ();
  2156. //ob_end_clean ();
  2157. $o = @join ('', @file ($url));
  2158. return $o;
  2159. } elseif ($node['attributes']['type'] == 'xml') {
  2160. $this->ignoreUntilLevel (-1);
  2161. $this->open = true;
  2162. $this->xmlinc = array (
  2163. 'node' => $node,
  2164. 'struct' => '',
  2165. );
  2166. $this->open_var =& $this->xmlinc['struct'];
  2167. return '';
  2168. } elseif ($node['attributes']['type'] == 'plain') {
  2169. return @join ('', @file ($this->path () . '/' . $node['attributes']['name']));
  2170. } else { // type is 'xt' or not specified
  2171. $o = template_xt ($node['attributes']['name'], $this->exp->register['object']);
  2172. if ($o === false) {
  2173. return '<!-- ' . template_error () . ' (' . template_err_line () . ', ' . template_err_colnum () . ') -->';
  2174. }
  2175. return $o;
  2176. }
  2177. }
  2178. /**
  2179. * Close inc tag handler.
  2180. *
  2181. * @access private
  2182. * @param associative array $node
  2183. * @return string
  2184. *
  2185. */
  2186. function _inc_end ($node) {
  2187. if (! $this->xmlinc) {
  2188. $this->ignoreUntilLevel (-1);
  2189. return '';
  2190. } else {
  2191. $node = $this->xmlinc['node'];
  2192. $struct = $this->xmlinc['struct'];
  2193. $this->xmlinc = false;
  2194. unset ($this->open_var);
  2195. $this->open = false;
  2196. if (! is_object ($this->sloppy)) {
  2197. loader_import ('saf.XML.Sloppy');
  2198. $this->sloppy = new SloppyDOM ();
  2199. }
  2200. if (! $doc = $this->sloppy->parseFromFile ($node['attributes']['name'])) {
  2201. $this->error = $this->sloppy->error;
  2202. $this->err_code = $this->sloppy->err_code;
  2203. $this->err_line = $this->sloppy->err_line;
  2204. $this->err_byte = $this->sloppy->err_byte;
  2205. $this->err_colnum = $this->sloppy->err_colnum;
  2206. return '';
  2207. }
  2208. if (! empty ($node['attributes']['item'])) {
  2209. $items =& $doc->query ($node['attributes']['item']);
  2210. if (! is_array ($items)) {
  2211. $this->error = 'No nodes returned by the specified path';
  2212. return '';
  2213. }
  2214. } else {
  2215. $items =& $doc->root->children;
  2216. }
  2217. $res = '';
  2218. foreach ($items as $item) {
  2219. $this->exp->register['xml'] = $item->makeObj ();
  2220. $res .= $this->fill ($this->wrap ($struct), $this->exp->getObject ('object'), true);
  2221. }
  2222. $this->exp->register['xml'] = false;
  2223. return $res;
  2224. }
  2225. }
  2226. // <xt:box name="syndicate" url="http://slashdot.org/slashdot.rdf" duration="1800" />
  2227. // all attributes are strings
  2228. /**
  2229. * Open box tag handler.
  2230. *
  2231. * @access private
  2232. * @param associative array $node
  2233. * @return string
  2234. *
  2235. */
  2236. function _box ($node) {
  2237. if ($node['type'] != 'complete') {
  2238. $this->ignoreUntilLevel ($node['level']);
  2239. }
  2240. foreach ($node['attributes'] as $key => $value) {
  2241. if (strstr ($value, '${')) {
  2242. $node['attributes'][$key] = $this->exp->evaluate ('string: ' . $value, $node, 'path', true);
  2243. }
  2244. }
  2245. return $this->box ($this->exp->evaluate ($node['attributes']['name'], $node, 'string', true), $node['attributes']);
  2246. }
  2247. /**
  2248. * Close box tag handler.
  2249. *
  2250. * @access private
  2251. * @param associative array $node
  2252. * @return string
  2253. *
  2254. */
  2255. function _box_end ($node) {
  2256. $this->ignoreUntilLevel (-1);
  2257. return '';
  2258. }
  2259. // <xt:form name="signup" />
  2260. /**
  2261. * Open form tag handler.
  2262. *
  2263. * @access private
  2264. * @param associative array $node
  2265. * @return string
  2266. *
  2267. */
  2268. function _form ($node) {
  2269. if ($node['type'] != 'complete') {
  2270. $this->ignoreUntilLevel ($node['level']);
  2271. }
  2272. foreach ($node['attributes'] as $key => $value) {
  2273. if (strstr ($value, '${')) {
  2274. $node['attributes'][$key] = $this->exp->evaluate ('string: ' . $value, $node, 'path', true);
  2275. }
  2276. }
  2277. return $this->form ($this->exp->evaluate ($node['attributes']['name'], $node, 'string', true));
  2278. }
  2279. /**
  2280. * Close form tag handler.
  2281. *
  2282. * @access private
  2283. * @param associative array $node
  2284. * @return string
  2285. *
  2286. */
  2287. function _form_end ($node) {
  2288. $this->ignoreUntilLevel (-1);
  2289. return '';
  2290. }
  2291. // <xt:intl>Translated text here</xt:intl>
  2292. // value is string
  2293. /**
  2294. * Intl tag handler.
  2295. *
  2296. * @access private
  2297. * @param associative array $node
  2298. * @return string
  2299. *
  2300. */
  2301. function _intl ($node) {
  2302. if ($this->open) {
  2303. return $this->_default ($node);
  2304. }
  2305. global $intl;
  2306. if (is_object ($intl)) {
  2307. if (isset ($node['value'])) {
  2308. $node['value'] = $this->reverseEntities ($node['value']);
  2309. }
  2310. if ($this->isHtml) {
  2311. // add <span lang=""></span> tags around the value
  2312. return $intl->get ($node['value'], $this->exp->register['object'], true);
  2313. } else {
  2314. return $intl->get ($node['value'], $this->exp->register['object']);
  2315. }
  2316. }
  2317. return $node['value'];
  2318. }
  2319. /**
  2320. * Alias of _intl tag handler.
  2321. *
  2322. * @access private
  2323. * @param associative array $node
  2324. * @return string
  2325. *
  2326. */
  2327. function _translate ($node) {
  2328. return $this->_intl ($node);
  2329. }
  2330. /**
  2331. * Alias of _intl tag handler.
  2332. *
  2333. * @access private
  2334. * @param associative array $node
  2335. * @return string
  2336. *
  2337. */
  2338. function _i18n ($node) {
  2339. return $this->_intl ($node);
  2340. }
  2341. // <xt:val name="foo" />
  2342. // name is expression
  2343. // <xt:date format="format">My date here</xt:date>
  2344. /**
  2345. * Date tag handler.
  2346. *
  2347. * @access private
  2348. * @param associative array $node
  2349. * @return string
  2350. *
  2351. */
  2352. function _date ($node) {
  2353. if ($this->open) {
  2354. return $this->_default ($node);
  2355. }
  2356. global $intl;
  2357. if (is_object ($intl)) {
  2358. $node['value'] = $this->reverseEntities ($node['value']);
  2359. if (empty( $node['value'] )) {
  2360. $node['value'] = date('r');
  2361. }
  2362. if ($this->isHtml) {
  2363. // add <span lang=""></span> tags around the value
  2364. return $intl->date ($node['attributes']['format'],
  2365. $node['value'], $this->exp->register['object'], true);
  2366. } else {
  2367. return $intl->date ($node['attributes']['format'],
  2368. $node['value'], $this->exp->register['object']);
  2369. }
  2370. }
  2371. return $node['value'];
  2372. }
  2373. /**
  2374. * Var tag handler.
  2375. *
  2376. * @access private
  2377. * @param associative array $node
  2378. * @return string
  2379. *
  2380. */
  2381. function _var ($node) {
  2382. if ($this->open) {
  2383. return $this->_default ($node);
  2384. }
  2385. return $this->getVal ($node['attributes']['name'], $node, 'path', true);
  2386. }
  2387. // <xt:code><![CDATA[ echo 'hello<br />'; ]]></xt:code>
  2388. // <xt:code highlighted="yes"><![CDATA[ echo 'hello<br />'; ]]></xt:code>
  2389. // <xt:code language="asp"><![CDATA[ Response.Write ("hello<br />") ]]></xt:code>
  2390. /**
  2391. * Code tag handler.
  2392. *
  2393. * @access private
  2394. * @param associative array $node
  2395. * @return string
  2396. *
  2397. */
  2398. function _code ($node) {
  2399. if ($node['attributes']['highlighted'] == 'yes') {
  2400. ob_start ();
  2401. highlight_string ('<?php' . $node['value'] . '?' . '>');
  2402. $out = ob_get_contents ();
  2403. ob_end_clean ();
  2404. return $out;
  2405. }
  2406. $languages = array (
  2407. 'php' => array ('<?php', '?' . '>'),
  2408. 'asp' => array ('<%', '%>'),
  2409. 'jsp' => array ('<%', '%>'),
  2410. 'javascript' => array ('<script language="javascript" type="text/javascript">', '</script>'),
  2411. 'runat_server' => array ('<script runat="server">', '</script>'),
  2412. 'asp_include' => array ('<%@', '%>'),
  2413. 'jsp_include' => array ('<%@', '%>'),
  2414. 'eperl' => array ('<:', ':>'),
  2415. 'eruby' => array ('<%', '%>'),
  2416. 'python' => array ('<%', '%>'),
  2417. );
  2418. if (! in_array ($node['attributes']['language'], array_keys ($languages))) {
  2419. $node['attributes']['language'] = 'php';
  2420. }
  2421. $open = $languages[$node['attributes']['language']][0];
  2422. $close = $languages[$node['attributes']['language']][1];
  2423. return $open . $node['value'] . $close;
  2424. }
  2425. // <xt:transform directives="
  2426. // date, func, Date::format (${date})
  2427. // " />
  2428. // directives is a newline-separated list of strings
  2429. /**
  2430. * Transform tag handler.
  2431. *
  2432. * @access private
  2433. * @param associative array $node
  2434. * @return string
  2435. *
  2436. */
  2437. function _transform ($node) {
  2438. foreach (preg_split ("/;[\r\n\t]*/", $node['attributes']['directives'], -1, PREG_SPLIT_NO_EMPTY) as $var) {
  2439. $var = ltrim (rtrim ($var));
  2440. if (! empty ($var)) {
  2441. list ($key, $type, $rule) = preg_split ('/, ?/', $var, 2);
  2442. $rule = preg_replace ('/([a-zA-Z0-9_-]+)\./', '$\1->', $rule);
  2443. $this->transformations[$key] = new TemplateTransformation ($type, $key, $rule);
  2444. }
  2445. }
  2446. return '';
  2447. }
  2448. // <xt:sql query="select * from people">
  2449. // query is an expression with default type string
  2450. /**
  2451. * Open sql tag handler.
  2452. *
  2453. * @access private
  2454. * @param associative array $node
  2455. * @return string
  2456. *
  2457. */
  2458. function _sql ($node) {
  2459. $this->sql[] = array (
  2460. 'query' => $this->exp->evaluate ($node['attributes']['query'], $node, 'string'),
  2461. 'totalquery' => $this->exp->evaluate ($node['attributes']['totalquery'], $node, 'string'),
  2462. 'bind' => array (),
  2463. 'sub' => '',
  2464. 'else' => '',
  2465. 'pager' => false,
  2466. 'limit' => 20,
  2467. 'offsetvar' => 'offset',
  2468. 'node' => $node,
  2469. );
  2470. if ($node['attributes']['pager'] == 'yes') {
  2471. $this->sql[count ($this->sql) - 1]['pager'] = true;
  2472. $this->sql[count ($this->sql) - 1]['limit'] = $node['attributes']['limit'];
  2473. //$this->sql[count ($this->sql) - 1]['offsetvar'] = $node['attributes']['offsetvar'];
  2474. }
  2475. return '';
  2476. }
  2477. // </xt:sql>
  2478. /**
  2479. * Close sql tag handler.
  2480. *
  2481. * @access private
  2482. * @param associative array $node
  2483. * @return string
  2484. *
  2485. */
  2486. function _sql_end ($node) {
  2487. global $db, $cgi;
  2488. $sql = array_shift ($this->sql);
  2489. $out = '';
  2490. if ($sql['pager']) {
  2491. if (empty ($sql['totalquery'])) {
  2492. $sql['totalquery'] = preg_replace ('/^select .+ from /i', 'select count(*) as total from ', $sql['query']);
  2493. }
  2494. $sql['totalquery'] = $this->exp->evaluate ($sql['totalquery'], $sql['node'], 'string', true);
  2495. $tot = $db->fetch ($sql['totalquery'], $sql['bind']);
  2496. if (! $tot) {
  2497. $total = 0;
  2498. } elseif (is_array ($tot)) {
  2499. $total = $tot[0]->total;
  2500. } else {
  2501. $total = $tot->total;
  2502. }
  2503. $limit = $sql['limit'];
  2504. if (! is_numeric ($limit)) {
  2505. $limit = 20;
  2506. }
  2507. $offset = $cgi->offset;
  2508. if (! is_numeric ($offset)) {
  2509. $offset = 0;
  2510. }
  2511. $sql['query'] .= ' limit ' . $offset . ', ' . $limit;
  2512. global $loader, $_SERVER;
  2513. $loader->import ('saf.GUI.Pager');
  2514. // Start: SEMIAS #177 Pagination.
  2515. // Not sure a fix is needed here.
  2516. $pager = new Pager ($offset, $limit, $total);
  2517. $pager->setUrl ($_SERVER['SCRIPT_NAME'] . $cgi->makeQuery (array ($sql['offsetvar'], 'page', 'mode')));
  2518. $pager->getInfo ();
  2519. template_simple_register ('pager', $pager);
  2520. // END: SEMIAS
  2521. $out .= template_simple ('<p>{spt PAGER_TEMPLATE_PREV_PAGE_LIST_NEXT}</p>', array ());
  2522. }
  2523. $sql['query'] = $this->exp->evaluate ($sql['query'], $sql['node'], 'string', true);
  2524. $res = $db->fetch (
  2525. $sql['query'],
  2526. $sql['bind']
  2527. );
  2528. if (! $res) {
  2529. //$this->error = $db->error;
  2530. $this->exp->setObject ($db, 'result');
  2531. return $this->fill ($this->wrap ($sql['else']), $sql['node'], true);
  2532. $this->exp->setObject (false, 'result');
  2533. } elseif (is_object ($res)) {
  2534. $res = array ($res);
  2535. }
  2536. foreach ($res as $row) {
  2537. $this->exp->setObject ($row, 'result');
  2538. $out .= $this->fill ($this->wrap ($sql['sub']), $sql['node'], true);
  2539. }
  2540. $this->exp->unsetObject ('result');
  2541. $this->rows = $db->rows;
  2542. return $out;
  2543. }
  2544. // <xt:bind value="expr" />
  2545. /**
  2546. * Bind tag handler.
  2547. *
  2548. * @access private
  2549. * @param associative array $node
  2550. * @return string
  2551. *
  2552. */
  2553. function _bind ($node) {
  2554. $this->sql[count ($this->sql) - 1]['bind'][] = $this->exp->evaluate ($node['attributes']['value'], $node);
  2555. return '';
  2556. }
  2557. // <xt:sub>
  2558. /**
  2559. * Open sub tag handler.
  2560. *
  2561. * @access private
  2562. * @param associative array $node
  2563. * @return string
  2564. *
  2565. */
  2566. function _sub ($node) {
  2567. if ($node['type'] != 'complete') {
  2568. $this->open = true;
  2569. $this->open_var =& $this->sql[count ($this->sql) - 1]['sub'];
  2570. return $node['value'];
  2571. } else {
  2572. $this->open = true;
  2573. $this->open_var =& $this->sql[count ($this->sql) - 1]['sub'];
  2574. $this->_output ($node['value']);
  2575. $this->open = false;
  2576. unset ($this->open_var);
  2577. return '';
  2578. }
  2579. }
  2580. // </xt:sub>
  2581. /**
  2582. * Close sub tag handler.
  2583. *
  2584. * @access private
  2585. * @param associative array $node
  2586. * @return string
  2587. *
  2588. */
  2589. function _sub_end ($node) {
  2590. $this->open = false;
  2591. unset ($this->open_var);
  2592. return '';
  2593. }
  2594. // <xt:else>
  2595. /**
  2596. * Open else tag handler.
  2597. *
  2598. * @access private
  2599. * @param associative array $node
  2600. * @return string
  2601. *
  2602. */
  2603. function _else ($node) {
  2604. if ($this->open) {
  2605. return $this->_default ($node);
  2606. }
  2607. if (count ($this->sql) > 0) {
  2608. if ($node['type'] != 'complete') {
  2609. $this->open = true;
  2610. $this->open_var =& $this->sql[count ($this->sql) - 1]['else'];
  2611. $this->openUntilLevel = $node['level'];
  2612. return $node['value'];
  2613. } else {
  2614. //$this->open = true;
  2615. //$this->open_var =& $this->sql[count ($this->sql) - 1]['else'];
  2616. $this->open = false;
  2617. unset ($this->open_var);
  2618. return '';
  2619. }
  2620. } elseif (count ($this->if) > 0) {
  2621. if ($node['type'] != 'complete') {
  2622. $this->open = true;
  2623. $this->open_var =& $this->if[count ($this->if) - 1]['else'];
  2624. $this->openUntilLevel = $node['level'];
  2625. return $node['value'];
  2626. } else {
  2627. //$this->open = true;
  2628. //$this->open_var =& $this->if[count ($this->if) - 1]['else'];
  2629. $this->open = false;
  2630. unset ($this->open_var);
  2631. return '';
  2632. }
  2633. }
  2634. }
  2635. // </xt:else>
  2636. /**
  2637. * Close else tag handler.
  2638. *
  2639. * @access private
  2640. * @param associative array $node
  2641. * @return string
  2642. *
  2643. */
  2644. function _else_end ($node) {
  2645. if ($this->open && $this->openUntilLevel !== false && $node['level'] == $this->openUntilLevel) {
  2646. $this->open = false;
  2647. unset ($this->open_var);
  2648. return '';
  2649. } else {
  2650. return $this->_default_end ($node);
  2651. }
  2652. }
  2653. // <xt:condition>
  2654. /**
  2655. * Open condition tag handler.
  2656. *
  2657. * @access private
  2658. * @param associative array $node
  2659. * @return string
  2660. *
  2661. */
  2662. function _condition ($node) {
  2663. if ($this->open) {
  2664. return $this->_default ($node);
  2665. }
  2666. $this->if[] = array (
  2667. 'condition' => array (),
  2668. 'else' => false,
  2669. );
  2670. return '';
  2671. }
  2672. // </xt:condition>
  2673. // must implement negation via a separate call to $this->exp->splitAssignment()
  2674. /**
  2675. * Close condition tag handler.
  2676. *
  2677. * @access private
  2678. * @param associative array $node
  2679. * @return string
  2680. *
  2681. */
  2682. function _condition_end ($node) {
  2683. if ($this->open) {
  2684. return $this->_default_end ($node);
  2685. }
  2686. $if = array_shift ($this->if);
  2687. //echo '<pre>';
  2688. //print_r ($if);
  2689. //echo '</pre>';
  2690. foreach ($if['condition'] as $key => $condition) {
  2691. $expr = $condition[0];
  2692. $output = $condition[1];
  2693. list ($one, $two) = $this->exp->splitAssignment ($expr);
  2694. if ($one === 'not') {
  2695. $negate = true;
  2696. $expr = $two;
  2697. } else {
  2698. $negate = false;
  2699. }
  2700. //echo '<pre>evaluating expression "' . "\t" . $expr . "\t" . '"</pre>';
  2701. if ($negate && ! $this->exp->evaluate ($expr, $node, 'path', true)) {
  2702. return $this->fill ($this->wrap ($output), $this->exp->register['object'], true);
  2703. } elseif (! $negate && $this->exp->evaluate ($expr, $node, 'path', true)) {
  2704. return $this->fill ($this->wrap ($output), $this->exp->register['object'], true);
  2705. }
  2706. }
  2707. if (! empty ($if['else'])) {
  2708. return $this->fill ($this->wrap ($if['else']), $this->exp->register['object'], true);
  2709. }
  2710. return '';
  2711. }
  2712. // <xt:if expr="foo/bar">
  2713. // expr is an expression
  2714. /**
  2715. * Open if tag handler.
  2716. *
  2717. * @access private
  2718. * @param associative array $node
  2719. * @return string
  2720. *
  2721. */
  2722. function _if ($node) {
  2723. if ($this->open) {
  2724. return $this->_default ($node);
  2725. }
  2726. $this->open = true;
  2727. $this->if[count ($this->if) - 1]['condition'][] = array ($node['attributes']['expr'], $node['value']);
  2728. $this->open_var =& $this->if[count ($this->if) - 1]['condition'][count ($this->if[count ($this->if) - 1]['condition']) - 1][1];
  2729. $this->openUntilLevel = $node['level'];
  2730. return '';
  2731. }
  2732. // </xt:if>
  2733. /**
  2734. * Close if tag handler.
  2735. *
  2736. * @access private
  2737. * @param associative array $node
  2738. * @return string
  2739. *
  2740. */
  2741. function _if_end ($node) {
  2742. if ($this->open && $this->openUntilLevel !== false && $node['level'] == $this->openUntilLevel) {
  2743. $this->open = false;
  2744. unset ($this->open_var);
  2745. $this->openUntilLevel = false;
  2746. return '';
  2747. } else {
  2748. return $this->_default_end ($node);
  2749. }
  2750. }
  2751. // <xt:elseif expr="foo/bar">
  2752. // expr is an expression
  2753. /**
  2754. * Open elseif tag handler.
  2755. *
  2756. * @access private
  2757. * @param associative array $node
  2758. * @return string
  2759. *
  2760. */
  2761. function _elseif ($node) {
  2762. if ($this->open) {
  2763. return $this->_default ($node);
  2764. }
  2765. $this->open = true;
  2766. $this->if[count ($this->if) - 1]['condition'][] = array ($node['attributes']['expr'], $node['value']);
  2767. $this->open_var =& $this->if[count ($this->if) - 1]['condition'][count ($this->if[count ($this->if) - 1]['condition']) - 1][1];
  2768. $this->openUntilLevel = $node['level'];
  2769. return '';
  2770. }
  2771. // </xt:if>
  2772. /**
  2773. * Close elseif tag handler.
  2774. *
  2775. * @access private
  2776. * @param associative array $node
  2777. * @return string
  2778. *
  2779. */
  2780. function _elseif_end ($node) {
  2781. if ($this->open && $this->openUntilLevel !== false && $node['level'] == $this->openUntilLevel) {
  2782. $this->open = false;
  2783. unset ($this->open_var);
  2784. $this->openUntilLevel = false;
  2785. return '';
  2786. } else {
  2787. return $this->_default_end ($node);
  2788. }
  2789. }
  2790. /**
  2791. * Open elsif tag handler. Alias of _elseif.
  2792. *
  2793. * @access private
  2794. * @param associative array $node
  2795. * @return string
  2796. *
  2797. */
  2798. function _elsif ($node) {
  2799. return $this->_elseif ($node);
  2800. }
  2801. /**
  2802. * Close elsif tag handler. Alias of _elseif_end.
  2803. *
  2804. * @access private
  2805. * @param associative array $node
  2806. * @return string
  2807. *
  2808. */
  2809. function _elsif_end ($node) {
  2810. return $this->_elseif_end ($node);
  2811. }
  2812. // <xt:loop through="item foo/list">
  2813. // through is an expression
  2814. /**
  2815. * Open loop tag handler.
  2816. *
  2817. * @access private
  2818. * @param associative array $node
  2819. * @return string
  2820. *
  2821. */
  2822. function _loop ($node) {
  2823. if ($this->open) {
  2824. return $this->_default ($node);
  2825. }
  2826. list ($varname, $expr) = $this->exp->splitAssignment ($node['attributes']['through']);
  2827. $this->loop[] = array (
  2828. 'varname' => $varname,
  2829. 'expr' => $expr,
  2830. 'struct' => $node['value'],
  2831. );
  2832. $this->open = true;
  2833. $this->open_var =& $this->loop[count ($this->loop) - 1]['struct'];
  2834. $this->openUntilLevel = $node['level'];
  2835. return '';
  2836. }
  2837. // </xt:loop>
  2838. /**
  2839. * Close loop tag handler.
  2840. *
  2841. * @access private
  2842. * @param associative array $node
  2843. * @return string
  2844. *
  2845. */
  2846. function _loop_end ($node) {
  2847. if ($this->open && $this->openUntilLevel !== false && $node['level'] == $this->openUntilLevel) {
  2848. $this->open = false;
  2849. unset ($this->open_var);
  2850. $this->openUntilLevel = false;
  2851. $loop = array_pop ($this->loop);
  2852. $res = '';
  2853. /*
  2854. ob_start ();
  2855. echo '<pre>';
  2856. print_r ($loop);
  2857. print_r ($this->exp->register['loop']);
  2858. echo '</pre>';
  2859. $res .= htmlentities (ob_get_contents ());
  2860. ob_end_clean ();
  2861. // echo '<pre>' . $res . '</pre>';
  2862. $res = '<pre>' . $res . '</pre>';
  2863. // $res = '';
  2864. */
  2865. $list =& $this->exp->repeat ($loop['expr'], $node);
  2866. $total = count ($list);
  2867. foreach ($list as $key => $item) {
  2868. $this->exp->setCurrent ($item, $loop['varname'], $key, $total);
  2869. $res .= $this->fill ($this->wrap ($loop['struct']), $this->exp->getObject ('object'), true);
  2870. }
  2871. //$this->exp->setCurrent (new StdClass, $loop['varname'], 0, 0);
  2872. return $res;
  2873. } else {
  2874. return $this->_default_end ($node);
  2875. }
  2876. }
  2877. // <xt:block name="foo">
  2878. // name is regular string
  2879. /**
  2880. * Open block tag handler.
  2881. *
  2882. * @access private
  2883. * @param associative array $node
  2884. * @return string
  2885. *
  2886. */
  2887. function _block ($node) {
  2888. if (! isset ($this->block[$node['attributes']['name']])) {
  2889. $this->block[$node['attributes']['name']] = '';
  2890. }
  2891. $this->open = true;
  2892. $this->open_var =& $this->block[$node['attributes']['name']];
  2893. $this->_output ($node['value']);
  2894. if ($node['type'] == 'complete') {
  2895. return $this->_block_end ($node);
  2896. }
  2897. return '';
  2898. }
  2899. // </xt:block>
  2900. /**
  2901. * Close block tag handler.
  2902. *
  2903. * @access private
  2904. * @param associative array $node
  2905. * @return string
  2906. *
  2907. */
  2908. function _block_end ($node) {
  2909. $this->open = false;
  2910. unset ($this->open_var);
  2911. return '';
  2912. }
  2913. // <xt:show block="blockname" paramOne="foo" secondParam="bar" />
  2914. // block is regular string, additional params pertain to block
  2915. // vars, ie. block/firstname, block/lastname
  2916. /**
  2917. * Open show tag handler.
  2918. *
  2919. * @access private
  2920. * @param associative array $node
  2921. * @return string
  2922. *
  2923. */
  2924. function _show ($node) {
  2925. if ($node['type'] != 'complete') {
  2926. $this->ignoreUntilLevel ($node['level']);
  2927. }
  2928. $this->exp->register['block'] = $node['attributes'];
  2929. return $this->fill ($this->wrap ($this->block[$node['attributes']['block']]), $this->exp->getObject (), true);
  2930. }
  2931. /**
  2932. * Close show tag handler.
  2933. *
  2934. * @access private
  2935. * @param associative array $node
  2936. * @return string
  2937. *
  2938. */
  2939. function _show_end ($node) {
  2940. $this->ignoreUntilLevel (-1);
  2941. return '';
  2942. }
  2943. /**
  2944. * Open cache tag handler.
  2945. *
  2946. * @access private
  2947. * @param associative array $node
  2948. * @return string
  2949. *
  2950. */
  2951. function _cache ($node) {
  2952. $user = '';
  2953. if ($node['attributes']['scope'] == 'session') {
  2954. if (session_valid ()) {
  2955. $user = session_username ();
  2956. $cacheable = true;
  2957. } else {
  2958. $cacheable = false;
  2959. }
  2960. } else {
  2961. $cacheable = true;
  2962. }
  2963. $this->cache = false;
  2964. if (! $cacheable) {
  2965. return '';
  2966. }
  2967. if (! isset ($node['attributes']['scope'])) {
  2968. $node['attributes']['scope'] = 'application';
  2969. }
  2970. if (isset ($node['attributes']['duration'])) {
  2971. $duration = (int) $node['attributes']['duration'];
  2972. } else {
  2973. $duration = $this->cacheDuration;
  2974. }
  2975. if (! isset ($node['attributes']['id'])) {
  2976. $this->cacheCount++;
  2977. $node['attributes']['id'] = $this->cacheCount;
  2978. }
  2979. loader_import ('saf.Cache');
  2980. $this->_cache = new Cache ($this->cacheLocation . $node['attributes']['scope']);
  2981. if ($this->_cache->expired ($this->file . ':' . $node['attributes']['id'] . ':' . $user, $duration)) {
  2982. // re-cache
  2983. $this->cache = $this->file . ':' . $node['attributes']['id'] . ':' . $user;
  2984. $this->output2 = $this->output;
  2985. $this->output = '';
  2986. return '';
  2987. } else {
  2988. // show from cache
  2989. $out = $this->_cache->show ($this->file . ':' . $node['attributes']['id'] . ':' . $user);
  2990. $this->ignoreUntilLevel ($node['level']);
  2991. }
  2992. return $out;
  2993. }
  2994. /**
  2995. * Close cache tag handler.
  2996. *
  2997. * @access private
  2998. * @param associative array $node
  2999. * @return string
  3000. *
  3001. */
  3002. function _cache_end ($node) {
  3003. if ($this->ignoreUntilLevel () == $node['level']) {
  3004. $this->ignoreUntilLevel (-1);
  3005. }
  3006. if (! $this->cache) {
  3007. return '';
  3008. }
  3009. $this->_cache->file ($this->cache, $this->output);
  3010. $out = $this->output;
  3011. $this->cache = false;
  3012. $this->_cache = false;
  3013. $this->output = $this->output2;
  3014. $this->output2 = '';
  3015. return $out;
  3016. }
  3017. /*
  3018. function _datagrid ($node) {
  3019. $this->grid[] = new StdClass;
  3020. foreach ($node['attributes'] as $attr => $value) {
  3021. $this->grid[count ($this->grid) - 1]->{$attr} = $value;
  3022. }
  3023. $this->grid[count ($this->grid) - 1]->columns = array ();
  3024. return '';
  3025. }
  3026. function _column ($node) {
  3027. $col = new StdClass;
  3028. foreach ($node['attributes'] as $attr => $value) {
  3029. $col->{$attr} = $value;
  3030. }
  3031. $col->rules = array ();
  3032. $this->grid[count ($this->grid) - 1]->columns[] = $col;
  3033. return '';
  3034. }
  3035. function _rule ($node) {
  3036. return '';
  3037. }
  3038. function _datagrid_end ($node) {
  3039. return '';
  3040. }
  3041. */
  3042. }
  3043. function template_xt ($tpl, $obj = '', $carry = false) {
  3044. return $GLOBALS['tpl']->fill ($tpl, $obj, $carry);
  3045. }
  3046. function template_messy ($tpl, $obj = '', $carry = false) {
  3047. return $GLOBALS['tpl']->messy ($tpl, $obj, $carry);
  3048. }
  3049. function template_validate ($data) {
  3050. return $GLOBALS['tpl']->validate ($data);
  3051. }
  3052. function template_wrap ($data) {
  3053. return $GLOBALS['tpl']->wrap ($data);
  3054. }
  3055. function template_error () {
  3056. return $GLOBALS['tpl']->error;
  3057. }
  3058. function template_err_line () {
  3059. return $GLOBALS['tpl']->err_line;
  3060. }
  3061. function template_err_colnum () {
  3062. return $GLOBALS['tpl']->err_colnum;
  3063. }
  3064. function template_convert_entities ($data) {
  3065. return $GLOBALS['tpl']->convertEntities ($data);
  3066. }
  3067. function template_toc ($title = '') {
  3068. return $GLOBALS['tpl']->makeToc ($title);
  3069. }
  3070. function template_bind ($path, $data) {
  3071. return $GLOBALS['tpl']->bind ($path, $data);
  3072. }
  3073. function template_bind_attr ($path, $attr, $value) {
  3074. return $GLOBALS['tpl']->bindAttr ($path, $attr, $value);
  3075. }
  3076. function template_parse_body ($body) {
  3077. // boxes and forms
  3078. preg_match_all ('/<xt:(box|form)([^>]+)>.*<\/xt:(box|form)>/m', $body, $matches, PREG_SET_ORDER);
  3079. foreach ($matches as $match) {
  3080. preg_match_all ('/([a-zA-Z0-9_]+)="([^"]*)"/m', $match[2], $attrs, PREG_SET_ORDER);
  3081. $box = array ();
  3082. foreach ($attrs as $attr) {
  3083. $box[$attr[1]] = $attr[2];
  3084. }
  3085. if ($match[1] == 'box') {
  3086. $body = str_replace ($match[0], loader_box ($box['name'], $box), $body);
  3087. } elseif ($match[1] == 'form') {
  3088. $body = str_replace ($match[0], loader_form ($box['name'], $box), $body);
  3089. }
  3090. }
  3091. // toc
  3092. preg_match_all ('/<(h[1-6]).*>(.*)<\/h[1-6]>/m', $body, $matches, PREG_SET_ORDER);
  3093. $toc = array ();
  3094. foreach ($matches as $match) {
  3095. $href = md5 ($match[1] /*. count ($toc)*/ . $match[2]);
  3096. $toc[] = array (
  3097. 'tag' => $match[1],
  3098. 'href' => $href,
  3099. 'value' => $match[2],
  3100. );
  3101. $body = str_replace ($match[0], '<a name="' . $href . '" style="margin: 0px; padding: 0px; display: inline"></a>' . $match[0], $body);
  3102. }
  3103. if (count ($toc) == 0) {
  3104. $GLOBALS['page']->toc = '';
  3105. } else {
  3106. $prev = '';
  3107. $out = '<ul>' . NEWLINE;
  3108. $c = 0;
  3109. foreach ($toc as $node) {
  3110. if ($prev == $node['tag'] || empty ($prev)) {
  3111. // same level
  3112. $out .= TAB . '<li><a href="#' . $node['href'] . '">' . $node['value'] . '</a></li>' . NEWLINE;
  3113. } elseif ($prev < $node['tag']) {
  3114. // this tag under
  3115. $c++;
  3116. $out .= '<ul>' . NEWLINE;
  3117. $out .= TAB . '<li><a href="#' . $node['href'] . '">' . $node['value'] . '</a></li>' . NEWLINE;
  3118. } elseif ($prev > $node['tag']) {
  3119. // close list and move down one
  3120. $c--;
  3121. $out .= '</ul>' . NEWLINE;
  3122. $out .= TAB . '<li><a href="#' . $node['href'] . '">' . $node['value'] . '</a></li>' . NEWLINE;
  3123. }
  3124. $prev = $node['tag'];
  3125. }
  3126. while ($c > 0) {
  3127. $out .= '</ul>' . NEWLINE;
  3128. $c--;
  3129. }
  3130. $GLOBALS['page']->toc = $out . '</ul>' . NEWLINEx2;
  3131. }
  3132. return $body;
  3133. }
  3134. ?>