Template.class.php 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566
  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. * Refuses to traverse directories called ".svn"
  602. *
  603. * @param string $template_set_id The template set in which to
  604. * search for files
  605. * @param array $file_list The file list so far to be added
  606. * to (allows recursive behavior)
  607. * (optional; default empty array).
  608. * @param string $directory The directory in which to search for
  609. * files (must be given as full path).
  610. * If empty, starts at top-level template
  611. * set directory (optional; default empty).
  612. * NOTE! Use with care, as behavior is
  613. * unpredictable if directory given is not
  614. * part of correct template set.
  615. *
  616. * @return mixed The top-level caller will have an array of template
  617. * files returned to it; recursive calls to this function
  618. * do not receive any return value at all. The format
  619. * of the template file array is as described for the
  620. * Template class attribute $template_file_cache
  621. *
  622. * @static
  623. *
  624. */
  625. function catalog_template_files($template_set_id, $file_list=array(), $directory='') {
  626. $template_base_dir = SM_PATH
  627. . Template::calculate_template_file_directory($template_set_id);
  628. if (empty($directory)) {
  629. $directory = $template_base_dir;
  630. }
  631. // bail if we have been asked to traverse a Subversion directory
  632. //
  633. if (strpos($directory, '/.svn') === strlen($directory) - 5) return $file_list;
  634. $files_and_dirs = list_files($directory, '', FALSE, TRUE, FALSE, TRUE);
  635. // recurse for all the subdirectories in the template set
  636. //
  637. foreach ($files_and_dirs['DIRECTORIES'] as $dir) {
  638. $file_list = Template::catalog_template_files($template_set_id, $file_list, $dir);
  639. }
  640. // place all found files in the cache
  641. // FIXME: assuming PHP template engine may not necessarily be a good thing
  642. //
  643. $engine = Template::get_template_config($template_set_id,
  644. 'template_engine', SQ_PHP_TEMPLATE);
  645. foreach ($files_and_dirs['FILES'] as $file) {
  646. // remove the part of the file path corresponding to the
  647. // template set's base directory
  648. //
  649. $relative_file = substr($file, strlen($template_base_dir));
  650. /**
  651. * only put file in cache if not already found in earlier template
  652. * PATH should be relative to SquirrelMail top directory
  653. */
  654. if (!isset($file_list[$relative_file])) {
  655. $file_list[$relative_file] = array(
  656. 'PATH' => substr($file,strlen(SM_PATH)),
  657. 'SET_ID' => $template_set_id,
  658. 'ENGINE' => $engine,
  659. );
  660. }
  661. }
  662. // now if we are currently at the top-level of the template
  663. // set base directory, we need to move on to the parent
  664. // template set, if any
  665. //
  666. if ($directory == $template_base_dir) {
  667. // use fallback when we run out of parents
  668. //
  669. $fallback_id = Template::get_fallback_template_set();
  670. $parent_id = Template::get_template_config($template_set_id,
  671. 'parent_template_set',
  672. $fallback_id);
  673. // were we already all the way to the last level? just exit
  674. //
  675. // note that this code allows the fallback set to have
  676. // a parent, too, but can result in endless loops
  677. // if ($parent_id == $template_set_id) {
  678. //
  679. if ($fallback_id == $template_set_id) {
  680. return $file_list;
  681. }
  682. $file_list = Template::catalog_template_files($parent_id, $file_list);
  683. }
  684. return $file_list;
  685. }
  686. /**
  687. * Look for a template file in a plugin; add to template
  688. * file cache if found.
  689. *
  690. * The file is searched for in the following order:
  691. *
  692. * - A directory for the current template set within the plugin:
  693. * SM_PATH/plugins/<plugin name>/templates/<template name>/
  694. * - In a directory for one of the current template set's ancestor
  695. * (inherited) template sets within the plugin:
  696. * SM_PATH/plugins/<plugin name>/templates/<parent template name>/
  697. * - In a directory for the fallback template set within the plugin:
  698. * SM_PATH/plugins/<plugin name>/templates/<fallback template name>/
  699. *
  700. * @param string $plugin The name of the plugin
  701. * @param string $file The name of the template file
  702. * @param string $template_set_id The ID of the template for which
  703. * to start looking for the file
  704. * (optional; default is current
  705. * template set ID).
  706. *
  707. * @return boolean TRUE if the template file was found, FALSE otherwise.
  708. *
  709. */
  710. function find_and_cache_plugin_template_file($plugin, $file, $template_set_id='') {
  711. if (empty($template_set_id))
  712. $template_set_id = $this->template_set_id;
  713. $file_path = SM_PATH . 'plugins/' . $plugin . '/'
  714. . $this->calculate_template_file_directory($template_set_id)
  715. . $file;
  716. if (file_exists($file_path)) {
  717. // FIXME: assuming PHP template engine may not necessarily be a good thing
  718. $engine = $this->get_template_config($template_set_id,
  719. 'template_engine', SQ_PHP_TEMPLATE);
  720. $file_list = array('plugins/' . $plugin . '/' . $file => array(
  721. 'PATH' => substr($file_path, strlen(SM_PATH)),
  722. 'SET_ID' => $template_set_id,
  723. 'ENGINE' => $engine,
  724. )
  725. );
  726. $this->template_file_cache
  727. = $this->cache_template_file_hierarchy(FALSE, $file_list);
  728. return TRUE;
  729. }
  730. // not found yet, try parent template set
  731. // (use fallback when we run out of parents)
  732. //
  733. $fallback_id = $this->get_fallback_template_set();
  734. $parent_id = $this->get_template_config($template_set_id,
  735. 'parent_template_set',
  736. $fallback_id);
  737. // were we already all the way to the last level? just exit
  738. //
  739. // note that this code allows the fallback set to have
  740. // a parent, too, but can result in endless loops
  741. // if ($parent_id == $template_set_id) {
  742. //
  743. if ($fallback_id == $template_set_id) {
  744. return FALSE;
  745. }
  746. return $this->find_and_cache_plugin_template_file($plugin, $file, $parent_id);
  747. }
  748. /**
  749. * Find the right template file.
  750. *
  751. * The template file is taken from the template file cache, thus
  752. * the file is taken from the current template, one of its
  753. * ancestors or the fallback template.
  754. *
  755. * Note that it is perfectly acceptable to load template files from
  756. * template subdirectories. For example, JavaScript templates found
  757. * in the js/ subdirectory would be loaded by passing
  758. * "js/<javascript file name>" as the $filename.
  759. *
  760. * Note that the caller can also ask for ALL files in a directory
  761. * (and those in the same directory for all ancestor template sets)
  762. * by giving a $filename that is a directory name (ending with a
  763. * slash).
  764. *
  765. * If not found and the file is a plugin template file (indicated
  766. * by the presence of "plugins/" on the beginning of $filename),
  767. * the target plugin is searched for a substitue template file
  768. * before just returning nothing.
  769. *
  770. * Plugin authors must note that the $filename MUST be prefaced
  771. * with "plugins/<plugin name>/" in order to correctly resolve the
  772. * template file.
  773. *
  774. * @param string $filename The name of the template file,
  775. * possibly prefaced with
  776. * "plugins/<plugin name>/"
  777. * indicating that it is a plugin
  778. * template, or ending with a
  779. * slash, indicating that all files
  780. * for that directory name should
  781. * be returned.
  782. * @param boolean $directories_ok When TRUE, directory names
  783. * are acceptable search values,
  784. * and when returning a list of
  785. * directory contents, sub-directory
  786. * names will also be included
  787. * (optional; default FALSE).
  788. * NOTE that empty directories
  789. * are NOT included in the cache!
  790. * @param boolean $directories_only When TRUE, only directory names
  791. * are included in the returned
  792. * results. (optional; default
  793. * FALSE). Setting this argument
  794. * to TRUE forces $directories_ok
  795. * to TRUE as well.
  796. * NOTE that empty directories
  797. * are NOT included in the cache!
  798. *
  799. * @return mixed The full path to the template file or a list
  800. * of all files in the given directory if $filename
  801. * ends with a slash; if not found, an empty string
  802. * is returned. The caller is responsible for
  803. * throwing errors or other actions if template
  804. * file is not found.
  805. *
  806. */
  807. function get_template_file_path($filename,
  808. $directories_ok=FALSE,
  809. $directories_only=FALSE) {
  810. if ($directories_only) $directories_ok = TRUE;
  811. // only looking for directory listing first...
  812. //
  813. // return list of all files in a directory (and that
  814. // of any ancestors)
  815. //
  816. if ($filename{strlen($filename) - 1} == '/') {
  817. $return_array = array();
  818. foreach ($this->template_file_cache as $file => $file_info) {
  819. // only want files in the requested directory
  820. // (AND not in a subdirectory!)
  821. //
  822. if (!$directories_only && strpos($file, $filename) === 0
  823. && strpos($file, '/', strlen($filename)) === FALSE)
  824. $return_array[] = SM_PATH . $file_info['PATH'];
  825. // directories too? detect by finding any
  826. // array key that matches a file in a sub-directory
  827. // of the directory being processed
  828. //
  829. if ($directories_ok && strpos($file, $filename) === 0
  830. && ($pos = strpos($file, '/', strlen($filename))) !== FALSE
  831. && strpos($file, '/', $pos + 1) === FALSE) {
  832. $directory_name = SM_PATH
  833. . substr($file_info['PATH'],
  834. 0,
  835. strrpos($file_info['PATH'], '/'));
  836. if (!in_array($directory_name, $return_array))
  837. $return_array[] = $directory_name;
  838. }
  839. }
  840. return $return_array;
  841. }
  842. // just looking for singular file or directory below...
  843. //
  844. // figure out what to do with files not found
  845. //
  846. if ($directories_only || empty($this->template_file_cache[$filename]['PATH'])) {
  847. // if looking for directories...
  848. // have to iterate through cache and detect
  849. // directory by matching any file inside of it
  850. //
  851. if ($directories_ok) {
  852. foreach ($this->template_file_cache as $file => $file_info) {
  853. if (strpos($file, $filename) === 0
  854. && ($pos = strpos($file, '/', strlen($filename))) !== FALSE
  855. && strpos($file, '/', $pos + 1) === FALSE) {
  856. return SM_PATH . substr($file_info['PATH'],
  857. 0,
  858. strrpos($file_info['PATH'], '/'));
  859. }
  860. }
  861. if ($directories_only) return '';
  862. }
  863. // plugins get one more chance
  864. //
  865. if (strpos($filename, 'plugins/') === 0) {
  866. $plugin_name = substr($filename, 8, strpos($filename, '/', 8) - 8);
  867. $file = substr($filename, strlen($plugin_name) + 9);
  868. if (!$this->find_and_cache_plugin_template_file($plugin_name, $file))
  869. return '';
  870. //FIXME: technically I guess we should check for directories
  871. // here too, but that's overkill (no need) presently
  872. // (plugin-provided alternate stylesheet dirs?!? bah.)
  873. }
  874. // nothing... return empty string (yes, the else is intentional!)
  875. //
  876. else return '';
  877. }
  878. return SM_PATH . $this->template_file_cache[$filename]['PATH'];
  879. }
  880. /**
  881. * Get template engine needed to render given template file.
  882. *
  883. * If at all possible, just returns a reference to $this, but
  884. * some template files may require a different engine, thus
  885. * an object for that engine (which will subsequently be kept
  886. * in this object for future use) is returned.
  887. *
  888. * @param string $filename The name of the template file,
  889. *
  890. * @return object The needed template object to render the template.
  891. *
  892. */
  893. function get_rendering_template_engine_object($filename) {
  894. // for files that we cannot find engine info for,
  895. // just return $this
  896. //
  897. if (empty($this->template_file_cache[$filename]['ENGINE']))
  898. return $this;
  899. // otherwise, compare $this' engine to the file's engine
  900. //
  901. $engine = $this->template_file_cache[$filename]['ENGINE'];
  902. if ($this->template_engine == $engine)
  903. return $this;
  904. // need to load another engine... if already instantiated,
  905. // and stored herein, return that
  906. // FIXME: this assumes same engine setup in all template
  907. // set config files that have same engine in common
  908. // (but keeping a separate class object for every
  909. // template set seems like overkill... for now we
  910. // won't do that unless it becomes a problem)
  911. //
  912. if (!empty($this->other_template_engine_objects[$engine])) {
  913. $rendering_engine = $this->other_template_engine_objects[$engine];
  914. // otherwise, instantiate new engine object, add to cache
  915. // and return it
  916. //
  917. } else {
  918. $template_set_id = $this->template_file_cache[$filename]['SET_ID'];
  919. $this->other_template_engine_objects[$engine]
  920. = $this->get_template_engine_subclass($template_set_id);
  921. $rendering_engine = $this->other_template_engine_objects[$engine];
  922. }
  923. // now, need to copy over all the assigned variables
  924. // from $this to the rendering engine (YUCK! -- we need
  925. // to discourage template authors from creating
  926. // situations where engine changes occur)
  927. //
  928. $rendering_engine->clear_all_assign();
  929. $rendering_engine->assign($this->get_template_vars());
  930. // finally ready to go
  931. //
  932. return $rendering_engine;
  933. }
  934. /**
  935. * Return all JavaScript files provided by the template.
  936. *
  937. * All files found in the template set's "js" directory (and
  938. * that of its ancestors) with the extension ".js" are returned.
  939. *
  940. * @param boolean $full_path When FALSE, only the file names
  941. * are included in the return array;
  942. * otherwise, path information is
  943. * included (relative to SM_PATH)
  944. * (OPTIONAL; default only file names)
  945. *
  946. * @return array The required file names/paths.
  947. *
  948. */
  949. function get_javascript_includes($full_path=FALSE) {
  950. // since any page from a parent template set
  951. // could end up being loaded, we have to load
  952. // all js files from ancestor template sets,
  953. // not just this set
  954. //
  955. //$directory = SM_PATH . $this->get_template_file_directory() . 'js';
  956. //$js_files = list_files($directory, '.js', !$full_path);
  957. //
  958. $js_files = $this->get_template_file_path('js/');
  959. // parse out .js files only
  960. //
  961. $return_array = array();
  962. foreach ($js_files as $file) {
  963. if (substr($file, strlen($file) - 3) != '.js') continue;
  964. if ($full_path) {
  965. $return_array[] = $file;
  966. } else {
  967. $return_array[] = basename($file);
  968. }
  969. }
  970. return $return_array;
  971. }
  972. /**
  973. * Return all alternate stylesheets provided by template.
  974. *
  975. * All (non-empty) directories found in the template set's
  976. * "css/alternates" directory (and that of its ancestors)
  977. * are returned.
  978. *
  979. * Note that prettified names are constructed herein by
  980. * taking the directory name, changing underscores to spaces
  981. * and capitalizing each word in the resultant name.
  982. *
  983. * @param boolean $full_path When FALSE, only the file names
  984. * are included in the return array;
  985. * otherwise, path information is
  986. * included (relative to SM_PATH)
  987. * (OPTIONAL; default only file names)
  988. *
  989. * @return array A list of the available alternate stylesheets,
  990. * where the keys are the file names (formatted
  991. * according to $full_path) for the stylesheets,
  992. * and the values are the prettified version of
  993. * the file names for display to the user.
  994. *
  995. */
  996. function get_alternative_stylesheets($full_path=FALSE) {
  997. // since any page from a parent template set
  998. // could end up being loaded, we will load
  999. // all alternate css files from ancestor
  1000. // template sets, not just this set
  1001. //
  1002. $css_directories = $this->get_template_file_path('css/alternates/', TRUE, TRUE);
  1003. // prettify names
  1004. //
  1005. $return_array = array();
  1006. foreach ($css_directories as $directory) {
  1007. // CVS and SVN directories are not wanted
  1008. //
  1009. if ((strpos($directory, '/CVS') === strlen($directory) - 4)
  1010. || (strpos($directory, '/.svn') === strlen($directory) - 5)) continue;
  1011. $pretty_name = ucwords(str_replace('_', ' ', basename($directory)));
  1012. if ($full_path) {
  1013. $return_array[$directory] = $pretty_name;
  1014. } else {
  1015. $return_array[basename($directory)] = $pretty_name;
  1016. }
  1017. }
  1018. return $return_array;
  1019. }
  1020. /**
  1021. * Return all standard stylsheets provided by the template.
  1022. *
  1023. * All files found in the template set's "css" directory (and
  1024. * that of its ancestors) with the extension ".css" except
  1025. * "rtl.css" (which is dealt with separately) are returned.
  1026. *
  1027. * @param boolean $full_path When FALSE, only the file names
  1028. * are included in the return array;
  1029. * otherwise, path information is
  1030. * included (relative to SM_PATH)
  1031. * (OPTIONAL; default only file names)
  1032. *
  1033. * @return array The required file names/paths.
  1034. *
  1035. */
  1036. function get_stylesheets($full_path=FALSE) {
  1037. // since any page from a parent template set
  1038. // could end up being loaded, we have to load
  1039. // all css files from ancestor template sets,
  1040. // not just this set
  1041. //
  1042. //$directory = SM_PATH . $this->get_template_file_directory() . 'css';
  1043. //$css_files = list_files($directory, '.css', !$full_path);
  1044. //
  1045. $css_files = $this->get_template_file_path('css/');
  1046. // need to leave out "rtl.css"
  1047. //
  1048. $return_array = array();
  1049. foreach ($css_files as $file) {
  1050. if (substr($file, strlen($file) - 4) != '.css') continue;
  1051. if (strtolower(basename($file)) == 'rtl.css') continue;
  1052. if ($full_path) {
  1053. $return_array[] = $file;
  1054. } else {
  1055. $return_array[] = basename($file);
  1056. }
  1057. }
  1058. // return sheets for the current template set
  1059. // last so we can enable any custom overrides
  1060. // of styles in ancestor sheets
  1061. //
  1062. return array_reverse($return_array);
  1063. }
  1064. /**
  1065. * Generate links to all this template set's standard stylesheets
  1066. *
  1067. * Subclasses can override this function if stylesheets are
  1068. * created differently for the template set's target output
  1069. * interface.
  1070. *
  1071. * @return string The stylesheet links as they should be sent
  1072. * to the browser.
  1073. *
  1074. */
  1075. function fetch_standard_stylesheet_links()
  1076. {
  1077. $sheets = $this->get_stylesheets(TRUE);
  1078. return $this->fetch_external_stylesheet_links($sheets);
  1079. }
  1080. /**
  1081. * Push out any other stylesheet links as provided (for
  1082. * stylesheets not included with the current template set)
  1083. *
  1084. * Subclasses can override this function if stylesheets are
  1085. * created differently for the template set's target output
  1086. * interface.
  1087. *
  1088. * @param mixed $sheets List of the desired stylesheets
  1089. * (file path to be used in stylesheet
  1090. * href attribute) to output (or single
  1091. * stylesheet file path).
  1092. FIXME: We could make the incoming array more complex so it can
  1093. also contain the other parameters for create_css_link()
  1094. such as $name, $alt, $mtype, and $xhtml_end
  1095. But do we need to?
  1096. *
  1097. * @return string The stylesheet links as they should be sent
  1098. * to the browser.
  1099. *
  1100. */
  1101. function fetch_external_stylesheet_links($sheets)
  1102. {
  1103. if (!is_array($sheets)) $sheets = array($sheets);
  1104. $output = '';
  1105. foreach ($sheets as $sheet) {
  1106. $output .= create_css_link($sheet);
  1107. }
  1108. return $output;
  1109. }
  1110. /**
  1111. * Send HTTP header(s) to browser.
  1112. *
  1113. * Subclasses can override this function if headers are
  1114. * managed differently in the engine's target output
  1115. * interface.
  1116. *
  1117. * @param mixed $headers A list of (or a single) header
  1118. * text to be sent.
  1119. *
  1120. */
  1121. function header($headers)
  1122. {
  1123. if (!is_array($headers)) $headers = array($headers);
  1124. foreach ($headers as $header) {
  1125. $this->assign('header', $header);
  1126. header($this->fetch('header.tpl'));
  1127. }
  1128. }
  1129. /**
  1130. * Generate a link to the right-to-left stylesheet for
  1131. * this template set by getting the "rtl.css" file from
  1132. * this template set, its parent (or grandparent, etc.)
  1133. * template set, the fall-back template set, or finally,
  1134. * fall back to SquirrelMail's own "rtl.css" if need be.
  1135. *
  1136. * Subclasses can override this function if stylesheets are
  1137. * created differently for the template set's target output
  1138. * interface.
  1139. *
  1140. * @return string The stylesheet link as it should be sent
  1141. * to the browser.
  1142. *
  1143. */
  1144. function fetch_right_to_left_stylesheet_link()
  1145. {
  1146. // get right template file
  1147. //
  1148. $sheet = $this->get_template_file_path('css/rtl.css');
  1149. // fall back to SquirrelMail's own default stylesheet
  1150. //
  1151. if (empty($sheet)) {
  1152. $sheet = SM_PATH . 'css/rtl.css';
  1153. }
  1154. return create_css_link($sheet);
  1155. }
  1156. /**
  1157. * Display the template
  1158. *
  1159. * @param string $file The template file to use
  1160. *
  1161. */
  1162. function display($file)
  1163. {
  1164. echo $this->fetch($file);
  1165. }
  1166. /**
  1167. * Applies the template and returns the resultant content string.
  1168. *
  1169. * @param string $file The template file to use
  1170. *
  1171. * @return string The template contents after applying the given template
  1172. *
  1173. */
  1174. function fetch($file) {
  1175. // get right template file
  1176. //
  1177. $template = $this->get_template_file_path($file);
  1178. // special case stylesheet.tpl falls back to SquirrelMail's
  1179. // own default stylesheet
  1180. //
  1181. if (empty($template) && $file == 'css/stylesheet.tpl') {
  1182. $template = SM_PATH . 'css/default.css';
  1183. }
  1184. if (empty($template)) {
  1185. trigger_error('The template "' . htmlspecialchars($file)
  1186. . '" could not be fetched!', E_USER_ERROR);
  1187. } else {
  1188. $aPluginOutput = array();
  1189. $temp = array(&$aPluginOutput, &$this);
  1190. $aPluginOutput = concat_hook_function('template_construct_' . $file,
  1191. $temp, TRUE);
  1192. $this->assign('plugin_output', $aPluginOutput);
  1193. //$output = $this->apply_template($template);
  1194. $rendering_engine = $this->get_rendering_template_engine_object($file);
  1195. $output = $rendering_engine->apply_template($template);
  1196. // CAUTION: USE OF THIS HOOK IS HIGHLY DISCOURAGED AND CAN
  1197. // RESULT IN NOTICABLE PERFORMANCE DEGREDATION. Plugins
  1198. // using this hook will probably be rejected by the
  1199. // SquirrelMail team.
  1200. //
  1201. do_hook('template_output', $output);
  1202. return $output;
  1203. }
  1204. }
  1205. /**
  1206. * Assigns values to template variables
  1207. *
  1208. * Note: this is an abstract method that must be implemented by subclass.
  1209. *
  1210. * @param array|string $tpl_var the template variable name(s)
  1211. * @param mixed $value the value to assign
  1212. *
  1213. */
  1214. function assign($tpl_var, $value = NULL) {
  1215. trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the assign() method.', E_USER_ERROR);
  1216. }
  1217. /**
  1218. * Assigns values to template variables by reference
  1219. *
  1220. * Note: this is an abstract method that must be implemented by subclass.
  1221. *
  1222. * @param string $tpl_var the template variable name
  1223. * @param mixed $value the referenced value to assign
  1224. *
  1225. */
  1226. function assign_by_ref($tpl_var, &$value) {
  1227. trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the assign_by_ref() method.', E_USER_ERROR);
  1228. }
  1229. /**
  1230. * Clears the values of all assigned varaiables.
  1231. *
  1232. */
  1233. function clear_all_assign() {
  1234. trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the clear_all_assign() method.', E_USER_ERROR);
  1235. }
  1236. /**
  1237. * Returns assigned variable value(s).
  1238. *
  1239. * @param string $varname If given, the value of that variable
  1240. * is returned, assuming it has been
  1241. * previously assigned. If not specified
  1242. * an array of all assigned variables is
  1243. * returned. (optional)
  1244. *
  1245. * @return mixed Desired single variable value or list of all
  1246. * assigned variable values.
  1247. *
  1248. */
  1249. function get_template_vars($varname=NULL) {
  1250. trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the get_template_vars() method.', E_USER_ERROR);
  1251. }
  1252. /**
  1253. * Appends values to template variables
  1254. *
  1255. * Note: this is an abstract method that must be implemented by subclass.
  1256. *
  1257. * @param array|string $tpl_var the template variable name(s)
  1258. * @param mixed $value the value to append
  1259. * @param boolean $merge when $value is given as an array,
  1260. * this indicates whether or not that
  1261. * array itself should be appended as
  1262. * a new template variable value or if
  1263. * that array's values should be merged
  1264. * into the existing array of template
  1265. * variable values
  1266. *
  1267. */
  1268. function append($tpl_var, $value = NULL, $merge = FALSE) {
  1269. trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the append() method.', E_USER_ERROR);
  1270. }
  1271. /**
  1272. * Appends values to template variables by reference
  1273. *
  1274. * Note: this is an abstract method that must be implemented by subclass.
  1275. *
  1276. * @param string $tpl_var the template variable name
  1277. * @param mixed $value the referenced value to append
  1278. * @param boolean $merge when $value is given as an array,
  1279. * this indicates whether or not that
  1280. * array itself should be appended as
  1281. * a new template variable value or if
  1282. * that array's values should be merged
  1283. * into the existing array of template
  1284. * variable values
  1285. *
  1286. */
  1287. function append_by_ref($tpl_var, &$value, $merge = FALSE) {
  1288. trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the append_by_ref() method.', E_USER_ERROR);
  1289. }
  1290. /**
  1291. * Applys the template and generates final output destined
  1292. * for the user's browser
  1293. *
  1294. * Note: this is an abstract method that must be implemented by subclass.
  1295. *
  1296. * @param string $filepath The full file path to the template to be applied
  1297. *
  1298. * @return string The output for the given template
  1299. *
  1300. */
  1301. function apply_template($filepath) {
  1302. trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the apply_template() method.', E_USER_ERROR);
  1303. }
  1304. }