Drupal PHP Cross Reference Content Management Systems

Source: /includes/theme.inc - 2886 lines - 109120 bytes - Summary - Text - Print

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

title

Description

title

Description

title

Description

title

title

Body