diff -r fcf75e232c5b -r 0ff3ba646492 web/drupal/includes/theme.inc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/drupal/includes/theme.inc Fri Aug 21 16:26:26 2009 +0000 @@ -0,0 +1,1982 @@ +theme) && !empty($themes[$user->theme]->status) ? $user->theme : variable_get('theme_default', 'garland'); + + // Allow modules to override the present theme... only select custom theme + // if it is available in the list of installed themes. + $theme = $custom_theme && $themes[$custom_theme] ? $custom_theme : $theme; + + // Store the identifier for retrieving theme settings with. + $theme_key = $theme; + + // Find all our ancestor themes and put them in an array. + $base_theme = array(); + $ancestor = $theme; + while ($ancestor && isset($themes[$ancestor]->base_theme)) { + $base_theme[] = $new_base_theme = $themes[$themes[$ancestor]->base_theme]; + $ancestor = $themes[$ancestor]->base_theme; + } + _init_theme($themes[$theme], array_reverse($base_theme)); +} + +/** + * Initialize the theme system given already loaded information. This + * function is useful to initialize a theme when no database is present. + * + * @param $theme + * An object with the following information: + * filename + * The .info file for this theme. The 'path' to + * the theme will be in this file's directory. (Required) + * owner + * The path to the .theme file or the .engine file to load for + * the theme. (Required) + * stylesheet + * The primary stylesheet for the theme. (Optional) + * engine + * The name of theme engine to use. (Optional) + * @param $base_theme + * An optional array of objects that represent the 'base theme' if the + * theme is meant to be derivative of another theme. It requires + * the same information as the $theme object. It should be in + * 'oldest first' order, meaning the top level of the chain will + * be first. + * @param $registry_callback + * The callback to invoke to set the theme registry. + */ +function _init_theme($theme, $base_theme = array(), $registry_callback = '_theme_load_registry') { + global $theme_info, $base_theme_info, $theme_engine, $theme_path; + $theme_info = $theme; + $base_theme_info = $base_theme; + + $theme_path = dirname($theme->filename); + + // Prepare stylesheets from this theme as well as all ancestor themes. + // We work it this way so that we can have child themes override parent + // theme stylesheets easily. + $final_stylesheets = array(); + + // Grab stylesheets from base theme + foreach ($base_theme as $base) { + if (!empty($base->stylesheets)) { + foreach ($base->stylesheets as $media => $stylesheets) { + foreach ($stylesheets as $name => $stylesheet) { + $final_stylesheets[$media][$name] = $stylesheet; + } + } + } + } + + // Add stylesheets used by this theme. + if (!empty($theme->stylesheets)) { + foreach ($theme->stylesheets as $media => $stylesheets) { + foreach ($stylesheets as $name => $stylesheet) { + $final_stylesheets[$media][$name] = $stylesheet; + } + } + } + + // And now add the stylesheets properly + foreach ($final_stylesheets as $media => $stylesheets) { + foreach ($stylesheets as $stylesheet) { + drupal_add_css($stylesheet, 'theme', $media); + } + } + + // Do basically the same as the above for scripts + $final_scripts = array(); + + // Grab scripts from base theme + foreach ($base_theme as $base) { + if (!empty($base->scripts)) { + foreach ($base->scripts as $name => $script) { + $final_scripts[$name] = $script; + } + } + } + + // Add scripts used by this theme. + if (!empty($theme->scripts)) { + foreach ($theme->scripts as $name => $script) { + $final_scripts[$name] = $script; + } + } + + // Add scripts used by this theme. + foreach ($final_scripts as $script) { + drupal_add_js($script, 'theme'); + } + + $theme_engine = NULL; + + // Initialize the theme. + if (isset($theme->engine)) { + // Include the engine. + include_once './'. $theme->owner; + + $theme_engine = $theme->engine; + if (function_exists($theme_engine .'_init')) { + foreach ($base_theme as $base) { + call_user_func($theme_engine .'_init', $base); + } + call_user_func($theme_engine .'_init', $theme); + } + } + else { + // include non-engine theme files + foreach ($base_theme as $base) { + // Include the theme file or the engine. + if (!empty($base->owner)) { + include_once './'. $base->owner; + } + } + // and our theme gets one too. + if (!empty($theme->owner)) { + include_once './'. $theme->owner; + } + } + + $registry_callback($theme, $base_theme, $theme_engine); +} + +/** + * Retrieve the stored theme registry. If the theme registry is already + * in memory it will be returned; otherwise it will attempt to load the + * registry from cache. If this fails, it will construct the registry and + * cache it. + */ +function theme_get_registry($registry = NULL) { + static $theme_registry = NULL; + if (isset($registry)) { + $theme_registry = $registry; + } + + return $theme_registry; +} + +/** + * Store the theme registry in memory. + */ +function _theme_set_registry($registry) { + // Pass through for setting of static variable. + return theme_get_registry($registry); +} + +/** + * Get the theme_registry cache from the database; if it doesn't exist, build + * it. + * + * @param $theme + * The loaded $theme object. + * @param $base_theme + * An array of loaded $theme objects representing the ancestor themes in + * oldest first order. + * @param theme_engine + * The name of the theme engine. + */ +function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL) { + // Check the theme registry cache; if it exists, use it. + $cache = cache_get("theme_registry:$theme->name", 'cache'); + if (isset($cache->data)) { + $registry = $cache->data; + } + else { + // If not, build one and cache it. + $registry = _theme_build_registry($theme, $base_theme, $theme_engine); + _theme_save_registry($theme, $registry); + } + _theme_set_registry($registry); +} + +/** + * Write the theme_registry cache into the database. + */ +function _theme_save_registry($theme, $registry) { + cache_set("theme_registry:$theme->name", $registry); +} + +/** + * Force the system to rebuild the theme registry; this should be called + * when modules are added to the system, or when a dynamic system needs + * to add more theme hooks. + */ +function drupal_rebuild_theme_registry() { + cache_clear_all('theme_registry', 'cache', TRUE); +} + +/** + * Process a single invocation of the theme hook. $type will be one + * of 'module', 'theme_engine', 'base_theme_engine', 'theme', or 'base_theme' + * and it tells us some important information. + * + * Because $cache is a reference, the cache will be continually + * expanded upon; new entries will replace old entries in the + * array_merge, but we are careful to ensure some data is carried + * forward, such as the arguments a theme hook needs. + * + * An override flag can be set for preprocess functions. When detected the + * cached preprocessors for the hook will not be merged with the newly set. + * This can be useful to themes and theme engines by giving them more control + * over how and when the preprocess functions are run. + */ +function _theme_process_registry(&$cache, $name, $type, $theme, $path) { + $result = array(); + $function = $name .'_theme'; + if (function_exists($function)) { + $result = $function($cache, $type, $theme, $path); + + foreach ($result as $hook => $info) { + $result[$hook]['type'] = $type; + $result[$hook]['theme path'] = $path; + // if function and file are left out, default to standard naming + // conventions. + if (!isset($info['template']) && !isset($info['function'])) { + $result[$hook]['function'] = ($type == 'module' ? 'theme_' : $name .'_') . $hook; + } + // If a path is set in the info, use what was set. Otherwise use the + // default path. This is mostly so system.module can declare theme + // functions on behalf of core .include files. + // All files are included to be safe. Conditionally included + // files can prevent them from getting registered. + if (isset($info['file']) && !isset($info['path'])) { + $result[$hook]['file'] = $path .'/'. $info['file']; + include_once($result[$hook]['file']); + } + elseif (isset($info['file']) && isset($info['path'])) { + include_once($info['path'] .'/'. $info['file']); + } + + if (isset($info['template']) && !isset($info['path'])) { + $result[$hook]['template'] = $path .'/'. $info['template']; + } + // If 'arguments' have been defined previously, carry them forward. + // This should happen if a theme overrides a Drupal defined theme + // function, for example. + if (!isset($info['arguments']) && isset($cache[$hook])) { + $result[$hook]['arguments'] = $cache[$hook]['arguments']; + } + // Likewise with theme paths. These are used for template naming suggestions. + // Theme implementations can occur in multiple paths. Suggestions should follow. + if (!isset($info['theme paths']) && isset($cache[$hook])) { + $result[$hook]['theme paths'] = $cache[$hook]['theme paths']; + } + // Check for sub-directories. + $result[$hook]['theme paths'][] = isset($info['path']) ? $info['path'] : $path; + + // Check for default _preprocess_ functions. Ensure arrayness. + if (!isset($info['preprocess functions']) || !is_array($info['preprocess functions'])) { + $info['preprocess functions'] = array(); + $prefixes = array(); + if ($type == 'module') { + // Default preprocessor prefix. + $prefixes[] = 'template'; + // Add all modules so they can intervene with their own preprocessors. This allows them + // to provide preprocess functions even if they are not the owner of the current hook. + $prefixes += module_list(); + } + elseif ($type == 'theme_engine' || $type == 'base_theme_engine') { + // Theme engines get an extra set that come before the normally named preprocessors. + $prefixes[] = $name .'_engine'; + // The theme engine also registers on behalf of the theme. The theme or engine name can be used. + $prefixes[] = $name; + $prefixes[] = $theme; + } + else { + // This applies when the theme manually registers their own preprocessors. + $prefixes[] = $name; + } + + foreach ($prefixes as $prefix) { + if (function_exists($prefix .'_preprocess')) { + $info['preprocess functions'][] = $prefix .'_preprocess'; + } + + if (function_exists($prefix .'_preprocess_'. $hook)) { + $info['preprocess functions'][] = $prefix .'_preprocess_'. $hook; + } + + if (!empty($info['original hook']) && function_exists($prefix .'_preprocess_'. $info['original hook'])) { + $info['preprocess functions'][] = $prefix .'_preprocess_'. $info['original hook']; + } + } + } + // Check for the override flag and prevent the cached preprocess functions from being used. + // This allows themes or theme engines to remove preprocessors set earlier in the registry build. + if (!empty($info['override preprocess functions'])) { + // Flag not needed inside the registry. + unset($result[$hook]['override preprocess functions']); + } + elseif (isset($cache[$hook]['preprocess functions']) && is_array($cache[$hook]['preprocess functions'])) { + $info['preprocess functions'] = array_merge($cache[$hook]['preprocess functions'], $info['preprocess functions']); + } + elseif (isset($info['original hook']) && isset($cache[$info['original hook']]['preprocess functions']) && is_array($cache[$info['original hook']]['preprocess functions'])) { + $info['preprocess functions'] = array_merge($cache[$info['original hook']]['preprocess functions'], $info['preprocess functions']); + } + $result[$hook]['preprocess functions'] = $info['preprocess functions']; + } + + // Merge the newly created theme hooks into the existing cache. + $cache = array_merge($cache, $result); + } + + // Let themes have preprocess functions even if they didn't register a template. + if ($type == 'theme' || $type == 'base_theme') { + foreach ($cache as $hook => $info) { + // Check only if it's a template and not registered by the theme or engine. + if (!empty($info['template']) && empty($result[$hook])) { + if (!isset($info['preprocess functions'])) { + $cache[$hook]['preprocess functions'] = array(); + } + if (function_exists($name .'_preprocess')) { + $cache[$hook]['preprocess functions'][] = $name .'_preprocess'; + } + if (function_exists($name .'_preprocess_'. $hook)) { + $cache[$hook]['preprocess functions'][] = $name .'_preprocess_'. $hook; + } + // Ensure uniqueness. + $cache[$hook]['preprocess functions'] = array_unique($cache[$hook]['preprocess functions']); + } + } + } +} + +/** + * Rebuild the hook theme_registry cache. + * + * @param $theme + * The loaded $theme object. + * @param $base_theme + * An array of loaded $theme objects representing the ancestor themes in + * oldest first order. + * @param theme_engine + * The name of the theme engine. + */ +function _theme_build_registry($theme, $base_theme, $theme_engine) { + $cache = array(); + // First, process the theme hooks advertised by modules. This will + // serve as the basic registry. + foreach (module_implements('theme') as $module) { + _theme_process_registry($cache, $module, 'module', $module, drupal_get_path('module', $module)); + } + + // Process each base theme. + foreach ($base_theme as $base) { + // If the base theme uses a theme engine, process its hooks. + $base_path = dirname($base->filename); + if ($theme_engine) { + _theme_process_registry($cache, $theme_engine, 'base_theme_engine', $base->name, $base_path); + } + _theme_process_registry($cache, $base->name, 'base_theme', $base->name, $base_path); + } + + // And then the same thing, but for the theme. + if ($theme_engine) { + _theme_process_registry($cache, $theme_engine, 'theme_engine', $theme->name, dirname($theme->filename)); + } + + // Finally, hooks provided by the theme itself. + _theme_process_registry($cache, $theme->name, 'theme', $theme->name, dirname($theme->filename)); + + // Let modules alter the registry + drupal_alter('theme_registry', $cache); + return $cache; +} + +/** + * Provides a list of currently available themes. + * + * If the database is active then it will be retrieved from the database. + * Otherwise it will retrieve a new list. + * + * @param $refresh + * Whether to reload the list of themes from the database. + * @return + * An array of the currently available themes. + */ +function list_themes($refresh = FALSE) { + static $list = array(); + + if ($refresh) { + $list = array(); + } + + if (empty($list)) { + $list = array(); + $themes = array(); + // Extract from the database only when it is available. + // Also check that the site is not in the middle of an install or update. + if (db_is_active() && !defined('MAINTENANCE_MODE')) { + $result = db_query("SELECT * FROM {system} WHERE type = '%s'", 'theme'); + while ($theme = db_fetch_object($result)) { + if (file_exists($theme->filename)) { + $theme->info = unserialize($theme->info); + $themes[] = $theme; + } + } + } + else { + // Scan the installation when the database should not be read. + $themes = _system_theme_data(); + } + + foreach ($themes as $theme) { + foreach ($theme->info['stylesheets'] as $media => $stylesheets) { + foreach ($stylesheets as $stylesheet => $path) { + $theme->stylesheets[$media][$stylesheet] = $path; + } + } + foreach ($theme->info['scripts'] as $script => $path) { + if (file_exists($path)) { + $theme->scripts[$script] = $path; + } + } + if (isset($theme->info['engine'])) { + $theme->engine = $theme->info['engine']; + } + if (isset($theme->info['base theme'])) { + $theme->base_theme = $theme->info['base theme']; + } + // Status is normally retrieved from the database. Add zero values when + // read from the installation directory to prevent notices. + if (!isset($theme->status)) { + $theme->status = 0; + } + $list[$theme->name] = $theme; + } + } + + return $list; +} + +/** + * Generate the themed output. + * + * All requests for theme hooks must go through this function. It examines + * the request and routes it to the appropriate theme function. The theme + * registry is checked to determine which implementation to use, which may + * be a function or a template. + * + * If the implementation is a function, it is executed and its return value + * passed along. + * + * If the implementation is a template, the arguments are converted to a + * $variables array. This array is then modified by the module implementing + * the hook, theme engine (if applicable) and the theme. The following + * functions may be used to modify the $variables array. They are processed in + * this order when available: + * + * - template_preprocess(&$variables) + * This sets a default set of variables for all template implementations. + * + * - template_preprocess_HOOK(&$variables) + * This is the first preprocessor called specific to the hook; it should be + * implemented by the module that registers it. + * + * - MODULE_preprocess(&$variables) + * This will be called for all templates; it should only be used if there + * is a real need. It's purpose is similar to template_preprocess(). + * + * - MODULE_preprocess_HOOK(&$variables) + * This is for modules that want to alter or provide extra variables for + * theming hooks not registered to itself. For example, if a module named + * "foo" wanted to alter the $submitted variable for the hook "node" a + * preprocess function of foo_preprocess_node() can be created to intercept + * and alter the variable. + * + * - ENGINE_engine_preprocess(&$variables) + * This function should only be implemented by theme engines and exists + * so that it can set necessary variables for all hooks. + * + * - ENGINE_engine_preprocess_HOOK(&$variables) + * This is the same as the previous function, but it is called for a single + * theming hook. + * + * - ENGINE_preprocess(&$variables) + * This is meant to be used by themes that utilize a theme engine. It is + * provided so that the preprocessor is not locked into a specific theme. + * This makes it easy to share and transport code but theme authors must be + * careful to prevent fatal re-declaration errors when using sub-themes that + * have their own preprocessor named exactly the same as its base theme. In + * the default theme engine (PHPTemplate), sub-themes will load their own + * template.php file in addition to the one used for its parent theme. This + * increases the risk for these errors. A good practice is to use the engine + * name for the base theme and the theme name for the sub-themes to minimize + * this possibility. + * + * - ENGINE_preprocess_HOOK(&$variables) + * The same applies from the previous function, but it is called for a + * specific hook. + * + * - THEME_preprocess(&$variables) + * These functions are based upon the raw theme; they should primarily be + * used by themes that do not use an engine or by sub-themes. It serves the + * same purpose as ENGINE_preprocess(). + * + * - THEME_preprocess_HOOK(&$variables) + * The same applies from the previous function, but it is called for a + * specific hook. + * + * There are two special variables that these hooks can set: + * 'template_file' and 'template_files'. These will be merged together + * to form a list of 'suggested' alternate template files to use, in + * reverse order of priority. template_file will always be a higher + * priority than items in template_files. theme() will then look for these + * files, one at a time, and use the first one + * that exists. + * @param $hook + * The name of the theme function to call. May be an array, in which + * case the first hook that actually has an implementation registered + * will be used. This can be used to choose 'fallback' theme implementations, + * so that if the specific theme hook isn't implemented anywhere, a more + * generic one will be used. This can allow themes to create specific theme + * implementations for named objects. + * @param ... + * Additional arguments to pass along to the theme function. + * @return + * An HTML string that generates the themed output. + */ +function theme() { + $args = func_get_args(); + $hook = array_shift($args); + + static $hooks = NULL; + if (!isset($hooks)) { + init_theme(); + $hooks = theme_get_registry(); + } + + if (is_array($hook)) { + foreach ($hook as $candidate) { + if (isset($hooks[$candidate])) { + break; + } + } + $hook = $candidate; + } + + if (!isset($hooks[$hook])) { + return; + } + + $info = $hooks[$hook]; + global $theme_path; + $temp = $theme_path; + // point path_to_theme() to the currently used theme path: + $theme_path = $hooks[$hook]['theme path']; + + // Include a file if the theme function or preprocess function is held elsewhere. + if (!empty($info['file'])) { + $include_file = $info['file']; + if (isset($info['path'])) { + $include_file = $info['path'] .'/'. $include_file; + } + include_once($include_file); + } + if (isset($info['function'])) { + // The theme call is a function. + $output = call_user_func_array($info['function'], $args); + } + else { + // The theme call is a template. + $variables = array( + 'template_files' => array() + ); + if (!empty($info['arguments'])) { + $count = 0; + foreach ($info['arguments'] as $name => $default) { + $variables[$name] = isset($args[$count]) ? $args[$count] : $default; + $count++; + } + } + + // default render function and extension. + $render_function = 'theme_render_template'; + $extension = '.tpl.php'; + + // Run through the theme engine variables, if necessary + global $theme_engine; + if (isset($theme_engine)) { + // If theme or theme engine is implementing this, it may have + // a different extension and a different renderer. + if ($hooks[$hook]['type'] != 'module') { + if (function_exists($theme_engine .'_render_template')) { + $render_function = $theme_engine .'_render_template'; + } + $extension_function = $theme_engine .'_extension'; + if (function_exists($extension_function)) { + $extension = $extension_function(); + } + } + } + + if (isset($info['preprocess functions']) && is_array($info['preprocess functions'])) { + // This construct ensures that we can keep a reference through + // call_user_func_array. + $args = array(&$variables, $hook); + foreach ($info['preprocess functions'] as $preprocess_function) { + if (function_exists($preprocess_function)) { + call_user_func_array($preprocess_function, $args); + } + } + } + + // Get suggestions for alternate templates out of the variables + // that were set. This lets us dynamically choose a template + // from a list. The order is FILO, so this array is ordered from + // least appropriate first to most appropriate last. + $suggestions = array(); + + if (isset($variables['template_files'])) { + $suggestions = $variables['template_files']; + } + if (isset($variables['template_file'])) { + $suggestions[] = $variables['template_file']; + } + + if ($suggestions) { + $template_file = drupal_discover_template($info['theme paths'], $suggestions, $extension); + } + + if (empty($template_file)) { + $template_file = $hooks[$hook]['template'] . $extension; + if (isset($hooks[$hook]['path'])) { + $template_file = $hooks[$hook]['path'] .'/'. $template_file; + } + } + $output = $render_function($template_file, $variables); + } + // restore path_to_theme() + $theme_path = $temp; + // Add final markup to the full page. + if ($hook == 'page' || $hook == 'book_export_html') { + $output = drupal_final_markup($output); + } + return $output; +} + +/** + * Choose which template file to actually render. These are all suggested + * templates from themes and modules. Theming implementations can occur on + * multiple levels. All paths are checked to account for this. + */ +function drupal_discover_template($paths, $suggestions, $extension = '.tpl.php') { + global $theme_engine; + + // Remove slashes or null to prevent files from being included from + // an unexpected location (especially on Windows servers). + $extension = str_replace(array("/", "\\", "\0"), '', $extension); + + // Loop through all paths and suggestions in FIFO order. + $suggestions = array_reverse($suggestions); + $paths = array_reverse($paths); + foreach ($suggestions as $suggestion) { + if (!empty($suggestion)) { + $suggestion = str_replace(array("/", "\\", "\0"), '', $suggestion); + foreach ($paths as $path) { + if (file_exists($file = $path .'/'. $suggestion . $extension)) { + return $file; + } + } + } + } +} + +/** + * Return the path to the current themed element. + * + * It can point to the active theme or the module handling a themed implementation. + * For example, when invoked within the scope of a theming call it will depend + * on where the theming function is handled. If implemented from a module, it + * will point to the module. If implemented from the active theme, it will point + * to the active theme. When called outside the scope of a theming call, it will + * always point to the active theme. + */ +function path_to_theme() { + global $theme_path; + + if (!isset($theme_path)) { + init_theme(); + } + + return $theme_path; +} + +/** + * Find overridden theme functions. Called by themes and/or theme engines to + * easily discover theme functions. + * + * @param $cache + * The existing cache of theme hooks to test against. + * @param $prefixes + * An array of prefixes to test, in reverse order of importance. + * + * @return $templates + * The functions found, suitable for returning from hook_theme; + */ +function drupal_find_theme_functions($cache, $prefixes) { + $templates = array(); + $functions = get_defined_functions(); + + foreach ($cache as $hook => $info) { + foreach ($prefixes as $prefix) { + if (!empty($info['pattern'])) { + $matches = preg_grep('/^'. $prefix .'_'. $info['pattern'] .'/', $functions['user']); + if ($matches) { + foreach ($matches as $match) { + $new_hook = str_replace($prefix .'_', '', $match); + $templates[$new_hook] = array( + 'function' => $match, + 'arguments' => $info['arguments'], + 'original hook' => $hook, + ); + } + } + } + if (function_exists($prefix .'_'. $hook)) { + $templates[$hook] = array( + 'function' => $prefix .'_'. $hook, + ); + // Ensure that the pattern is maintained from base themes to its sub-themes. + // Each sub-theme will have their functions scanned so the pattern must be + // held for subsequent runs. + if (isset($info['pattern'])) { + $templates[$hook]['pattern'] = $info['pattern']; + } + } + } + } + + return $templates; +} + +/** + * Find overridden theme templates. Called by themes and/or theme engines to + * easily discover templates. + * + * @param $cache + * The existing cache of theme hooks to test against. + * @param $extension + * The extension that these templates will have. + * @param $path + * The path to search. + */ +function drupal_find_theme_templates($cache, $extension, $path) { + $templates = array(); + + // Collect paths to all sub-themes grouped by base themes. These will be + // used for filtering. This allows base themes to have sub-themes in its + // folder hierarchy without affecting the base themes template discovery. + $theme_paths = array(); + foreach (list_themes() as $theme_info) { + if (!empty($theme_info->base_theme)) { + $theme_paths[$theme_info->base_theme][$theme_info->name] = dirname($theme_info->filename); + } + } + foreach ($theme_paths as $basetheme => $subthemes) { + foreach ($subthemes as $subtheme => $subtheme_path) { + if (isset($theme_paths[$subtheme])) { + $theme_paths[$basetheme] = array_merge($theme_paths[$basetheme], $theme_paths[$subtheme]); + } + } + } + global $theme; + $subtheme_paths = isset($theme_paths[$theme]) ? $theme_paths[$theme] : array(); + + // Escape the periods in the extension. + $regex = str_replace('.', '\.', $extension) .'$'; + // Because drupal_system_listing works the way it does, we check for real + // templates separately from checking for patterns. + $files = drupal_system_listing($regex, $path, 'name', 0); + foreach ($files as $template => $file) { + // Ignore sub-theme templates for the current theme. + if (strpos($file->filename, str_replace($subtheme_paths, '', $file->filename)) !== 0) { + continue; + } + // Chop off the remaining extensions if there are any. $template already + // has the rightmost extension removed, but there might still be more, + // such as with .tpl.php, which still has .tpl in $template at this point. + if (($pos = strpos($template, '.')) !== FALSE) { + $template = substr($template, 0, $pos); + } + // Transform - in filenames to _ to match function naming scheme + // for the purposes of searching. + $hook = strtr($template, '-', '_'); + if (isset($cache[$hook])) { + $templates[$hook] = array( + 'template' => $template, + 'path' => dirname($file->filename), + ); + } + // Ensure that the pattern is maintained from base themes to its sub-themes. + // Each sub-theme will have their templates scanned so the pattern must be + // held for subsequent runs. + if (isset($cache[$hook]['pattern'])) { + $templates[$hook]['pattern'] = $cache[$hook]['pattern']; + } + } + + $patterns = array_keys($files); + + foreach ($cache as $hook => $info) { + if (!empty($info['pattern'])) { + // Transform _ in pattern to - to match file naming scheme + // for the purposes of searching. + $pattern = strtr($info['pattern'], '_', '-'); + + $matches = preg_grep('/^'. $pattern .'/', $patterns); + if ($matches) { + foreach ($matches as $match) { + $file = substr($match, 0, strpos($match, '.')); + // Put the underscores back in for the hook name and register this pattern. + $templates[strtr($file, '-', '_')] = array( + 'template' => $file, + 'path' => dirname($files[$match]->filename), + 'arguments' => $info['arguments'], + 'original hook' => $hook, + ); + } + } + } + } + return $templates; +} + +/** + * Retrieve an associative array containing the settings for a theme. + * + * The final settings are arrived at by merging the default settings, + * the site-wide settings, and the settings defined for the specific theme. + * If no $key was specified, only the site-wide theme defaults are retrieved. + * + * The default values for each of settings are also defined in this function. + * To add new settings, add their default values here, and then add form elements + * to system_theme_settings() in system.module. + * + * @param $key + * The template/style value for a given theme. + * + * @return + * An associative array containing theme settings. + */ +function theme_get_settings($key = NULL) { + $defaults = array( + 'mission' => '', + 'default_logo' => 1, + 'logo_path' => '', + 'default_favicon' => 1, + 'favicon_path' => '', + 'primary_links' => 1, + 'secondary_links' => 1, + 'toggle_logo' => 1, + 'toggle_favicon' => 1, + 'toggle_name' => 1, + 'toggle_search' => 1, + 'toggle_slogan' => 0, + 'toggle_mission' => 1, + 'toggle_node_user_picture' => 0, + 'toggle_comment_user_picture' => 0, + 'toggle_primary_links' => 1, + 'toggle_secondary_links' => 1, + ); + + if (module_exists('node')) { + foreach (node_get_types() as $type => $name) { + $defaults['toggle_node_info_'. $type] = 1; + } + } + $settings = array_merge($defaults, variable_get('theme_settings', array())); + + if ($key) { + $settings = array_merge($settings, variable_get(str_replace('/', '_', 'theme_'. $key .'_settings'), array())); + } + + // Only offer search box if search.module is enabled. + if (!module_exists('search') || !user_access('search content')) { + $settings['toggle_search'] = 0; + } + + return $settings; +} + +/** + * Retrieve a setting for the current theme. + * This function is designed for use from within themes & engines + * to determine theme settings made in the admin interface. + * + * Caches values for speed (use $refresh = TRUE to refresh cache) + * + * @param $setting_name + * The name of the setting to be retrieved. + * + * @param $refresh + * Whether to reload the cache of settings. + * + * @return + * The value of the requested setting, NULL if the setting does not exist. + */ +function theme_get_setting($setting_name, $refresh = FALSE) { + global $theme_key; + static $settings; + + if (empty($settings) || $refresh) { + $settings = theme_get_settings($theme_key); + + $themes = list_themes(); + $theme_object = $themes[$theme_key]; + + if ($settings['mission'] == '') { + $settings['mission'] = variable_get('site_mission', ''); + } + + if (!$settings['toggle_mission']) { + $settings['mission'] = ''; + } + + if ($settings['toggle_logo']) { + if ($settings['default_logo']) { + $settings['logo'] = base_path() . dirname($theme_object->filename) .'/logo.png'; + } + elseif ($settings['logo_path']) { + $settings['logo'] = base_path() . $settings['logo_path']; + } + } + + if ($settings['toggle_favicon']) { + if ($settings['default_favicon']) { + if (file_exists($favicon = dirname($theme_object->filename) .'/favicon.ico')) { + $settings['favicon'] = base_path() . $favicon; + } + else { + $settings['favicon'] = base_path() .'misc/favicon.ico'; + } + } + elseif ($settings['favicon_path']) { + $settings['favicon'] = base_path() . $settings['favicon_path']; + } + else { + $settings['toggle_favicon'] = FALSE; + } + } + } + + return isset($settings[$setting_name]) ? $settings[$setting_name] : NULL; +} + +/** + * Render a system default template, which is essentially a PHP template. + * + * @param $template_file + * The filename of the template to render. Note that this will overwrite + * anything stored in $variables['template_file'] if using a preprocess hook. + * @param $variables + * A keyed array of variables that will appear in the output. + * + * @return + * The output generated by the template. + */ +function theme_render_template($template_file, $variables) { + extract($variables, EXTR_SKIP); // Extract the variables to a local namespace + ob_start(); // Start output buffering + include "./$template_file"; // Include the template file + $contents = ob_get_contents(); // Get the contents of the buffer + ob_end_clean(); // End buffering and discard + return $contents; // Return the contents +} + +/** + * @defgroup themeable Default theme implementations + * @{ + * Functions and templates that present output to the user, and can be + * implemented by themes. + * + * Drupal's presentation layer is a pluggable system known as the theme + * layer. Each theme can take control over most of Drupal's output, and + * has complete control over the CSS. + * + * Inside Drupal, the theme layer is utilized by the use of the theme() + * function, which is passed the name of a component (the theme hook) + * and several arguments. For example, theme('table', $header, $rows); + * Additionally, the theme() function can take an array of theme + * hooks, which can be used to provide 'fallback' implementations to + * allow for more specific control of output. For example, the function: + * theme(array('table__foo', 'table'), $header, $rows) would look to see if + * 'table__foo' is registered anywhere; if it is not, it would 'fall back' + * to the generic 'table' implementation. This can be used to attach specific + * theme functions to named objects, allowing the themer more control over + * specific types of output. + * + * As of Drupal 6, every theme hook is required to be registered by the + * module that owns it, so that Drupal can tell what to do with it and + * to make it simple for themes to identify and override the behavior + * for these calls. + * + * The theme hooks are registered via hook_theme(), which returns an + * array of arrays with information about the hook. It describes the + * arguments the function or template will need, and provides + * defaults for the template in case they are not filled in. If the default + * implementation is a function, by convention it is named theme_HOOK(). + * + * Each module should provide a default implementation for theme_hooks that + * it registers. This implementation may be either a function or a template; + * if it is a function it must be specified via hook_theme(). By convention, + * default implementations of theme hooks are named theme_HOOK. Default + * template implementations are stored in the module directory. + * + * Drupal's default template renderer is a simple PHP parsing engine that + * includes the template and stores the output. Drupal's theme engines + * can provide alternate template engines, such as XTemplate, Smarty and + * PHPTal. The most common template engine is PHPTemplate (included with + * Drupal and implemented in phptemplate.engine, which uses Drupal's default + * template renderer. + * + * In order to create theme-specific implementations of these hooks, + * themes can implement their own version of theme hooks, either as functions + * or templates. These implementations will be used instead of the default + * implementation. If using a pure .theme without an engine, the .theme is + * required to implement its own version of hook_theme() to tell Drupal what + * it is implementing; themes utilizing an engine will have their well-named + * theming functions automatically registered for them. While this can vary + * based upon the theme engine, the standard set by phptemplate is that theme + * functions should be named either phptemplate_HOOK or THEMENAME_HOOK. For + * example, for Drupal's default theme (Garland) to implement the 'table' hook, + * the phptemplate.engine would find phptemplate_table() or garland_table(). + * The ENGINE_HOOK() syntax is preferred, as this can be used by sub-themes + * (which are themes that share code but use different stylesheets). + * + * The theme system is described and defined in theme.inc. + * + * @see theme() + * @see hook_theme() + */ + +/** + * Formats text for emphasized display in a placeholder inside a sentence. + * Used automatically by t(). + * + * @param $text + * The text to format (plain-text). + * @return + * The formatted text (html). + */ +function theme_placeholder($text) { + return ''. check_plain($text) .''; +} + +/** + * Return a themed set of status and/or error messages. The messages are grouped + * by type. + * + * @param $display + * (optional) Set to 'status' or 'error' to display only messages of that type. + * + * @return + * A string containing the messages. + */ +function theme_status_messages($display = NULL) { + $output = ''; + foreach (drupal_get_messages($display) as $type => $messages) { + $output .= "
\n"; + } + return $output; +} + +/** + * Return a themed set of links. + * + * @param $links + * A keyed array of links to be themed. + * @param $attributes + * A keyed array of attributes + * @return + * A string containing an unordered list of links. + */ +function theme_links($links, $attributes = array('class' => 'links')) { + global $language; + $output = ''; + + if (count($links) > 0) { + $output = '