cms/drupal/includes/theme.inc
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * @file
       
     5  * The theme system, which controls the output of Drupal.
       
     6  *
       
     7  * The theme system allows for nearly all output of the Drupal system to be
       
     8  * customized by user themes.
       
     9  */
       
    10 
       
    11 /**
       
    12  * @defgroup content_flags Content markers
       
    13  * @{
       
    14  * Markers used by theme_mark() and node_mark() to designate content.
       
    15  * @see theme_mark(), node_mark()
       
    16  */
       
    17 
       
    18 /**
       
    19  * Mark content as read.
       
    20  */
       
    21 define('MARK_READ', 0);
       
    22 
       
    23 /**
       
    24  * Mark content as being new.
       
    25  */
       
    26 define('MARK_NEW', 1);
       
    27 
       
    28 /**
       
    29  * Mark content as being updated.
       
    30  */
       
    31 define('MARK_UPDATED', 2);
       
    32 
       
    33 /**
       
    34  * @} End of "Content markers".
       
    35  */
       
    36 
       
    37 /**
       
    38  * Determines if a theme is available to use.
       
    39  *
       
    40  * @param $theme
       
    41  *   Either the name of a theme or a full theme object.
       
    42  *
       
    43  * @return
       
    44  *   Boolean TRUE if the theme is enabled or is the site administration theme;
       
    45  *   FALSE otherwise.
       
    46  */
       
    47 function drupal_theme_access($theme) {
       
    48   if (is_object($theme)) {
       
    49     return _drupal_theme_access($theme);
       
    50   }
       
    51   else {
       
    52     $themes = list_themes();
       
    53     return isset($themes[$theme]) && _drupal_theme_access($themes[$theme]);
       
    54   }
       
    55 }
       
    56 
       
    57 /**
       
    58  * Helper function for determining access to a theme.
       
    59  *
       
    60  * @see drupal_theme_access()
       
    61  */
       
    62 function _drupal_theme_access($theme) {
       
    63   $admin_theme = variable_get('admin_theme');
       
    64   return !empty($theme->status) || ($admin_theme && $theme->name == $admin_theme);
       
    65 }
       
    66 
       
    67 /**
       
    68  * Initializes the theme system by loading the theme.
       
    69  */
       
    70 function drupal_theme_initialize() {
       
    71   global $theme, $user, $theme_key;
       
    72 
       
    73   // If $theme is already set, assume the others are set, too, and do nothing
       
    74   if (isset($theme)) {
       
    75     return;
       
    76   }
       
    77 
       
    78   drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);
       
    79   $themes = list_themes();
       
    80 
       
    81   // Only select the user selected theme if it is available in the
       
    82   // list of themes that can be accessed.
       
    83   $theme = !empty($user->theme) && drupal_theme_access($user->theme) ? $user->theme : variable_get('theme_default', 'bartik');
       
    84 
       
    85   // Allow modules to override the theme. Validation has already been performed
       
    86   // inside menu_get_custom_theme(), so we do not need to check it again here.
       
    87   $custom_theme = menu_get_custom_theme();
       
    88   $theme = !empty($custom_theme) ? $custom_theme : $theme;
       
    89 
       
    90   // Store the identifier for retrieving theme settings with.
       
    91   $theme_key = $theme;
       
    92 
       
    93   // Find all our ancestor themes and put them in an array.
       
    94   $base_theme = array();
       
    95   $ancestor = $theme;
       
    96   while ($ancestor && isset($themes[$ancestor]->base_theme)) {
       
    97     $ancestor = $themes[$ancestor]->base_theme;
       
    98     $base_theme[] = $themes[$ancestor];
       
    99   }
       
   100   _drupal_theme_initialize($themes[$theme], array_reverse($base_theme));
       
   101 
       
   102   // Themes can have alter functions, so reset the drupal_alter() cache.
       
   103   drupal_static_reset('drupal_alter');
       
   104 
       
   105   // Provide the page with information about the theme that's used, so that a
       
   106   // later Ajax request can be rendered using the same theme.
       
   107   // @see ajax_base_page_theme()
       
   108   $setting['ajaxPageState'] = array(
       
   109     'theme' => $theme_key,
       
   110     'theme_token' => drupal_get_token($theme_key),
       
   111   );
       
   112   drupal_add_js($setting, 'setting');
       
   113 }
       
   114 
       
   115 /**
       
   116  * Initializes the theme system given already loaded information.
       
   117  *
       
   118  * This function is useful to initialize a theme when no database is present.
       
   119  *
       
   120  * @param $theme
       
   121  *   An object with the following information:
       
   122  *     filename
       
   123  *       The .info file for this theme. The 'path' to
       
   124  *       the theme will be in this file's directory. (Required)
       
   125  *     owner
       
   126  *       The path to the .theme file or the .engine file to load for
       
   127  *       the theme. (Required)
       
   128  *     stylesheet
       
   129  *       The primary stylesheet for the theme. (Optional)
       
   130  *     engine
       
   131  *       The name of theme engine to use. (Optional)
       
   132  * @param $base_theme
       
   133  *    An optional array of objects that represent the 'base theme' if the
       
   134  *    theme is meant to be derivative of another theme. It requires
       
   135  *    the same information as the $theme object. It should be in
       
   136  *    'oldest first' order, meaning the top level of the chain will
       
   137  *    be first.
       
   138  * @param $registry_callback
       
   139  *   The callback to invoke to set the theme registry.
       
   140  */
       
   141 function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callback = '_theme_load_registry') {
       
   142   global $theme_info, $base_theme_info, $theme_engine, $theme_path;
       
   143   $theme_info = $theme;
       
   144   $base_theme_info = $base_theme;
       
   145 
       
   146   $theme_path = dirname($theme->filename);
       
   147 
       
   148   // Prepare stylesheets from this theme as well as all ancestor themes.
       
   149   // We work it this way so that we can have child themes override parent
       
   150   // theme stylesheets easily.
       
   151   $final_stylesheets = array();
       
   152 
       
   153   // Grab stylesheets from base theme
       
   154   foreach ($base_theme as $base) {
       
   155     if (!empty($base->stylesheets)) {
       
   156       foreach ($base->stylesheets as $media => $stylesheets) {
       
   157         foreach ($stylesheets as $name => $stylesheet) {
       
   158           $final_stylesheets[$media][$name] = $stylesheet;
       
   159         }
       
   160       }
       
   161     }
       
   162   }
       
   163 
       
   164   // Add stylesheets used by this theme.
       
   165   if (!empty($theme->stylesheets)) {
       
   166     foreach ($theme->stylesheets as $media => $stylesheets) {
       
   167       foreach ($stylesheets as $name => $stylesheet) {
       
   168         $final_stylesheets[$media][$name] = $stylesheet;
       
   169       }
       
   170     }
       
   171   }
       
   172 
       
   173   // And now add the stylesheets properly
       
   174   foreach ($final_stylesheets as $media => $stylesheets) {
       
   175     foreach ($stylesheets as $stylesheet) {
       
   176       drupal_add_css($stylesheet, array('group' => CSS_THEME, 'every_page' => TRUE, 'media' => $media));
       
   177     }
       
   178   }
       
   179 
       
   180   // Do basically the same as the above for scripts
       
   181   $final_scripts = array();
       
   182 
       
   183   // Grab scripts from base theme
       
   184   foreach ($base_theme as $base) {
       
   185     if (!empty($base->scripts)) {
       
   186       foreach ($base->scripts as $name => $script) {
       
   187         $final_scripts[$name] = $script;
       
   188       }
       
   189     }
       
   190   }
       
   191 
       
   192   // Add scripts used by this theme.
       
   193   if (!empty($theme->scripts)) {
       
   194     foreach ($theme->scripts as $name => $script) {
       
   195       $final_scripts[$name] = $script;
       
   196     }
       
   197   }
       
   198 
       
   199   // Add scripts used by this theme.
       
   200   foreach ($final_scripts as $script) {
       
   201     drupal_add_js($script, array('group' => JS_THEME, 'every_page' => TRUE));
       
   202   }
       
   203 
       
   204   $theme_engine = NULL;
       
   205 
       
   206   // Initialize the theme.
       
   207   if (isset($theme->engine)) {
       
   208     // Include the engine.
       
   209     include_once DRUPAL_ROOT . '/' . $theme->owner;
       
   210 
       
   211     $theme_engine = $theme->engine;
       
   212     if (function_exists($theme_engine . '_init')) {
       
   213       foreach ($base_theme as $base) {
       
   214         call_user_func($theme_engine . '_init', $base);
       
   215       }
       
   216       call_user_func($theme_engine . '_init', $theme);
       
   217     }
       
   218   }
       
   219   else {
       
   220     // include non-engine theme files
       
   221     foreach ($base_theme as $base) {
       
   222       // Include the theme file or the engine.
       
   223       if (!empty($base->owner)) {
       
   224         include_once DRUPAL_ROOT . '/' . $base->owner;
       
   225       }
       
   226     }
       
   227     // and our theme gets one too.
       
   228     if (!empty($theme->owner)) {
       
   229       include_once DRUPAL_ROOT . '/' . $theme->owner;
       
   230     }
       
   231   }
       
   232 
       
   233   if (isset($registry_callback)) {
       
   234     _theme_registry_callback($registry_callback, array($theme, $base_theme, $theme_engine));
       
   235   }
       
   236 }
       
   237 
       
   238 /**
       
   239  * Gets the theme registry.
       
   240  *
       
   241  * @param $complete
       
   242  *   Optional boolean to indicate whether to return the complete theme registry
       
   243  *   array or an instance of the ThemeRegistry class. If TRUE, the complete
       
   244  *   theme registry array will be returned. This is useful if you want to
       
   245  *   foreach over the whole registry, use array_* functions or inspect it in a
       
   246  *   debugger. If FALSE, an instance of the ThemeRegistry class will be
       
   247  *   returned, this provides an ArrayObject which allows it to be accessed
       
   248  *   with array syntax and  isset(), and should be more lightweight
       
   249  *   than the full registry. Defaults to TRUE.
       
   250  *
       
   251  * @return
       
   252  *   The complete theme registry array, or an instance of the ThemeRegistry
       
   253  *   class.
       
   254  */
       
   255 function theme_get_registry($complete = TRUE) {
       
   256   // Use the advanced drupal_static() pattern, since this is called very often.
       
   257   static $drupal_static_fast;
       
   258   if (!isset($drupal_static_fast)) {
       
   259     $drupal_static_fast['registry'] = &drupal_static('theme_get_registry');
       
   260   }
       
   261   $theme_registry = &$drupal_static_fast['registry'];
       
   262 
       
   263   // Initialize the theme, if this is called early in the bootstrap, or after
       
   264   // static variables have been reset.
       
   265   if (!is_array($theme_registry)) {
       
   266     drupal_theme_initialize();
       
   267     $theme_registry = array();
       
   268   }
       
   269 
       
   270   $key = (int) $complete;
       
   271 
       
   272   if (!isset($theme_registry[$key])) {
       
   273     list($callback, $arguments) = _theme_registry_callback();
       
   274     if (!$complete) {
       
   275       $arguments[] = FALSE;
       
   276     }
       
   277     $theme_registry[$key] = call_user_func_array($callback, $arguments);
       
   278   }
       
   279 
       
   280   return $theme_registry[$key];
       
   281 }
       
   282 
       
   283 /**
       
   284  * Sets the callback that will be used by theme_get_registry().
       
   285  *
       
   286  * @param $callback
       
   287  *   The name of the callback function.
       
   288  * @param $arguments
       
   289  *   The arguments to pass to the function.
       
   290  */
       
   291 function _theme_registry_callback($callback = NULL, array $arguments = array()) {
       
   292   static $stored;
       
   293   if (isset($callback)) {
       
   294     $stored = array($callback, $arguments);
       
   295   }
       
   296   return $stored;
       
   297 }
       
   298 
       
   299 /**
       
   300  * Gets the theme_registry cache; if it doesn't exist, builds it.
       
   301  *
       
   302  * @param $theme
       
   303  *   The loaded $theme object as returned by list_themes().
       
   304  * @param $base_theme
       
   305  *   An array of loaded $theme objects representing the ancestor themes in
       
   306  *   oldest first order.
       
   307  * @param $theme_engine
       
   308  *   The name of the theme engine.
       
   309  * @param $complete
       
   310  *   Whether to load the complete theme registry or an instance of the
       
   311  *   ThemeRegistry class.
       
   312  *
       
   313  * @return
       
   314  *   The theme registry array, or an instance of the ThemeRegistry class.
       
   315  */
       
   316 function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL, $complete = TRUE) {
       
   317   if ($complete) {
       
   318     // Check the theme registry cache; if it exists, use it.
       
   319     $cached = cache_get("theme_registry:$theme->name");
       
   320     if (isset($cached->data)) {
       
   321       $registry = $cached->data;
       
   322     }
       
   323     else {
       
   324       // If not, build one and cache it.
       
   325       $registry = _theme_build_registry($theme, $base_theme, $theme_engine);
       
   326       // Only persist this registry if all modules are loaded. This assures a
       
   327       // complete set of theme hooks.
       
   328       if (module_load_all(NULL)) {
       
   329         _theme_save_registry($theme, $registry);
       
   330       }
       
   331     }
       
   332     return $registry;
       
   333   }
       
   334   else {
       
   335     return new ThemeRegistry('theme_registry:runtime:' . $theme->name, 'cache');
       
   336   }
       
   337 }
       
   338 
       
   339 /**
       
   340  * Writes the theme_registry cache into the database.
       
   341  */
       
   342 function _theme_save_registry($theme, $registry) {
       
   343   cache_set("theme_registry:$theme->name", $registry);
       
   344 }
       
   345 
       
   346 /**
       
   347  * Forces the system to rebuild the theme registry.
       
   348  *
       
   349  * This function should be called when modules are added to the system, or when
       
   350  * a dynamic system needs to add more theme hooks.
       
   351  */
       
   352 function drupal_theme_rebuild() {
       
   353   drupal_static_reset('theme_get_registry');
       
   354   cache_clear_all('theme_registry', 'cache', TRUE);
       
   355 }
       
   356 
       
   357 /**
       
   358  * Builds the run-time theme registry.
       
   359  *
       
   360  * Extends DrupalCacheArray to allow the theme registry to be accessed as a
       
   361  * complete registry, while internally caching only the parts of the registry
       
   362  * that are actually in use on the site. On cache misses the complete
       
   363  * theme registry is loaded and used to update the run-time cache.
       
   364  */
       
   365 class ThemeRegistry Extends DrupalCacheArray {
       
   366 
       
   367   /**
       
   368    * Whether the partial registry can be persisted to the cache.
       
   369    *
       
   370    * This is only allowed if all modules and the request method is GET. theme()
       
   371    * should be very rarely called on POST requests and this avoids polluting
       
   372    * the runtime cache.
       
   373    */
       
   374   protected $persistable;
       
   375 
       
   376   /**
       
   377    * The complete theme registry array.
       
   378    */
       
   379   protected $completeRegistry;
       
   380 
       
   381   function __construct($cid, $bin) {
       
   382     $this->cid = $cid;
       
   383     $this->bin = $bin;
       
   384     $this->persistable = module_load_all(NULL) && $_SERVER['REQUEST_METHOD'] == 'GET';
       
   385 
       
   386     $data = array();
       
   387     if ($this->persistable && $cached = cache_get($this->cid, $this->bin)) {
       
   388       $data = $cached->data;
       
   389     }
       
   390     else {
       
   391       // If there is no runtime cache stored, fetch the full theme registry,
       
   392       // but then initialize each value to NULL. This allows offsetExists()
       
   393       // to function correctly on non-registered theme hooks without triggering
       
   394       // a call to resolveCacheMiss().
       
   395       $data = $this->initializeRegistry();
       
   396       if ($this->persistable) {
       
   397         $this->set($data);
       
   398       }
       
   399     }
       
   400     $this->storage = $data;
       
   401   }
       
   402 
       
   403   /**
       
   404    * Initializes the full theme registry.
       
   405    *
       
   406    * @return
       
   407    *   An array with the keys of the full theme registry, but the values
       
   408    *   initialized to NULL.
       
   409    */
       
   410   function initializeRegistry() {
       
   411     $this->completeRegistry = theme_get_registry();
       
   412 
       
   413     return array_fill_keys(array_keys($this->completeRegistry), NULL);
       
   414   }
       
   415 
       
   416   public function offsetExists($offset) {
       
   417     // Since the theme registry allows for theme hooks to be requested that
       
   418     // are not registered, just check the existence of the key in the registry.
       
   419     // Use array_key_exists() here since a NULL value indicates that the theme
       
   420     // hook exists but has not yet been requested.
       
   421     return array_key_exists($offset, $this->storage);
       
   422   }
       
   423 
       
   424   public function offsetGet($offset) {
       
   425     // If the offset is set but empty, it is a registered theme hook that has
       
   426     // not yet been requested. Offsets that do not exist at all were not
       
   427     // registered in hook_theme().
       
   428     if (isset($this->storage[$offset])) {
       
   429       return $this->storage[$offset];
       
   430     }
       
   431     elseif (array_key_exists($offset, $this->storage)) {
       
   432       return $this->resolveCacheMiss($offset);
       
   433     }
       
   434   }
       
   435 
       
   436   public function resolveCacheMiss($offset) {
       
   437     if (!isset($this->completeRegistry)) {
       
   438       $this->completeRegistry = theme_get_registry();
       
   439     }
       
   440     $this->storage[$offset] = $this->completeRegistry[$offset];
       
   441     if ($this->persistable) {
       
   442       $this->persist($offset);
       
   443     }
       
   444     return $this->storage[$offset];
       
   445   }
       
   446 
       
   447   public function set($data, $lock = TRUE) {
       
   448     $lock_name = $this->cid . ':' . $this->bin;
       
   449     if (!$lock || lock_acquire($lock_name)) {
       
   450       if ($cached = cache_get($this->cid, $this->bin)) {
       
   451         // Use array merge instead of union so that filled in values in $data
       
   452         // overwrite empty values in the current cache.
       
   453         $data = array_merge($cached->data, $data);
       
   454       }
       
   455       else {
       
   456         $registry = $this->initializeRegistry();
       
   457         $data = array_merge($registry, $data);
       
   458       }
       
   459       cache_set($this->cid, $data, $this->bin);
       
   460       if ($lock) {
       
   461         lock_release($lock_name);
       
   462       }
       
   463     }
       
   464   }
       
   465 }
       
   466 
       
   467 /**
       
   468  * Process a single implementation of hook_theme().
       
   469  *
       
   470  * @param $cache
       
   471  *   The theme registry that will eventually be cached; It is an associative
       
   472  *   array keyed by theme hooks, whose values are associative arrays describing
       
   473  *   the hook:
       
   474  *   - 'type': The passed-in $type.
       
   475  *   - 'theme path': The passed-in $path.
       
   476  *   - 'function': The name of the function generating output for this theme
       
   477  *     hook. Either defined explicitly in hook_theme() or, if neither 'function'
       
   478  *     nor 'template' is defined, then the default theme function name is used.
       
   479  *     The default theme function name is the theme hook prefixed by either
       
   480  *     'theme_' for modules or '$name_' for everything else. If 'function' is
       
   481  *     defined, 'template' is not used.
       
   482  *   - 'template': The filename of the template generating output for this
       
   483  *     theme hook. The template is in the directory defined by the 'path' key of
       
   484  *     hook_theme() or defaults to $path.
       
   485  *   - 'variables': The variables for this theme hook as defined in
       
   486  *     hook_theme(). If there is more than one implementation and 'variables' is
       
   487  *     not specified in a later one, then the previous definition is kept.
       
   488  *   - 'render element': The renderable element for this theme hook as defined
       
   489  *     in hook_theme(). If there is more than one implementation and
       
   490  *     'render element' is not specified in a later one, then the previous
       
   491  *     definition is kept.
       
   492  *   - 'preprocess functions': See theme() for detailed documentation.
       
   493  *   - 'process functions': See theme() for detailed documentation.
       
   494  * @param $name
       
   495  *   The name of the module, theme engine, base theme engine, theme or base
       
   496  *   theme implementing hook_theme().
       
   497  * @param $type
       
   498  *   One of 'module', 'theme_engine', 'base_theme_engine', 'theme', or
       
   499  *   'base_theme'. Unlike regular hooks that can only be implemented by modules,
       
   500  *   each of these can implement hook_theme(). _theme_process_registry() is
       
   501  *   called in aforementioned order and new entries override older ones. For
       
   502  *   example, if a theme hook is both defined by a module and a theme, then the
       
   503  *   definition in the theme will be used.
       
   504  * @param $theme
       
   505  *   The loaded $theme object as returned from list_themes().
       
   506  * @param $path
       
   507  *   The directory where $name is. For example, modules/system or
       
   508  *   themes/bartik.
       
   509  *
       
   510  * @see theme()
       
   511  * @see _theme_build_registry()
       
   512  * @see hook_theme()
       
   513  * @see list_themes()
       
   514  */
       
   515 function _theme_process_registry(&$cache, $name, $type, $theme, $path) {
       
   516   $result = array();
       
   517 
       
   518   // Processor functions work in two distinct phases with the process
       
   519   // functions always being executed after the preprocess functions.
       
   520   $variable_process_phases = array(
       
   521     'preprocess functions' => 'preprocess',
       
   522     'process functions'    => 'process',
       
   523   );
       
   524 
       
   525   $hook_defaults = array(
       
   526     'variables' => TRUE,
       
   527     'render element' => TRUE,
       
   528     'pattern' => TRUE,
       
   529     'base hook' => TRUE,
       
   530   );
       
   531 
       
   532   // Invoke the hook_theme() implementation, process what is returned, and
       
   533   // merge it into $cache.
       
   534   $function = $name . '_theme';
       
   535   if (function_exists($function)) {
       
   536     $result = $function($cache, $type, $theme, $path);
       
   537     foreach ($result as $hook => $info) {
       
   538       // When a theme or engine overrides a module's theme function
       
   539       // $result[$hook] will only contain key/value pairs for information being
       
   540       // overridden.  Pull the rest of the information from what was defined by
       
   541       // an earlier hook.
       
   542 
       
   543       // Fill in the type and path of the module, theme, or engine that
       
   544       // implements this theme function.
       
   545       $result[$hook]['type'] = $type;
       
   546       $result[$hook]['theme path'] = $path;
       
   547 
       
   548       // If function and file are omitted, default to standard naming
       
   549       // conventions.
       
   550       if (!isset($info['template']) && !isset($info['function'])) {
       
   551         $result[$hook]['function'] = ($type == 'module' ? 'theme_' : $name . '_') . $hook;
       
   552       }
       
   553 
       
   554       if (isset($cache[$hook]['includes'])) {
       
   555         $result[$hook]['includes'] = $cache[$hook]['includes'];
       
   556       }
       
   557 
       
   558       // If the theme implementation defines a file, then also use the path
       
   559       // that it defined. Otherwise use the default path. This allows
       
   560       // system.module to declare theme functions on behalf of core .include
       
   561       // files.
       
   562       if (isset($info['file'])) {
       
   563         $include_file = isset($info['path']) ? $info['path'] : $path;
       
   564         $include_file .= '/' . $info['file'];
       
   565         include_once DRUPAL_ROOT . '/' . $include_file;
       
   566         $result[$hook]['includes'][] = $include_file;
       
   567       }
       
   568 
       
   569       // If the default keys are not set, use the default values registered
       
   570       // by the module.
       
   571       if (isset($cache[$hook])) {
       
   572         $result[$hook] += array_intersect_key($cache[$hook], $hook_defaults);
       
   573       }
       
   574 
       
   575       // The following apply only to theming hooks implemented as templates.
       
   576       if (isset($info['template'])) {
       
   577         // Prepend the current theming path when none is set.
       
   578         if (!isset($info['path'])) {
       
   579           $result[$hook]['template'] = $path . '/' . $info['template'];
       
   580         }
       
   581       }
       
   582 
       
   583       // Allow variable processors for all theming hooks, whether the hook is
       
   584       // implemented as a template or as a function.
       
   585       foreach ($variable_process_phases as $phase_key => $phase) {
       
   586         // Check for existing variable processors. Ensure arrayness.
       
   587         if (!isset($info[$phase_key]) || !is_array($info[$phase_key])) {
       
   588           $info[$phase_key] = array();
       
   589           $prefixes = array();
       
   590           if ($type == 'module') {
       
   591             // Default variable processor prefix.
       
   592             $prefixes[] = 'template';
       
   593             // Add all modules so they can intervene with their own variable
       
   594             // processors. This allows them to provide variable processors even
       
   595             // if they are not the owner of the current hook.
       
   596             $prefixes += module_list();
       
   597           }
       
   598           elseif ($type == 'theme_engine' || $type == 'base_theme_engine') {
       
   599             // Theme engines get an extra set that come before the normally
       
   600             // named variable processors.
       
   601             $prefixes[] = $name . '_engine';
       
   602             // The theme engine registers on behalf of the theme using the
       
   603             // theme's name.
       
   604             $prefixes[] = $theme;
       
   605           }
       
   606           else {
       
   607             // This applies when the theme manually registers their own variable
       
   608             // processors.
       
   609             $prefixes[] = $name;
       
   610           }
       
   611           foreach ($prefixes as $prefix) {
       
   612             // Only use non-hook-specific variable processors for theming hooks
       
   613             // implemented as templates. See theme().
       
   614             if (isset($info['template']) && function_exists($prefix . '_' . $phase)) {
       
   615               $info[$phase_key][] = $prefix . '_' . $phase;
       
   616             }
       
   617             if (function_exists($prefix . '_' . $phase . '_' . $hook)) {
       
   618               $info[$phase_key][] = $prefix . '_' . $phase . '_' . $hook;
       
   619             }
       
   620           }
       
   621         }
       
   622         // Check for the override flag and prevent the cached variable
       
   623         // processors from being used. This allows themes or theme engines to
       
   624         // remove variable processors set earlier in the registry build.
       
   625         if (!empty($info['override ' . $phase_key])) {
       
   626           // Flag not needed inside the registry.
       
   627           unset($result[$hook]['override ' . $phase_key]);
       
   628         }
       
   629         elseif (isset($cache[$hook][$phase_key]) && is_array($cache[$hook][$phase_key])) {
       
   630           $info[$phase_key] = array_merge($cache[$hook][$phase_key], $info[$phase_key]);
       
   631         }
       
   632         $result[$hook][$phase_key] = $info[$phase_key];
       
   633       }
       
   634     }
       
   635 
       
   636     // Merge the newly created theme hooks into the existing cache.
       
   637     $cache = $result + $cache;
       
   638   }
       
   639 
       
   640   // Let themes have variable processors even if they didn't register a
       
   641   // template.
       
   642   if ($type == 'theme' || $type == 'base_theme') {
       
   643     foreach ($cache as $hook => $info) {
       
   644       // Check only if not registered by the theme or engine.
       
   645       if (empty($result[$hook])) {
       
   646         foreach ($variable_process_phases as $phase_key => $phase) {
       
   647           if (!isset($info[$phase_key])) {
       
   648             $cache[$hook][$phase_key] = array();
       
   649           }
       
   650           // Only use non-hook-specific variable processors for theming hooks
       
   651           // implemented as templates. See theme().
       
   652           if (isset($info['template']) && function_exists($name . '_' . $phase)) {
       
   653             $cache[$hook][$phase_key][] = $name . '_' . $phase;
       
   654           }
       
   655           if (function_exists($name . '_' . $phase . '_' . $hook)) {
       
   656             $cache[$hook][$phase_key][] = $name . '_' . $phase . '_' . $hook;
       
   657             $cache[$hook]['theme path'] = $path;
       
   658           }
       
   659           // Ensure uniqueness.
       
   660           $cache[$hook][$phase_key] = array_unique($cache[$hook][$phase_key]);
       
   661         }
       
   662       }
       
   663     }
       
   664   }
       
   665 }
       
   666 
       
   667 /**
       
   668  * Builds the theme registry cache.
       
   669  *
       
   670  * @param $theme
       
   671  *   The loaded $theme object as returned by list_themes().
       
   672  * @param $base_theme
       
   673  *   An array of loaded $theme objects representing the ancestor themes in
       
   674  *   oldest first order.
       
   675  * @param $theme_engine
       
   676  *   The name of the theme engine.
       
   677  */
       
   678 function _theme_build_registry($theme, $base_theme, $theme_engine) {
       
   679   $cache = array();
       
   680   // First, process the theme hooks advertised by modules. This will
       
   681   // serve as the basic registry. Since the list of enabled modules is the same
       
   682   // regardless of the theme used, this is cached in its own entry to save
       
   683   // building it for every theme.
       
   684   if ($cached = cache_get('theme_registry:build:modules')) {
       
   685     $cache = $cached->data;
       
   686   }
       
   687   else {
       
   688     foreach (module_implements('theme') as $module) {
       
   689       _theme_process_registry($cache, $module, 'module', $module, drupal_get_path('module', $module));
       
   690     }
       
   691     // Only cache this registry if all modules are loaded.
       
   692     if (module_load_all(NULL)) {
       
   693       cache_set('theme_registry:build:modules', $cache);
       
   694     }
       
   695   }
       
   696 
       
   697   // Process each base theme.
       
   698   foreach ($base_theme as $base) {
       
   699     // If the base theme uses a theme engine, process its hooks.
       
   700     $base_path = dirname($base->filename);
       
   701     if ($theme_engine) {
       
   702       _theme_process_registry($cache, $theme_engine, 'base_theme_engine', $base->name, $base_path);
       
   703     }
       
   704     _theme_process_registry($cache, $base->name, 'base_theme', $base->name, $base_path);
       
   705   }
       
   706 
       
   707   // And then the same thing, but for the theme.
       
   708   if ($theme_engine) {
       
   709     _theme_process_registry($cache, $theme_engine, 'theme_engine', $theme->name, dirname($theme->filename));
       
   710   }
       
   711 
       
   712   // Finally, hooks provided by the theme itself.
       
   713   _theme_process_registry($cache, $theme->name, 'theme', $theme->name, dirname($theme->filename));
       
   714 
       
   715   // Let modules alter the registry.
       
   716   drupal_alter('theme_registry', $cache);
       
   717 
       
   718   // Optimize the registry to not have empty arrays for functions.
       
   719   foreach ($cache as $hook => $info) {
       
   720     foreach (array('preprocess functions', 'process functions') as $phase) {
       
   721       if (empty($info[$phase])) {
       
   722         unset($cache[$hook][$phase]);
       
   723       }
       
   724     }
       
   725   }
       
   726   return $cache;
       
   727 }
       
   728 
       
   729 /**
       
   730  * Returns a list of all currently available themes.
       
   731  *
       
   732  * Retrieved from the database, if available and the site is not in maintenance
       
   733  * mode; otherwise compiled freshly from the filesystem.
       
   734  *
       
   735  * @param $refresh
       
   736  *   Whether to reload the list of themes from the database. Defaults to FALSE.
       
   737  *
       
   738  * @return
       
   739  *   An associative array of the currently available themes. The keys are the
       
   740  *   themes' machine names and the values are objects having the following
       
   741  *   properties:
       
   742  *   - filename: The filepath and name of the .info file.
       
   743  *   - name: The machine name of the theme.
       
   744  *   - status: 1 for enabled, 0 for disabled themes.
       
   745  *   - info: The contents of the .info file.
       
   746  *   - stylesheets: A two dimensional array, using the first key for the
       
   747  *     media attribute (e.g. 'all'), the second for the name of the file
       
   748  *     (e.g. style.css). The value is a complete filepath (e.g.
       
   749  *     themes/bartik/style.css). Not set if no stylesheets are defined in the
       
   750  *     .info file.
       
   751  *   - scripts: An associative array of JavaScripts, using the filename as key
       
   752  *     and the complete filepath as value. Not set if no scripts are defined in
       
   753  *     the .info file.
       
   754  *   - prefix: The base theme engine prefix.
       
   755  *   - engine: The machine name of the theme engine.
       
   756  *   - base_theme: If this is a sub-theme, the machine name of the base theme
       
   757  *     defined in the .info file. Otherwise, the element is not set.
       
   758  *   - base_themes: If this is a sub-theme, an associative array of the
       
   759  *     base-theme ancestors of this theme, starting with this theme's base
       
   760  *     theme, then the base theme's own base theme, etc. Each entry has an
       
   761  *     array key equal to the theme's machine name, and a value equal to the
       
   762  *     human-readable theme name; if a theme with matching machine name does
       
   763  *     not exist in the system, the value will instead be NULL (and since the
       
   764  *     system would not know whether that theme itself has a base theme, that
       
   765  *     will end the array of base themes). This is not set if the theme is not
       
   766  *     a sub-theme.
       
   767  *   - sub_themes: An associative array of themes on the system that are
       
   768  *     either direct sub-themes (that is, they declare this theme to be
       
   769  *     their base theme), direct sub-themes of sub-themes, etc. The keys are
       
   770  *     the themes' machine names, and the values are the themes' human-readable
       
   771  *     names. This element is not set if there are no themes on the system that
       
   772  *     declare this theme as their base theme.
       
   773 */
       
   774 function list_themes($refresh = FALSE) {
       
   775   $list = &drupal_static(__FUNCTION__, array());
       
   776 
       
   777   if ($refresh) {
       
   778     $list = array();
       
   779     system_list_reset();
       
   780   }
       
   781 
       
   782   if (empty($list)) {
       
   783     $list = array();
       
   784     $themes = array();
       
   785     // Extract from the database only when it is available.
       
   786     // Also check that the site is not in the middle of an install or update.
       
   787     if (!defined('MAINTENANCE_MODE')) {
       
   788       try {
       
   789         $themes = system_list('theme');
       
   790       }
       
   791       catch (Exception $e) {
       
   792         // If the database is not available, rebuild the theme data.
       
   793         $themes = _system_rebuild_theme_data();
       
   794       }
       
   795     }
       
   796     else {
       
   797       // Scan the installation when the database should not be read.
       
   798       $themes = _system_rebuild_theme_data();
       
   799     }
       
   800 
       
   801     foreach ($themes as $theme) {
       
   802       foreach ($theme->info['stylesheets'] as $media => $stylesheets) {
       
   803         foreach ($stylesheets as $stylesheet => $path) {
       
   804           $theme->stylesheets[$media][$stylesheet] = $path;
       
   805         }
       
   806       }
       
   807       foreach ($theme->info['scripts'] as $script => $path) {
       
   808         $theme->scripts[$script] = $path;
       
   809       }
       
   810       if (isset($theme->info['engine'])) {
       
   811         $theme->engine = $theme->info['engine'];
       
   812       }
       
   813       if (isset($theme->info['base theme'])) {
       
   814         $theme->base_theme = $theme->info['base theme'];
       
   815       }
       
   816       // Status is normally retrieved from the database. Add zero values when
       
   817       // read from the installation directory to prevent notices.
       
   818       if (!isset($theme->status)) {
       
   819         $theme->status = 0;
       
   820       }
       
   821       $list[$theme->name] = $theme;
       
   822     }
       
   823   }
       
   824 
       
   825   return $list;
       
   826 }
       
   827 
       
   828 /**
       
   829  * Find all the base themes for the specified theme.
       
   830  *
       
   831  * Themes can inherit templates and function implementations from earlier themes.
       
   832  *
       
   833  * @param $themes
       
   834  *   An array of available themes.
       
   835  * @param $key
       
   836  *   The name of the theme whose base we are looking for.
       
   837  * @param $used_keys
       
   838  *   A recursion parameter preventing endless loops.
       
   839  * @return
       
   840  *   Returns an array of all of the theme's ancestors; the first element's value
       
   841  *   will be NULL if an error occurred.
       
   842  */
       
   843 function drupal_find_base_themes($themes, $key, $used_keys = array()) {
       
   844   $base_key = $themes[$key]->info['base theme'];
       
   845   // Does the base theme exist?
       
   846   if (!isset($themes[$base_key])) {
       
   847     return array($base_key => NULL);
       
   848   }
       
   849 
       
   850   $current_base_theme = array($base_key => $themes[$base_key]->info['name']);
       
   851 
       
   852   // Is the base theme itself a child of another theme?
       
   853   if (isset($themes[$base_key]->info['base theme'])) {
       
   854     // Do we already know the base themes of this theme?
       
   855     if (isset($themes[$base_key]->base_themes)) {
       
   856       return $themes[$base_key]->base_themes + $current_base_theme;
       
   857     }
       
   858     // Prevent loops.
       
   859     if (!empty($used_keys[$base_key])) {
       
   860       return array($base_key => NULL);
       
   861     }
       
   862     $used_keys[$base_key] = TRUE;
       
   863     return drupal_find_base_themes($themes, $base_key, $used_keys) + $current_base_theme;
       
   864   }
       
   865   // If we get here, then this is our parent theme.
       
   866   return $current_base_theme;
       
   867 }
       
   868 
       
   869 /**
       
   870  * Generates themed output.
       
   871  *
       
   872  * All requests for themed output must go through this function (however,
       
   873  * calling the theme() function directly is strongly discouraged - see next
       
   874  * paragraph). It examines the request and routes it to the appropriate
       
   875  * @link themeable theme function or template @endlink, by checking the theme
       
   876  * registry.
       
   877  *
       
   878  * Avoid calling this function directly. It is preferable to replace direct
       
   879  * calls to the theme() function with calls to drupal_render() by passing a
       
   880  * render array with a #theme key to drupal_render(), which in turn calls
       
   881  * theme().
       
   882  *
       
   883  * @section sec_theme_hooks Theme Hooks
       
   884  * Most commonly, the first argument to this function is the name of the theme
       
   885  * hook. For instance, to theme a taxonomy term, the theme hook name is
       
   886  * 'taxonomy_term'. Modules register theme hooks within a hook_theme()
       
   887  * implementation and provide a default implementation via a function named
       
   888  * theme_HOOK() (e.g., theme_taxonomy_term()) or via a template file named
       
   889  * according to the value of the 'template' key registered with the theme hook
       
   890  * (see hook_theme() for details). Default templates are implemented with the
       
   891  * PHPTemplate rendering engine and are named the same as the theme hook, with
       
   892  * underscores changed to hyphens, so for the 'taxonomy_term' theme hook, the
       
   893  * default template is 'taxonomy-term.tpl.php'.
       
   894  *
       
   895  * @subsection sub_overriding_theme_hooks Overriding Theme Hooks
       
   896  * Themes may also register new theme hooks within a hook_theme()
       
   897  * implementation, but it is more common for themes to override default
       
   898  * implementations provided by modules than to register entirely new theme
       
   899  * hooks. Themes can override a default implementation by implementing a
       
   900  * function named THEME_HOOK() (for example, the 'bartik' theme overrides the
       
   901  * default implementation of the 'menu_tree' theme hook by implementing a
       
   902  * bartik_menu_tree() function), or by adding a template file within its folder
       
   903  * structure that follows the template naming structure used by the theme's
       
   904  * rendering engine (for example, since the Bartik theme uses the PHPTemplate
       
   905  * rendering engine, it overrides the default implementation of the 'page' theme
       
   906  * hook by containing a 'page.tpl.php' file within its folder structure).
       
   907  *
       
   908  * @subsection sub_preprocess_templates Preprocessing for Template Files
       
   909  * If the implementation is a template file, several functions are called
       
   910  * before the template file is invoked, to modify the $variables array. These
       
   911  * fall into the "preprocessing" phase and the "processing" phase, and are
       
   912  * executed (if they exist), in the following order (note that in the following
       
   913  * list, HOOK indicates the theme hook name, MODULE indicates a module name,
       
   914  * THEME indicates a theme name, and ENGINE indicates a theme engine name):
       
   915  * - template_preprocess(&$variables, $hook): Creates a default set of
       
   916  *   variables for all theme hooks with template implementations.
       
   917  * - template_preprocess_HOOK(&$variables): Should be implemented by the module
       
   918  *   that registers the theme hook, to set up default variables.
       
   919  * - MODULE_preprocess(&$variables, $hook): hook_preprocess() is invoked on all
       
   920  *   implementing modules.
       
   921  * - MODULE_preprocess_HOOK(&$variables): hook_preprocess_HOOK() is invoked on
       
   922  *   all implementing modules, so that modules that didn't define the theme
       
   923  *   hook can alter the variables.
       
   924  * - ENGINE_engine_preprocess(&$variables, $hook): Allows the theme engine to
       
   925  *   set necessary variables for all theme hooks with template implementations.
       
   926  * - ENGINE_engine_preprocess_HOOK(&$variables): Allows the theme engine to set
       
   927  *   necessary variables for the particular theme hook.
       
   928  * - THEME_preprocess(&$variables, $hook): Allows the theme to set necessary
       
   929  *   variables for all theme hooks with template implementations.
       
   930  * - THEME_preprocess_HOOK(&$variables): Allows the theme to set necessary
       
   931  *   variables specific to the particular theme hook.
       
   932  * - template_process(&$variables, $hook): Creates an additional set of default
       
   933  *   variables for all theme hooks with template implementations. The variables
       
   934  *   created in this function are derived from ones created by
       
   935  *   template_preprocess(), but potentially altered by the other preprocess
       
   936  *   functions listed above. For example, any preprocess function can add to or
       
   937  *   modify the $variables['attributes_array'] variable, and after all of them
       
   938  *   have finished executing, template_process() flattens it into a
       
   939  *   $variables['attributes'] string for convenient use by templates.
       
   940  * - template_process_HOOK(&$variables): Should be implemented by the module
       
   941  *   that registers the theme hook, if it needs to perform additional variable
       
   942  *   processing after all preprocess functions have finished.
       
   943  * - MODULE_process(&$variables, $hook): hook_process() is invoked on all
       
   944  *   implementing modules.
       
   945  * - MODULE_process_HOOK(&$variables): hook_process_HOOK() is invoked on
       
   946  *   on all implementing modules, so that modules that didn't define the theme
       
   947  *   hook can alter the variables.
       
   948  * - ENGINE_engine_process(&$variables, $hook): Allows the theme engine to
       
   949  *   process variables for all theme hooks with template implementations.
       
   950  * - ENGINE_engine_process_HOOK(&$variables): Allows the theme engine to process
       
   951  *   the variables specific to the theme hook.
       
   952  * - THEME_process(&$variables, $hook):  Allows the theme to process the
       
   953  *   variables for all theme hooks with template implementations.
       
   954  * - THEME_process_HOOK(&$variables):  Allows the theme to process the
       
   955  *   variables specific to the theme hook.
       
   956  *
       
   957  * @subsection sub_preprocess_theme_funcs Preprocessing for Theme Functions
       
   958  * If the implementation is a function, only the theme-hook-specific preprocess
       
   959  * and process functions (the ones ending in _HOOK) are called from the
       
   960  * list above. This is because theme hooks with function implementations
       
   961  * need to be fast, and calling the non-theme-hook-specific preprocess and
       
   962  * process functions for them would incur a noticeable performance penalty.
       
   963  *
       
   964  * @subsection sub_alternate_suggestions Suggesting Alternate Hooks
       
   965  * There are two special variables that these preprocess and process functions
       
   966  * can set: 'theme_hook_suggestion' and 'theme_hook_suggestions'. These will be
       
   967  * merged together to form a list of 'suggested' alternate theme hooks to use,
       
   968  * in reverse order of priority. theme_hook_suggestion will always be a higher
       
   969  * priority than items in theme_hook_suggestions. theme() will use the
       
   970  * highest priority implementation that exists. If none exists, theme() will
       
   971  * use the implementation for the theme hook it was called with. These
       
   972  * suggestions are similar to and are used for similar reasons as calling
       
   973  * theme() with an array as the $hook parameter (see below). The difference
       
   974  * is whether the suggestions are determined by the code that calls theme() or
       
   975  * by a preprocess or process function.
       
   976  *
       
   977  * @param $hook
       
   978  *   The name of the theme hook to call. If the name contains a
       
   979  *   double-underscore ('__') and there isn't an implementation for the full
       
   980  *   name, the part before the '__' is checked. This allows a fallback to a
       
   981  *   more generic implementation. For example, if theme('links__node', ...) is
       
   982  *   called, but there is no implementation of that theme hook, then the
       
   983  *   'links' implementation is used. This process is iterative, so if
       
   984  *   theme('links__contextual__node', ...) is called, theme() checks for the
       
   985  *   following implementations, and uses the first one that exists:
       
   986  *   - links__contextual__node
       
   987  *   - links__contextual
       
   988  *   - links
       
   989  *   This allows themes to create specific theme implementations for named
       
   990  *   objects and contexts of otherwise generic theme hooks. The $hook parameter
       
   991  *   may also be an array, in which case the first theme hook that has an
       
   992  *   implementation is used. This allows for the code that calls theme() to
       
   993  *   explicitly specify the fallback order in a situation where using the '__'
       
   994  *   convention is not desired or is insufficient.
       
   995  * @param $variables
       
   996  *   An associative array of variables to merge with defaults from the theme
       
   997  *   registry, pass to preprocess and process functions for modification, and
       
   998  *   finally, pass to the function or template implementing the theme hook.
       
   999  *   Alternatively, this can be a renderable array, in which case, its
       
  1000  *   properties are mapped to variables expected by the theme hook
       
  1001  *   implementations.
       
  1002  *
       
  1003  * @return
       
  1004  *   An HTML string representing the themed output.
       
  1005  *
       
  1006  * @see drupal_render()
       
  1007  * @see themeable
       
  1008  * @see hook_theme()
       
  1009  * @see template_preprocess()
       
  1010  * @see template_process()
       
  1011  */
       
  1012 function theme($hook, $variables = array()) {
       
  1013   // If called before all modules are loaded, we do not necessarily have a full
       
  1014   // theme registry to work with, and therefore cannot process the theme
       
  1015   // request properly. See also _theme_load_registry().
       
  1016   if (!module_load_all(NULL) && !defined('MAINTENANCE_MODE')) {
       
  1017     throw new Exception(t('theme() may not be called until all modules are loaded.'));
       
  1018   }
       
  1019 
       
  1020   $hooks = theme_get_registry(FALSE);
       
  1021 
       
  1022   // If an array of hook candidates were passed, use the first one that has an
       
  1023   // implementation.
       
  1024   if (is_array($hook)) {
       
  1025     foreach ($hook as $candidate) {
       
  1026       if (isset($hooks[$candidate])) {
       
  1027         break;
       
  1028       }
       
  1029     }
       
  1030     $hook = $candidate;
       
  1031   }
       
  1032   $theme_hook_original = $hook;
       
  1033 
       
  1034   // If there's no implementation, check for more generic fallbacks. If there's
       
  1035   // still no implementation, log an error and return an empty string.
       
  1036   if (!isset($hooks[$hook])) {
       
  1037     // Iteratively strip everything after the last '__' delimiter, until an
       
  1038     // implementation is found.
       
  1039     while ($pos = strrpos($hook, '__')) {
       
  1040       $hook = substr($hook, 0, $pos);
       
  1041       if (isset($hooks[$hook])) {
       
  1042         break;
       
  1043       }
       
  1044     }
       
  1045     if (!isset($hooks[$hook])) {
       
  1046       // Only log a message when not trying theme suggestions ($hook being an
       
  1047       // array).
       
  1048       if (!isset($candidate)) {
       
  1049         watchdog('theme', 'Theme hook %hook not found.', array('%hook' => $hook), WATCHDOG_WARNING);
       
  1050       }
       
  1051       return '';
       
  1052     }
       
  1053   }
       
  1054 
       
  1055   $info = $hooks[$hook];
       
  1056   global $theme_path;
       
  1057   $temp = $theme_path;
       
  1058   // point path_to_theme() to the currently used theme path:
       
  1059   $theme_path = $info['theme path'];
       
  1060 
       
  1061   // Include a file if the theme function or variable processor is held
       
  1062   // elsewhere.
       
  1063   if (!empty($info['includes'])) {
       
  1064     foreach ($info['includes'] as $include_file) {
       
  1065       include_once DRUPAL_ROOT . '/' . $include_file;
       
  1066     }
       
  1067   }
       
  1068 
       
  1069   // If a renderable array is passed as $variables, then set $variables to
       
  1070   // the arguments expected by the theme function.
       
  1071   if (isset($variables['#theme']) || isset($variables['#theme_wrappers'])) {
       
  1072     $element = $variables;
       
  1073     $variables = array();
       
  1074     if (isset($info['variables'])) {
       
  1075       foreach (array_keys($info['variables']) as $name) {
       
  1076         if (isset($element["#$name"])) {
       
  1077           $variables[$name] = $element["#$name"];
       
  1078         }
       
  1079       }
       
  1080     }
       
  1081     else {
       
  1082       $variables[$info['render element']] = $element;
       
  1083     }
       
  1084   }
       
  1085 
       
  1086   // Merge in argument defaults.
       
  1087   if (!empty($info['variables'])) {
       
  1088     $variables += $info['variables'];
       
  1089   }
       
  1090   elseif (!empty($info['render element'])) {
       
  1091     $variables += array($info['render element'] => array());
       
  1092   }
       
  1093 
       
  1094   $variables['theme_hook_original'] = $theme_hook_original;
       
  1095 
       
  1096   // Invoke the variable processors, if any. The processors may specify
       
  1097   // alternate suggestions for which hook's template/function to use. If the
       
  1098   // hook is a suggestion of a base hook, invoke the variable processors of
       
  1099   // the base hook, but retain the suggestion as a high priority suggestion to
       
  1100   // be used unless overridden by a variable processor function.
       
  1101   if (isset($info['base hook'])) {
       
  1102     $base_hook = $info['base hook'];
       
  1103     $base_hook_info = $hooks[$base_hook];
       
  1104     // Include files required by the base hook, since its variable processors
       
  1105     // might reside there.
       
  1106     if (!empty($base_hook_info['includes'])) {
       
  1107       foreach ($base_hook_info['includes'] as $include_file) {
       
  1108         include_once DRUPAL_ROOT . '/' . $include_file;
       
  1109       }
       
  1110     }
       
  1111     if (isset($base_hook_info['preprocess functions']) || isset($base_hook_info['process functions'])) {
       
  1112       $variables['theme_hook_suggestion'] = $hook;
       
  1113       $hook = $base_hook;
       
  1114       $info = $base_hook_info;
       
  1115     }
       
  1116   }
       
  1117   if (isset($info['preprocess functions']) || isset($info['process functions'])) {
       
  1118     $variables['theme_hook_suggestions'] = array();
       
  1119     foreach (array('preprocess functions', 'process functions') as $phase) {
       
  1120       if (!empty($info[$phase])) {
       
  1121         foreach ($info[$phase] as $processor_function) {
       
  1122           if (function_exists($processor_function)) {
       
  1123             // We don't want a poorly behaved process function changing $hook.
       
  1124             $hook_clone = $hook;
       
  1125             $processor_function($variables, $hook_clone);
       
  1126           }
       
  1127         }
       
  1128       }
       
  1129     }
       
  1130     // If the preprocess/process functions specified hook suggestions, and the
       
  1131     // suggestion exists in the theme registry, use it instead of the hook that
       
  1132     // theme() was called with. This allows the preprocess/process step to
       
  1133     // route to a more specific theme hook. For example, a function may call
       
  1134     // theme('node', ...), but a preprocess function can add 'node__article' as
       
  1135     // a suggestion, enabling a theme to have an alternate template file for
       
  1136     // article nodes. Suggestions are checked in the following order:
       
  1137     // - The 'theme_hook_suggestion' variable is checked first. It overrides
       
  1138     //   all others.
       
  1139     // - The 'theme_hook_suggestions' variable is checked in FILO order, so the
       
  1140     //   last suggestion added to the array takes precedence over suggestions
       
  1141     //   added earlier.
       
  1142     $suggestions = array();
       
  1143     if (!empty($variables['theme_hook_suggestions'])) {
       
  1144       $suggestions = $variables['theme_hook_suggestions'];
       
  1145     }
       
  1146     if (!empty($variables['theme_hook_suggestion'])) {
       
  1147       $suggestions[] = $variables['theme_hook_suggestion'];
       
  1148     }
       
  1149     foreach (array_reverse($suggestions) as $suggestion) {
       
  1150       if (isset($hooks[$suggestion])) {
       
  1151         $info = $hooks[$suggestion];
       
  1152         break;
       
  1153       }
       
  1154     }
       
  1155   }
       
  1156 
       
  1157   // Generate the output using either a function or a template.
       
  1158   $output = '';
       
  1159   if (isset($info['function'])) {
       
  1160     if (function_exists($info['function'])) {
       
  1161       $output = $info['function']($variables);
       
  1162     }
       
  1163   }
       
  1164   else {
       
  1165     // Default render function and extension.
       
  1166     $render_function = 'theme_render_template';
       
  1167     $extension = '.tpl.php';
       
  1168 
       
  1169     // The theme engine may use a different extension and a different renderer.
       
  1170     global $theme_engine;
       
  1171     if (isset($theme_engine)) {
       
  1172       if ($info['type'] != 'module') {
       
  1173         if (function_exists($theme_engine . '_render_template')) {
       
  1174           $render_function = $theme_engine . '_render_template';
       
  1175         }
       
  1176         $extension_function = $theme_engine . '_extension';
       
  1177         if (function_exists($extension_function)) {
       
  1178           $extension = $extension_function();
       
  1179         }
       
  1180       }
       
  1181     }
       
  1182 
       
  1183     // In some cases, a template implementation may not have had
       
  1184     // template_preprocess() run (for example, if the default implementation is
       
  1185     // a function, but a template overrides that default implementation). In
       
  1186     // these cases, a template should still be able to expect to have access to
       
  1187     // the variables provided by template_preprocess(), so we add them here if
       
  1188     // they don't already exist. We don't want to run template_preprocess()
       
  1189     // twice (it would be inefficient and mess up zebra striping), so we use the
       
  1190     // 'directory' variable to determine if it has already run, which while not
       
  1191     // completely intuitive, is reasonably safe, and allows us to save on the
       
  1192     // overhead of adding some new variable to track that.
       
  1193     if (!isset($variables['directory'])) {
       
  1194       $default_template_variables = array();
       
  1195       template_preprocess($default_template_variables, $hook);
       
  1196       $variables += $default_template_variables;
       
  1197     }
       
  1198 
       
  1199     // Render the output using the template file.
       
  1200     $template_file = $info['template'] . $extension;
       
  1201     if (isset($info['path'])) {
       
  1202       $template_file = $info['path'] . '/' . $template_file;
       
  1203     }
       
  1204     if (variable_get('theme_debug', FALSE)) {
       
  1205       $output = _theme_render_template_debug($render_function, $template_file, $variables, $extension);
       
  1206     }
       
  1207     else {
       
  1208       $output = $render_function($template_file, $variables);
       
  1209     }
       
  1210   }
       
  1211 
       
  1212   // restore path_to_theme()
       
  1213   $theme_path = $temp;
       
  1214   return $output;
       
  1215 }
       
  1216 
       
  1217 /**
       
  1218  * Returns the path to the current themed element.
       
  1219  *
       
  1220  * It can point to the active theme or the module handling a themed
       
  1221  * implementation. For example, when invoked within the scope of a theming call
       
  1222  * it will depend on where the theming function is handled. If implemented from
       
  1223  * a module, it will point to the module. If implemented from the active theme,
       
  1224  * it will point to the active theme. When called outside the scope of a
       
  1225  * theming call, it will always point to the active theme.
       
  1226  */
       
  1227 function path_to_theme() {
       
  1228   global $theme_path;
       
  1229 
       
  1230   if (!isset($theme_path)) {
       
  1231     drupal_theme_initialize();
       
  1232   }
       
  1233 
       
  1234   return $theme_path;
       
  1235 }
       
  1236 
       
  1237 /**
       
  1238  * Allows themes and/or theme engines to discover overridden theme functions.
       
  1239  *
       
  1240  * @param $cache
       
  1241  *   The existing cache of theme hooks to test against.
       
  1242  * @param $prefixes
       
  1243  *   An array of prefixes to test, in reverse order of importance.
       
  1244  *
       
  1245  * @return $implementations
       
  1246  *   The functions found, suitable for returning from hook_theme;
       
  1247  */
       
  1248 function drupal_find_theme_functions($cache, $prefixes) {
       
  1249   $implementations = array();
       
  1250   $functions = get_defined_functions();
       
  1251   $theme_functions = preg_grep('/^(' . implode(')|(', $prefixes) . ')_/', $functions['user']);
       
  1252 
       
  1253   foreach ($cache as $hook => $info) {
       
  1254     foreach ($prefixes as $prefix) {
       
  1255       // Find theme functions that implement possible "suggestion" variants of
       
  1256       // registered theme hooks and add those as new registered theme hooks.
       
  1257       // The 'pattern' key defines a common prefix that all suggestions must
       
  1258       // start with. The default is the name of the hook followed by '__'. An
       
  1259       // 'base hook' key is added to each entry made for a found suggestion,
       
  1260       // so that common functionality can be implemented for all suggestions of
       
  1261       // the same base hook. To keep things simple, deep hierarchy of
       
  1262       // suggestions is not supported: each suggestion's 'base hook' key
       
  1263       // refers to a base hook, not to another suggestion, and all suggestions
       
  1264       // are found using the base hook's pattern, not a pattern from an
       
  1265       // intermediary suggestion.
       
  1266       $pattern = isset($info['pattern']) ? $info['pattern'] : ($hook . '__');
       
  1267       if (!isset($info['base hook']) && !empty($pattern)) {
       
  1268         $matches = preg_grep('/^' . $prefix . '_' . $pattern . '/', $theme_functions);
       
  1269         if ($matches) {
       
  1270           foreach ($matches as $match) {
       
  1271             $new_hook = substr($match, strlen($prefix) + 1);
       
  1272             $arg_name = isset($info['variables']) ? 'variables' : 'render element';
       
  1273             $implementations[$new_hook] = array(
       
  1274               'function' => $match,
       
  1275               $arg_name => $info[$arg_name],
       
  1276               'base hook' => $hook,
       
  1277             );
       
  1278           }
       
  1279         }
       
  1280       }
       
  1281       // Find theme functions that implement registered theme hooks and include
       
  1282       // that in what is returned so that the registry knows that the theme has
       
  1283       // this implementation.
       
  1284       if (function_exists($prefix . '_' . $hook)) {
       
  1285         $implementations[$hook] = array(
       
  1286           'function' => $prefix . '_' . $hook,
       
  1287         );
       
  1288       }
       
  1289     }
       
  1290   }
       
  1291 
       
  1292   return $implementations;
       
  1293 }
       
  1294 
       
  1295 /**
       
  1296  * Allows themes and/or theme engines to easily discover overridden templates.
       
  1297  *
       
  1298  * @param $cache
       
  1299  *   The existing cache of theme hooks to test against.
       
  1300  * @param $extension
       
  1301  *   The extension that these templates will have.
       
  1302  * @param $path
       
  1303  *   The path to search.
       
  1304  */
       
  1305 function drupal_find_theme_templates($cache, $extension, $path) {
       
  1306   $implementations = array();
       
  1307 
       
  1308   // Collect paths to all sub-themes grouped by base themes. These will be
       
  1309   // used for filtering. This allows base themes to have sub-themes in its
       
  1310   // folder hierarchy without affecting the base themes template discovery.
       
  1311   $theme_paths = array();
       
  1312   foreach (list_themes() as $theme_info) {
       
  1313     if (!empty($theme_info->base_theme)) {
       
  1314       $theme_paths[$theme_info->base_theme][$theme_info->name] = dirname($theme_info->filename);
       
  1315     }
       
  1316   }
       
  1317   foreach ($theme_paths as $basetheme => $subthemes) {
       
  1318     foreach ($subthemes as $subtheme => $subtheme_path) {
       
  1319       if (isset($theme_paths[$subtheme])) {
       
  1320         $theme_paths[$basetheme] = array_merge($theme_paths[$basetheme], $theme_paths[$subtheme]);
       
  1321       }
       
  1322     }
       
  1323   }
       
  1324   global $theme;
       
  1325   $subtheme_paths = isset($theme_paths[$theme]) ? $theme_paths[$theme] : array();
       
  1326 
       
  1327   // Escape the periods in the extension.
       
  1328   $regex = '/' . str_replace('.', '\.', $extension) . '$/';
       
  1329   // Get a listing of all template files in the path to search.
       
  1330   $files = drupal_system_listing($regex, $path, 'name', 0);
       
  1331 
       
  1332   // Find templates that implement registered theme hooks and include that in
       
  1333   // what is returned so that the registry knows that the theme has this
       
  1334   // implementation.
       
  1335   foreach ($files as $template => $file) {
       
  1336     // Ignore sub-theme templates for the current theme.
       
  1337     if (strpos($file->uri, str_replace($subtheme_paths, '', $file->uri)) !== 0) {
       
  1338       continue;
       
  1339     }
       
  1340     // Chop off the remaining extensions if there are any. $template already
       
  1341     // has the rightmost extension removed, but there might still be more,
       
  1342     // such as with .tpl.php, which still has .tpl in $template at this point.
       
  1343     if (($pos = strpos($template, '.')) !== FALSE) {
       
  1344       $template = substr($template, 0, $pos);
       
  1345     }
       
  1346     // Transform - in filenames to _ to match function naming scheme
       
  1347     // for the purposes of searching.
       
  1348     $hook = strtr($template, '-', '_');
       
  1349     if (isset($cache[$hook])) {
       
  1350       $implementations[$hook] = array(
       
  1351         'template' => $template,
       
  1352         'path' => dirname($file->uri),
       
  1353       );
       
  1354     }
       
  1355   }
       
  1356 
       
  1357   // Find templates that implement possible "suggestion" variants of registered
       
  1358   // theme hooks and add those as new registered theme hooks. See
       
  1359   // drupal_find_theme_functions() for more information about suggestions and
       
  1360   // the use of 'pattern' and 'base hook'.
       
  1361   $patterns = array_keys($files);
       
  1362   foreach ($cache as $hook => $info) {
       
  1363     $pattern = isset($info['pattern']) ? $info['pattern'] : ($hook . '__');
       
  1364     if (!isset($info['base hook']) && !empty($pattern)) {
       
  1365       // Transform _ in pattern to - to match file naming scheme
       
  1366       // for the purposes of searching.
       
  1367       $pattern = strtr($pattern, '_', '-');
       
  1368 
       
  1369       $matches = preg_grep('/^' . $pattern . '/', $patterns);
       
  1370       if ($matches) {
       
  1371         foreach ($matches as $match) {
       
  1372           $file = substr($match, 0, strpos($match, '.'));
       
  1373           // Put the underscores back in for the hook name and register this
       
  1374           // pattern.
       
  1375           $arg_name = isset($info['variables']) ? 'variables' : 'render element';
       
  1376           $implementations[strtr($file, '-', '_')] = array(
       
  1377             'template' => $file,
       
  1378             'path' => dirname($files[$match]->uri),
       
  1379             $arg_name => $info[$arg_name],
       
  1380             'base hook' => $hook,
       
  1381           );
       
  1382         }
       
  1383       }
       
  1384     }
       
  1385   }
       
  1386   return $implementations;
       
  1387 }
       
  1388 
       
  1389 /**
       
  1390  * Retrieves a setting for the current theme or for a given theme.
       
  1391  *
       
  1392  * The final setting is obtained from the last value found in the following
       
  1393  * sources:
       
  1394  * - the default global settings specified in this function
       
  1395  * - the default theme-specific settings defined in any base theme's .info file
       
  1396  * - the default theme-specific settings defined in the theme's .info file
       
  1397  * - the saved values from the global theme settings form
       
  1398  * - the saved values from the theme's settings form
       
  1399  * To only retrieve the default global theme setting, an empty string should be
       
  1400  * given for $theme.
       
  1401  *
       
  1402  * @param $setting_name
       
  1403  *   The name of the setting to be retrieved.
       
  1404  * @param $theme
       
  1405  *   The name of a given theme; defaults to the current theme.
       
  1406  *
       
  1407  * @return
       
  1408  *   The value of the requested setting, NULL if the setting does not exist.
       
  1409  */
       
  1410 function theme_get_setting($setting_name, $theme = NULL) {
       
  1411   $cache = &drupal_static(__FUNCTION__, array());
       
  1412 
       
  1413   // If no key is given, use the current theme if we can determine it.
       
  1414   if (!isset($theme)) {
       
  1415     $theme = !empty($GLOBALS['theme_key']) ? $GLOBALS['theme_key'] : '';
       
  1416   }
       
  1417 
       
  1418   if (empty($cache[$theme])) {
       
  1419     // Set the default values for each global setting.
       
  1420     // To add new global settings, add their default values below, and then
       
  1421     // add form elements to system_theme_settings() in system.admin.inc.
       
  1422     $cache[$theme] = array(
       
  1423       'default_logo'                     =>  1,
       
  1424       'logo_path'                        =>  '',
       
  1425       'default_favicon'                  =>  1,
       
  1426       'favicon_path'                     =>  '',
       
  1427       // Use the IANA-registered MIME type for ICO files as default.
       
  1428       'favicon_mimetype'                 =>  'image/vnd.microsoft.icon',
       
  1429     );
       
  1430     // Turn on all default features.
       
  1431     $features = _system_default_theme_features();
       
  1432     foreach ($features as $feature) {
       
  1433       $cache[$theme]['toggle_' . $feature] = 1;
       
  1434     }
       
  1435 
       
  1436     // Get the values for the theme-specific settings from the .info files of
       
  1437     // the theme and all its base themes.
       
  1438     if ($theme) {
       
  1439       $themes = list_themes();
       
  1440       $theme_object = $themes[$theme];
       
  1441 
       
  1442       // Create a list which includes the current theme and all its base themes.
       
  1443       if (isset($theme_object->base_themes)) {
       
  1444         $theme_keys = array_keys($theme_object->base_themes);
       
  1445         $theme_keys[] = $theme;
       
  1446       }
       
  1447       else {
       
  1448         $theme_keys = array($theme);
       
  1449       }
       
  1450       foreach ($theme_keys as $theme_key) {
       
  1451         if (!empty($themes[$theme_key]->info['settings'])) {
       
  1452           $cache[$theme] = array_merge($cache[$theme], $themes[$theme_key]->info['settings']);
       
  1453         }
       
  1454       }
       
  1455     }
       
  1456 
       
  1457     // Get the saved global settings from the database.
       
  1458     $cache[$theme] = array_merge($cache[$theme], variable_get('theme_settings', array()));
       
  1459 
       
  1460     if ($theme) {
       
  1461       // Get the saved theme-specific settings from the database.
       
  1462       $cache[$theme] = array_merge($cache[$theme], variable_get('theme_' . $theme . '_settings', array()));
       
  1463 
       
  1464       // If the theme does not support a particular feature, override the global
       
  1465       // setting and set the value to NULL.
       
  1466       if (!empty($theme_object->info['features'])) {
       
  1467         foreach ($features as $feature) {
       
  1468           if (!in_array($feature, $theme_object->info['features'])) {
       
  1469             $cache[$theme]['toggle_' . $feature] = NULL;
       
  1470           }
       
  1471         }
       
  1472       }
       
  1473 
       
  1474       // Generate the path to the logo image.
       
  1475       if ($cache[$theme]['toggle_logo']) {
       
  1476         if ($cache[$theme]['default_logo']) {
       
  1477           $cache[$theme]['logo'] = file_create_url(dirname($theme_object->filename) . '/logo.png');
       
  1478         }
       
  1479         elseif ($cache[$theme]['logo_path']) {
       
  1480           $cache[$theme]['logo'] = file_create_url($cache[$theme]['logo_path']);
       
  1481         }
       
  1482       }
       
  1483 
       
  1484       // Generate the path to the favicon.
       
  1485       if ($cache[$theme]['toggle_favicon']) {
       
  1486         if ($cache[$theme]['default_favicon']) {
       
  1487           if (file_exists($favicon = dirname($theme_object->filename) . '/favicon.ico')) {
       
  1488             $cache[$theme]['favicon'] = file_create_url($favicon);
       
  1489           }
       
  1490           else {
       
  1491             $cache[$theme]['favicon'] = file_create_url('misc/favicon.ico');
       
  1492           }
       
  1493         }
       
  1494         elseif ($cache[$theme]['favicon_path']) {
       
  1495           $cache[$theme]['favicon'] = file_create_url($cache[$theme]['favicon_path']);
       
  1496         }
       
  1497         else {
       
  1498           $cache[$theme]['toggle_favicon'] = FALSE;
       
  1499         }
       
  1500       }
       
  1501     }
       
  1502   }
       
  1503 
       
  1504   return isset($cache[$theme][$setting_name]) ? $cache[$theme][$setting_name] : NULL;
       
  1505 }
       
  1506 
       
  1507 /**
       
  1508  * Renders a system default template, which is essentially a PHP template.
       
  1509  *
       
  1510  * @param $template_file
       
  1511  *   The filename of the template to render.
       
  1512  * @param $variables
       
  1513  *   A keyed array of variables that will appear in the output.
       
  1514  *
       
  1515  * @return
       
  1516  *   The output generated by the template.
       
  1517  */
       
  1518 function theme_render_template($template_file, $variables) {
       
  1519   // Extract the variables to a local namespace
       
  1520   extract($variables, EXTR_SKIP);
       
  1521 
       
  1522   // Start output buffering
       
  1523   ob_start();
       
  1524 
       
  1525   // Include the template file
       
  1526   include DRUPAL_ROOT . '/' . $template_file;
       
  1527 
       
  1528   // End buffering and return its contents
       
  1529   return ob_get_clean();
       
  1530 }
       
  1531 
       
  1532 /**
       
  1533  * Renders a template for any engine.
       
  1534  *
       
  1535  * Includes the possibility to get debug output by setting the
       
  1536  * theme_debug variable to TRUE.
       
  1537  *
       
  1538  * @param string $template_function
       
  1539  *   The function to call for rendering the template.
       
  1540  * @param string $template_file
       
  1541  *   The filename of the template to render.
       
  1542  * @param array $variables
       
  1543  *   A keyed array of variables that will appear in the output.
       
  1544  * @param string $extension
       
  1545  *   The extension used by the theme engine for template files.
       
  1546  *
       
  1547  * @return string
       
  1548  *   The output generated by the template including debug information.
       
  1549  */
       
  1550 function _theme_render_template_debug($template_function, $template_file, $variables, $extension) {
       
  1551   $output = array(
       
  1552     'debug_prefix' => '',
       
  1553     'debug_info' => '',
       
  1554     'rendered_markup' => call_user_func($template_function, $template_file, $variables),
       
  1555     'debug_suffix' => '',
       
  1556   );
       
  1557   $output['debug_prefix'] .= "\n\n<!-- THEME DEBUG -->";
       
  1558   $output['debug_prefix'] .= "\n<!-- CALL: theme('" . check_plain($variables['theme_hook_original']) . "') -->";
       
  1559   // If there are theme suggestions, reverse the array so more specific
       
  1560   // suggestions are shown first.
       
  1561   if (!empty($variables['theme_hook_suggestions'])) {
       
  1562     $variables['theme_hook_suggestions'] = array_reverse($variables['theme_hook_suggestions']);
       
  1563   }
       
  1564   // Add debug output for directly called suggestions like
       
  1565   // '#theme' => 'comment__node__article'.
       
  1566   if (strpos($variables['theme_hook_original'], '__') !== FALSE) {
       
  1567     $derived_suggestions[] = $hook = $variables['theme_hook_original'];
       
  1568     while ($pos = strrpos($hook, '__')) {
       
  1569       $hook = substr($hook, 0, $pos);
       
  1570       $derived_suggestions[] = $hook;
       
  1571     }
       
  1572     // Get the value of the base hook (last derived suggestion) and append it
       
  1573     // to the end of all theme suggestions.
       
  1574     $base_hook = array_pop($derived_suggestions);
       
  1575     $variables['theme_hook_suggestions'] = array_merge($derived_suggestions, $variables['theme_hook_suggestions']);
       
  1576     $variables['theme_hook_suggestions'][] = $base_hook;
       
  1577   }
       
  1578   if (!empty($variables['theme_hook_suggestions'])) {
       
  1579     $current_template = basename($template_file);
       
  1580     $suggestions = $variables['theme_hook_suggestions'];
       
  1581     // Only add the original theme hook if it wasn't a directly called
       
  1582     // suggestion.
       
  1583     if (strpos($variables['theme_hook_original'], '__') === FALSE) {
       
  1584       $suggestions[] = $variables['theme_hook_original'];
       
  1585     }
       
  1586     foreach ($suggestions as &$suggestion) {
       
  1587       $template = strtr($suggestion, '_', '-') . $extension;
       
  1588       $prefix = ($template == $current_template) ? 'x' : '*';
       
  1589       $suggestion = $prefix . ' ' . $template;
       
  1590     }
       
  1591     $output['debug_info'] .= "\n<!-- FILE NAME SUGGESTIONS:\n   " . check_plain(implode("\n   ", $suggestions)) . "\n-->";
       
  1592   }
       
  1593   $output['debug_info'] .= "\n<!-- BEGIN OUTPUT from '" . check_plain($template_file) . "' -->\n";
       
  1594   $output['debug_suffix'] .= "\n<!-- END OUTPUT from '" . check_plain($template_file) . "' -->\n\n";
       
  1595   return implode('', $output);
       
  1596 }
       
  1597 
       
  1598 /**
       
  1599  * Enables a given list of themes.
       
  1600  *
       
  1601  * @param $theme_list
       
  1602  *   An array of theme names.
       
  1603  */
       
  1604 function theme_enable($theme_list) {
       
  1605   drupal_clear_css_cache();
       
  1606 
       
  1607   foreach ($theme_list as $key) {
       
  1608     db_update('system')
       
  1609       ->fields(array('status' => 1))
       
  1610       ->condition('type', 'theme')
       
  1611       ->condition('name', $key)
       
  1612       ->execute();
       
  1613   }
       
  1614 
       
  1615   list_themes(TRUE);
       
  1616   menu_rebuild();
       
  1617   drupal_theme_rebuild();
       
  1618 
       
  1619   // Invoke hook_themes_enabled() after the themes have been enabled.
       
  1620   module_invoke_all('themes_enabled', $theme_list);
       
  1621 }
       
  1622 
       
  1623 /**
       
  1624  * Disables a given list of themes.
       
  1625  *
       
  1626  * @param $theme_list
       
  1627  *   An array of theme names.
       
  1628  */
       
  1629 function theme_disable($theme_list) {
       
  1630   // Don't disable the default theme.
       
  1631   if ($pos = array_search(variable_get('theme_default', 'bartik'), $theme_list) !== FALSE) {
       
  1632     unset($theme_list[$pos]);
       
  1633     if (empty($theme_list)) {
       
  1634       return;
       
  1635     }
       
  1636   }
       
  1637 
       
  1638   drupal_clear_css_cache();
       
  1639 
       
  1640   foreach ($theme_list as $key) {
       
  1641     db_update('system')
       
  1642       ->fields(array('status' => 0))
       
  1643       ->condition('type', 'theme')
       
  1644       ->condition('name', $key)
       
  1645       ->execute();
       
  1646   }
       
  1647 
       
  1648   list_themes(TRUE);
       
  1649   menu_rebuild();
       
  1650   drupal_theme_rebuild();
       
  1651 
       
  1652   // Invoke hook_themes_disabled after the themes have been disabled.
       
  1653   module_invoke_all('themes_disabled', $theme_list);
       
  1654 }
       
  1655 
       
  1656 /**
       
  1657  * @addtogroup themeable
       
  1658  * @{
       
  1659  */
       
  1660 
       
  1661 /**
       
  1662  * Returns HTML for status and/or error messages, grouped by type.
       
  1663  *
       
  1664  * An invisible heading identifies the messages for assistive technology.
       
  1665  * Sighted users see a colored box. See http://www.w3.org/TR/WCAG-TECHS/H69.html
       
  1666  * for info.
       
  1667  *
       
  1668  * @param $variables
       
  1669  *   An associative array containing:
       
  1670  *   - display: (optional) Set to 'status' or 'error' to display only messages
       
  1671  *     of that type.
       
  1672  */
       
  1673 function theme_status_messages($variables) {
       
  1674   $display = $variables['display'];
       
  1675   $output = '';
       
  1676 
       
  1677   $status_heading = array(
       
  1678     'status' => t('Status message'),
       
  1679     'error' => t('Error message'),
       
  1680     'warning' => t('Warning message'),
       
  1681   );
       
  1682   foreach (drupal_get_messages($display) as $type => $messages) {
       
  1683     $output .= "<div class=\"messages $type\">\n";
       
  1684     if (!empty($status_heading[$type])) {
       
  1685       $output .= '<h2 class="element-invisible">' . $status_heading[$type] . "</h2>\n";
       
  1686     }
       
  1687     if (count($messages) > 1) {
       
  1688       $output .= " <ul>\n";
       
  1689       foreach ($messages as $message) {
       
  1690         $output .= '  <li>' . $message . "</li>\n";
       
  1691       }
       
  1692       $output .= " </ul>\n";
       
  1693     }
       
  1694     else {
       
  1695       $output .= reset($messages);
       
  1696     }
       
  1697     $output .= "</div>\n";
       
  1698   }
       
  1699   return $output;
       
  1700 }
       
  1701 
       
  1702 /**
       
  1703  * Returns HTML for a link.
       
  1704  *
       
  1705  * All Drupal code that outputs a link should call the l() function. That
       
  1706  * function performs some initial preprocessing, and then, if necessary, calls
       
  1707  * theme('link') for rendering the anchor tag.
       
  1708  *
       
  1709  * To optimize performance for sites that don't need custom theming of links,
       
  1710  * the l() function includes an inline copy of this function, and uses that
       
  1711  * copy if none of the enabled modules or the active theme implement any
       
  1712  * preprocess or process functions or override this theme implementation.
       
  1713  *
       
  1714  * @param array $variables
       
  1715  *   An associative array containing the keys:
       
  1716  *   - text: The text of the link.
       
  1717  *   - path: The internal path or external URL being linked to. It is used as
       
  1718  *     the $path parameter of the url() function.
       
  1719  *   - options: (optional) An array that defaults to empty, but can contain:
       
  1720  *     - attributes: Can contain optional attributes:
       
  1721  *       - class: must be declared in an array. Example: 'class' =>
       
  1722  *         array('class_name1','class_name2').
       
  1723  *       - title: must be a string. Example: 'title' => 'Example title'
       
  1724  *       - Others are more flexible as long as they work with
       
  1725  *         drupal_attributes($variables['options']['attributes]).
       
  1726  *     - html: Boolean flag that tells whether text contains HTML or plain
       
  1727  *       text. If set to TRUE, the text value will not be sanitized so the
       
  1728          calling function must ensure that it already contains safe HTML.
       
  1729  *   The elements $variables['options']['attributes'] and
       
  1730  *   $variables['options']['html'] are used in this function similarly to the
       
  1731  *   way that $options['attributes'] and $options['html'] are used in l().
       
  1732  *   The link itself is built by the url() function, which takes
       
  1733  *   $variables['path'] and $variables['options'] as arguments.
       
  1734  *
       
  1735  * @see l()
       
  1736  * @see url()
       
  1737  */
       
  1738 function theme_link($variables) {
       
  1739   return '<a href="' . check_plain(url($variables['path'], $variables['options'])) . '"' . drupal_attributes($variables['options']['attributes']) . '>' . ($variables['options']['html'] ? $variables['text'] : check_plain($variables['text'])) . '</a>';
       
  1740 }
       
  1741 
       
  1742 /**
       
  1743  * Returns HTML for a set of links.
       
  1744  *
       
  1745  * @param $variables
       
  1746  *   An associative array containing:
       
  1747  *   - links: An associative array of links to be themed. The key for each link
       
  1748  *     is used as its CSS class. Each link should be itself an array, with the
       
  1749  *     following elements:
       
  1750  *     - title: The link text.
       
  1751  *     - href: The link URL. If omitted, the 'title' is shown as a plain text
       
  1752  *       item in the links list.
       
  1753  *     - html: (optional) Whether or not 'title' is HTML. If set, the title
       
  1754  *       will not be passed through check_plain().
       
  1755  *     - attributes: (optional) Attributes for the anchor, or for the <span>
       
  1756  *       tag used in its place if no 'href' is supplied. If element 'class' is
       
  1757  *       included, it must be an array of one or more class names.
       
  1758  *     If the 'href' element is supplied, the entire link array is passed to
       
  1759  *     l() as its $options parameter.
       
  1760  *   - attributes: A keyed array of attributes for the UL containing the
       
  1761  *     list of links.
       
  1762  *   - heading: (optional) A heading to precede the links. May be an
       
  1763  *     associative array or a string. If it's an array, it can have the
       
  1764  *     following elements:
       
  1765  *     - text: The heading text.
       
  1766  *     - level: The heading level (e.g. 'h2', 'h3').
       
  1767  *     - class: (optional) An array of the CSS classes for the heading.
       
  1768  *     When using a string it will be used as the text of the heading and the
       
  1769  *     level will default to 'h2'. Headings should be used on navigation menus
       
  1770  *     and any list of links that consistently appears on multiple pages. To
       
  1771  *     make the heading invisible use the 'element-invisible' CSS class. Do not
       
  1772  *     use 'display:none', which removes it from screen-readers and assistive
       
  1773  *     technology. Headings allow screen-reader and keyboard only users to
       
  1774  *     navigate to or skip the links. See
       
  1775  *     http://juicystudio.com/article/screen-readers-display-none.php and
       
  1776  *     http://www.w3.org/TR/WCAG-TECHS/H42.html for more information.
       
  1777  */
       
  1778 function theme_links($variables) {
       
  1779   $links = $variables['links'];
       
  1780   $attributes = $variables['attributes'];
       
  1781   $heading = $variables['heading'];
       
  1782   global $language_url;
       
  1783   $output = '';
       
  1784 
       
  1785   if (count($links) > 0) {
       
  1786     // Treat the heading first if it is present to prepend it to the
       
  1787     // list of links.
       
  1788     if (!empty($heading)) {
       
  1789       if (is_string($heading)) {
       
  1790         // Prepare the array that will be used when the passed heading
       
  1791         // is a string.
       
  1792         $heading = array(
       
  1793           'text' => $heading,
       
  1794           // Set the default level of the heading.
       
  1795           'level' => 'h2',
       
  1796         );
       
  1797       }
       
  1798       $output .= '<' . $heading['level'];
       
  1799       if (!empty($heading['class'])) {
       
  1800         $output .= drupal_attributes(array('class' => $heading['class']));
       
  1801       }
       
  1802       $output .= '>' . check_plain($heading['text']) . '</' . $heading['level'] . '>';
       
  1803     }
       
  1804 
       
  1805     $output .= '<ul' . drupal_attributes($attributes) . '>';
       
  1806 
       
  1807     $num_links = count($links);
       
  1808     $i = 1;
       
  1809 
       
  1810     foreach ($links as $key => $link) {
       
  1811       $class = array($key);
       
  1812 
       
  1813       // Add first, last and active classes to the list of links to help out
       
  1814       // themers.
       
  1815       if ($i == 1) {
       
  1816         $class[] = 'first';
       
  1817       }
       
  1818       if ($i == $num_links) {
       
  1819         $class[] = 'last';
       
  1820       }
       
  1821       if (isset($link['href']) && ($link['href'] == $_GET['q'] || ($link['href'] == '<front>' && drupal_is_front_page()))
       
  1822           && (empty($link['language']) || $link['language']->language == $language_url->language)) {
       
  1823         $class[] = 'active';
       
  1824       }
       
  1825       $output .= '<li' . drupal_attributes(array('class' => $class)) . '>';
       
  1826 
       
  1827       if (isset($link['href'])) {
       
  1828         // Pass in $link as $options, they share the same keys.
       
  1829         $output .= l($link['title'], $link['href'], $link);
       
  1830       }
       
  1831       elseif (!empty($link['title'])) {
       
  1832         // Some links are actually not links, but we wrap these in <span> for
       
  1833         // adding title and class attributes.
       
  1834         if (empty($link['html'])) {
       
  1835           $link['title'] = check_plain($link['title']);
       
  1836         }
       
  1837         $span_attributes = '';
       
  1838         if (isset($link['attributes'])) {
       
  1839           $span_attributes = drupal_attributes($link['attributes']);
       
  1840         }
       
  1841         $output .= '<span' . $span_attributes . '>' . $link['title'] . '</span>';
       
  1842       }
       
  1843 
       
  1844       $i++;
       
  1845       $output .= "</li>\n";
       
  1846     }
       
  1847 
       
  1848     $output .= '</ul>';
       
  1849   }
       
  1850 
       
  1851   return $output;
       
  1852 }
       
  1853 
       
  1854 /**
       
  1855  * Returns HTML for an image.
       
  1856  *
       
  1857  * @param $variables
       
  1858  *   An associative array containing:
       
  1859  *   - path: Either the path of the image file (relative to base_path()) or a
       
  1860  *     full URL.
       
  1861  *   - width: The width of the image (if known).
       
  1862  *   - height: The height of the image (if known).
       
  1863  *   - alt: The alternative text for text-based browsers. HTML 4 and XHTML 1.0
       
  1864  *     always require an alt attribute. The HTML 5 draft allows the alt
       
  1865  *     attribute to be omitted in some cases. Therefore, this variable defaults
       
  1866  *     to an empty string, but can be set to NULL for the attribute to be
       
  1867  *     omitted. Usually, neither omission nor an empty string satisfies
       
  1868  *     accessibility requirements, so it is strongly encouraged for code
       
  1869  *     calling theme('image') to pass a meaningful value for this variable.
       
  1870  *     - http://www.w3.org/TR/REC-html40/struct/objects.html#h-13.8
       
  1871  *     - http://www.w3.org/TR/xhtml1/dtds.html
       
  1872  *     - http://dev.w3.org/html5/spec/Overview.html#alt
       
  1873  *   - title: The title text is displayed when the image is hovered in some
       
  1874  *     popular browsers.
       
  1875  *   - attributes: Associative array of attributes to be placed in the img tag.
       
  1876  */
       
  1877 function theme_image($variables) {
       
  1878   $attributes = $variables['attributes'];
       
  1879   $attributes['src'] = file_create_url($variables['path']);
       
  1880 
       
  1881   foreach (array('width', 'height', 'alt', 'title') as $key) {
       
  1882 
       
  1883     if (isset($variables[$key])) {
       
  1884       $attributes[$key] = $variables[$key];
       
  1885     }
       
  1886   }
       
  1887 
       
  1888   return '<img' . drupal_attributes($attributes) . ' />';
       
  1889 }
       
  1890 
       
  1891 /**
       
  1892  * Returns HTML for a breadcrumb trail.
       
  1893  *
       
  1894  * @param $variables
       
  1895  *   An associative array containing:
       
  1896  *   - breadcrumb: An array containing the breadcrumb links.
       
  1897  */
       
  1898 function theme_breadcrumb($variables) {
       
  1899   $breadcrumb = $variables['breadcrumb'];
       
  1900 
       
  1901   if (!empty($breadcrumb)) {
       
  1902     // Provide a navigational heading to give context for breadcrumb links to
       
  1903     // screen-reader users. Make the heading invisible with .element-invisible.
       
  1904     $output = '<h2 class="element-invisible">' . t('You are here') . '</h2>';
       
  1905 
       
  1906     $output .= '<div class="breadcrumb">' . implode(' » ', $breadcrumb) . '</div>';
       
  1907     return $output;
       
  1908   }
       
  1909 }
       
  1910 
       
  1911 /**
       
  1912  * Returns HTML for a table.
       
  1913  *
       
  1914  * @param $variables
       
  1915  *   An associative array containing:
       
  1916  *   - header: An array containing the table headers. Each element of the array
       
  1917  *     can be either a localized string or an associative array with the
       
  1918  *     following keys:
       
  1919  *     - "data": The localized title of the table column.
       
  1920  *     - "field": The database field represented in the table column (required
       
  1921  *       if user is to be able to sort on this column).
       
  1922  *     - "sort": A default sort order for this column ("asc" or "desc"). Only
       
  1923  *       one column should be given a default sort order because table sorting
       
  1924  *       only applies to one column at a time.
       
  1925  *     - Any HTML attributes, such as "colspan", to apply to the column header
       
  1926  *       cell.
       
  1927  *   - rows: An array of table rows. Every row is an array of cells, or an
       
  1928  *     associative array with the following keys:
       
  1929  *     - "data": an array of cells
       
  1930  *     - Any HTML attributes, such as "class", to apply to the table row.
       
  1931  *     - "no_striping": a boolean indicating that the row should receive no
       
  1932  *       'even / odd' styling. Defaults to FALSE.
       
  1933  *     Each cell can be either a string or an associative array with the
       
  1934  *     following keys:
       
  1935  *     - "data": The string to display in the table cell.
       
  1936  *     - "header": Indicates this cell is a header.
       
  1937  *     - Any HTML attributes, such as "colspan", to apply to the table cell.
       
  1938  *     Here's an example for $rows:
       
  1939  *     @code
       
  1940  *     $rows = array(
       
  1941  *       // Simple row
       
  1942  *       array(
       
  1943  *         'Cell 1', 'Cell 2', 'Cell 3'
       
  1944  *       ),
       
  1945  *       // Row with attributes on the row and some of its cells.
       
  1946  *       array(
       
  1947  *         'data' => array('Cell 1', array('data' => 'Cell 2', 'colspan' => 2)), 'class' => array('funky')
       
  1948  *       )
       
  1949  *     );
       
  1950  *     @endcode
       
  1951  *   - attributes: An array of HTML attributes to apply to the table tag.
       
  1952  *   - caption: A localized string to use for the <caption> tag.
       
  1953  *   - colgroups: An array of column groups. Each element of the array can be
       
  1954  *     either:
       
  1955  *     - An array of columns, each of which is an associative array of HTML
       
  1956  *       attributes applied to the COL element.
       
  1957  *     - An array of attributes applied to the COLGROUP element, which must
       
  1958  *       include a "data" attribute. To add attributes to COL elements, set the
       
  1959  *       "data" attribute with an array of columns, each of which is an
       
  1960  *       associative array of HTML attributes.
       
  1961  *     Here's an example for $colgroup:
       
  1962  *     @code
       
  1963  *     $colgroup = array(
       
  1964  *       // COLGROUP with one COL element.
       
  1965  *       array(
       
  1966  *         array(
       
  1967  *           'class' => array('funky'), // Attribute for the COL element.
       
  1968  *         ),
       
  1969  *       ),
       
  1970  *       // Colgroup with attributes and inner COL elements.
       
  1971  *       array(
       
  1972  *         'data' => array(
       
  1973  *           array(
       
  1974  *             'class' => array('funky'), // Attribute for the COL element.
       
  1975  *           ),
       
  1976  *         ),
       
  1977  *         'class' => array('jazzy'), // Attribute for the COLGROUP element.
       
  1978  *       ),
       
  1979  *     );
       
  1980  *     @endcode
       
  1981  *     These optional tags are used to group and set properties on columns
       
  1982  *     within a table. For example, one may easily group three columns and
       
  1983  *     apply same background style to all.
       
  1984  *   - sticky: Use a "sticky" table header.
       
  1985  *   - empty: The message to display in an extra row if table does not have any
       
  1986  *     rows.
       
  1987  */
       
  1988 function theme_table($variables) {
       
  1989   $header = $variables['header'];
       
  1990   $rows = $variables['rows'];
       
  1991   $attributes = $variables['attributes'];
       
  1992   $caption = $variables['caption'];
       
  1993   $colgroups = $variables['colgroups'];
       
  1994   $sticky = $variables['sticky'];
       
  1995   $empty = $variables['empty'];
       
  1996 
       
  1997   // Add sticky headers, if applicable.
       
  1998   if (count($header) && $sticky) {
       
  1999     drupal_add_js('misc/tableheader.js');
       
  2000     // Add 'sticky-enabled' class to the table to identify it for JS.
       
  2001     // This is needed to target tables constructed by this function.
       
  2002     $attributes['class'][] = 'sticky-enabled';
       
  2003   }
       
  2004 
       
  2005   $output = '<table' . drupal_attributes($attributes) . ">\n";
       
  2006 
       
  2007   if (isset($caption)) {
       
  2008     $output .= '<caption>' . $caption . "</caption>\n";
       
  2009   }
       
  2010 
       
  2011   // Format the table columns:
       
  2012   if (count($colgroups)) {
       
  2013     foreach ($colgroups as $number => $colgroup) {
       
  2014       $attributes = array();
       
  2015 
       
  2016       // Check if we're dealing with a simple or complex column
       
  2017       if (isset($colgroup['data'])) {
       
  2018         foreach ($colgroup as $key => $value) {
       
  2019           if ($key == 'data') {
       
  2020             $cols = $value;
       
  2021           }
       
  2022           else {
       
  2023             $attributes[$key] = $value;
       
  2024           }
       
  2025         }
       
  2026       }
       
  2027       else {
       
  2028         $cols = $colgroup;
       
  2029       }
       
  2030 
       
  2031       // Build colgroup
       
  2032       if (is_array($cols) && count($cols)) {
       
  2033         $output .= ' <colgroup' . drupal_attributes($attributes) . '>';
       
  2034         $i = 0;
       
  2035         foreach ($cols as $col) {
       
  2036           $output .= ' <col' . drupal_attributes($col) . ' />';
       
  2037         }
       
  2038         $output .= " </colgroup>\n";
       
  2039       }
       
  2040       else {
       
  2041         $output .= ' <colgroup' . drupal_attributes($attributes) . " />\n";
       
  2042       }
       
  2043     }
       
  2044   }
       
  2045 
       
  2046   // Add the 'empty' row message if available.
       
  2047   if (!count($rows) && $empty) {
       
  2048     $header_count = 0;
       
  2049     foreach ($header as $header_cell) {
       
  2050       if (is_array($header_cell)) {
       
  2051         $header_count += isset($header_cell['colspan']) ? $header_cell['colspan'] : 1;
       
  2052       }
       
  2053       else {
       
  2054         $header_count++;
       
  2055       }
       
  2056     }
       
  2057     $rows[] = array(array('data' => $empty, 'colspan' => $header_count, 'class' => array('empty', 'message')));
       
  2058   }
       
  2059 
       
  2060   // Format the table header:
       
  2061   if (count($header)) {
       
  2062     $ts = tablesort_init($header);
       
  2063     // HTML requires that the thead tag has tr tags in it followed by tbody
       
  2064     // tags. Using ternary operator to check and see if we have any rows.
       
  2065     $output .= (count($rows) ? ' <thead><tr>' : ' <tr>');
       
  2066     foreach ($header as $cell) {
       
  2067       $cell = tablesort_header($cell, $header, $ts);
       
  2068       $output .= _theme_table_cell($cell, TRUE);
       
  2069     }
       
  2070     // Using ternary operator to close the tags based on whether or not there are rows
       
  2071     $output .= (count($rows) ? " </tr></thead>\n" : "</tr>\n");
       
  2072   }
       
  2073   else {
       
  2074     $ts = array();
       
  2075   }
       
  2076 
       
  2077   // Format the table rows:
       
  2078   if (count($rows)) {
       
  2079     $output .= "<tbody>\n";
       
  2080     $flip = array('even' => 'odd', 'odd' => 'even');
       
  2081     $class = 'even';
       
  2082     foreach ($rows as $number => $row) {
       
  2083       // Check if we're dealing with a simple or complex row
       
  2084       if (isset($row['data'])) {
       
  2085         $cells = $row['data'];
       
  2086         $no_striping = isset($row['no_striping']) ? $row['no_striping'] : FALSE;
       
  2087 
       
  2088         // Set the attributes array and exclude 'data' and 'no_striping'.
       
  2089         $attributes = $row;
       
  2090         unset($attributes['data']);
       
  2091         unset($attributes['no_striping']);
       
  2092       }
       
  2093       else {
       
  2094         $cells = $row;
       
  2095         $attributes = array();
       
  2096         $no_striping = FALSE;
       
  2097       }
       
  2098       if (count($cells)) {
       
  2099         // Add odd/even class
       
  2100         if (!$no_striping) {
       
  2101           $class = $flip[$class];
       
  2102           $attributes['class'][] = $class;
       
  2103         }
       
  2104 
       
  2105         // Build row
       
  2106         $output .= ' <tr' . drupal_attributes($attributes) . '>';
       
  2107         $i = 0;
       
  2108         foreach ($cells as $cell) {
       
  2109           $cell = tablesort_cell($cell, $header, $ts, $i++);
       
  2110           $output .= _theme_table_cell($cell);
       
  2111         }
       
  2112         $output .= " </tr>\n";
       
  2113       }
       
  2114     }
       
  2115     $output .= "</tbody>\n";
       
  2116   }
       
  2117 
       
  2118   $output .= "</table>\n";
       
  2119   return $output;
       
  2120 }
       
  2121 
       
  2122 /**
       
  2123  * Returns HTML for a sort icon.
       
  2124  *
       
  2125  * @param $variables
       
  2126  *   An associative array containing:
       
  2127  *   - style: Set to either 'asc' or 'desc', this determines which icon to
       
  2128  *     show.
       
  2129  */
       
  2130 function theme_tablesort_indicator($variables) {
       
  2131   if ($variables['style'] == "asc") {
       
  2132     return theme('image', array('path' => 'misc/arrow-asc.png', 'width' => 13, 'height' => 13, 'alt' => t('sort ascending'), 'title' => t('sort ascending')));
       
  2133   }
       
  2134   else {
       
  2135     return theme('image', array('path' => 'misc/arrow-desc.png', 'width' => 13, 'height' => 13, 'alt' => t('sort descending'), 'title' => t('sort descending')));
       
  2136   }
       
  2137 }
       
  2138 
       
  2139 /**
       
  2140  * Returns HTML for a marker for new or updated content.
       
  2141  *
       
  2142  * @param $variables
       
  2143  *   An associative array containing:
       
  2144  *   - type: Number representing the marker type to display. See MARK_NEW,
       
  2145  *     MARK_UPDATED, MARK_READ.
       
  2146  */
       
  2147 function theme_mark($variables) {
       
  2148   $type = $variables['type'];
       
  2149   global $user;
       
  2150   if ($user->uid) {
       
  2151     if ($type == MARK_NEW) {
       
  2152       return ' <span class="marker">' . t('new') . '</span>';
       
  2153     }
       
  2154     elseif ($type == MARK_UPDATED) {
       
  2155       return ' <span class="marker">' . t('updated') . '</span>';
       
  2156     }
       
  2157   }
       
  2158 }
       
  2159 
       
  2160 /**
       
  2161  * Returns HTML for a list or nested list of items.
       
  2162  *
       
  2163  * @param $variables
       
  2164  *   An associative array containing:
       
  2165  *   - items: An array of items to be displayed in the list. If an item is a
       
  2166  *     string, then it is used as is. If an item is an array, then the "data"
       
  2167  *     element of the array is used as the contents of the list item. If an item
       
  2168  *     is an array with a "children" element, those children are displayed in a
       
  2169  *     nested list. All other elements are treated as attributes of the list
       
  2170  *     item element.
       
  2171  *   - title: The title of the list.
       
  2172  *   - type: The type of list to return (e.g. "ul", "ol").
       
  2173  *   - attributes: The attributes applied to the list element.
       
  2174  */
       
  2175 function theme_item_list($variables) {
       
  2176   $items = $variables['items'];
       
  2177   $title = $variables['title'];
       
  2178   $type = $variables['type'];
       
  2179   $attributes = $variables['attributes'];
       
  2180 
       
  2181   // Only output the list container and title, if there are any list items.
       
  2182   // Check to see whether the block title exists before adding a header.
       
  2183   // Empty headers are not semantic and present accessibility challenges.
       
  2184   $output = '<div class="item-list">';
       
  2185   if (isset($title) && $title !== '') {
       
  2186     $output .= '<h3>' . $title . '</h3>';
       
  2187   }
       
  2188 
       
  2189   if (!empty($items)) {
       
  2190     $output .= "<$type" . drupal_attributes($attributes) . '>';
       
  2191     $num_items = count($items);
       
  2192     $i = 0;
       
  2193     foreach ($items as $item) {
       
  2194       $attributes = array();
       
  2195       $children = array();
       
  2196       $data = '';
       
  2197       $i++;
       
  2198       if (is_array($item)) {
       
  2199         foreach ($item as $key => $value) {
       
  2200           if ($key == 'data') {
       
  2201             $data = $value;
       
  2202           }
       
  2203           elseif ($key == 'children') {
       
  2204             $children = $value;
       
  2205           }
       
  2206           else {
       
  2207             $attributes[$key] = $value;
       
  2208           }
       
  2209         }
       
  2210       }
       
  2211       else {
       
  2212         $data = $item;
       
  2213       }
       
  2214       if (count($children) > 0) {
       
  2215         // Render nested list.
       
  2216         $data .= theme_item_list(array('items' => $children, 'title' => NULL, 'type' => $type, 'attributes' => $attributes));
       
  2217       }
       
  2218       if ($i == 1) {
       
  2219         $attributes['class'][] = 'first';
       
  2220       }
       
  2221       if ($i == $num_items) {
       
  2222         $attributes['class'][] = 'last';
       
  2223       }
       
  2224       $output .= '<li' . drupal_attributes($attributes) . '>' . $data . "</li>\n";
       
  2225     }
       
  2226     $output .= "</$type>";
       
  2227   }
       
  2228   $output .= '</div>';
       
  2229   return $output;
       
  2230 }
       
  2231 
       
  2232 /**
       
  2233  * Returns HTML for a "more help" link.
       
  2234  *
       
  2235  * @param $variables
       
  2236  *   An associative array containing:
       
  2237  *   - url: The URL for the link.
       
  2238  */
       
  2239 function theme_more_help_link($variables) {
       
  2240   return '<div class="more-help-link">' . l(t('More help'), $variables['url']) . '</div>';
       
  2241 }
       
  2242 
       
  2243 /**
       
  2244  * Returns HTML for a feed icon.
       
  2245  *
       
  2246  * @param $variables
       
  2247  *   An associative array containing:
       
  2248  *   - url: An internal system path or a fully qualified external URL of the
       
  2249  *     feed.
       
  2250  *   - title: A descriptive title of the feed.
       
  2251  */
       
  2252 function theme_feed_icon($variables) {
       
  2253   $text = t('Subscribe to !feed-title', array('!feed-title' => $variables['title']));
       
  2254   if ($image = theme('image', array('path' => 'misc/feed.png', 'width' => 16, 'height' => 16, 'alt' => $text))) {
       
  2255     return l($image, $variables['url'], array('html' => TRUE, 'attributes' => array('class' => array('feed-icon'), 'title' => $text)));
       
  2256   }
       
  2257 }
       
  2258 
       
  2259 /**
       
  2260  * Returns HTML for a generic HTML tag with attributes.
       
  2261  *
       
  2262  * @param $variables
       
  2263  *   An associative array containing:
       
  2264  *   - element: An associative array describing the tag:
       
  2265  *     - #tag: The tag name to output. Typical tags added to the HTML HEAD:
       
  2266  *       - meta: To provide meta information, such as a page refresh.
       
  2267  *       - link: To refer to stylesheets and other contextual information.
       
  2268  *       - script: To load JavaScript.
       
  2269  *     - #attributes: (optional) An array of HTML attributes to apply to the
       
  2270  *       tag.
       
  2271  *     - #value: (optional) A string containing tag content, such as inline
       
  2272  *       CSS.
       
  2273  *     - #value_prefix: (optional) A string to prepend to #value, e.g. a CDATA
       
  2274  *       wrapper prefix.
       
  2275  *     - #value_suffix: (optional) A string to append to #value, e.g. a CDATA
       
  2276  *       wrapper suffix.
       
  2277  */
       
  2278 function theme_html_tag($variables) {
       
  2279   $element = $variables['element'];
       
  2280   $attributes = isset($element['#attributes']) ? drupal_attributes($element['#attributes']) : '';
       
  2281   if (!isset($element['#value'])) {
       
  2282     return '<' . $element['#tag'] . $attributes . " />\n";
       
  2283   }
       
  2284   else {
       
  2285     $output = '<' . $element['#tag'] . $attributes . '>';
       
  2286     if (isset($element['#value_prefix'])) {
       
  2287       $output .= $element['#value_prefix'];
       
  2288     }
       
  2289     $output .= $element['#value'];
       
  2290     if (isset($element['#value_suffix'])) {
       
  2291       $output .= $element['#value_suffix'];
       
  2292     }
       
  2293     $output .= '</' . $element['#tag'] . ">\n";
       
  2294     return $output;
       
  2295   }
       
  2296 }
       
  2297 
       
  2298 /**
       
  2299  * Returns HTML for a "more" link, like those used in blocks.
       
  2300  *
       
  2301  * @param $variables
       
  2302  *   An associative array containing:
       
  2303  *   - url: The URL of the main page.
       
  2304  *   - title: A descriptive verb for the link, like 'Read more'.
       
  2305  */
       
  2306 function theme_more_link($variables) {
       
  2307   return '<div class="more-link">' . l(t('More'), $variables['url'], array('attributes' => array('title' => $variables['title']))) . '</div>';
       
  2308 }
       
  2309 
       
  2310 /**
       
  2311  * Returns HTML for a username, potentially linked to the user's page.
       
  2312  *
       
  2313  * @param $variables
       
  2314  *   An associative array containing:
       
  2315  *   - account: The user object to format.
       
  2316  *   - name: The user's name, sanitized.
       
  2317  *   - extra: Additional text to append to the user's name, sanitized.
       
  2318  *   - link_path: The path or URL of the user's profile page, home page, or
       
  2319  *     other desired page to link to for more information about the user.
       
  2320  *   - link_options: An array of options to pass to the l() function's $options
       
  2321  *     parameter if linking the user's name to the user's page.
       
  2322  *   - attributes_array: An array of attributes to pass to the
       
  2323  *     drupal_attributes() function if not linking to the user's page.
       
  2324  *
       
  2325  * @see template_preprocess_username()
       
  2326  * @see template_process_username()
       
  2327  */
       
  2328 function theme_username($variables) {
       
  2329   if (isset($variables['link_path'])) {
       
  2330     // We have a link path, so we should generate a link using l().
       
  2331     // Additional classes may be added as array elements like
       
  2332     // $variables['link_options']['attributes']['class'][] = 'myclass';
       
  2333     $output = l($variables['name'] . $variables['extra'], $variables['link_path'], $variables['link_options']);
       
  2334   }
       
  2335   else {
       
  2336     // Modules may have added important attributes so they must be included
       
  2337     // in the output. Additional classes may be added as array elements like
       
  2338     // $variables['attributes_array']['class'][] = 'myclass';
       
  2339     $output = '<span' . drupal_attributes($variables['attributes_array']) . '>' . $variables['name'] . $variables['extra'] . '</span>';
       
  2340   }
       
  2341   return $output;
       
  2342 }
       
  2343 
       
  2344 /**
       
  2345  * Returns HTML for a progress bar.
       
  2346  *
       
  2347  * Note that the core Batch API uses this only for non-JavaScript batch jobs.
       
  2348  *
       
  2349  * @param $variables
       
  2350  *   An associative array containing:
       
  2351  *   - percent: The percentage of the progress.
       
  2352  *   - message: A string containing information to be displayed.
       
  2353  */
       
  2354 function theme_progress_bar($variables) {
       
  2355   $output = '<div id="progress" class="progress">';
       
  2356   $output .= '<div class="bar"><div class="filled" style="width: ' . $variables['percent'] . '%"></div></div>';
       
  2357   $output .= '<div class="percentage">' . $variables['percent'] . '%</div>';
       
  2358   $output .= '<div class="message">' . $variables['message'] . '</div>';
       
  2359   $output .= '</div>';
       
  2360 
       
  2361   return $output;
       
  2362 }
       
  2363 
       
  2364 /**
       
  2365  * Returns HTML for an indentation div; used for drag and drop tables.
       
  2366  *
       
  2367  * @param $variables
       
  2368  *   An associative array containing:
       
  2369  *   - size: Optional. The number of indentations to create.
       
  2370  */
       
  2371 function theme_indentation($variables) {
       
  2372   $output = '';
       
  2373   for ($n = 0; $n < $variables['size']; $n++) {
       
  2374     $output .= '<div class="indentation">&nbsp;</div>';
       
  2375   }
       
  2376   return $output;
       
  2377 }
       
  2378 
       
  2379 /**
       
  2380  * @} End of "addtogroup themeable".
       
  2381  */
       
  2382 
       
  2383 /**
       
  2384  * Returns HTML output for a single table cell for theme_table().
       
  2385  *
       
  2386  * @param $cell
       
  2387  *   Array of cell information, or string to display in cell.
       
  2388  * @param bool $header
       
  2389  *   TRUE if this cell is a table header cell, FALSE if it is an ordinary
       
  2390  *   table cell. If $cell is an array with element 'header' set to TRUE, that
       
  2391  *   will override the $header parameter.
       
  2392  *
       
  2393  * @return
       
  2394  *   HTML for the cell.
       
  2395  */
       
  2396 function _theme_table_cell($cell, $header = FALSE) {
       
  2397   $attributes = '';
       
  2398 
       
  2399   if (is_array($cell)) {
       
  2400     $data = isset($cell['data']) ? $cell['data'] : '';
       
  2401     // Cell's data property can be a string or a renderable array.
       
  2402     if (is_array($data)) {
       
  2403       $data = drupal_render($data);
       
  2404     }
       
  2405     $header |= isset($cell['header']);
       
  2406     unset($cell['data']);
       
  2407     unset($cell['header']);
       
  2408     $attributes = drupal_attributes($cell);
       
  2409   }
       
  2410   else {
       
  2411     $data = $cell;
       
  2412   }
       
  2413 
       
  2414   if ($header) {
       
  2415     $output = "<th$attributes>$data</th>";
       
  2416   }
       
  2417   else {
       
  2418     $output = "<td$attributes>$data</td>";
       
  2419   }
       
  2420 
       
  2421   return $output;
       
  2422 }
       
  2423 
       
  2424 /**
       
  2425  * Adds a default set of helper variables for variable processors and templates.
       
  2426  *
       
  2427  * This function is called for theme hooks implemented as templates only, not
       
  2428  * for theme hooks implemented as functions. This preprocess function is the
       
  2429  * first in the sequence of preprocessing and processing functions that is
       
  2430  * called when preparing variables for a template. See theme() for more details
       
  2431  * about the full sequence.
       
  2432  *
       
  2433  * @see theme()
       
  2434  * @see template_process()
       
  2435  */
       
  2436 function template_preprocess(&$variables, $hook) {
       
  2437   global $user;
       
  2438   static $count = array();
       
  2439 
       
  2440   // Track run count for each hook to provide zebra striping. See
       
  2441   // "template_preprocess_block()" which provides the same feature specific to
       
  2442   // blocks.
       
  2443   $count[$hook] = isset($count[$hook]) && is_int($count[$hook]) ? $count[$hook] : 1;
       
  2444   $variables['zebra'] = ($count[$hook] % 2) ? 'odd' : 'even';
       
  2445   $variables['id'] = $count[$hook]++;
       
  2446 
       
  2447   // Tell all templates where they are located.
       
  2448   $variables['directory'] = path_to_theme();
       
  2449 
       
  2450   // Initialize html class attribute for the current hook.
       
  2451   $variables['classes_array'] = array(drupal_html_class($hook));
       
  2452 
       
  2453   // Merge in variables that don't depend on hook and don't change during a
       
  2454   // single page request.
       
  2455   // Use the advanced drupal_static() pattern, since this is called very often.
       
  2456   static $drupal_static_fast;
       
  2457   if (!isset($drupal_static_fast)) {
       
  2458     $drupal_static_fast['default_variables'] = &drupal_static(__FUNCTION__);
       
  2459   }
       
  2460   $default_variables = &$drupal_static_fast['default_variables'];
       
  2461   // Global $user object shouldn't change during a page request once rendering
       
  2462   // has started, but if there's an edge case where it does, re-fetch the
       
  2463   // variables appropriate for the new user.
       
  2464   if (!isset($default_variables) || ($user !== $default_variables['user'])) {
       
  2465     $default_variables = _template_preprocess_default_variables();
       
  2466   }
       
  2467   $variables += $default_variables;
       
  2468 }
       
  2469 
       
  2470 /**
       
  2471  * Returns hook-independent variables to template_preprocess().
       
  2472  */
       
  2473 function _template_preprocess_default_variables() {
       
  2474   global $user;
       
  2475 
       
  2476   // Variables that don't depend on a database connection.
       
  2477   $variables = array(
       
  2478     'attributes_array' => array(),
       
  2479     'title_attributes_array' => array(),
       
  2480     'content_attributes_array' => array(),
       
  2481     'title_prefix' => array(),
       
  2482     'title_suffix' => array(),
       
  2483     'user' => $user,
       
  2484     'db_is_active' => !defined('MAINTENANCE_MODE'),
       
  2485     'is_admin' => FALSE,
       
  2486     'logged_in' => FALSE,
       
  2487   );
       
  2488 
       
  2489   // The user object has no uid property when the database does not exist during
       
  2490   // install. The user_access() check deals with issues when in maintenance mode
       
  2491   // as uid is set but the user.module has not been included.
       
  2492   if (isset($user->uid) && function_exists('user_access')) {
       
  2493     $variables['is_admin'] = user_access('access administration pages');
       
  2494     $variables['logged_in'] = ($user->uid > 0);
       
  2495   }
       
  2496 
       
  2497   // drupal_is_front_page() might throw an exception.
       
  2498   try {
       
  2499     $variables['is_front'] = drupal_is_front_page();
       
  2500   }
       
  2501   catch (Exception $e) {
       
  2502     // If the database is not yet available, set default values for these
       
  2503     // variables.
       
  2504     $variables['is_front'] = FALSE;
       
  2505     $variables['db_is_active'] = FALSE;
       
  2506   }
       
  2507 
       
  2508   return $variables;
       
  2509 }
       
  2510 
       
  2511 /**
       
  2512  * Adds helper variables derived from variables defined during preprocessing.
       
  2513  *
       
  2514  * When preparing variables for a theme hook implementation, all 'preprocess'
       
  2515  * functions run first, then all 'process' functions (see theme() for details
       
  2516  * about the full sequence).
       
  2517  *
       
  2518  * This function serializes array variables manipulated during the preprocessing
       
  2519  * phase into strings for convenient use by templates. As with
       
  2520  * template_preprocess(), this function does not get called for theme hooks
       
  2521  * implemented as functions.
       
  2522  *
       
  2523  * @see theme()
       
  2524  * @see template_preprocess()
       
  2525  */
       
  2526 function template_process(&$variables, $hook) {
       
  2527   // Flatten out classes.
       
  2528   $variables['classes'] = implode(' ', $variables['classes_array']);
       
  2529 
       
  2530   // Flatten out attributes, title_attributes, and content_attributes.
       
  2531   // Because this function can be called very often, and often with empty
       
  2532   // attributes, optimize performance by only calling drupal_attributes() if
       
  2533   // necessary.
       
  2534   $variables['attributes'] = $variables['attributes_array'] ? drupal_attributes($variables['attributes_array']) : '';
       
  2535   $variables['title_attributes'] = $variables['title_attributes_array'] ? drupal_attributes($variables['title_attributes_array']) : '';
       
  2536   $variables['content_attributes'] = $variables['content_attributes_array'] ? drupal_attributes($variables['content_attributes_array']) : '';
       
  2537 }
       
  2538 
       
  2539 /**
       
  2540  * Preprocess variables for html.tpl.php
       
  2541  *
       
  2542  * @see system_elements()
       
  2543  * @see html.tpl.php
       
  2544  */
       
  2545 function template_preprocess_html(&$variables) {
       
  2546   // Compile a list of classes that are going to be applied to the body element.
       
  2547   // This allows advanced theming based on context (home page, node of certain type, etc.).
       
  2548   // Add a class that tells us whether we're on the front page or not.
       
  2549   $variables['classes_array'][] = $variables['is_front'] ? 'front' : 'not-front';
       
  2550   // Add a class that tells us whether the page is viewed by an authenticated user or not.
       
  2551   $variables['classes_array'][] = $variables['logged_in'] ? 'logged-in' : 'not-logged-in';
       
  2552 
       
  2553   // Add information about the number of sidebars.
       
  2554   if (!empty($variables['page']['sidebar_first']) && !empty($variables['page']['sidebar_second'])) {
       
  2555     $variables['classes_array'][] = 'two-sidebars';
       
  2556   }
       
  2557   elseif (!empty($variables['page']['sidebar_first'])) {
       
  2558     $variables['classes_array'][] = 'one-sidebar sidebar-first';
       
  2559   }
       
  2560   elseif (!empty($variables['page']['sidebar_second'])) {
       
  2561     $variables['classes_array'][] = 'one-sidebar sidebar-second';
       
  2562   }
       
  2563   else {
       
  2564     $variables['classes_array'][] = 'no-sidebars';
       
  2565   }
       
  2566 
       
  2567   // Populate the body classes.
       
  2568   if ($suggestions = theme_get_suggestions(arg(), 'page', '-')) {
       
  2569     foreach ($suggestions as $suggestion) {
       
  2570       if ($suggestion != 'page-front') {
       
  2571         // Add current suggestion to page classes to make it possible to theme
       
  2572         // the page depending on the current page type (e.g. node, admin, user,
       
  2573         // etc.) as well as more specific data like node-12 or node-edit.
       
  2574         $variables['classes_array'][] = drupal_html_class($suggestion);
       
  2575       }
       
  2576     }
       
  2577   }
       
  2578 
       
  2579   // If on an individual node page, add the node type to body classes.
       
  2580   if ($node = menu_get_object()) {
       
  2581     $variables['classes_array'][] = drupal_html_class('node-type-' . $node->type);
       
  2582   }
       
  2583 
       
  2584   // RDFa allows annotation of XHTML pages with RDF data, while GRDDL provides
       
  2585   // mechanisms for extraction of this RDF content via XSLT transformation
       
  2586   // using an associated GRDDL profile.
       
  2587   $variables['rdf_namespaces']    = drupal_get_rdf_namespaces();
       
  2588   $variables['grddl_profile']     = 'http://www.w3.org/1999/xhtml/vocab';
       
  2589   $variables['language']          = $GLOBALS['language'];
       
  2590   $variables['language']->dir     = $GLOBALS['language']->direction ? 'rtl' : 'ltr';
       
  2591 
       
  2592   // Add favicon.
       
  2593   if (theme_get_setting('toggle_favicon')) {
       
  2594     $favicon = theme_get_setting('favicon');
       
  2595     $type = theme_get_setting('favicon_mimetype');
       
  2596     drupal_add_html_head_link(array('rel' => 'shortcut icon', 'href' => drupal_strip_dangerous_protocols($favicon), 'type' => $type));
       
  2597   }
       
  2598 
       
  2599   // Construct page title.
       
  2600   if (drupal_get_title()) {
       
  2601     $head_title = array(
       
  2602       'title' => strip_tags(drupal_get_title()),
       
  2603       'name' => check_plain(variable_get('site_name', 'Drupal')),
       
  2604     );
       
  2605   }
       
  2606   else {
       
  2607     $head_title = array('name' => check_plain(variable_get('site_name', 'Drupal')));
       
  2608     if (variable_get('site_slogan', '')) {
       
  2609       $head_title['slogan'] = filter_xss_admin(variable_get('site_slogan', ''));
       
  2610     }
       
  2611   }
       
  2612   $variables['head_title_array'] = $head_title;
       
  2613   $variables['head_title'] = implode(' | ', $head_title);
       
  2614 
       
  2615   // Populate the page template suggestions.
       
  2616   if ($suggestions = theme_get_suggestions(arg(), 'html')) {
       
  2617     $variables['theme_hook_suggestions'] = $suggestions;
       
  2618   }
       
  2619 }
       
  2620 
       
  2621 /**
       
  2622  * Preprocess variables for page.tpl.php
       
  2623  *
       
  2624  * Most themes utilize their own copy of page.tpl.php. The default is located
       
  2625  * inside "modules/system/page.tpl.php". Look in there for the full list of
       
  2626  * variables.
       
  2627  *
       
  2628  * Uses the arg() function to generate a series of page template suggestions
       
  2629  * based on the current path.
       
  2630  *
       
  2631  * Any changes to variables in this preprocessor should also be changed inside
       
  2632  * template_preprocess_maintenance_page() to keep all of them consistent.
       
  2633  *
       
  2634  * @see drupal_render_page()
       
  2635  * @see template_process_page()
       
  2636  * @see page.tpl.php
       
  2637  */
       
  2638 function template_preprocess_page(&$variables) {
       
  2639   // Move some variables to the top level for themer convenience and template cleanliness.
       
  2640   $variables['show_messages'] = $variables['page']['#show_messages'];
       
  2641 
       
  2642   foreach (system_region_list($GLOBALS['theme'], REGIONS_ALL, FALSE) as $region_key) {
       
  2643     if (!isset($variables['page'][$region_key])) {
       
  2644       $variables['page'][$region_key] = array();
       
  2645     }
       
  2646     if ($region_content = drupal_get_region_content($region_key)) {
       
  2647       $variables['page'][$region_key][]['#markup'] = $region_content;
       
  2648     }
       
  2649   }
       
  2650 
       
  2651   // Set up layout variable.
       
  2652   $variables['layout'] = 'none';
       
  2653   if (!empty($variables['page']['sidebar_first'])) {
       
  2654     $variables['layout'] = 'first';
       
  2655   }
       
  2656   if (!empty($variables['page']['sidebar_second'])) {
       
  2657     $variables['layout'] = ($variables['layout'] == 'first') ? 'both' : 'second';
       
  2658   }
       
  2659 
       
  2660   $variables['base_path']         = base_path();
       
  2661   $variables['front_page']        = url();
       
  2662   $variables['feed_icons']        = drupal_get_feeds();
       
  2663   $variables['language']          = $GLOBALS['language'];
       
  2664   $variables['language']->dir     = $GLOBALS['language']->direction ? 'rtl' : 'ltr';
       
  2665   $variables['logo']              = theme_get_setting('logo');
       
  2666   $variables['main_menu']         = theme_get_setting('toggle_main_menu') ? menu_main_menu() : array();
       
  2667   $variables['secondary_menu']    = theme_get_setting('toggle_secondary_menu') ? menu_secondary_menu() : array();
       
  2668   $variables['action_links']      = menu_local_actions();
       
  2669   $variables['site_name']         = (theme_get_setting('toggle_name') ? filter_xss_admin(variable_get('site_name', 'Drupal')) : '');
       
  2670   $variables['site_slogan']       = (theme_get_setting('toggle_slogan') ? filter_xss_admin(variable_get('site_slogan', '')) : '');
       
  2671   $variables['tabs']              = menu_local_tabs();
       
  2672 
       
  2673   if ($node = menu_get_object()) {
       
  2674     $variables['node'] = $node;
       
  2675   }
       
  2676 
       
  2677   // Populate the page template suggestions.
       
  2678   if ($suggestions = theme_get_suggestions(arg(), 'page')) {
       
  2679     $variables['theme_hook_suggestions'] = $suggestions;
       
  2680   }
       
  2681 }
       
  2682 
       
  2683 /**
       
  2684  * Process variables for page.tpl.php
       
  2685  *
       
  2686  * Perform final addition of variables before passing them into the template.
       
  2687  * To customize these variables, simply set them in an earlier step.
       
  2688  *
       
  2689  * @see template_preprocess_page()
       
  2690  * @see page.tpl.php
       
  2691  */
       
  2692 function template_process_page(&$variables) {
       
  2693   if (!isset($variables['breadcrumb'])) {
       
  2694     // Build the breadcrumb last, so as to increase the chance of being able to
       
  2695     // re-use the cache of an already rendered menu containing the active link
       
  2696     // for the current page.
       
  2697     // @see menu_tree_page_data()
       
  2698     $variables['breadcrumb'] = theme('breadcrumb', array('breadcrumb' => drupal_get_breadcrumb()));
       
  2699   }
       
  2700   if (!isset($variables['title'])) {
       
  2701     $variables['title'] = drupal_get_title();
       
  2702   }
       
  2703 
       
  2704   // Generate messages last in order to capture as many as possible for the
       
  2705   // current page.
       
  2706   if (!isset($variables['messages'])) {
       
  2707     $variables['messages'] = $variables['show_messages'] ? theme('status_messages') : '';
       
  2708   }
       
  2709 }
       
  2710 
       
  2711 /**
       
  2712  * Process variables for html.tpl.php
       
  2713  *
       
  2714  * Perform final addition and modification of variables before passing into
       
  2715  * the template. To customize these variables, call drupal_render() on elements
       
  2716  * in $variables['page'] during THEME_preprocess_page().
       
  2717  *
       
  2718  * @see template_preprocess_html()
       
  2719  * @see html.tpl.php
       
  2720  */
       
  2721 function template_process_html(&$variables) {
       
  2722   // Render page_top and page_bottom into top level variables.
       
  2723   $variables['page_top'] = drupal_render($variables['page']['page_top']);
       
  2724   $variables['page_bottom'] = drupal_render($variables['page']['page_bottom']);
       
  2725   // Place the rendered HTML for the page body into a top level variable.
       
  2726   $variables['page']              = $variables['page']['#children'];
       
  2727   $variables['page_bottom'] .= drupal_get_js('footer');
       
  2728 
       
  2729   $variables['head']    = drupal_get_html_head();
       
  2730   $variables['css']     = drupal_add_css();
       
  2731   $variables['styles']  = drupal_get_css();
       
  2732   $variables['scripts'] = drupal_get_js();
       
  2733 }
       
  2734 
       
  2735 /**
       
  2736  * Generate an array of suggestions from path arguments.
       
  2737  *
       
  2738  * This is typically called for adding to the 'theme_hook_suggestions' or
       
  2739  * 'classes_array' variables from within preprocess functions, when wanting to
       
  2740  * base the additional suggestions on the path of the current page.
       
  2741  *
       
  2742  * @param $args
       
  2743  *   An array of path arguments, such as from function arg().
       
  2744  * @param $base
       
  2745  *   A string identifying the base 'thing' from which more specific suggestions
       
  2746  *   are derived. For example, 'page' or 'html'.
       
  2747  * @param $delimiter
       
  2748  *   The string used to delimit increasingly specific information. The default
       
  2749  *   of '__' is appropriate for theme hook suggestions. '-' is appropriate for
       
  2750  *   extra classes.
       
  2751  *
       
  2752  * @return
       
  2753  *   An array of suggestions, suitable for adding to
       
  2754  *   $variables['theme_hook_suggestions'] within a preprocess function or to
       
  2755  *   $variables['classes_array'] if the suggestions represent extra CSS classes.
       
  2756  */
       
  2757 function theme_get_suggestions($args, $base, $delimiter = '__') {
       
  2758 
       
  2759   // Build a list of suggested theme hooks or body classes in order of
       
  2760   // specificity. One suggestion is made for every element of the current path,
       
  2761   // though numeric elements are not carried to subsequent suggestions. For
       
  2762   // example, for $base='page', http://www.example.com/node/1/edit would result
       
  2763   // in the following suggestions and body classes:
       
  2764   //
       
  2765   // page__node              page-node
       
  2766   // page__node__%           page-node-%
       
  2767   // page__node__1           page-node-1
       
  2768   // page__node__edit        page-node-edit
       
  2769 
       
  2770   $suggestions = array();
       
  2771   $prefix = $base;
       
  2772   foreach ($args as $arg) {
       
  2773     // Remove slashes or null per SA-CORE-2009-003 and change - (hyphen) to _
       
  2774     // (underscore).
       
  2775     //
       
  2776     // When we discover templates in @see drupal_find_theme_templates,
       
  2777     // hyphens (-) are converted to underscores (_) before the theme hook
       
  2778     // is registered. We do this because the hyphens used for delimiters
       
  2779     // in hook suggestions cannot be used in the function names of the
       
  2780     // associated preprocess functions. Any page templates designed to be used
       
  2781     // on paths that contain a hyphen are also registered with these hyphens
       
  2782     // converted to underscores so here we must convert any hyphens in path
       
  2783     // arguments to underscores here before fetching theme hook suggestions
       
  2784     // to ensure the templates are appropriately recognized.
       
  2785     $arg = str_replace(array("/", "\\", "\0", '-'), array('', '', '', '_'), $arg);
       
  2786     // The percent acts as a wildcard for numeric arguments since
       
  2787     // asterisks are not valid filename characters on many filesystems.
       
  2788     if (is_numeric($arg)) {
       
  2789       $suggestions[] = $prefix . $delimiter . '%';
       
  2790     }
       
  2791     $suggestions[] = $prefix . $delimiter . $arg;
       
  2792     if (!is_numeric($arg)) {
       
  2793       $prefix .= $delimiter . $arg;
       
  2794     }
       
  2795   }
       
  2796   if (drupal_is_front_page()) {
       
  2797     // Front templates should be based on root only, not prefixed arguments.
       
  2798     $suggestions[] = $base . $delimiter . 'front';
       
  2799   }
       
  2800 
       
  2801   return $suggestions;
       
  2802 }
       
  2803 
       
  2804 /**
       
  2805  * Process variables for maintenance-page.tpl.php.
       
  2806  *
       
  2807  * The variables array generated here is a mirror of
       
  2808  * template_preprocess_page(). This preprocessor will run its course when
       
  2809  * theme_maintenance_page() is invoked. An alternate template file of
       
  2810  * maintenance-page--offline.tpl.php can be used when the database is offline to
       
  2811  * hide errors and completely replace the content.
       
  2812  *
       
  2813  * The $variables array contains the following arguments:
       
  2814  * - $content
       
  2815  *
       
  2816  * @see maintenance-page.tpl.php
       
  2817  */
       
  2818 function template_preprocess_maintenance_page(&$variables) {
       
  2819   // Add favicon
       
  2820   if (theme_get_setting('toggle_favicon')) {
       
  2821     $favicon = theme_get_setting('favicon');
       
  2822     $type = theme_get_setting('favicon_mimetype');
       
  2823     drupal_add_html_head_link(array('rel' => 'shortcut icon', 'href' => drupal_strip_dangerous_protocols($favicon), 'type' => $type));
       
  2824   }
       
  2825 
       
  2826   global $theme;
       
  2827   // Retrieve the theme data to list all available regions.
       
  2828   $theme_data = list_themes();
       
  2829   $regions = $theme_data[$theme]->info['regions'];
       
  2830 
       
  2831   // Get all region content set with drupal_add_region_content().
       
  2832   foreach (array_keys($regions) as $region) {
       
  2833     // Assign region to a region variable.
       
  2834     $region_content = drupal_get_region_content($region);
       
  2835     isset($variables[$region]) ? $variables[$region] .= $region_content : $variables[$region] = $region_content;
       
  2836   }
       
  2837 
       
  2838   // Setup layout variable.
       
  2839   $variables['layout'] = 'none';
       
  2840   if (!empty($variables['sidebar_first'])) {
       
  2841     $variables['layout'] = 'first';
       
  2842   }
       
  2843   if (!empty($variables['sidebar_second'])) {
       
  2844     $variables['layout'] = ($variables['layout'] == 'first') ? 'both' : 'second';
       
  2845   }
       
  2846 
       
  2847   // Construct page title
       
  2848   if (drupal_get_title()) {
       
  2849     $head_title = array(
       
  2850       'title' => strip_tags(drupal_get_title()),
       
  2851       'name' => variable_get('site_name', 'Drupal'),
       
  2852     );
       
  2853   }
       
  2854   else {
       
  2855     $head_title = array('name' => variable_get('site_name', 'Drupal'));
       
  2856     if (variable_get('site_slogan', '')) {
       
  2857       $head_title['slogan'] = variable_get('site_slogan', '');
       
  2858     }
       
  2859   }
       
  2860 
       
  2861   // set the default language if necessary
       
  2862   $language = isset($GLOBALS['language']) ? $GLOBALS['language'] : language_default();
       
  2863 
       
  2864   $variables['head_title_array']  = $head_title;
       
  2865   $variables['head_title']        = implode(' | ', $head_title);
       
  2866   $variables['base_path']         = base_path();
       
  2867   $variables['front_page']        = url();
       
  2868   $variables['breadcrumb']        = '';
       
  2869   $variables['feed_icons']        = '';
       
  2870   $variables['help']              = '';
       
  2871   $variables['language']          = $language;
       
  2872   $variables['language']->dir     = $language->direction ? 'rtl' : 'ltr';
       
  2873   $variables['logo']              = theme_get_setting('logo');
       
  2874   $variables['messages']          = $variables['show_messages'] ? theme('status_messages') : '';
       
  2875   $variables['main_menu']         = array();
       
  2876   $variables['secondary_menu']    = array();
       
  2877   $variables['site_name']         = (theme_get_setting('toggle_name') ? variable_get('site_name', 'Drupal') : '');
       
  2878   $variables['site_slogan']       = (theme_get_setting('toggle_slogan') ? variable_get('site_slogan', '') : '');
       
  2879   $variables['tabs']              = '';
       
  2880   $variables['title']             = drupal_get_title();
       
  2881 
       
  2882   // Compile a list of classes that are going to be applied to the body element.
       
  2883   $variables['classes_array'][] = 'in-maintenance';
       
  2884   if (isset($variables['db_is_active']) && !$variables['db_is_active']) {
       
  2885     $variables['classes_array'][] = 'db-offline';
       
  2886   }
       
  2887   if ($variables['layout'] == 'both') {
       
  2888     $variables['classes_array'][] = 'two-sidebars';
       
  2889   }
       
  2890   elseif ($variables['layout'] == 'none') {
       
  2891     $variables['classes_array'][] = 'no-sidebars';
       
  2892   }
       
  2893   else {
       
  2894     $variables['classes_array'][] = 'one-sidebar sidebar-' . $variables['layout'];
       
  2895   }
       
  2896 
       
  2897   // Dead databases will show error messages so supplying this template will
       
  2898   // allow themers to override the page and the content completely.
       
  2899   if (isset($variables['db_is_active']) && !$variables['db_is_active']) {
       
  2900     $variables['theme_hook_suggestion'] = 'maintenance_page__offline';
       
  2901   }
       
  2902 }
       
  2903 
       
  2904 /**
       
  2905  * Theme process function for theme_maintenance_field().
       
  2906  *
       
  2907  * The variables array generated here is a mirror of template_process_html().
       
  2908  * This processor will run its course when theme_maintenance_page() is invoked.
       
  2909  *
       
  2910  * @see maintenance-page.tpl.php
       
  2911  * @see template_process_html()
       
  2912  */
       
  2913 function template_process_maintenance_page(&$variables) {
       
  2914   $variables['head']    = drupal_get_html_head();
       
  2915   $variables['css']     = drupal_add_css();
       
  2916   $variables['styles']  = drupal_get_css();
       
  2917   $variables['scripts'] = drupal_get_js();
       
  2918 }
       
  2919 
       
  2920 /**
       
  2921  * Preprocess variables for region.tpl.php
       
  2922  *
       
  2923  * Prepares the values passed to the theme_region function to be passed into a
       
  2924  * pluggable template engine. Uses the region name to generate a template file
       
  2925  * suggestions. If none are found, the default region.tpl.php is used.
       
  2926  *
       
  2927  * @see drupal_region_class()
       
  2928  * @see region.tpl.php
       
  2929  */
       
  2930 function template_preprocess_region(&$variables) {
       
  2931   // Create the $content variable that templates expect.
       
  2932   $variables['content'] = $variables['elements']['#children'];
       
  2933   $variables['region'] = $variables['elements']['#region'];
       
  2934 
       
  2935   $variables['classes_array'][] = drupal_region_class($variables['region']);
       
  2936   $variables['theme_hook_suggestions'][] = 'region__' . $variables['region'];
       
  2937 }
       
  2938 
       
  2939 /**
       
  2940  * Preprocesses variables for theme_username().
       
  2941  *
       
  2942  * Modules that make any changes to variables like 'name' or 'extra' must insure
       
  2943  * that the final string is safe to include directly in the output by using
       
  2944  * check_plain() or filter_xss().
       
  2945  *
       
  2946  * @see template_process_username()
       
  2947  */
       
  2948 function template_preprocess_username(&$variables) {
       
  2949   $account = $variables['account'];
       
  2950 
       
  2951   $variables['extra'] = '';
       
  2952   if (empty($account->uid)) {
       
  2953    $variables['uid'] = 0;
       
  2954    if (theme_get_setting('toggle_comment_user_verification')) {
       
  2955      $variables['extra'] = ' (' . t('not verified') . ')';
       
  2956    }
       
  2957   }
       
  2958   else {
       
  2959     $variables['uid'] = (int) $account->uid;
       
  2960   }
       
  2961 
       
  2962   // Set the name to a formatted name that is safe for printing and
       
  2963   // that won't break tables by being too long. Keep an unshortened,
       
  2964   // unsanitized version, in case other preprocess functions want to implement
       
  2965   // their own shortening logic or add markup. If they do so, they must ensure
       
  2966   // that $variables['name'] is safe for printing.
       
  2967   $name = $variables['name_raw'] = format_username($account);
       
  2968   if (drupal_strlen($name) > 20) {
       
  2969     $name = drupal_substr($name, 0, 15) . '...';
       
  2970   }
       
  2971   $variables['name'] = check_plain($name);
       
  2972 
       
  2973   $variables['profile_access'] = user_access('access user profiles');
       
  2974   $variables['link_attributes'] = array();
       
  2975   // Populate link path and attributes if appropriate.
       
  2976   if ($variables['uid'] && $variables['profile_access']) {
       
  2977     // We are linking to a local user.
       
  2978     $variables['link_attributes'] = array('title' => t('View user profile.'));
       
  2979     $variables['link_path'] = 'user/' . $variables['uid'];
       
  2980   }
       
  2981   elseif (!empty($account->homepage)) {
       
  2982     // Like the 'class' attribute, the 'rel' attribute can hold a
       
  2983     // space-separated set of values, so initialize it as an array to make it
       
  2984     // easier for other preprocess functions to append to it.
       
  2985     $variables['link_attributes'] = array('rel' => array('nofollow'));
       
  2986     $variables['link_path'] = $account->homepage;
       
  2987     $variables['homepage'] = $account->homepage;
       
  2988   }
       
  2989   // We do not want the l() function to check_plain() a second time.
       
  2990   $variables['link_options']['html'] = TRUE;
       
  2991   // Set a default class.
       
  2992   $variables['attributes_array'] = array('class' => array('username'));
       
  2993 }
       
  2994 
       
  2995 /**
       
  2996  * Processes variables for theme_username().
       
  2997  *
       
  2998  * @see template_preprocess_username()
       
  2999  */
       
  3000 function template_process_username(&$variables) {
       
  3001   // Finalize the link_options array for passing to the l() function.
       
  3002   // This is done in the process phase so that attributes may be added by
       
  3003   // modules or the theme during the preprocess phase.
       
  3004   if (isset($variables['link_path'])) {
       
  3005     // $variables['attributes_array'] contains attributes that should be applied
       
  3006     // regardless of whether a link is being rendered or not.
       
  3007     // $variables['link_attributes'] contains attributes that should only be
       
  3008     // applied if a link is being rendered. Preprocess functions are encouraged
       
  3009     // to use the former unless they want to add attributes on the link only.
       
  3010     // If a link is being rendered, these need to be merged. Some attributes are
       
  3011     // themselves arrays, so the merging needs to be recursive.
       
  3012     $variables['link_options']['attributes'] = array_merge_recursive($variables['link_attributes'], $variables['attributes_array']);
       
  3013   }
       
  3014 }