Spike PHPCoverage Details: theme.inc

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