home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / CMS / drupal-6.0.exe / drupal-6.0 / includes / theme.inc < prev    next >
Encoding:
Text File  |  2008-01-27  |  63.7 KB  |  1,923 lines

  1. <?php
  2. // $Id: theme.inc,v 1.415 2008/01/27 19:47:06 goba Exp $
  3.  
  4. /**
  5.  * @file
  6.  * The theme system, which controls the output of Drupal.
  7.  *
  8.  * The theme system allows for nearly all output of the Drupal system to be
  9.  * customized by user themes.
  10.  *
  11.  * @see <a href="http://drupal.org/node/253">Theme system</a>
  12.  * @see themeable
  13.  */
  14.  
  15. /**
  16.  * @name Content markers
  17.  * @{
  18.  * Markers used by theme_mark() and node_mark() to designate content.
  19.  * @see theme_mark(), node_mark()
  20.  */
  21. define('MARK_READ',    0);
  22. define('MARK_NEW',     1);
  23. define('MARK_UPDATED', 2);
  24. /**
  25.  * @} End of "Content markers".
  26.  */
  27.  
  28. /**
  29.  * Initialize the theme system by loading the theme.
  30.  */
  31. function init_theme() {
  32.   global $theme, $user, $custom_theme, $theme_key;
  33.  
  34.   // If $theme is already set, assume the others are set, too, and do nothing
  35.   if (isset($theme)) {
  36.     return;
  37.   }
  38.  
  39.   drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);
  40.   $themes = list_themes();
  41.  
  42.   // Only select the user selected theme if it is available in the
  43.   // list of enabled themes.
  44.   $theme = !empty($user->theme) && !empty($themes[$user->theme]->status) ? $user->theme : variable_get('theme_default', 'garland');
  45.  
  46.   // Allow modules to override the present theme... only select custom theme
  47.   // if it is available in the list of installed themes.
  48.   $theme = $custom_theme && $themes[$custom_theme] ? $custom_theme : $theme;
  49.  
  50.   // Store the identifier for retrieving theme settings with.
  51.   $theme_key = $theme;
  52.  
  53.   // Find all our ancestor themes and put them in an array.
  54.   $base_theme = array();
  55.   $ancestor = $theme;
  56.   while ($ancestor && isset($themes[$ancestor]->base_theme)) {
  57.     $base_theme[] = $new_base_theme = $themes[$themes[$ancestor]->base_theme];
  58.     $ancestor = $themes[$ancestor]->base_theme;
  59.   }
  60.   _init_theme($themes[$theme], array_reverse($base_theme));
  61. }
  62.  
  63. /**
  64.  * Initialize the theme system given already loaded information. This
  65.  * function is useful to initialize a theme when no database is present.
  66.  *
  67.  * @param $theme
  68.  *   An object with the following information:
  69.  *     filename
  70.  *       The .info file for this theme. The 'path' to
  71.  *       the theme will be in this file's directory. (Required)
  72.  *     owner
  73.  *       The path to the .theme file or the .engine file to load for
  74.  *       the theme. (Required)
  75.  *     stylesheet
  76.  *       The primary stylesheet for the theme. (Optional)
  77.  *     engine
  78.  *       The name of theme engine to use. (Optional)
  79.  * @param $base_theme
  80.  *    An optional array of objects that represent the 'base theme' if the
  81.  *    theme is meant to be derivative of another theme. It requires
  82.  *    the same information as the $theme object. It should be in
  83.  *    'oldest first' order, meaning the top level of the chain will
  84.  *    be first.
  85.  * @param $registry_callback
  86.  *   The callback to invoke to set the theme registry.
  87.  */
  88. function _init_theme($theme, $base_theme = array(), $registry_callback = '_theme_load_registry') {
  89.   global $theme_info, $base_theme_info, $theme_engine, $theme_path;
  90.   $theme_info = $theme;
  91.   $base_theme_info = $base_theme;
  92.  
  93.   $theme_path = dirname($theme->filename);
  94.  
  95.   // Prepare stylesheets from this theme as well as all ancestor themes.
  96.   // We work it this way so that we can have child themes override parent
  97.   // theme stylesheets easily.
  98.   $final_stylesheets = array();
  99.  
  100.   // Grab stylesheets from base theme
  101.   foreach ($base_theme as $base) {
  102.     if (!empty($base->stylesheets)) {
  103.       foreach ($base->stylesheets as $media => $stylesheets) {
  104.         foreach ($stylesheets as $name => $stylesheet) {
  105.           $final_stylesheets[$media][$name] = $stylesheet;
  106.         }
  107.       }
  108.     }
  109.   }
  110.  
  111.   // Add stylesheets used by this theme.
  112.   if (!empty($theme->stylesheets)) {
  113.     foreach ($theme->stylesheets as $media => $stylesheets) {
  114.       foreach ($stylesheets as $name => $stylesheet) {
  115.         $final_stylesheets[$media][$name] = $stylesheet;
  116.       }
  117.     }
  118.   }
  119.  
  120.   // And now add the stylesheets properly
  121.   foreach ($final_stylesheets as $media => $stylesheets) {
  122.     foreach ($stylesheets as $stylesheet) {
  123.       drupal_add_css($stylesheet, 'theme', $media);
  124.     }
  125.   }
  126.  
  127.   // Do basically the same as the above for scripts
  128.   $final_scripts = array();
  129.  
  130.   // Grab scripts from base theme
  131.   foreach ($base_theme as $base) {
  132.     if (!empty($base->scripts)) {
  133.       foreach ($base->scripts as $name => $script) {
  134.         $final_scripts[$name] = $script;
  135.       }
  136.     }
  137.   }
  138.  
  139.   // Add scripts used by this theme.
  140.   if (!empty($theme->scripts)) {
  141.     foreach ($theme->scripts as $name => $script) {
  142.       $final_scripts[$name] = $script;
  143.     }
  144.   }
  145.  
  146.   // Add scripts used by this theme.
  147.   foreach ($final_scripts as $script) {
  148.     drupal_add_js($script, 'theme');
  149.   }
  150.  
  151.   $theme_engine = NULL;
  152.  
  153.   // Initialize the theme.
  154.   if (isset($theme->engine)) {
  155.     // Include the engine.
  156.     include_once './'. $theme->owner;
  157.  
  158.     $theme_engine = $theme->engine;
  159.     if (function_exists($theme_engine .'_init')) {
  160.       foreach ($base_theme as $base) {
  161.         call_user_func($theme_engine .'_init', $base);
  162.       }
  163.       call_user_func($theme_engine .'_init', $theme);
  164.     }
  165.   }
  166.   else {
  167.     // include non-engine theme files
  168.     foreach ($base_theme as $base) {
  169.       // Include the theme file or the engine.
  170.       if (!empty($base->owner)) {
  171.         include_once './'. $base->owner;
  172.       }
  173.     }
  174.     // and our theme gets one too.
  175.     if (!empty($theme->owner)) {
  176.       include_once './'. $theme->owner;
  177.     }
  178.   }
  179.  
  180.   $registry_callback($theme, $base_theme, $theme_engine);
  181. }
  182.  
  183. /**
  184.  * Retrieve the stored theme registry. If the theme registry is already
  185.  * in memory it will be returned; otherwise it will attempt to load the
  186.  * registry from cache. If this fails, it will construct the registry and
  187.  * cache it.
  188.  */
  189. function theme_get_registry($registry = NULL) {
  190.   static $theme_registry = NULL;
  191.   if (isset($registry)) {
  192.     $theme_registry = $registry;
  193.   }
  194.  
  195.   return $theme_registry;
  196. }
  197.  
  198. /**
  199.  * Store the theme registry in memory.
  200.  */
  201. function _theme_set_registry($registry) {
  202.   // Pass through for setting of static variable.
  203.   return theme_get_registry($registry);
  204. }
  205.  
  206. /**
  207.  * Get the theme_registry cache from the database; if it doesn't exist, build
  208.  * it.
  209.  *
  210.  * @param $theme
  211.  *   The loaded $theme object.
  212.  * @param $base_theme
  213.  *   An array of loaded $theme objects representing the ancestor themes in
  214.  *   oldest first order.
  215.  * @param theme_engine
  216.  *   The name of the theme engine.
  217.  */
  218. function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL) {
  219.   // Check the theme registry cache; if it exists, use it.
  220.   $cache = cache_get("theme_registry:$theme->name", 'cache');
  221.   if (isset($cache->data)) {
  222.     $registry = $cache->data;
  223.   }
  224.   else {
  225.     // If not, build one and cache it.
  226.     $registry = _theme_build_registry($theme, $base_theme, $theme_engine);
  227.     _theme_save_registry($theme, $registry);
  228.   }
  229.   _theme_set_registry($registry);
  230. }
  231.  
  232. /**
  233.  * Write the theme_registry cache into the database.
  234.  */
  235. function _theme_save_registry($theme, $registry) {
  236.   cache_set("theme_registry:$theme->name", $registry);
  237. }
  238.  
  239. /**
  240.  * Force the system to rebuild the theme registry; this should be called
  241.  * when modules are added to the system, or when a dynamic system needs
  242.  * to add more theme hooks.
  243.  */
  244. function drupal_rebuild_theme_registry() {
  245.   cache_clear_all('theme_registry', 'cache', TRUE);
  246. }
  247.  
  248. /**
  249.  * Process a single invocation of the theme hook. $type will be one
  250.  * of 'module', 'theme_engine' or 'theme' and it tells us some
  251.  * important information.
  252.  *
  253.  * Because $cache is a reference, the cache will be continually
  254.  * expanded upon; new entries will replace old entries in the
  255.  * array_merge, but we are careful to ensure some data is carried
  256.  * forward, such as the arguments a theme hook needs.
  257.  *
  258.  * An override flag can be set for preprocess functions. When detected the
  259.  * cached preprocessors for the hook will not be merged with the newly set.
  260.  * This can be useful to themes and theme engines by giving them more control
  261.  * over how and when the preprocess functions are run.
  262.  */
  263. function _theme_process_registry(&$cache, $name, $type, $theme, $path) {
  264.   $function = $name .'_theme';
  265.   if (function_exists($function)) {
  266.     $result = $function($cache, $type, $theme, $path);
  267.  
  268.     foreach ($result as $hook => $info) {
  269.       $result[$hook]['type'] = $type;
  270.       $result[$hook]['theme path'] = $path;
  271.       // if function and file are left out, default to standard naming
  272.       // conventions.
  273.       if (!isset($info['template']) && !isset($info['function'])) {
  274.         $result[$hook]['function'] = ($type == 'module' ? 'theme_' : $name .'_') . $hook;
  275.       }
  276.       // If a path is set in the info, use what was set. Otherwise use the
  277.       // default path. This is mostly so system.module can declare theme
  278.       // functions on behalf of core .include files.
  279.       // All files are included to be safe. Conditionally included
  280.       // files can prevent them from getting registered.
  281.       if (isset($info['file']) && !isset($info['path'])) {
  282.         $result[$hook]['file'] = $path .'/'. $info['file'];
  283.         include_once($result[$hook]['file']);
  284.       }
  285.       elseif (isset($info['file']) && isset($info['path'])) {
  286.         include_once($info['path'] .'/'. $info['file']);
  287.       }
  288.  
  289.       if (isset($info['template']) && !isset($info['path'])) {
  290.         $result[$hook]['template'] = $path .'/'. $info['template'];
  291.       }
  292.       // If 'arguments' have been defined previously, carry them forward.
  293.       // This should happen if a theme overrides a Drupal defined theme
  294.       // function, for example.
  295.       if (!isset($info['arguments']) && isset($cache[$hook])) {
  296.         $result[$hook]['arguments'] = $cache[$hook]['arguments'];
  297.       }
  298.       // Likewise with theme paths. These are used for template naming suggestions.
  299.       // Theme implementations can occur in multiple paths. Suggestions should follow.
  300.       if (!isset($info['theme paths']) && isset($cache[$hook])) {
  301.         $result[$hook]['theme paths'] = $cache[$hook]['theme paths'];
  302.       }
  303.       // Check for sub-directories.
  304.       $result[$hook]['theme paths'][] = isset($info['path']) ? $info['path'] : $path;
  305.  
  306.       // Check for default _preprocess_ functions. Ensure arrayness.
  307.       if (!isset($info['preprocess functions']) || !is_array($info['preprocess functions'])) {
  308.         $info['preprocess functions'] = array();
  309.         $prefixes = array();
  310.         if ($type == 'module') {
  311.           // Default preprocessor prefix.
  312.           $prefixes[] = 'template';
  313.           // Add all modules so they can intervene with their own preprocessors. This allows them
  314.           // to provide preprocess functions even if they are not the owner of the current hook.
  315.           $prefixes += module_list();
  316.         }
  317.         elseif ($type == 'theme_engine') {
  318.           // Theme engines get an extra set that come before the normally named preprocessors.
  319.           $prefixes[] = $name .'_engine';
  320.           // The theme engine also registers on behalf of the theme. The theme or engine name can be used.
  321.           $prefixes[] = $name;
  322.           $prefixes[] = $theme;
  323.         }
  324.         else {
  325.           // This applies when the theme manually registers their own preprocessors.
  326.           $prefixes[] = $name;
  327.         }
  328.  
  329.         foreach ($prefixes as $prefix) {
  330.           if (function_exists($prefix .'_preprocess')) {
  331.             $info['preprocess functions'][] = $prefix .'_preprocess';
  332.           }
  333.           if (function_exists($prefix .'_preprocess_'. $hook)) {
  334.             $info['preprocess functions'][] = $prefix .'_preprocess_'. $hook;
  335.           }
  336.         }
  337.       }
  338.       // Check for the override flag and prevent the cached preprocess functions from being used.
  339.       // This allows themes or theme engines to remove preprocessors set earlier in the registry build.
  340.       if (!empty($info['override preprocess functions'])) {
  341.         // Flag not needed inside the registry.
  342.         unset($result[$hook]['override preprocess functions']);
  343.       }
  344.       elseif (isset($cache[$hook]['preprocess functions']) && is_array($cache[$hook]['preprocess functions'])) {
  345.         $info['preprocess functions'] = array_merge($cache[$hook]['preprocess functions'], $info['preprocess functions']);
  346.       }
  347.       $result[$hook]['preprocess functions'] = $info['preprocess functions'];
  348.     }
  349.  
  350.     // Merge the newly created theme hooks into the existing cache.
  351.     $cache = array_merge($cache, $result);
  352.   }
  353. }
  354.  
  355. /**
  356.  * Rebuild the hook theme_registry cache.
  357.  *
  358.  * @param $theme
  359.  *   The loaded $theme object.
  360.  * @param $base_theme
  361.  *   An array of loaded $theme objects representing the ancestor themes in
  362.  *   oldest first order.
  363.  * @param theme_engine
  364.  *   The name of the theme engine.
  365.  */
  366. function _theme_build_registry($theme, $base_theme, $theme_engine) {
  367.   $cache = array();
  368.   // First, process the theme hooks advertised by modules. This will
  369.   // serve as the basic registry.
  370.   foreach (module_implements('theme') as $module) {
  371.     _theme_process_registry($cache, $module, 'module', $module, drupal_get_path('module', $module));
  372.   }
  373.  
  374.   // Process each base theme.
  375.   foreach ($base_theme as $base) {
  376.     // If the theme uses a theme engine, process its hooks.
  377.     $base_path = dirname($base->filename);
  378.     if ($theme_engine) {
  379.       _theme_process_registry($cache, $theme_engine, 'base_theme_engine', $base->name, $base_path);
  380.     }
  381.     _theme_process_registry($cache, $base->name, 'base_theme', $base->name, $base_path);
  382.   }
  383.  
  384.   // And then the same thing, but for the theme.
  385.   if ($theme_engine) {
  386.     _theme_process_registry($cache, $theme_engine, 'theme_engine', $theme->name, dirname($theme->filename));
  387.   }
  388.  
  389.   // Finally, hooks provided by the theme itself.
  390.   _theme_process_registry($cache, $theme->name, 'theme', $theme->name, dirname($theme->filename));
  391.  
  392.   // Let modules alter the registry
  393.   drupal_alter('theme_registry', $cache);
  394.   return $cache;
  395. }
  396.  
  397. /**
  398.  * Provides a list of currently available themes.
  399.  *
  400.  * If the database is active then it will be retrieved from the database.
  401.  * Otherwise it will retrieve a new list.
  402.  *
  403.  * @param $refresh
  404.  *   Whether to reload the list of themes from the database.
  405.  * @return
  406.  *   An array of the currently available themes.
  407.  */
  408. function list_themes($refresh = FALSE) {
  409.   static $list = array();
  410.  
  411.   if ($refresh) {
  412.     $list = array();
  413.   }
  414.  
  415.   if (empty($list)) {
  416.     $list = array();
  417.     $themes = array();
  418.     // Extract from the database only when it is available.
  419.     // Also check that the site is not in the middle of an install or update.
  420.     if (db_is_active() && !defined('MAINTENANCE_MODE')) {
  421.       $result = db_query("SELECT * FROM {system} WHERE type = '%s'", 'theme');
  422.       while ($theme = db_fetch_object($result)) {
  423.         if (file_exists($theme->filename)) {
  424.           $theme->info = unserialize($theme->info);
  425.           $themes[] = $theme;
  426.         }
  427.       }
  428.     }
  429.     else {
  430.       // Scan the installation when the database should not be read.
  431.       $themes = _system_theme_data();
  432.     }
  433.  
  434.     foreach ($themes as $theme) {
  435.       foreach ($theme->info['stylesheets'] as $media => $stylesheets) {
  436.         foreach ($stylesheets as $stylesheet => $path) {
  437.           if (file_exists($path)) {
  438.             $theme->stylesheets[$media][$stylesheet] = $path;
  439.           }
  440.         }
  441.       }
  442.       foreach ($theme->info['scripts'] as $script => $path) {
  443.         if (file_exists($path)) {
  444.           $theme->scripts[$script] = $path;
  445.         }
  446.       }
  447.       if (isset($theme->info['engine'])) {
  448.         $theme->engine = $theme->info['engine'];
  449.       }
  450.       if (isset($theme->info['base theme'])) {
  451.         $theme->base_theme = $theme->info['base theme'];
  452.       }
  453.       // Status is normally retrieved from the database. Add zero values when
  454.       // read from the installation directory to prevent notices.
  455.       if (!isset($theme->status)) {
  456.         $theme->status = 0;
  457.       }
  458.       $list[$theme->name] = $theme;
  459.     }
  460.   }
  461.  
  462.   return $list;
  463. }
  464.  
  465. /**
  466.  * Generate the themed output.
  467.  *
  468.  * All requests for theme hooks must go through this function. It examines
  469.  * the request and routes it to the appropriate theme function. The theme
  470.  * registry is checked to determine which implementation to use, which may
  471.  * be a function or a template.
  472.  *
  473.  * If the implementation is a function, it is executed and its return value
  474.  * passed along.
  475.  *
  476.  * If the implementation is a template, the arguments are converted to a
  477.  * $variables array. This array is then modified by the module implementing
  478.  * the hook, theme engine (if applicable) and the theme. The following
  479.  * functions may be used to modify the $variables array. They are processed in
  480.  * this order when available:
  481.  *
  482.  * - template_preprocess(&$variables)
  483.  *   This sets a default set of variables for all template implementations.
  484.  *
  485.  * - template_preprocess_HOOK(&$variables)
  486.  *   This is the first preprocessor called specific to the hook; it should be
  487.  *   implemented by the module that registers it.
  488.  *
  489.  * - MODULE_preprocess(&$variables)
  490.  *   This will be called for all templates; it should only be used if there
  491.  *   is a real need. It's purpose is similar to template_preprocess().
  492.  *
  493.  * - MODULE_preprocess_HOOK(&$variables)
  494.  *   This is for modules that want to alter or provide extra variables for
  495.  *   theming hooks not registered to itself. For example, if a module named
  496.  *   "foo" wanted to alter the $submitted variable for the hook "node" a
  497.  *   preprocess function of foo_preprocess_node() can be created to intercept
  498.  *   and alter the variable.
  499.  *
  500.  * - ENGINE_engine_preprocess(&$variables)
  501.  *   This function should only be implemented by theme engines and exists
  502.  *   so that it can set necessary variables for all hooks.
  503.  *
  504.  * - ENGINE_engine_preprocess_HOOK(&$variables)
  505.  *   This is the same as the previous function, but it is called for a single
  506.  *   theming hook.
  507.  *
  508.  * - ENGINE_preprocess(&$variables)
  509.  *   This is meant to be used by themes that utilize a theme engine. It is
  510.  *   provided so that the preprocessor is not locked into a specific theme.
  511.  *   This makes it easy to share and transport code but theme authors must be
  512.  *   careful to prevent fatal re-declaration errors when using sub-themes that
  513.  *   have their own preprocessor named exactly the same as its base theme. In
  514.  *   the default theme engine (PHPTemplate), sub-themes will load their own
  515.  *   template.php file in addition to the one used for its parent theme. This
  516.  *   increases the risk for these errors. A good practice is to use the engine
  517.  *   name for the base theme and the theme name for the sub-themes to minimize
  518.  *   this possibility.
  519.  *
  520.  * - ENGINE_preprocess_HOOK(&$variables)
  521.  *   The same applies from the previous function, but it is called for a
  522.  *   specific hook.
  523.  *
  524.  * - THEME_preprocess(&$variables)
  525.  *   These functions are based upon the raw theme; they should primarily be
  526.  *   used by themes that do not use an engine or by sub-themes. It serves the
  527.  *   same purpose as ENGINE_preprocess().
  528.  *
  529.  * - THEME_preprocess_HOOK(&$variables)
  530.  *   The same applies from the previous function, but it is called for a
  531.  *   specific hook.
  532.  *
  533.  * There are two special variables that these hooks can set:
  534.  *   'template_file' and 'template_files'. These will be merged together
  535.  *   to form a list of 'suggested' alternate template files to use, in
  536.  *   reverse order of priority. template_file will always be a higher
  537.  *   priority than items in template_files. theme() will then look for these
  538.  *   files, one at a time, and use the first one
  539.  *   that exists.
  540.  * @param $hook
  541.  *   The name of the theme function to call. May be an array, in which
  542.  *   case the first hook that actually has an implementation registered
  543.  *   will be used. This can be used to choose 'fallback' theme implementations,
  544.  *   so that if the specific theme hook isn't implemented anywhere, a more
  545.  *   generic one will be used. This can allow themes to create specific theme
  546.  *   implementations for named objects.
  547.  * @param ...
  548.  *   Additional arguments to pass along to the theme function.
  549.  * @return
  550.  *   An HTML string that generates the themed output.
  551.  */
  552. function theme() {
  553.   $args = func_get_args();
  554.   $hook = array_shift($args);
  555.  
  556.   static $hooks = NULL;
  557.   if (!isset($hooks)) {
  558.     init_theme();
  559.     $hooks = theme_get_registry();
  560.   }
  561.  
  562.   if (is_array($hook)) {
  563.     foreach ($hook as $candidate) {
  564.       if (isset($hooks[$candidate])) {
  565.         break;
  566.       }
  567.     }
  568.     $hook = $candidate;
  569.   }
  570.  
  571.   if (!isset($hooks[$hook])) {
  572.     return;
  573.   }
  574.  
  575.   $info = $hooks[$hook];
  576.   global $theme_path;
  577.   $temp = $theme_path;
  578.   // point path_to_theme() to the currently used theme path:
  579.   $theme_path = $hooks[$hook]['theme path'];
  580.  
  581.   // Include a file if the theme function or preprocess function is held elsewhere.
  582.   if (!empty($info['file'])) {
  583.     $include_file = $info['file'];
  584.     if (isset($info['path'])) {
  585.       $include_file = $info['path'] .'/'. $include_file;
  586.     }
  587.     include_once($include_file);
  588.   }
  589.   if (isset($info['function'])) {
  590.     // The theme call is a function.
  591.     $output = call_user_func_array($info['function'], $args);
  592.   }
  593.   else {
  594.     // The theme call is a template.
  595.     $variables = array(
  596.       'template_files' => array()
  597.     );
  598.     if (!empty($info['arguments'])) {
  599.       $count = 0;
  600.       foreach ($info['arguments'] as $name => $default) {
  601.         $variables[$name] = isset($args[$count]) ? $args[$count] : $default;
  602.         $count++;
  603.       }
  604.     }
  605.  
  606.     // default render function and extension.
  607.     $render_function = 'theme_render_template';
  608.     $extension = '.tpl.php';
  609.  
  610.     // Run through the theme engine variables, if necessary
  611.     global $theme_engine;
  612.     if (isset($theme_engine)) {
  613.       // If theme or theme engine is implementing this, it may have
  614.       // a different extension and a different renderer.
  615.       if ($hooks[$hook]['type'] != 'module') {
  616.         if (function_exists($theme_engine .'_render_template')) {
  617.           $render_function = $theme_engine .'_render_template';
  618.         }
  619.         $extension_function = $theme_engine .'_extension';
  620.         if (function_exists($extension_function)) {
  621.           $extension = $extension_function();
  622.         }
  623.       }
  624.     }
  625.  
  626.     if (isset($info['preprocess functions']) && is_array($info['preprocess functions'])) {
  627.       // This construct ensures that we can keep a reference through
  628.       // call_user_func_array.
  629.       $args = array(&$variables, $hook);
  630.       foreach ($info['preprocess functions'] as $preprocess_function) {
  631.         if (function_exists($preprocess_function)) {
  632.           call_user_func_array($preprocess_function, $args);
  633.         }
  634.       }
  635.     }
  636.  
  637.     // Get suggestions for alternate templates out of the variables
  638.     // that were set. This lets us dynamically choose a template
  639.     // from a list. The order is FILO, so this array is ordered from
  640.     // least appropriate first to most appropriate last.
  641.     $suggestions = array();
  642.  
  643.     if (isset($variables['template_files'])) {
  644.       $suggestions = $variables['template_files'];
  645.     }
  646.     if (isset($variables['template_file'])) {
  647.       $suggestions[] = $variables['template_file'];
  648.     }
  649.  
  650.     if ($suggestions) {
  651.       $template_file = drupal_discover_template($info['theme paths'], $suggestions, $extension);
  652.     }
  653.  
  654.     if (empty($template_file)) {
  655.       $template_file = $hooks[$hook]['template'] . $extension;
  656.       if (isset($hooks[$hook]['path'])) {
  657.         $template_file = $hooks[$hook]['path'] .'/'. $template_file;
  658.       }
  659.     }
  660.     $output = $render_function($template_file, $variables);
  661.   }
  662.   // restore path_to_theme()
  663.   $theme_path = $temp;
  664.   return $output;
  665. }
  666.  
  667. /**
  668.  * Choose which template file to actually render. These are all suggested
  669.  * templates from themes and modules. Theming implementations can occur on
  670.  * multiple levels. All paths are checked to account for this.
  671.  */
  672. function drupal_discover_template($paths, $suggestions, $extension = '.tpl.php') {
  673.   global $theme_engine;
  674.  
  675.   // Loop through all paths and suggestions in FIFO order.
  676.   $suggestions = array_reverse($suggestions);
  677.   $paths = array_reverse($paths);
  678.   foreach ($suggestions as $suggestion) {
  679.     if (!empty($suggestion)) {
  680.       foreach ($paths as $path) {
  681.         if (file_exists($file = $path .'/'. $suggestion . $extension)) {
  682.           return $file;
  683.         }
  684.       }
  685.     }
  686.   }
  687. }
  688.  
  689. /**
  690.  * Return the path to the currently selected theme.
  691.  */
  692. function path_to_theme() {
  693.   global $theme_path;
  694.  
  695.   if (!isset($theme_path)) {
  696.     init_theme();
  697.   }
  698.  
  699.   return $theme_path;
  700. }
  701.  
  702. /**
  703.  * Find overridden theme functions. Called by themes and/or theme engines to
  704.  * easily discover theme functions.
  705.  *
  706.  * @param $cache
  707.  *   The existing cache of theme hooks to test against.
  708.  * @param $prefixes
  709.  *   An array of prefixes to test, in reverse order of importance.
  710.  *
  711.  * @return $templates
  712.  *   The functions found, suitable for returning from hook_theme;
  713.  */
  714. function drupal_find_theme_functions($cache, $prefixes) {
  715.   $templates = array();
  716.   $functions = get_defined_functions();
  717.  
  718.   foreach ($cache as $hook => $info) {
  719.     foreach ($prefixes as $prefix) {
  720.       if (!empty($info['pattern'])) {
  721.         $matches = preg_grep('/^'. $prefix .'_'. $info['pattern'] .'/', $functions['user']);
  722.         if ($matches) {
  723.           foreach ($matches as $match) {
  724.             $new_hook = str_replace($prefix .'_', '', $match);
  725.             $templates[$new_hook] = array(
  726.               'function' => $match,
  727.               'arguments' => $info['arguments'],
  728.             );
  729.           }
  730.         }
  731.       }
  732.       if (function_exists($prefix .'_'. $hook)) {
  733.         $templates[$hook] = array(
  734.           'function' => $prefix .'_'. $hook,
  735.         );
  736.       }
  737.     }
  738.   }
  739.  
  740.   return $templates;
  741. }
  742.  
  743. /**
  744.  * Find overridden theme templates. Called by themes and/or theme engines to
  745.  * easily discover templates.
  746.  *
  747.  * @param $cache
  748.  *   The existing cache of theme hooks to test against.
  749.  * @param $extension
  750.  *   The extension that these templates will have.
  751.  * @param $path
  752.  *   The path to search.
  753.  */
  754. function drupal_find_theme_templates($cache, $extension, $path) {
  755.   $templates = array();
  756.  
  757.   // Collect paths to all sub-themes grouped by base themes. These will be
  758.   // used for filtering. This allows base themes to have sub-themes in its
  759.   // folder hierarchy without affecting the base themes template discovery.
  760.   $theme_paths = array();
  761.   foreach (list_themes() as $theme_info) {
  762.     if (!empty($theme_info->base_theme)) {
  763.       $theme_paths[$theme_info->base_theme][$theme_info->name] = dirname($theme_info->filename);
  764.     }
  765.   }
  766.   foreach ($theme_paths as $basetheme => $subthemes) {
  767.     foreach ($subthemes as $subtheme => $subtheme_path) {
  768.       if (isset($theme_paths[$subtheme])) {
  769.         $theme_paths[$basetheme] = array_merge($theme_paths[$basetheme], $theme_paths[$subtheme]);
  770.       }
  771.     }
  772.   }
  773.   global $theme;
  774.   $subtheme_paths = isset($theme_paths[$theme]) ? $theme_paths[$theme] : array();
  775.  
  776.   // Escape the periods in the extension.
  777.   $regex = str_replace('.', '\.', $extension) .'$';
  778.   // Because drupal_system_listing works the way it does, we check for real
  779.   // templates separately from checking for patterns.
  780.   $files = drupal_system_listing($regex, $path, 'name', 0);
  781.   foreach ($files as $template => $file) {
  782.     // Ignore sub-theme templates for the current theme.
  783.     if (strpos($file->filename, str_replace($subtheme_paths, '', $file->filename)) !== 0) {
  784.       continue;
  785.     }
  786.     // Chop off the remaining extensions if there are any. $template already
  787.     // has the rightmost extension removed, but there might still be more,
  788.     // such as with .tpl.php, which still has .tpl in $template at this point.
  789.     if (($pos = strpos($template, '.')) !== FALSE) {
  790.       $template = substr($template, 0, $pos);
  791.     }
  792.     // Transform - in filenames to _ to match function naming scheme
  793.     // for the purposes of searching.
  794.     $hook = strtr($template, '-', '_');
  795.     if (isset($cache[$hook])) {
  796.       $templates[$hook] = array(
  797.         'template' => $template,
  798.         'path' => dirname($file->filename),
  799.       );
  800.     }
  801.   }
  802.  
  803.   $patterns = array_keys($files);
  804.  
  805.   foreach ($cache as $hook => $info) {
  806.     if (!empty($info['pattern'])) {
  807.       // Transform _ in pattern to - to match file naming scheme
  808.       // for the purposes of searching.
  809.       $pattern = strtr($info['pattern'], '_', '-');
  810.  
  811.       $matches = preg_grep('/^'. $pattern .'/', $patterns);
  812.       if ($matches) {
  813.         foreach ($matches as $match) {
  814.           $file = substr($match, 0, strpos($match, '.'));
  815.           // Put the underscores back in for the hook name and register this pattern.
  816.           $templates[strtr($file, '-', '_')] = array(
  817.             'template' => $file,
  818.             'path' => dirname($files[$match]->filename),
  819.             'arguments' => $info['arguments'],
  820.           );
  821.         }
  822.       }
  823.     }
  824.   }
  825.   return $templates;
  826. }
  827.  
  828. /**
  829.  * Retrieve an associative array containing the settings for a theme.
  830.  *
  831.  * The final settings are arrived at by merging the default settings,
  832.  * the site-wide settings, and the settings defined for the specific theme.
  833.  * If no $key was specified, only the site-wide theme defaults are retrieved.
  834.  *
  835.  * The default values for each of settings are also defined in this function.
  836.  * To add new settings, add their default values here, and then add form elements
  837.  * to system_theme_settings() in system.module.
  838.  *
  839.  * @param $key
  840.  *  The template/style value for a given theme.
  841.  *
  842.  * @return
  843.  *   An associative array containing theme settings.
  844.  */
  845. function theme_get_settings($key = NULL) {
  846.   $defaults = array(
  847.     'mission'                       =>  '',
  848.     'default_logo'                  =>  1,
  849.     'logo_path'                     =>  '',
  850.     'default_favicon'               =>  1,
  851.     'favicon_path'                  =>  '',
  852.     'primary_links'                 =>  1,
  853.     'secondary_links'               =>  1,
  854.     'toggle_logo'                   =>  1,
  855.     'toggle_favicon'                =>  1,
  856.     'toggle_name'                   =>  1,
  857.     'toggle_search'                 =>  1,
  858.     'toggle_slogan'                 =>  0,
  859.     'toggle_mission'                =>  1,
  860.     'toggle_node_user_picture'      =>  0,
  861.     'toggle_comment_user_picture'   =>  0,
  862.     'toggle_primary_links'          =>  1,
  863.     'toggle_secondary_links'        =>  1,
  864.   );
  865.  
  866.   if (module_exists('node')) {
  867.     foreach (node_get_types() as $type => $name) {
  868.       $defaults['toggle_node_info_'. $type] = 1;
  869.     }
  870.   }
  871.   $settings = array_merge($defaults, variable_get('theme_settings', array()));
  872.  
  873.   if ($key) {
  874.     $settings = array_merge($settings, variable_get(str_replace('/', '_', 'theme_'. $key .'_settings'), array()));
  875.   }
  876.  
  877.   // Only offer search box if search.module is enabled.
  878.   if (!module_exists('search') || !user_access('search content')) {
  879.     $settings['toggle_search'] = 0;
  880.   }
  881.  
  882.   return $settings;
  883. }
  884.  
  885. /**
  886.  * Retrieve a setting for the current theme.
  887.  * This function is designed for use from within themes & engines
  888.  * to determine theme settings made in the admin interface.
  889.  *
  890.  * Caches values for speed (use $refresh = TRUE to refresh cache)
  891.  *
  892.  * @param $setting_name
  893.  *  The name of the setting to be retrieved.
  894.  *
  895.  * @param $refresh
  896.  *  Whether to reload the cache of settings.
  897.  *
  898.  * @return
  899.  *   The value of the requested setting, NULL if the setting does not exist.
  900.  */
  901. function theme_get_setting($setting_name, $refresh = FALSE) {
  902.   global $theme_key;
  903.   static $settings;
  904.  
  905.   if (empty($settings) || $refresh) {
  906.     $settings = theme_get_settings($theme_key);
  907.  
  908.     $themes = list_themes();
  909.     $theme_object = $themes[$theme_key];
  910.  
  911.     if ($settings['mission'] == '') {
  912.       $settings['mission'] = variable_get('site_mission', '');
  913.     }
  914.  
  915.     if (!$settings['toggle_mission']) {
  916.       $settings['mission'] = '';
  917.     }
  918.  
  919.     if ($settings['toggle_logo']) {
  920.       if ($settings['default_logo']) {
  921.         $settings['logo'] = base_path() . dirname($theme_object->filename) .'/logo.png';
  922.       }
  923.       elseif ($settings['logo_path']) {
  924.         $settings['logo'] = base_path() . $settings['logo_path'];
  925.       }
  926.     }
  927.  
  928.     if ($settings['toggle_favicon']) {
  929.       if ($settings['default_favicon']) {
  930.         if (file_exists($favicon = dirname($theme_object->filename) .'/favicon.ico')) {
  931.           $settings['favicon'] = base_path() . $favicon;
  932.         }
  933.         else {
  934.           $settings['favicon'] = base_path() .'misc/favicon.ico';
  935.         }
  936.       }
  937.       elseif ($settings['favicon_path']) {
  938.         $settings['favicon'] = base_path() . $settings['favicon_path'];
  939.       }
  940.       else {
  941.         $settings['toggle_favicon'] = FALSE;
  942.       }
  943.     }
  944.   }
  945.  
  946.   return isset($settings[$setting_name]) ? $settings[$setting_name] : NULL;
  947. }
  948.  
  949. /**
  950.  * Render a system default template, which is essentially a PHP template.
  951.  *
  952.  * @param $file
  953.  *   The filename of the template to render.
  954.  * @param $variables
  955.  *   A keyed array of variables that will appear in the output.
  956.  *
  957.  * @return
  958.  *   The output generated by the template.
  959.  */
  960. function theme_render_template($file, $variables) {
  961.   extract($variables, EXTR_SKIP);  // Extract the variables to a local namespace
  962.   ob_start();                      // Start output buffering
  963.   include "./$file";               // Include the file
  964.   $contents = ob_get_contents();   // Get the contents of the buffer
  965.   ob_end_clean();                  // End buffering and discard
  966.   return $contents;                // Return the contents
  967. }
  968.  
  969. /**
  970.  * @defgroup themeable Default theme implementations
  971.  * @{
  972.  * Functions and templates that present output to the user, and can be
  973.  * implemented by themes.
  974.  *
  975.  * Drupal's presentation layer is a pluggable system known as the theme
  976.  * layer. Each theme can take control over most of Drupal's output, and
  977.  * has complete control over the CSS.
  978.  *
  979.  * Inside Drupal, the theme layer is utilized by the use of the theme()
  980.  * function, which is passed the name of a component (the theme hook)
  981.  * and several arguments. For example, theme('table', $header, $rows);
  982.  * Additionally, the theme() function can take an array of theme
  983.  * hooks, which can be used to provide 'fallback' implementations to
  984.  * allow for more specific control of output. For example, the function:
  985.  * theme(array('table__foo', 'table'), $header, $rows) would look to see if
  986.  * 'table__foo' is registered anywhere; if it is not, it would 'fall back'
  987.  * to the generic 'table' implementation. This can be used to attach specific
  988.  * theme functions to named objects, allowing the themer more control over
  989.  * specific types of output.
  990.  *
  991.  * As of Drupal 6, every theme hook is required to be registered by the
  992.  * module that owns it, so that Drupal can tell what to do with it and
  993.  * to make it simple for themes to identify and override the behavior
  994.  * for these calls.
  995.  *
  996.  * The theme hooks are registered via hook_theme(), which returns an
  997.  * array of arrays with information about the hook. It describes the
  998.  * arguments the function or template will need, and provides
  999.  * defaults for the template in case they are not filled in. If the default
  1000.  * implementation is a function, by convention it is named theme_HOOK().
  1001.  *
  1002.  * Each module should provide a default implementation for themes that
  1003.  * it registers. This implementation may be either a function or a template;
  1004.  * if it is a function it must be specified via hook_theme(). By convention,
  1005.  * default implementations of theme hooks are named theme_HOOK. Default
  1006.  * template implementations are stored in the module directory.
  1007.  *
  1008.  * Drupal's default template renderer is a simple PHP parsing engine that
  1009.  * includes the template and stores the output. Drupal's theme engines
  1010.  * can provide alternate template engines, such as XTemplate, Smarty and
  1011.  * PHPTal. The most common template engine is PHPTemplate (included with
  1012.  * Drupal and implemented in phptemplate.engine, which uses Drupal's default
  1013.  * template renderer.
  1014.  *
  1015.  * In order to create theme-specific implementations of these hooks,
  1016.  * themes can implement their own version of theme hooks, either as functions
  1017.  * or templates. These implementations will be used instead of the default
  1018.  * implementation. If using a pure .theme without an engine, the .theme is
  1019.  * required to implement its own version of hook_theme() to tell Drupal what
  1020.  * it is implementing; themes utilizing an engine will have their well-named
  1021.  * theming functions automatically registered for them. While this can vary
  1022.  * based upon the theme engine, the standard set by phptemplate is that theme
  1023.  * functions should be named either phptemplate_HOOK or THEMENAME_HOOK. For
  1024.  * example, for Drupal's default theme (Garland) to implement the 'table' hook,
  1025.  * the phptemplate.engine would find phptemplate_table() or garland_table().
  1026.  * The ENGINE_HOOK() syntax is preferred, as this can be used by sub-themes
  1027.  * (which are themes that share code but use different stylesheets).
  1028.  *
  1029.  * The theme system is described and defined in theme.inc.
  1030.  *
  1031.  * @see theme()
  1032.  * @see hook_theme()
  1033.  */
  1034.  
  1035. /**
  1036.  * Formats text for emphasized display in a placeholder inside a sentence.
  1037.  * Used automatically by t().
  1038.  *
  1039.  * @param $text
  1040.  *   The text to format (plain-text).
  1041.  * @return
  1042.  *   The formatted text (html).
  1043.  */
  1044. function theme_placeholder($text) {
  1045.   return '<em>'. check_plain($text) .'</em>';
  1046. }
  1047.  
  1048. /**
  1049.  * Return a themed set of status and/or error messages. The messages are grouped
  1050.  * by type.
  1051.  *
  1052.  * @param $display
  1053.  *   (optional) Set to 'status' or 'error' to display only messages of that type.
  1054.  *
  1055.  * @return
  1056.  *   A string containing the messages.
  1057.  */
  1058. function theme_status_messages($display = NULL) {
  1059.   $output = '';
  1060.   foreach (drupal_get_messages($display) as $type => $messages) {
  1061.     $output .= "<div class=\"messages $type\">\n";
  1062.     if (count($messages) > 1) {
  1063.       $output .= " <ul>\n";
  1064.       foreach ($messages as $message) {
  1065.         $output .= '  <li>'. $message ."</li>\n";
  1066.       }
  1067.       $output .= " </ul>\n";
  1068.     }
  1069.     else {
  1070.       $output .= $messages[0];
  1071.     }
  1072.     $output .= "</div>\n";
  1073.   }
  1074.   return $output;
  1075. }
  1076.  
  1077. /**
  1078.  * Return a themed set of links.
  1079.  *
  1080.  * @param $links
  1081.  *   A keyed array of links to be themed.
  1082.  * @param $attributes
  1083.  *   A keyed array of attributes
  1084.  * @return
  1085.  *   A string containing an unordered list of links.
  1086.  */
  1087. function theme_links($links, $attributes = array('class' => 'links')) {
  1088.   $output = '';
  1089.  
  1090.   if (count($links) > 0) {
  1091.     $output = '<ul'. drupal_attributes($attributes) .'>';
  1092.  
  1093.     $num_links = count($links);
  1094.     $i = 1;
  1095.  
  1096.     foreach ($links as $key => $link) {
  1097.       $class = $key;
  1098.  
  1099.       // Add first, last and active classes to the list of links to help out themers.
  1100.       if ($i == 1) {
  1101.         $class .= ' first';
  1102.       }
  1103.       if ($i == $num_links) {
  1104.         $class .= ' last';
  1105.       }
  1106.       if (isset($link['href']) && $link['href'] == $_GET['q']) {
  1107.         $class .= ' active';
  1108.       }
  1109.       $output .= '<li class="'. $class .'">';
  1110.  
  1111.       if (isset($link['href'])) {
  1112.         // Pass in $link as $options, they share the same keys.
  1113.         $output .= l($link['title'], $link['href'], $link);
  1114.       }
  1115.       else if (!empty($link['title'])) {
  1116.         // Some links are actually not links, but we wrap these in <span> for adding title and class attributes
  1117.         if (empty($link['html'])) {
  1118.           $link['title'] = check_plain($link['title']);
  1119.         }
  1120.         $span_attributes = '';
  1121.         if (isset($link['attributes'])) {
  1122.           $span_attributes = drupal_attributes($link['attributes']);
  1123.         }
  1124.         $output .= '<span'. $span_attributes .'>'. $link['title'] .'</span>';
  1125.       }
  1126.  
  1127.       $i++;
  1128.       $output .= "</li>\n";
  1129.     }
  1130.  
  1131.     $output .= '</ul>';
  1132.   }
  1133.  
  1134.   return $output;
  1135. }
  1136.  
  1137. /**
  1138.  * Return a themed image.
  1139.  *
  1140.  * @param $path
  1141.  *   Either the path of the image file (relative to base_path()) or a full URL.
  1142.  * @param $alt
  1143.  *   The alternative text for text-based browsers.
  1144.  * @param $title
  1145.  *   The title text is displayed when the image is hovered in some popular browsers.
  1146.  * @param $attributes
  1147.  *   Associative array of attributes to be placed in the img tag.
  1148.  * @param $getsize
  1149.  *   If set to TRUE, the image's dimension are fetched and added as width/height attributes.
  1150.  * @return
  1151.  *   A string containing the image tag.
  1152.  */
  1153. function theme_image($path, $alt = '', $title = '', $attributes = NULL, $getsize = TRUE) {
  1154.   if (!$getsize || (is_file($path) && (list($width, $height, $type, $image_attributes) = @getimagesize($path)))) {
  1155.     $attributes = drupal_attributes($attributes);
  1156.     $url = (url($path) == $path) ? $path : (base_path() . $path);
  1157.     return '<img src="'. check_url($url) .'" alt="'. check_plain($alt) .'" title="'. check_plain($title) .'" '. (isset($image_attributes) ? $image_attributes : '') . $attributes .' />';
  1158.   }
  1159. }
  1160.  
  1161. /**
  1162.  * Return a themed breadcrumb trail.
  1163.  *
  1164.  * @param $breadcrumb
  1165.  *   An array containing the breadcrumb links.
  1166.  * @return a string containing the breadcrumb output.
  1167.  */
  1168. function theme_breadcrumb($breadcrumb) {
  1169.   if (!empty($breadcrumb)) {
  1170.     return '<div class="breadcrumb">'. implode(' ┬╗ ', $breadcrumb) .'</div>';
  1171.   }
  1172. }
  1173.  
  1174. /**
  1175.  * Return a themed help message.
  1176.  *
  1177.  * @return a string containing the helptext for the current page.
  1178.  */
  1179. function theme_help() {
  1180.   if ($help = menu_get_active_help()) {
  1181.     return '<div class="help">'. $help .'</div>';
  1182.   }
  1183. }
  1184.  
  1185. /**
  1186.  * Return a themed submenu, typically displayed under the tabs.
  1187.  *
  1188.  * @param $links
  1189.  *   An array of links.
  1190.  */
  1191. function theme_submenu($links) {
  1192.   return '<div class="submenu">'. implode(' | ', $links) .'</div>';
  1193. }
  1194.  
  1195. /**
  1196.  * Return a themed table.
  1197.  *
  1198.  * @param $header
  1199.  *   An array containing the table headers. Each element of the array can be
  1200.  *   either a localized string or an associative array with the following keys:
  1201.  *   - "data": The localized title of the table column.
  1202.  *   - "field": The database field represented in the table column (required if
  1203.  *     user is to be able to sort on this column).
  1204.  *   - "sort": A default sort order for this column ("asc" or "desc").
  1205.  *   - Any HTML attributes, such as "colspan", to apply to the column header cell.
  1206.  * @param $rows
  1207.  *   An array of table rows. Every row is an array of cells, or an associative
  1208.  *   array with the following keys:
  1209.  *   - "data": an array of cells
  1210.  *   - Any HTML attributes, such as "class", to apply to the table row.
  1211.  *
  1212.  *   Each cell can be either a string or an associative array with the following keys:
  1213.  *   - "data": The string to display in the table cell.
  1214.  *   - "header": Indicates this cell is a header.
  1215.  *   - Any HTML attributes, such as "colspan", to apply to the table cell.
  1216.  *
  1217.  *   Here's an example for $rows:
  1218.  *   @verbatim
  1219.  *   $rows = array(
  1220.  *     // Simple row
  1221.  *     array(
  1222.  *       'Cell 1', 'Cell 2', 'Cell 3'
  1223.  *     ),
  1224.  *     // Row with attributes on the row and some of its cells.
  1225.  *     array(
  1226.  *       'data' => array('Cell 1', array('data' => 'Cell 2', 'colspan' => 2)), 'class' => 'funky'
  1227.  *     )
  1228.  *   );
  1229.  *   @endverbatim
  1230.  *
  1231.  * @param $attributes
  1232.  *   An array of HTML attributes to apply to the table tag.
  1233.  * @param $caption
  1234.  *   A localized string to use for the <caption> tag.
  1235.  * @return
  1236.  *   An HTML string representing the table.
  1237.  */
  1238. function theme_table($header, $rows, $attributes = array(), $caption = NULL) {
  1239.  
  1240.   // Add sticky headers, if applicable.
  1241.   if (count($header)) {
  1242.     drupal_add_js('misc/tableheader.js');
  1243.     // Add 'sticky-enabled' class to the table to identify it for JS.
  1244.     // This is needed to target tables constructed by this function.
  1245.     $attributes['class'] = empty($attributes['class']) ? 'sticky-enabled' : ($attributes['class'] .' sticky-enabled');
  1246.   }
  1247.  
  1248.   $output = '<table'. drupal_attributes($attributes) .">\n";
  1249.  
  1250.   if (isset($caption)) {
  1251.     $output .= '<caption>'. $caption ."</caption>\n";
  1252.   }
  1253.  
  1254.   // Format the table header:
  1255.   if (count($header)) {
  1256.     $ts = tablesort_init($header);
  1257.     // HTML requires that the thead tag has tr tags in it follwed by tbody
  1258.     // tags. Using ternary operator to check and see if we have any rows.
  1259.     $output .= (count($rows) ? ' <thead><tr>' : ' <tr>');
  1260.     foreach ($header as $cell) {
  1261.       $cell = tablesort_header($cell, $header, $ts);
  1262.       $output .= _theme_table_cell($cell, TRUE);
  1263.     }
  1264.     // Using ternary operator to close the tags based on whether or not there are rows
  1265.     $output .= (count($rows) ? " </tr></thead>\n" : "</tr>\n");
  1266.   }
  1267.   else {
  1268.     $ts = array();
  1269.   }
  1270.  
  1271.   // Format the table rows:
  1272.   if (count($rows)) {
  1273.     $output .= "<tbody>\n";
  1274.     $flip = array('even' => 'odd', 'odd' => 'even');
  1275.     $class = 'even';
  1276.     foreach ($rows as $number => $row) {
  1277.       $attributes = array();
  1278.  
  1279.       // Check if we're dealing with a simple or complex row
  1280.       if (isset($row['data'])) {
  1281.         foreach ($row as $key => $value) {
  1282.           if ($key == 'data') {
  1283.             $cells = $value;
  1284.           }
  1285.           else {
  1286.             $attributes[$key] = $value;
  1287.           }
  1288.         }
  1289.       }
  1290.       else {
  1291.         $cells = $row;
  1292.       }
  1293.       if (count($cells)) {
  1294.         // Add odd/even class
  1295.         $class = $flip[$class];
  1296.         if (isset($attributes['class'])) {
  1297.           $attributes['class'] .= ' '. $class;
  1298.         }
  1299.         else {
  1300.           $attributes['class'] = $class;
  1301.         }
  1302.  
  1303.         // Build row
  1304.         $output .= ' <tr'. drupal_attributes($attributes) .'>';
  1305.         $i = 0;
  1306.         foreach ($cells as $cell) {
  1307.           $cell = tablesort_cell($cell, $header, $ts, $i++);
  1308.           $output .= _theme_table_cell($cell);
  1309.         }
  1310.         $output .= " </tr>\n";
  1311.       }
  1312.     }
  1313.     $output .= "</tbody>\n";
  1314.   }
  1315.  
  1316.   $output .= "</table>\n";
  1317.   return $output;
  1318. }
  1319.  
  1320. /**
  1321.  * Returns a header cell for tables that have a select all functionality.
  1322.  */
  1323. function theme_table_select_header_cell() {
  1324.   drupal_add_js('misc/tableselect.js');
  1325.  
  1326.   return array('class' => 'select-all');
  1327. }
  1328.  
  1329. /**
  1330.  * Return a themed sort icon.
  1331.  *
  1332.  * @param $style
  1333.  *   Set to either asc or desc. This sets which icon to show.
  1334.  * @return
  1335.  *   A themed sort icon.
  1336.  */
  1337. function theme_tablesort_indicator($style) {
  1338.   if ($style == "asc") {
  1339.     return theme('image', 'misc/arrow-asc.png', t('sort icon'), t('sort ascending'));
  1340.   }
  1341.   else {
  1342.     return theme('image', 'misc/arrow-desc.png', t('sort icon'), t('sort descending'));
  1343.   }
  1344. }
  1345.  
  1346. /**
  1347.  * Return a themed box.
  1348.  *
  1349.  * @param $title
  1350.  *   The subject of the box.
  1351.  * @param $content
  1352.  *   The content of the box.
  1353.  * @param $region
  1354.  *   The region in which the box is displayed.
  1355.  * @return
  1356.  *   A string containing the box output.
  1357.  */
  1358. function theme_box($title, $content, $region = 'main') {
  1359.   $output = '<h2 class="title">'. $title .'</h2><div>'. $content .'</div>';
  1360.   return $output;
  1361. }
  1362.  
  1363. /**
  1364.  * Return a themed marker, useful for marking new or updated
  1365.  * content.
  1366.  *
  1367.  * @param $type
  1368.  *   Number representing the marker type to display
  1369.  * @see MARK_NEW, MARK_UPDATED, MARK_READ
  1370.  * @return
  1371.  *   A string containing the marker.
  1372.  */
  1373. function theme_mark($type = MARK_NEW) {
  1374.   global $user;
  1375.   if ($user->uid) {
  1376.     if ($type == MARK_NEW) {
  1377.       return ' <span class="marker">'. t('new') .'</span>';
  1378.     }
  1379.     else if ($type == MARK_UPDATED) {
  1380.       return ' <span class="marker">'. t('updated') .'</span>';
  1381.     }
  1382.   }
  1383. }
  1384.  
  1385. /**
  1386.  * Return a themed list of items.
  1387.  *
  1388.  * @param $items
  1389.  *   An array of items to be displayed in the list. If an item is a string,
  1390.  *   then it is used as is. If an item is an array, then the "data" element of
  1391.  *   the array is used as the contents of the list item. If an item is an array
  1392.  *   with a "children" element, those children are displayed in a nested list.
  1393.  *   All other elements are treated as attributes of the list item element.
  1394.  * @param $title
  1395.  *   The title of the list.
  1396.  * @param $attributes
  1397.  *   The attributes applied to the list element.
  1398.  * @param $type
  1399.  *   The type of list to return (e.g. "ul", "ol")
  1400.  * @return
  1401.  *   A string containing the list output.
  1402.  */
  1403. function theme_item_list($items = array(), $title = NULL, $type = 'ul', $attributes = NULL) {
  1404.   $output = '<div class="item-list">';
  1405.   if (isset($title)) {
  1406.     $output .= '<h3>'. $title .'</h3>';
  1407.   }
  1408.  
  1409.   if (!empty($items)) {
  1410.     $output .= "<$type". drupal_attributes($attributes) .'>';
  1411.     $num_items = count($items);
  1412.     foreach ($items as $i => $item) {
  1413.       $attributes = array();
  1414.       $children = array();
  1415.       if (is_array($item)) {
  1416.         foreach ($item as $key => $value) {
  1417.           if ($key == 'data') {
  1418.             $data = $value;
  1419.           }
  1420.           elseif ($key == 'children') {
  1421.             $children = $value;
  1422.           }
  1423.           else {
  1424.             $attributes[$key] = $value;
  1425.           }
  1426.         }
  1427.       }
  1428.       else {
  1429.         $data = $item;
  1430.       }
  1431.       if (count($children) > 0) {
  1432.         $data .= theme_item_list($children, NULL, $type, $attributes); // Render nested list
  1433.       }
  1434.       if ($i == 0) {
  1435.         $attributes['class'] = empty($attributes['class']) ? 'first' : ($attributes['class'] .' first');
  1436.       }
  1437.       if ($i == $num_items - 1) {
  1438.         $attributes['class'] = empty($attributes['class']) ? 'last' : ($attributes['class'] .' last');
  1439.       }
  1440.       $output .= '<li'. drupal_attributes($attributes) .'>'. $data ."</li>\n";
  1441.     }
  1442.     $output .= "</$type>";
  1443.   }
  1444.   $output .= '</div>';
  1445.   return $output;
  1446. }
  1447.  
  1448. /**
  1449.  * Returns code that emits the 'more help'-link.
  1450.  */
  1451. function theme_more_help_link($url) {
  1452.   return '<div class="more-help-link">'. t('[<a href="@link">more help...</a>]', array('@link' => check_url($url))) .'</div>';
  1453. }
  1454.  
  1455. /**
  1456.  * Return code that emits an XML icon.
  1457.  * 
  1458.  * For most use cases, this function has been superseded by theme_feed_icon().
  1459.  * 
  1460.  * @see theme_feed_icon()
  1461.  * @param $url
  1462.  *   The url of the feed.
  1463.  */
  1464. function theme_xml_icon($url) {
  1465.   if ($image = theme('image', 'misc/xml.png', t('XML feed'), t('XML feed'))) {
  1466.     return '<a href="'. check_url($url) .'" class="xml-icon">'. $image .'</a>';
  1467.   }
  1468. }
  1469.  
  1470. /**
  1471.  * Return code that emits an feed icon.
  1472.  *
  1473.  * @param $url
  1474.  *   The url of the feed.
  1475.  * @param $title
  1476.  *   A descriptive title of the feed.
  1477.   */
  1478. function theme_feed_icon($url, $title) {
  1479.   if ($image = theme('image', 'misc/feed.png', t('Syndicate content'), $title)) {
  1480.     return '<a href="'. check_url($url) .'" class="feed-icon">'. $image .'</a>';
  1481.   }
  1482. }
  1483.  
  1484. /**
  1485.  * Returns code that emits the 'more' link used on blocks.
  1486.  *
  1487.  * @param $url
  1488.  *   The url of the main page
  1489.  * @param $title
  1490.  *   A descriptive verb for the link, like 'Read more'
  1491.  */
  1492. function theme_more_link($url, $title) {
  1493.   return '<div class="more-link">'. t('<a href="@link" title="@title">more</a>', array('@link' => check_url($url), '@title' => $title)) .'</div>';
  1494. }
  1495.  
  1496. /**
  1497.  * Execute hook_footer() which is run at the end of the page right before the
  1498.  * close of the body tag.
  1499.  *
  1500.  * @param $main (optional)
  1501.  *   Whether the current page is the front page of the site.
  1502.  * @return
  1503.  *   A string containing the results of the hook_footer() calls.
  1504.  */
  1505. function theme_closure($main = 0) {
  1506.   $footer = module_invoke_all('footer', $main);
  1507.   return implode("\n", $footer) . drupal_get_js('footer');
  1508. }
  1509.  
  1510. /**
  1511.  * Return a set of blocks available for the current user.
  1512.  *
  1513.  * @param $region
  1514.  *   Which set of blocks to retrieve.
  1515.  * @return
  1516.  *   A string containing the themed blocks for this region.
  1517.  */
  1518. function theme_blocks($region) {
  1519.   $output = '';
  1520.  
  1521.   if ($list = block_list($region)) {
  1522.     foreach ($list as $key => $block) {
  1523.       // $key == <i>module</i>_<i>delta</i>
  1524.       $output .= theme('block', $block);
  1525.     }
  1526.   }
  1527.  
  1528.   // Add any content assigned to this region through drupal_set_content() calls.
  1529.   $output .= drupal_get_content($region);
  1530.  
  1531.   return $output;
  1532. }
  1533.  
  1534. /**
  1535.  * Format a username.
  1536.  *
  1537.  * @param $object
  1538.  *   The user object to format, usually returned from user_load().
  1539.  * @return
  1540.  *   A string containing an HTML link to the user's page if the passed object
  1541.  *   suggests that this is a site user. Otherwise, only the username is returned.
  1542.  */
  1543. function theme_username($object) {
  1544.  
  1545.   if ($object->uid && $object->name) {
  1546.     // Shorten the name when it is too long or it will break many tables.
  1547.     if (drupal_strlen($object->name) > 20) {
  1548.       $name = drupal_substr($object->name, 0, 15) .'...';
  1549.     }
  1550.     else {
  1551.       $name = $object->name;
  1552.     }
  1553.  
  1554.     if (user_access('access user profiles')) {
  1555.       $output = l($name, 'user/'. $object->uid, array('title' => t('View user profile.')));
  1556.     }
  1557.     else {
  1558.       $output = check_plain($name);
  1559.     }
  1560.   }
  1561.   else if ($object->name) {
  1562.     // Sometimes modules display content composed by people who are
  1563.     // not registered members of the site (e.g. mailing list or news
  1564.     // aggregator modules). This clause enables modules to display
  1565.     // the true author of the content.
  1566.     if (!empty($object->homepage)) {
  1567.       $output = l($object->name, $object->homepage, array('rel' => 'nofollow'));
  1568.     }
  1569.     else {
  1570.       $output = check_plain($object->name);
  1571.     }
  1572.  
  1573.     $output .= ' ('. t('not verified') .')';
  1574.   }
  1575.   else {
  1576.     $output = variable_get('anonymous', t('Anonymous'));
  1577.   }
  1578.  
  1579.   return $output;
  1580. }
  1581.  
  1582. /**
  1583.  * Return a themed progress bar.
  1584.  *
  1585.  * @param $percent
  1586.  *   The percentage of the progress.
  1587.  * @param $message
  1588.  *   A string containing information to be displayed.
  1589.  * @return
  1590.  *   A themed HTML string representing the progress bar.
  1591.  */
  1592. function theme_progress_bar($percent, $message) {
  1593.   $output = '<div id="progress" class="progress">';
  1594.   $output .= '<div class="bar"><div class="filled" style="width: '. $percent .'%"></div></div>';
  1595.   $output .= '<div class="percentage">'. $percent .'%</div>';
  1596.   $output .= '<div class="message">'. $message .'</div>';
  1597.   $output .= '</div>';
  1598.  
  1599.   return $output;
  1600. }
  1601.  
  1602. /**
  1603.  * Create a standard indentation div. Used for drag and drop tables.
  1604.  *
  1605.  * @param $size
  1606.  *   Optional. The number of indentations to create.
  1607.  * @return
  1608.  *   A string containing indentations.
  1609.  */
  1610. function theme_indentation($size = 1) {
  1611.   $output = '';
  1612.   for ($n = 0; $n < $size; $n++) {
  1613.     $output .= '<div class="indentation"> </div>';
  1614.   }
  1615.   return $output;
  1616. }
  1617.  
  1618. /**
  1619.  * @} End of "defgroup themeable".
  1620.  */
  1621.  
  1622. function _theme_table_cell($cell, $header = FALSE) {
  1623.   $attributes = '';
  1624.  
  1625.   if (is_array($cell)) {
  1626.     $data = isset($cell['data']) ? $cell['data'] : '';
  1627.     $header |= isset($cell['header']);
  1628.     unset($cell['data']);
  1629.     unset($cell['header']);
  1630.     $attributes = drupal_attributes($cell);
  1631.   }
  1632.   else {
  1633.     $data = $cell;
  1634.   }
  1635.  
  1636.   if ($header) {
  1637.     $output = "<th$attributes>$data</th>";
  1638.   }
  1639.   else {
  1640.     $output = "<td$attributes>$data</td>";
  1641.   }
  1642.  
  1643.   return $output;
  1644. }
  1645.  
  1646. /**
  1647.  * Adds a default set of helper variables for preprocess functions and
  1648.  * templates. This comes in before any other preprocess function which makes
  1649.  * it possible to be used in default theme implementations (non-overriden
  1650.  * theme functions).
  1651.  */
  1652. function template_preprocess(&$variables, $hook) {
  1653.   global $user;
  1654.   static $count = array();
  1655.  
  1656.   // Track run count for each hook to provide zebra striping.
  1657.   // See "template_preprocess_block()" which provides the same feature specific to blocks.
  1658.   $count[$hook] = isset($count[$hook]) && is_int($count[$hook]) ? $count[$hook] : 1;
  1659.   $variables['zebra'] = ($count[$hook] % 2) ? 'odd' : 'even';
  1660.   $variables['id'] = $count[$hook]++;
  1661.  
  1662.   // Tell all templates where they are located.
  1663.   $variables['directory'] = path_to_theme();
  1664.  
  1665.   // Set default variables that depend on the database.
  1666.   $variables['is_admin']            = FALSE;
  1667.   $variables['is_front']            = FALSE;
  1668.   $variables['logged_in']           = FALSE;
  1669.   if ($variables['db_is_active'] = db_is_active()  && !defined('MAINTENANCE_MODE')) {
  1670.     // Check for administrators.
  1671.     if (user_access('access administration pages')) {
  1672.       $variables['is_admin'] = TRUE;
  1673.     }
  1674.     // Flag front page status.
  1675.     $variables['is_front'] = drupal_is_front_page();
  1676.     // Tell all templates by which kind of user they're viewed.
  1677.     $variables['logged_in'] = ($user->uid > 0);
  1678.     // Provide user object to all templates
  1679.     $variables['user'] = $user;
  1680.   }
  1681. }
  1682.  
  1683. /**
  1684.  * Process variables for page.tpl.php
  1685.  *
  1686.  * Most themes utilize their own copy of page.tpl.php. The default is located
  1687.  * inside "modules/system/page.tpl.php". Look in there for the full list of
  1688.  * variables.
  1689.  *
  1690.  * Uses the arg() function to generate a series of page template suggestions
  1691.  * based on the current path.
  1692.  *
  1693.  * Any changes to variables in this preprocessor should also be changed inside
  1694.  * template_preprocess_maintenance_page() to keep all them consistent.
  1695.  *
  1696.  * The $variables array contains the following arguments:
  1697.  * - $content
  1698.  * - $show_blocks
  1699.  *
  1700.  * @see page.tpl.php
  1701.  */
  1702. function template_preprocess_page(&$variables) {
  1703.   // Add favicon
  1704.   if (theme_get_setting('toggle_favicon')) {
  1705.     drupal_set_html_head('<link rel="shortcut icon" href="'. check_url(theme_get_setting('favicon')) .'" type="image/x-icon" />');
  1706.   }
  1707.  
  1708.   global $theme;
  1709.   // Populate all block regions.
  1710.   $regions = system_region_list($theme);
  1711.   // Load all region content assigned via blocks.
  1712.   foreach (array_keys($regions) as $region) {
  1713.     // Prevent left and right regions from rendering blocks when 'show_blocks' == FALSE.
  1714.     if (!(!$variables['show_blocks'] && ($region == 'left' || $region == 'right'))) {
  1715.       $blocks = theme('blocks', $region);
  1716.     }
  1717.     else {
  1718.       $blocks = '';
  1719.     }
  1720.     // Assign region to a region variable.
  1721.     isset($variables[$region]) ? $variables[$region] .= $blocks : $variables[$region] = $blocks;
  1722.   }
  1723.  
  1724.   // Set up layout variable.
  1725.   $variables['layout'] = 'none';
  1726.   if (!empty($variables['left'])) {
  1727.     $variables['layout'] = 'left';
  1728.   }
  1729.   if (!empty($variables['right'])) {
  1730.     $variables['layout'] = ($variables['layout'] == 'left') ? 'both' : 'right';
  1731.   }
  1732.  
  1733.   // Set mission when viewing the frontpage.
  1734.   if (drupal_is_front_page()) {
  1735.     $mission = filter_xss_admin(theme_get_setting('mission'));
  1736.   }
  1737.  
  1738.   // Construct page title
  1739.   if (drupal_get_title()) {
  1740.     $head_title = array(strip_tags(drupal_get_title()), variable_get('site_name', 'Drupal'));
  1741.   }
  1742.   else {
  1743.     $head_title = array(variable_get('site_name', 'Drupal'));
  1744.     if (variable_get('site_slogan', '')) {
  1745.       $head_title[] = variable_get('site_slogan', '');
  1746.     }
  1747.   }
  1748.   $variables['head_title']        = implode(' | ', $head_title);
  1749.   $variables['base_path']         = base_path();
  1750.   $variables['front_page']        = url();
  1751.   $variables['breadcrumb']        = theme('breadcrumb', drupal_get_breadcrumb());
  1752.   $variables['feed_icons']        = drupal_get_feeds();
  1753.   $variables['footer_message']    = filter_xss_admin(variable_get('site_footer', FALSE));
  1754.   $variables['head']              = drupal_get_html_head();
  1755.   $variables['help']              = theme('help');
  1756.   $variables['language']          = $GLOBALS['language'];
  1757.   $variables['language']->dir     = $GLOBALS['language']->direction ? 'rtl' : 'ltr';
  1758.   $variables['logo']              = theme_get_setting('logo');
  1759.   $variables['messages']          = $variables['show_messages'] ? theme('status_messages') : '';
  1760.   $variables['mission']           = isset($mission) ? $mission : '';
  1761.   $variables['primary_links']     = theme_get_setting('toggle_primary_links') ? menu_primary_links() : array();
  1762.   $variables['secondary_links']   = theme_get_setting('toggle_secondary_links') ? menu_secondary_links() : array();
  1763.   $variables['search_box']        = (theme_get_setting('toggle_search') ? drupal_get_form('search_theme_form') : '');
  1764.   $variables['site_name']         = (theme_get_setting('toggle_name') ? variable_get('site_name', 'Drupal') : '');
  1765.   $variables['site_slogan']       = (theme_get_setting('toggle_slogan') ? variable_get('site_slogan', '') : '');
  1766.   $variables['css']               = drupal_add_css();
  1767.   $variables['styles']            = drupal_get_css();
  1768.   $variables['scripts']           = drupal_get_js();
  1769.   $variables['tabs']              = theme('menu_local_tasks');
  1770.   $variables['title']             = drupal_get_title();
  1771.   // Closure should be filled last.
  1772.   $variables['closure']           = theme('closure');
  1773.  
  1774.   if ($node = menu_get_object()) {
  1775.     $variables['node'] = $node;
  1776.   }
  1777.  
  1778.   // Compile a list of classes that are going to be applied to the body element.
  1779.   // This allows advanced theming based on context (home page, node of certain type, etc.).
  1780.   $body_classes = array();
  1781.   // Add a class that tells us whether we're on the front page or not.
  1782.   $body_classes[] = $variables['is_front'] ? 'front' : 'not-front';
  1783.   // Add a class that tells us whether the page is viewed by an authenticated user or not.
  1784.   $body_classes[] = $variables['logged_in'] ? 'logged-in' : 'not-logged-in';
  1785.   // Add arg(0) to make it possible to theme the page depending on the current page
  1786.   // type (e.g. node, admin, user, etc.). To avoid illegal characters in the class,
  1787.   // we're removing everything disallowed. We are not using 'a-z' as that might leave
  1788.   // in certain international characters (e.g. German umlauts).
  1789.   $body_classes[] = preg_replace('![^abcdefghijklmnopqrstuvwxyz0-9-_]+!s', '', 'page-'. form_clean_id(drupal_strtolower(arg(0))));
  1790.   // If on an individual node page, add the node type.
  1791.   if (isset($variables['node']) && $variables['node']->type) {
  1792.     $body_classes[] = 'node-type-'. form_clean_id($variables['node']->type);
  1793.   }
  1794.   // Add information about the number of sidebars.
  1795.   if ($variables['layout'] == 'both') {
  1796.     $body_classes[] = 'two-sidebars';
  1797.   }
  1798.   elseif ($variables['layout'] == 'none') {
  1799.     $body_classes[] = 'no-sidebars';
  1800.   }
  1801.   else {
  1802.     $body_classes[] = 'one-sidebar sidebar-'. $variables['layout'];
  1803.   }
  1804.   // Implode with spaces.
  1805.   $variables['body_classes'] = implode(' ', $body_classes);
  1806.  
  1807.   // Build a list of suggested template files in order of specificity. One
  1808.   // suggestion is made for every element of the current path, though
  1809.   // numeric elements are not carried to subsequent suggestions. For example,
  1810.   // http://www.example.com/node/1/edit would result in the following
  1811.   // suggestions:
  1812.   //
  1813.   // page-node-edit.tpl.php
  1814.   // page-node-1.tpl.php
  1815.   // page-node.tpl.php
  1816.   // page.tpl.php
  1817.   $i = 0;
  1818.   $suggestion = 'page';
  1819.   $suggestions = array();
  1820.   while ($arg = arg($i++)) {
  1821.     $suggestions[] = $suggestion .'-'. $arg;
  1822.     if (!is_numeric($arg)) {
  1823.       $suggestion .= '-'. $arg;
  1824.     }
  1825.   }
  1826.   if (drupal_is_front_page()) {
  1827.     $suggestions[] = 'page-front';
  1828.   }
  1829.  
  1830.   if ($suggestions) {
  1831.     $variables['template_files'] = $suggestions;
  1832.   }
  1833. }
  1834.  
  1835. /**
  1836.  * Process variables for node.tpl.php
  1837.  *
  1838.  * Most themes utilize their own copy of node.tpl.php. The default is located
  1839.  * inside "modules/node/node.tpl.php". Look in there for the full list of
  1840.  * variables.
  1841.  *
  1842.  * The $variables array contains the following arguments:
  1843.  * - $node
  1844.  * - $teaser
  1845.  * - $page
  1846.  *
  1847.  * @see node.tpl.php
  1848.  */
  1849. function template_preprocess_node(&$variables) {
  1850.   $node = $variables['node'];
  1851.   if (module_exists('taxonomy')) {
  1852.     $variables['taxonomy'] = taxonomy_link('taxonomy terms', $node);
  1853.   }
  1854.   else {
  1855.     $variables['taxonomy'] = array();
  1856.   }
  1857.  
  1858.   if ($variables['teaser'] && $node->teaser) {
  1859.     $variables['content'] = $node->teaser;
  1860.   }
  1861.   elseif (isset($node->body)) {
  1862.     $variables['content'] = $node->body;
  1863.   }
  1864.   else {
  1865.     $variables['content'] = '';
  1866.   }
  1867.  
  1868.   $variables['date']      = format_date($node->created);
  1869.   $variables['links']     = !empty($node->links) ? theme('links', $node->links, array('class' => 'links inline')) : '';
  1870.   $variables['name']      = theme('username', $node);
  1871.   $variables['node_url']  = url('node/'. $node->nid);
  1872.   $variables['terms']     = theme('links', $variables['taxonomy'], array('class' => 'links inline'));
  1873.   $variables['title']     = check_plain($node->title);
  1874.  
  1875.   // Flatten the node object's member fields.
  1876.   $variables = array_merge((array)$node, $variables);
  1877.  
  1878.   // Display info only on certain node types.
  1879.   if (theme_get_setting('toggle_node_info_'. $node->type)) {
  1880.     $variables['submitted'] = theme('node_submitted', $node);
  1881.     $variables['picture'] = theme_get_setting('toggle_node_user_picture') ? theme('user_picture', $node) : '';
  1882.   }
  1883.   else {
  1884.     $variables['submitted'] = '';
  1885.     $variables['picture'] = '';
  1886.   }
  1887.   // Clean up name so there are no underscores.
  1888.   $variables['template_files'][] = 'node-'. $node->type;
  1889. }
  1890.  
  1891. /**
  1892.  * Process variables for block.tpl.php
  1893.  *
  1894.  * Prepare the values passed to the theme_block function to be passed
  1895.  * into a pluggable template engine. Uses block properties to generate a
  1896.  * series of template file suggestions. If none are found, the default
  1897.  * block.tpl.php is used.
  1898.  *
  1899.  * Most themes utilize their own copy of block.tpl.php. The default is located
  1900.  * inside "modules/system/block.tpl.php". Look in there for the full list of
  1901.  * variables.
  1902.  *
  1903.  * The $variables array contains the following arguments:
  1904.  * - $block
  1905.  *
  1906.  * @see block.tpl.php
  1907.  */
  1908. function template_preprocess_block(&$variables) {
  1909.   static $block_counter = array();
  1910.   // All blocks get an independent counter for each region.
  1911.   if (!isset($block_counter[$variables['block']->region])) {
  1912.     $block_counter[$variables['block']->region] = 1;
  1913.   }
  1914.   // Same with zebra striping.
  1915.   $variables['block_zebra'] = ($block_counter[$variables['block']->region] % 2) ? 'odd' : 'even';
  1916.   $variables['block_id'] = $block_counter[$variables['block']->region]++;
  1917.  
  1918.   $variables['template_files'][] = 'block-'. $variables['block']->region;
  1919.   $variables['template_files'][] = 'block-'. $variables['block']->module;
  1920.   $variables['template_files'][] = 'block-'. $variables['block']->module .'-'. $variables['block']->delta;
  1921. }
  1922.  
  1923.