Source for file Template.class.php

Documentation is available at Template.class.php

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

Documentation generated on Mon, 13 Jan 2020 04:23:41 +0100 by phpDocumentor 1.4.3