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