Template.class.php 59 KB

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