Template.class.php 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557
  1. <?php
  2. /**
  3. * Template.class.php
  4. *
  5. * This file contains an abstract (PHP 4, so "abstract" is relative)
  6. * class meant to define the basic template interface for the
  7. * SquirrelMail core application. Subclasses should extend this
  8. * class with any custom functionality needed to interface a target
  9. * templating engine with SquirrelMail.
  10. *
  11. * @copyright &copy; 2003-2007 The SquirrelMail Project Team
  12. * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  13. * @version $Id$
  14. * @package squirrelmail
  15. * @subpackage Template
  16. * @since 1.5.2
  17. *
  18. */
  19. /** load template functions */
  20. require(SM_PATH . 'functions/template/general_util.php');
  21. /**
  22. * The SquirrelMail Template class.
  23. *
  24. * Basic template class for capturing values and pluging them into a template.
  25. * This class uses a similar API to Smarty.
  26. *
  27. * Methods that must be implemented by subclasses are as follows (see method
  28. * stubs below for further information about expected behavior):
  29. *
  30. * assign()
  31. * assign_by_ref()
  32. * clear_all_assign()
  33. * get_template_vars()
  34. * append()
  35. * append_by_ref()
  36. * apply_template()
  37. *
  38. * @author Paul Lesniewski <paul at squirrelmail.org>
  39. * @package squirrelmail
  40. *
  41. */
  42. class Template
  43. {
  44. /**
  45. * The template ID
  46. *
  47. * @var string
  48. *
  49. */
  50. var $template_set_id = '';
  51. /**
  52. * The template set base directory (relative path from
  53. * the main SquirrelMail directory (SM_PATH))
  54. *
  55. * @var string
  56. *
  57. */
  58. var $template_dir = '';
  59. /**
  60. * The template engine (please use constants defined in constants.php)
  61. *
  62. * @var string
  63. *
  64. */
  65. var $template_engine = '';
  66. /**
  67. * The fall-back template ID
  68. *
  69. * @var string
  70. *
  71. */
  72. var $fallback_template_set_id = '';
  73. /**
  74. * The fall-back template directory (relative
  75. * path from the main SquirrelMail directory (SM_PATH))
  76. *
  77. * @var string
  78. *
  79. */
  80. var $fallback_template_dir = '';
  81. /**
  82. * The fall-back template engine (please use
  83. * constants defined in constants.php)
  84. *
  85. * @var string
  86. *
  87. */
  88. var $fallback_template_engine = '';
  89. /**
  90. * Template file cache. Structured as an array, whose keys
  91. * are all the template file names (with path information relative
  92. * to the template set's base directory, e.g., "css/style.css")
  93. * found in all parent template sets including the ultimate fall-back
  94. * template set. Array values are sub-arrays with the
  95. * following key-value pairs:
  96. *
  97. * PATH -- file path, relative to SM_PATH
  98. * SET_ID -- the ID of the template set that this file belongs to
  99. * ENGINE -- the engine needed to render this template file
  100. *
  101. */
  102. var $template_file_cache = array();
  103. /**
  104. * Extra template engine class objects for rendering templates
  105. * that require a different engine than the one for the current
  106. * template set. Keys should be the name of the template engine,
  107. * values are the corresponding class objects.
  108. *
  109. * @var array
  110. *
  111. */
  112. var $other_template_engine_objects = array();
  113. /**
  114. * Constructor
  115. *
  116. * Please do not call directly. Use Template::construct_template().
  117. *
  118. * @param string $template_set_id the template ID
  119. *
  120. */
  121. function Template($template_set_id) {
  122. //FIXME: find a way to test that this is ONLY ever called
  123. // from the construct_template() method (I doubt it
  124. // is worth the trouble to parse the current stack trace)
  125. // if (???)
  126. // trigger_error('Please do not use default Template() constructor. Instead, use Template::construct_template().', E_USER_ERROR);
  127. $this->set_up_template($template_set_id);
  128. }
  129. /**
  130. * Construct Template
  131. *
  132. * This method should always be called instead of trying
  133. * to get a Template object from the normal/default constructor,
  134. * and is necessary in order to control the return value.
  135. *
  136. * @param string $template_set_id the template ID
  137. *
  138. * @return object The correct Template object for the given template set
  139. *
  140. * @static
  141. *
  142. */
  143. function construct_template($template_set_id) {
  144. $template = new Template($template_set_id);
  145. $template->override_plugins();
  146. return $template->get_template_engine_subclass();
  147. }
  148. /**
  149. * Set up internal attributes
  150. *
  151. * This method does most of the work for setting up
  152. * newly constructed objects.
  153. *
  154. * @param string $template_set_id the template ID
  155. *
  156. */
  157. function set_up_template($template_set_id) {
  158. // FIXME: do we want to place any restrictions on the ID like
  159. // making sure no slashes included?
  160. // get template ID
  161. //
  162. $this->template_set_id = $template_set_id;
  163. $this->fallback_template_set_id = Template::get_fallback_template_set();
  164. // set up template directories
  165. //
  166. $this->template_dir
  167. = Template::calculate_template_file_directory($this->template_set_id);
  168. $this->fallback_template_dir
  169. = Template::calculate_template_file_directory($this->fallback_template_set_id);
  170. // determine template engine
  171. // FIXME: assuming PHP template engine may not necessarily be a good thing
  172. //
  173. $this->template_engine = Template::get_template_config($this->template_set_id,
  174. 'template_engine',
  175. SQ_PHP_TEMPLATE);
  176. // get template file cache
  177. //
  178. $this->template_file_cache = Template::cache_template_file_hierarchy();
  179. }
  180. /**
  181. * Determine what the ultimate fallback template set is.
  182. *
  183. * NOTE that if the fallback setting cannot be found in the
  184. * main SquirrelMail configuration settings that the value
  185. * of $default is returned.
  186. *
  187. * @param string $default The template set ID to use if
  188. * the fallback setting cannot be
  189. * found in SM config (optional;
  190. * defaults to "default").
  191. *
  192. * @return string The ID of the fallback template set.
  193. *
  194. * @static
  195. *
  196. */
  197. function get_fallback_template_set($default='default') {
  198. // FIXME: do we want to place any restrictions on the ID such as
  199. // making sure no slashes included?
  200. // values are in main SM config file
  201. //
  202. global $templateset_fallback, $aTemplateSet;
  203. $aTemplateSet = (!isset($aTemplateSet) || !is_array($aTemplateSet)
  204. ? array() : $aTemplateSet);
  205. $templateset_fallback = (!isset($templateset_fallback)
  206. ? $default : $templateset_fallback);
  207. // iterate through all template sets, is this a valid skin ID?
  208. //
  209. $found_it = FALSE;
  210. foreach ($aTemplateSet as $aTemplate) {
  211. if ($aTemplate['ID'] === $templateset_fallback) {
  212. $found_it = TRUE;
  213. break;
  214. }
  215. }
  216. if ($found_it)
  217. return $templateset_fallback;
  218. // FIXME: note that it is possible for $default to
  219. // point to an invalid (nonexistent) template set
  220. // and that error will not be caught here
  221. //
  222. return $default;
  223. }
  224. /**
  225. * Determine what the default template set is.
  226. *
  227. * NOTE that if the default setting cannot be found in the
  228. * main SquirrelMail configuration settings that the value
  229. * of $default is returned.
  230. *
  231. * @param string $default The template set ID to use if
  232. * the default setting cannot be
  233. * found in SM config (optional;
  234. * defaults to "default").
  235. *
  236. * @return string The ID of the default template set.
  237. *
  238. * @static
  239. *
  240. */
  241. function get_default_template_set($default='default') {
  242. // FIXME: do we want to place any restrictions on the ID such as
  243. // making sure no slashes included?
  244. // values are in main SM config file
  245. //
  246. global $templateset_default, $aTemplateSet;
  247. $aTemplateSet = (!isset($aTemplateSet) || !is_array($aTemplateSet)
  248. ? array() : $aTemplateSet);
  249. $templateset_default = (!isset($templateset_default)
  250. ? $default : $templateset_default);
  251. // iterate through all template sets, is this a valid skin ID?
  252. //
  253. $found_it = FALSE;
  254. foreach ($aTemplateSet as $aTemplate) {
  255. if ($aTemplate['ID'] === $templateset_default) {
  256. $found_it = TRUE;
  257. break;
  258. }
  259. }
  260. if ($found_it)
  261. return $templateset_default;
  262. // FIXME: note that it is possible for $default to
  263. // point to an invalid (nonexistent) template set
  264. // and that error will not be caught here
  265. //
  266. return $default;
  267. }
  268. /**
  269. * Allow template set to override plugin configuration by either
  270. * adding or removing plugins.
  271. *
  272. * NOTE: due to when this code executes, plugins activated here
  273. * do not have access to the config_override and loading_prefs
  274. * hooks; instead, such plugins can use the
  275. * "template_plugins_override_after" hook defined below.
  276. *
  277. */
  278. function override_plugins() {
  279. global $disable_plugins, $plugins, $squirrelmail_plugin_hooks, $null;
  280. if ($disable_plugins) return;
  281. $add_plugins = Template::get_template_config($this->template_set_id,
  282. 'add_plugins', array());
  283. $remove_plugins = Template::get_template_config($this->template_set_id,
  284. 'remove_plugins', array());
  285. //FIXME (?) we assume $add_plugins and $remove_plugins are arrays -- we could
  286. // error check here, or just assume that template authors or admins
  287. // won't screw up their config files
  288. // disable all plugins? (can still add some by using $add_plugins)
  289. //
  290. if (in_array('*', $remove_plugins)) {
  291. $plugins = array();
  292. $squirrelmail_plugin_hooks = array();
  293. $remove_plugins = array();
  294. }
  295. foreach ($add_plugins as $plugin_name) {
  296. // add plugin to global plugin array
  297. //
  298. $plugins[] = $plugin_name;
  299. // enable plugin -- emulate code from use_plugin() function
  300. // in SquirrelMail core, but also need to call the
  301. // "squirrelmail_plugin_init_<plugin_name>" function, which
  302. // in static configuration is not called (this inconsistency
  303. // could be a source of anomalous-seeming bugs in poorly
  304. // coded plugins)
  305. //
  306. if (file_exists(SM_PATH . "plugins/$plugin_name/setup.php")) {
  307. include_once(SM_PATH . "plugins/$plugin_name/setup.php");
  308. $function = "squirrelmail_plugin_init_$plugin_name";
  309. if (function_exists($function))
  310. $function();
  311. }
  312. }
  313. foreach ($remove_plugins as $plugin_name) {
  314. // remove plugin from both global plugin & plugin hook arrays
  315. //
  316. $plugin_key = array_search($plugin_name, $plugins);
  317. if (!is_null($plugin_key) && $plugin_key !== FALSE) {
  318. unset($plugins[$plugin_key]);
  319. if (is_array($squirrelmail_plugin_hooks))
  320. foreach (array_keys($squirrelmail_plugin_hooks) as $hookName) {
  321. unset($squirrelmail_plugin_hooks[$hookName][$plugin_name]);
  322. }
  323. }
  324. }
  325. do_hook('template_plugins_override_after', $null);
  326. }
  327. /**
  328. * Instantiate and return correct subclass for this template
  329. * set's templating engine.
  330. *
  331. * @param string $template_set_id The template set whose engine
  332. * is to be used as an override
  333. * (if not given, this template
  334. * set's engine is used) (optional).
  335. *
  336. * @return object The Template subclass object for the template engine.
  337. *
  338. */
  339. function get_template_engine_subclass($template_set_id='') {
  340. if (empty($template_set_id)) $template_set_id = $this->template_set_id;
  341. // FIXME: assuming PHP template engine may not necessarily be a good thing
  342. $engine = Template::get_template_config($template_set_id,
  343. 'template_engine', SQ_PHP_TEMPLATE);
  344. $engine_class_file = SM_PATH . 'class/template/'
  345. . $engine . 'Template.class.php';
  346. if (!file_exists($engine_class_file)) {
  347. trigger_error('Unknown template engine (' . $engine
  348. . ') was specified in template configuration file',
  349. E_USER_ERROR);
  350. }
  351. $engine_class = $engine . 'Template';
  352. require_once($engine_class_file);
  353. return new $engine_class($template_set_id);
  354. }
  355. /**
  356. * Determine the relative template directory path for
  357. * the given template ID.
  358. *
  359. * @param string $template_set_id The template ID from which to build
  360. * the directory path
  361. *
  362. * @return string The relative template path (based off of SM_PATH)
  363. *
  364. * @static
  365. *
  366. */
  367. function calculate_template_file_directory($template_set_id) {
  368. return 'templates/' . $template_set_id . '/';
  369. }
  370. /**
  371. * Determine the relative images directory path for
  372. * the given template ID.
  373. *
  374. * @param string $template_set_id The template ID from which to build
  375. * the directory path
  376. *
  377. * @return string The relative images path (based off of SM_PATH)
  378. *
  379. * @static
  380. *
  381. */
  382. function calculate_template_images_directory($template_set_id) {
  383. return 'templates/' . $template_set_id . '/images/';
  384. }
  385. /**
  386. * Return the relative template directory path for this template set.
  387. *
  388. * @return string The relative path to the template directory based
  389. * from the main SquirrelMail directory (SM_PATH).
  390. *
  391. */
  392. function get_template_file_directory() {
  393. return $this->template_dir;
  394. }
  395. /**
  396. * Return the template ID for the fallback template set.
  397. *
  398. * @return string The ID of the fallback template set.
  399. *
  400. */
  401. function get_fallback_template_set_id() {
  402. return $this->fallback_template_set_id;
  403. }
  404. /**
  405. * Return the relative template directory path for the
  406. * fallback template set.
  407. *
  408. * @return string The relative path to the fallback template
  409. * directory based from the main SquirrelMail
  410. * directory (SM_PATH).
  411. *
  412. */
  413. function get_fallback_template_file_directory() {
  414. return $this->fallback_template_dir;
  415. }
  416. /**
  417. * Get template set config setting
  418. *
  419. * Given a template set ID and setting name, returns the
  420. * setting's value. Note that settings are cached in
  421. * session, so "live" changes to template configuration
  422. * won't be reflected until the user logs out and back
  423. * in again.
  424. *
  425. * @param string $template_set_id The template set for which
  426. * to look up the setting.
  427. * @param string $setting The name of the setting to
  428. * retrieve.
  429. * @param mixed $default When the requested setting
  430. * is not found, the contents
  431. * of this value are returned
  432. * instead (optional; default
  433. * is NULL).
  434. * NOTE that unlike sqGetGlobalVar(),
  435. * this function will also return
  436. * the default value if the
  437. * requested setting is found
  438. * but is empty.
  439. * @param boolean $live_config When TRUE, the target template
  440. * set's configuration file is
  441. * reloaded every time this
  442. * method is called. Default
  443. * behavior is to only load the
  444. * configuration file if it had
  445. * never been loaded before, but
  446. * not again after that (optional;
  447. * default FALSE). Use with care!
  448. * Should mostly be used for
  449. * debugging.
  450. *
  451. * @return mixed The desired setting's value or if not found,
  452. * the contents of $default are returned.
  453. *
  454. * @static
  455. *
  456. */
  457. function get_template_config($template_set_id, $setting,
  458. $default=NULL, $live_config=FALSE) {
  459. sqGetGlobalVar('template_configuration_settings',
  460. $template_configuration_settings,
  461. SQ_SESSION,
  462. array());
  463. if ($live_config) unset($template_configuration_settings[$template_set_id]);
  464. // NOTE: could use isset() instead of empty() below, but
  465. // this function is designed to replace empty values
  466. // as well as non-existing values with $default
  467. //
  468. if (!empty($template_configuration_settings[$template_set_id][$setting]))
  469. return $template_configuration_settings[$template_set_id][$setting];
  470. // if template set configuration has been loaded, but this
  471. // setting is not known, return $default
  472. //
  473. if (!empty($template_configuration_settings[$template_set_id]))
  474. return $default;
  475. // otherwise (template set configuration has not been loaded before),
  476. // load it into session and return the desired setting after that
  477. //
  478. $template_config_file = SM_PATH
  479. . Template::calculate_template_file_directory($template_set_id)
  480. . 'config.php';
  481. if (!file_exists($template_config_file)) {
  482. trigger_error('No template configuration file was found where expected: ("'
  483. . $template_config_file . '")', E_USER_ERROR);
  484. } else {
  485. // we require() the file to let PHP do the variable value
  486. // parsing for us, and read the file in manually so we can
  487. // know what variable names are used in the config file
  488. // (settings can be different depending on specific requirements
  489. // of different template engines)... the other way this may
  490. // be accomplished is to somehow diff the symbol table
  491. // before/after the require(), but anyway, this code should
  492. // only run once for this template set...
  493. //
  494. require($template_config_file);
  495. $file_contents = implode("\n", file($template_config_file));
  496. // note that this assumes no template settings have
  497. // a string in them that looks like a variable name like $x
  498. // also note that this will attempt to grab things like
  499. // $Id found in CVS headers, so we try to adjust for that
  500. // by checking that the variable is actually set
  501. //
  502. preg_match_all('/\$(\w+)/', $file_contents, $variables, PREG_PATTERN_ORDER);
  503. foreach ($variables[1] as $variable) {
  504. if (isset($$variable))
  505. $template_configuration_settings[$template_set_id][$variable]
  506. = $$variable;
  507. }
  508. sqsession_register($template_configuration_settings,
  509. 'template_configuration_settings');
  510. // NOTE: could use isset() instead of empty() below, but
  511. // this function is designed to replace empty values
  512. // as well as non-existing values with $default
  513. //
  514. if (!empty($template_configuration_settings[$template_set_id][$setting]))
  515. return $template_configuration_settings[$template_set_id][$setting];
  516. else
  517. return $default;
  518. }
  519. }
  520. /**
  521. * Obtain template file hierarchy from cache.
  522. *
  523. * If the file hierarchy does not exist in session, it is
  524. * constructed and stored in session before being returned
  525. * to the caller.
  526. *
  527. * @param boolean $regenerate_cache When TRUE, the file hierarchy
  528. * is reloaded and stored fresh
  529. * (optional; default FALSE).
  530. * @param array $additional_files Must be in same form as the
  531. * files in the file hierarchy
  532. * cache. These are then added
  533. * to the cache (optional; default
  534. * empty - no additional files).
  535. *
  536. * @return array Template file hierarchy array, whose keys
  537. * are all the template file names (with path
  538. * information relative to the template set's
  539. * base directory, e.g., "css/style.css")
  540. * found in all parent template sets including
  541. * the ultimate fall-back template set.
  542. * Array values are sub-arrays with the
  543. * following key-value pairs:
  544. *
  545. * PATH -- file path, relative to SM_PATH
  546. * SET_ID -- the ID of the template set that this file belongs to
  547. * ENGINE -- the engine needed to render this template file
  548. *
  549. * @static
  550. *
  551. */
  552. function cache_template_file_hierarchy($regenerate_cache=FALSE,
  553. $additional_files=array()) {
  554. sqGetGlobalVar('template_file_hierarchy', $template_file_hierarchy,
  555. SQ_SESSION, array());
  556. if ($regenerate_cache) unset($template_file_hierarchy);
  557. if (!empty($template_file_hierarchy)) {
  558. // have to add additional files if given before returning
  559. //
  560. if (!empty($additional_files)) {
  561. $template_file_hierarchy = array_merge($template_file_hierarchy,
  562. $additional_files);
  563. sqsession_register($template_file_hierarchy,
  564. 'template_file_hierarchy');
  565. }
  566. return $template_file_hierarchy;
  567. }
  568. // nothing in cache apparently, so go build it now
  569. //
  570. // FIXME: not sure if there is any possibility that
  571. // this could be called when $sTemplateID has
  572. // yet to be defined... throw error for now,
  573. // but if the error occurs, it's a coding error
  574. // rather than a configuration error
  575. //
  576. global $sTemplateID;
  577. if (empty($sTemplateID)) {
  578. trigger_error('Template set ID unknown', E_USER_ERROR);
  579. } else {
  580. $template_file_hierarchy = Template::catalog_template_files($sTemplateID);
  581. // additional files, if any
  582. //
  583. if (!empty($additional_files)) {
  584. $template_file_hierarchy = array_merge($template_file_hierarchy,
  585. $additional_files);
  586. }
  587. sqsession_register($template_file_hierarchy,
  588. 'template_file_hierarchy');
  589. return $template_file_hierarchy;
  590. }
  591. }
  592. /**
  593. * Traverse template hierarchy and catalogue all template
  594. * files (for storing in cache).
  595. *
  596. * Paths to all files in all parent, grand-parent, great grand
  597. * parent, etc. template sets (including the fallback template)
  598. * are catalogued; for identically named files, the file earlier
  599. * in the hierarchy (closest to this template set) is used.
  600. *
  601. * @param string $template_set_id The template set in which to
  602. * search for files
  603. * @param array $file_list The file list so far to be added
  604. * to (allows recursive behavior)
  605. * (optional; default empty array).
  606. * @param string $directory The directory in which to search for
  607. * files (must be given as full path).
  608. * If empty, starts at top-level template
  609. * set directory (optional; default empty).
  610. * NOTE! Use with care, as behavior is
  611. * unpredictable if directory given is not
  612. * part of correct template set.
  613. *
  614. * @return mixed The top-level caller will have an array of template
  615. * files returned to it; recursive calls to this function
  616. * do not receive any return value at all. The format
  617. * of the template file array is as described for the
  618. * Template class attribute $template_file_cache
  619. *
  620. * @static
  621. *
  622. */
  623. function catalog_template_files($template_set_id, $file_list=array(), $directory='') {
  624. $template_base_dir = SM_PATH
  625. . Template::calculate_template_file_directory($template_set_id);
  626. if (empty($directory)) {
  627. $directory = $template_base_dir;
  628. }
  629. $files_and_dirs = list_files($directory, '', FALSE, TRUE, FALSE, TRUE);
  630. // recurse for all the subdirectories in the template set
  631. //
  632. foreach ($files_and_dirs['DIRECTORIES'] as $dir) {
  633. $file_list = Template::catalog_template_files($template_set_id, $file_list, $dir);
  634. }
  635. // place all found files in the cache
  636. // FIXME: assuming PHP template engine may not necessarily be a good thing
  637. //
  638. $engine = Template::get_template_config($template_set_id,
  639. 'template_engine', SQ_PHP_TEMPLATE);
  640. foreach ($files_and_dirs['FILES'] as $file) {
  641. // remove the part of the file path corresponding to the
  642. // template set's base directory
  643. //
  644. $relative_file = substr($file, strlen($template_base_dir));
  645. /**
  646. * only put file in cache if not already found in earlier template
  647. * PATH should be relative to SquirrelMail top directory
  648. */
  649. if (!isset($file_list[$relative_file])) {
  650. $file_list[$relative_file] = array(
  651. 'PATH' => substr($file,strlen(SM_PATH)),
  652. 'SET_ID' => $template_set_id,
  653. 'ENGINE' => $engine,
  654. );
  655. }
  656. }
  657. // now if we are currently at the top-level of the template
  658. // set base directory, we need to move on to the parent
  659. // template set, if any
  660. //
  661. if ($directory == $template_base_dir) {
  662. // use fallback when we run out of parents
  663. //
  664. $fallback_id = Template::get_fallback_template_set();
  665. $parent_id = Template::get_template_config($template_set_id,
  666. 'parent_template_set',
  667. $fallback_id);
  668. // were we already all the way to the last level? just exit
  669. //
  670. // note that this code allows the fallback set to have
  671. // a parent, too, but can result in endless loops
  672. // if ($parent_id == $template_set_id) {
  673. //
  674. if ($fallback_id == $template_set_id) {
  675. return $file_list;
  676. }
  677. $file_list = Template::catalog_template_files($parent_id, $file_list);
  678. }
  679. return $file_list;
  680. }
  681. /**
  682. * Look for a template file in a plugin; add to template
  683. * file cache if found.
  684. *
  685. * The file is searched for in the following order:
  686. *
  687. * - A directory for the current template set within the plugin:
  688. * SM_PATH/plugins/<plugin name>/templates/<template name>/
  689. * - In a directory for one of the current template set's ancestor
  690. * (inherited) template sets within the plugin:
  691. * SM_PATH/plugins/<plugin name>/templates/<parent template name>/
  692. * - In a directory for the fallback template set within the plugin:
  693. * SM_PATH/plugins/<plugin name>/templates/<fallback template name>/
  694. *
  695. * @param string $plugin The name of the plugin
  696. * @param string $file The name of the template file
  697. * @param string $template_set_id The ID of the template for which
  698. * to start looking for the file
  699. * (optional; default is current
  700. * template set ID).
  701. *
  702. * @return boolean TRUE if the template file was found, FALSE otherwise.
  703. *
  704. */
  705. function find_and_cache_plugin_template_file($plugin, $file, $template_set_id='') {
  706. if (empty($template_set_id))
  707. $template_set_id = $this->template_set_id;
  708. $file_path = SM_PATH . 'plugins/' . $plugin . '/'
  709. . $this->calculate_template_file_directory($template_set_id)
  710. . $file;
  711. if (file_exists($file_path)) {
  712. // FIXME: assuming PHP template engine may not necessarily be a good thing
  713. $engine = $this->get_template_config($template_set_id,
  714. 'template_engine', SQ_PHP_TEMPLATE);
  715. $file_list = array('plugins/' . $plugin . '/' . $file => array(
  716. 'PATH' => substr($file_path, strlen(SM_PATH)),
  717. 'SET_ID' => $template_set_id,
  718. 'ENGINE' => $engine,
  719. )
  720. );
  721. $this->template_file_cache
  722. = $this->cache_template_file_hierarchy(FALSE, $file_list);
  723. return TRUE;
  724. }
  725. // not found yet, try parent template set
  726. // (use fallback when we run out of parents)
  727. //
  728. $fallback_id = $this->get_fallback_template_set();
  729. $parent_id = $this->get_template_config($template_set_id,
  730. 'parent_template_set',
  731. $fallback_id);
  732. // were we already all the way to the last level? just exit
  733. //
  734. // note that this code allows the fallback set to have
  735. // a parent, too, but can result in endless loops
  736. // if ($parent_id == $template_set_id) {
  737. //
  738. if ($fallback_id == $template_set_id) {
  739. return FALSE;
  740. }
  741. return $this->find_and_cache_plugin_template_file($plugin, $file, $parent_id);
  742. }
  743. /**
  744. * Find the right template file.
  745. *
  746. * The template file is taken from the template file cache, thus
  747. * the file is taken from the current template, one of its
  748. * ancestors or the fallback template.
  749. *
  750. * Note that it is perfectly acceptable to load template files from
  751. * template subdirectories. For example, JavaScript templates found
  752. * in the js/ subdirectory would be loaded by passing
  753. * "js/<javascript file name>" as the $filename.
  754. *
  755. * Note that the caller can also ask for ALL files in a directory
  756. * (and those in the same directory for all ancestor template sets)
  757. * by giving a $filename that is a directory name (ending with a
  758. * slash).
  759. *
  760. * If not found and the file is a plugin template file (indicated
  761. * by the presence of "plugins/" on the beginning of $filename),
  762. * the target plugin is searched for a substitue template file
  763. * before just returning nothing.
  764. *
  765. * Plugin authors must note that the $filename MUST be prefaced
  766. * with "plugins/<plugin name>/" in order to correctly resolve the
  767. * template file.
  768. *
  769. * @param string $filename The name of the template file,
  770. * possibly prefaced with
  771. * "plugins/<plugin name>/"
  772. * indicating that it is a plugin
  773. * template, or ending with a
  774. * slash, indicating that all files
  775. * for that directory name should
  776. * be returned.
  777. * @param boolean $directories_ok When TRUE, directory names
  778. * are acceptable search values,
  779. * and when returning a list of
  780. * directory contents, sub-directory
  781. * names will also be included
  782. * (optional; default FALSE).
  783. * NOTE that empty directories
  784. * are NOT included in the cache!
  785. * @param boolean $directories_only When TRUE, only directory names
  786. * are included in the returned
  787. * results. (optional; default
  788. * FALSE). Setting this argument
  789. * to TRUE forces $directories_ok
  790. * to TRUE as well.
  791. * NOTE that empty directories
  792. * are NOT included in the cache!
  793. *
  794. * @return mixed The full path to the template file or a list
  795. * of all files in the given directory if $filename
  796. * ends with a slash; if not found, an empty string
  797. * is returned. The caller is responsible for
  798. * throwing errors or other actions if template
  799. * file is not found.
  800. *
  801. */
  802. function get_template_file_path($filename,
  803. $directories_ok=FALSE,
  804. $directories_only=FALSE) {
  805. if ($directories_only) $directories_ok = TRUE;
  806. // only looking for directory listing first...
  807. //
  808. // return list of all files in a directory (and that
  809. // of any ancestors)
  810. //
  811. if ($filename{strlen($filename) - 1} == '/') {
  812. $return_array = array();
  813. foreach ($this->template_file_cache as $file => $file_info) {
  814. // only want files in the requested directory
  815. // (AND not in a subdirectory!)
  816. //
  817. if (!$directories_only && strpos($file, $filename) === 0
  818. && strpos($file, '/', strlen($filename)) === FALSE)
  819. $return_array[] = SM_PATH . $file_info['PATH'];
  820. // directories too? detect by finding any
  821. // array key that matches a file in a sub-directory
  822. // of the directory being processed
  823. //
  824. if ($directories_ok && strpos($file, $filename) === 0
  825. && ($pos = strpos($file, '/', strlen($filename))) !== FALSE
  826. && strpos($file, '/', $pos + 1) === FALSE) {
  827. $directory_name = SM_PATH
  828. . substr($file_info['PATH'],
  829. 0,
  830. strrpos($file_info['PATH'], '/'));
  831. if (!in_array($directory_name, $return_array))
  832. $return_array[] = $directory_name;
  833. }
  834. }
  835. return $return_array;
  836. }
  837. // just looking for singular file or directory below...
  838. //
  839. // figure out what to do with files not found
  840. //
  841. if ($directories_only || empty($this->template_file_cache[$filename]['PATH'])) {
  842. // if looking for directories...
  843. // have to iterate through cache and detect
  844. // directory by matching any file inside of it
  845. //
  846. if ($directories_ok) {
  847. foreach ($this->template_file_cache as $file => $file_info) {
  848. if (strpos($file, $filename) === 0
  849. && ($pos = strpos($file, '/', strlen($filename))) !== FALSE
  850. && strpos($file, '/', $pos + 1) === FALSE) {
  851. return SM_PATH . substr($file_info['PATH'],
  852. 0,
  853. strrpos($file_info['PATH'], '/'));
  854. }
  855. }
  856. if ($directories_only) return '';
  857. }
  858. // plugins get one more chance
  859. //
  860. if (strpos($filename, 'plugins/') === 0) {
  861. $plugin_name = substr($filename, 8, strpos($filename, '/', 8) - 8);
  862. $file = substr($filename, strlen($plugin_name) + 9);
  863. if (!$this->find_and_cache_plugin_template_file($plugin_name, $file))
  864. return '';
  865. //FIXME: technically I guess we should check for directories
  866. // here too, but that's overkill (no need) presently
  867. // (plugin-provided alternate stylesheet dirs?!? bah.)
  868. }
  869. // nothing... return empty string (yes, the else is intentional!)
  870. //
  871. else return '';
  872. }
  873. return SM_PATH . $this->template_file_cache[$filename]['PATH'];
  874. }
  875. /**
  876. * Get template engine needed to render given template file.
  877. *
  878. * If at all possible, just returns a reference to $this, but
  879. * some template files may require a different engine, thus
  880. * an object for that engine (which will subsequently be kept
  881. * in this object for future use) is returned.
  882. *
  883. * @param string $filename The name of the template file,
  884. *
  885. * @return object The needed template object to render the template.
  886. *
  887. */
  888. function get_rendering_template_engine_object($filename) {
  889. // for files that we cannot find engine info for,
  890. // just return $this
  891. //
  892. if (empty($this->template_file_cache[$filename]['ENGINE']))
  893. return $this;
  894. // otherwise, compare $this' engine to the file's engine
  895. //
  896. $engine = $this->template_file_cache[$filename]['ENGINE'];
  897. if ($this->template_engine == $engine)
  898. return $this;
  899. // need to load another engine... if already instantiated,
  900. // and stored herein, return that
  901. // FIXME: this assumes same engine setup in all template
  902. // set config files that have same engine in common
  903. // (but keeping a separate class object for every
  904. // template set seems like overkill... for now we
  905. // won't do that unless it becomes a problem)
  906. //
  907. if (!empty($this->other_template_engine_objects[$engine])) {
  908. $rendering_engine = $this->other_template_engine_objects[$engine];
  909. // otherwise, instantiate new engine object, add to cache
  910. // and return it
  911. //
  912. } else {
  913. $template_set_id = $this->template_file_cache[$filename]['SET_ID'];
  914. $this->other_template_engine_objects[$engine]
  915. = $this->get_template_engine_subclass($template_set_id);
  916. $rendering_engine = $this->other_template_engine_objects[$engine];
  917. }
  918. // now, need to copy over all the assigned variables
  919. // from $this to the rendering engine (YUCK! -- we need
  920. // to discourage template authors from creating
  921. // situations where engine changes occur)
  922. //
  923. $rendering_engine->clear_all_assign();
  924. $rendering_engine->assign($this->get_template_vars());
  925. // finally ready to go
  926. //
  927. return $rendering_engine;
  928. }
  929. /**
  930. * Return all JavaScript files provided by the template.
  931. *
  932. * All files found in the template set's "js" directory (and
  933. * that of its ancestors) with the extension ".js" are returned.
  934. *
  935. * @param boolean $full_path When FALSE, only the file names
  936. * are included in the return array;
  937. * otherwise, path information is
  938. * included (relative to SM_PATH)
  939. * (OPTIONAL; default only file names)
  940. *
  941. * @return array The required file names/paths.
  942. *
  943. */
  944. function get_javascript_includes($full_path=FALSE) {
  945. // since any page from a parent template set
  946. // could end up being loaded, we have to load
  947. // all js files from ancestor template sets,
  948. // not just this set
  949. //
  950. //$directory = SM_PATH . $this->get_template_file_directory() . 'js';
  951. //$js_files = list_files($directory, '.js', !$full_path);
  952. //
  953. $js_files = $this->get_template_file_path('js/');
  954. // parse out .js files only
  955. //
  956. $return_array = array();
  957. foreach ($js_files as $file) {
  958. if (substr($file, strlen($file) - 3) != '.js') continue;
  959. if ($full_path) {
  960. $return_array[] = $file;
  961. } else {
  962. $return_array[] = basename($file);
  963. }
  964. }
  965. return $return_array;
  966. }
  967. /**
  968. * Return all alternate stylesheets provided by template.
  969. *
  970. * All (non-empty) directories found in the template set's
  971. * "css/alternates" directory (and that of its ancestors)
  972. * are returned.
  973. *
  974. * Note that prettified names are constructed herein by
  975. * taking the directory name, changing underscores to spaces
  976. * and capitalizing each word in the resultant name.
  977. *
  978. * @param boolean $full_path When FALSE, only the file names
  979. * are included in the return array;
  980. * otherwise, path information is
  981. * included (relative to SM_PATH)
  982. * (OPTIONAL; default only file names)
  983. *
  984. * @return array A list of the available alternate stylesheets,
  985. * where the keys are the file names (formatted
  986. * according to $full_path) for the stylesheets,
  987. * and the values are the prettified version of
  988. * the file names for display to the user.
  989. *
  990. */
  991. function get_alternative_stylesheets($full_path=FALSE) {
  992. // since any page from a parent template set
  993. // could end up being loaded, we will load
  994. // all alternate css files from ancestor
  995. // template sets, not just this set
  996. //
  997. $css_directories = $this->get_template_file_path('css/alternates/', TRUE, TRUE);
  998. // prettify names
  999. //
  1000. $return_array = array();
  1001. foreach ($css_directories as $directory) {
  1002. // CVS directories are not wanted
  1003. //
  1004. if (strpos($directory, '/CVS') === strlen($directory) - 4) continue;
  1005. $pretty_name = ucwords(str_replace('_', ' ', basename($directory)));
  1006. if ($full_path) {
  1007. $return_array[$directory] = $pretty_name;
  1008. } else {
  1009. $return_array[basename($directory)] = $pretty_name;
  1010. }
  1011. }
  1012. return $return_array;
  1013. }
  1014. /**
  1015. * Return all standard stylsheets provided by the template.
  1016. *
  1017. * All files found in the template set's "css" directory (and
  1018. * that of its ancestors) with the extension ".css" except
  1019. * "rtl.css" (which is dealt with separately) are returned.
  1020. *
  1021. * @param boolean $full_path When FALSE, only the file names
  1022. * are included in the return array;
  1023. * otherwise, path information is
  1024. * included (relative to SM_PATH)
  1025. * (OPTIONAL; default only file names)
  1026. *
  1027. * @return array The required file names/paths.
  1028. *
  1029. */
  1030. function get_stylesheets($full_path=FALSE) {
  1031. // since any page from a parent template set
  1032. // could end up being loaded, we have to load
  1033. // all css files from ancestor template sets,
  1034. // not just this set
  1035. //
  1036. //$directory = SM_PATH . $this->get_template_file_directory() . 'css';
  1037. //$css_files = list_files($directory, '.css', !$full_path);
  1038. //
  1039. $css_files = $this->get_template_file_path('css/');
  1040. // need to leave out "rtl.css"
  1041. //
  1042. $return_array = array();
  1043. foreach ($css_files as $file) {
  1044. if (substr($file, strlen($file) - 4) != '.css') continue;
  1045. if (strtolower(basename($file)) == 'rtl.css') continue;
  1046. if ($full_path) {
  1047. $return_array[] = $file;
  1048. } else {
  1049. $return_array[] = basename($file);
  1050. }
  1051. }
  1052. // return sheets for the current template set
  1053. // last so we can enable any custom overrides
  1054. // of styles in ancestor sheets
  1055. //
  1056. return array_reverse($return_array);
  1057. }
  1058. /**
  1059. * Generate links to all this template set's standard stylesheets
  1060. *
  1061. * Subclasses can override this function if stylesheets are
  1062. * created differently for the template set's target output
  1063. * interface.
  1064. *
  1065. * @return string The stylesheet links as they should be sent
  1066. * to the browser.
  1067. *
  1068. */
  1069. function fetch_standard_stylesheet_links()
  1070. {
  1071. $sheets = $this->get_stylesheets(TRUE);
  1072. return $this->fetch_external_stylesheet_links($sheets);
  1073. }
  1074. /**
  1075. * Push out any other stylesheet links as provided (for
  1076. * stylesheets not included with the current template set)
  1077. *
  1078. * Subclasses can override this function if stylesheets are
  1079. * created differently for the template set's target output
  1080. * interface.
  1081. *
  1082. * @param mixed $sheets List of the desired stylesheets
  1083. * (file path to be used in stylesheet
  1084. * href attribute) to output (or single
  1085. * stylesheet file path).
  1086. FIXME: We could make the incoming array more complex so it can
  1087. also contain the other parameters for create_css_link()
  1088. such as $name, $alt, $mtype, and $xhtml_end
  1089. But do we need to?
  1090. *
  1091. * @return string The stylesheet links as they should be sent
  1092. * to the browser.
  1093. *
  1094. */
  1095. function fetch_external_stylesheet_links($sheets)
  1096. {
  1097. if (!is_array($sheets)) $sheets = array($sheets);
  1098. $output = '';
  1099. foreach ($sheets as $sheet) {
  1100. $output .= create_css_link($sheet);
  1101. }
  1102. return $output;
  1103. }
  1104. /**
  1105. * Send HTTP header(s) to browser.
  1106. *
  1107. * Subclasses can override this function if headers are
  1108. * managed differently in the engine's target output
  1109. * interface.
  1110. *
  1111. * @param mixed $headers A list of (or a single) header
  1112. * text to be sent.
  1113. *
  1114. */
  1115. function header($headers)
  1116. {
  1117. if (!is_array($headers)) $headers = array($headers);
  1118. foreach ($headers as $header) {
  1119. $this->assign('header', $header);
  1120. header($this->fetch('header.tpl'));
  1121. }
  1122. }
  1123. /**
  1124. * Generate a link to the right-to-left stylesheet for
  1125. * this template set by getting the "rtl.css" file from
  1126. * this template set, its parent (or grandparent, etc.)
  1127. * template set, the fall-back template set, or finally,
  1128. * fall back to SquirrelMail's own "rtl.css" if need be.
  1129. *
  1130. * Subclasses can override this function if stylesheets are
  1131. * created differently for the template set's target output
  1132. * interface.
  1133. *
  1134. * @return string The stylesheet link as it should be sent
  1135. * to the browser.
  1136. *
  1137. */
  1138. function fetch_right_to_left_stylesheet_link()
  1139. {
  1140. // get right template file
  1141. //
  1142. $sheet = $this->get_template_file_path('css/rtl.css');
  1143. // fall back to SquirrelMail's own default stylesheet
  1144. //
  1145. if (empty($sheet)) {
  1146. $sheet = SM_PATH . 'css/rtl.css';
  1147. }
  1148. return create_css_link($sheet);
  1149. }
  1150. /**
  1151. * Display the template
  1152. *
  1153. * @param string $file The template file to use
  1154. *
  1155. */
  1156. function display($file)
  1157. {
  1158. echo $this->fetch($file);
  1159. }
  1160. /**
  1161. * Applies the template and returns the resultant content string.
  1162. *
  1163. * @param string $file The template file to use
  1164. *
  1165. * @return string The template contents after applying the given template
  1166. *
  1167. */
  1168. function fetch($file) {
  1169. // get right template file
  1170. //
  1171. $template = $this->get_template_file_path($file);
  1172. // special case stylesheet.tpl falls back to SquirrelMail's
  1173. // own default stylesheet
  1174. //
  1175. if (empty($template) && $file == 'css/stylesheet.tpl') {
  1176. $template = SM_PATH . 'css/default.css';
  1177. }
  1178. if (empty($template)) {
  1179. trigger_error('The template "' . htmlspecialchars($file)
  1180. . '" could not be fetched!', E_USER_ERROR);
  1181. } else {
  1182. $aPluginOutput = array();
  1183. $aPluginOutput = concat_hook_function('template_construct_' . $file,
  1184. $temp=array(&$aPluginOutput, &$this),
  1185. TRUE);
  1186. $this->assign('plugin_output', $aPluginOutput);
  1187. //$output = $this->apply_template($template);
  1188. $rendering_engine = $this->get_rendering_template_engine_object($file);
  1189. $output = $rendering_engine->apply_template($template);
  1190. // CAUTION: USE OF THIS HOOK IS HIGHLY DISCOURAGED AND CAN
  1191. // RESULT IN NOTICABLE PERFORMANCE DEGREDATION. Plugins
  1192. // using this hook will probably be rejected by the
  1193. // SquirrelMail team.
  1194. //
  1195. do_hook('template_output', $output);
  1196. return $output;
  1197. }
  1198. }
  1199. /**
  1200. * Assigns values to template variables
  1201. *
  1202. * Note: this is an abstract method that must be implemented by subclass.
  1203. *
  1204. * @param array|string $tpl_var the template variable name(s)
  1205. * @param mixed $value the value to assign
  1206. *
  1207. */
  1208. function assign($tpl_var, $value = NULL) {
  1209. trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the assign() method.', E_USER_ERROR);
  1210. }
  1211. /**
  1212. * Assigns values to template variables by reference
  1213. *
  1214. * Note: this is an abstract method that must be implemented by subclass.
  1215. *
  1216. * @param string $tpl_var the template variable name
  1217. * @param mixed $value the referenced value to assign
  1218. *
  1219. */
  1220. function assign_by_ref($tpl_var, &$value) {
  1221. trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the assign_by_ref() method.', E_USER_ERROR);
  1222. }
  1223. /**
  1224. * Clears the values of all assigned varaiables.
  1225. *
  1226. */
  1227. function clear_all_assign() {
  1228. trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the clear_all_assign() method.', E_USER_ERROR);
  1229. }
  1230. /**
  1231. * Returns assigned variable value(s).
  1232. *
  1233. * @param string $varname If given, the value of that variable
  1234. * is returned, assuming it has been
  1235. * previously assigned. If not specified
  1236. * an array of all assigned variables is
  1237. * returned. (optional)
  1238. *
  1239. * @return mixed Desired single variable value or list of all
  1240. * assigned variable values.
  1241. *
  1242. */
  1243. function get_template_vars($varname=NULL) {
  1244. trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the get_template_vars() method.', E_USER_ERROR);
  1245. }
  1246. /**
  1247. * Appends values to template variables
  1248. *
  1249. * Note: this is an abstract method that must be implemented by subclass.
  1250. *
  1251. * @param array|string $tpl_var the template variable name(s)
  1252. * @param mixed $value the value to append
  1253. * @param boolean $merge when $value is given as an array,
  1254. * this indicates whether or not that
  1255. * array itself should be appended as
  1256. * a new template variable value or if
  1257. * that array's values should be merged
  1258. * into the existing array of template
  1259. * variable values
  1260. *
  1261. */
  1262. function append($tpl_var, $value = NULL, $merge = FALSE) {
  1263. trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the append() method.', E_USER_ERROR);
  1264. }
  1265. /**
  1266. * Appends values to template variables by reference
  1267. *
  1268. * Note: this is an abstract method that must be implemented by subclass.
  1269. *
  1270. * @param string $tpl_var the template variable name
  1271. * @param mixed $value the referenced value to append
  1272. * @param boolean $merge when $value is given as an array,
  1273. * this indicates whether or not that
  1274. * array itself should be appended as
  1275. * a new template variable value or if
  1276. * that array's values should be merged
  1277. * into the existing array of template
  1278. * variable values
  1279. *
  1280. */
  1281. function append_by_ref($tpl_var, &$value, $merge = FALSE) {
  1282. trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the append_by_ref() method.', E_USER_ERROR);
  1283. }
  1284. /**
  1285. * Applys the template and generates final output destined
  1286. * for the user's browser
  1287. *
  1288. * Note: this is an abstract method that must be implemented by subclass.
  1289. *
  1290. * @param string $filepath The full file path to the template to be applied
  1291. *
  1292. * @return string The output for the given template
  1293. *
  1294. */
  1295. function apply_template($filepath) {
  1296. trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the apply_template() method.', E_USER_ERROR);
  1297. }
  1298. }