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, array('type' => 'theme', 'media' => $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, array('weight' => JS_THEME)); } $theme_engine = NULL; // Initialize the theme. if (isset($theme->engine)) { // Include the engine. include_once DRUPAL_ROOT . '/' . $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 DRUPAL_ROOT . '/' . $base->owner; } } // and our theme gets one too. if (!empty($theme->owner)) { include_once DRUPAL_ROOT . '/' . $theme->owner; } } if (drupal_function_exists($registry_callback)) { $registry_callback($theme, $base_theme, $theme_engine); } } /** * Get the theme registry. * @return * The theme registry array if it has been stored in memory, NULL otherwise. */ function theme_get_registry() { return _theme_set_registry(); } /** * Store the theme registry in memory. * @param $registry * A registry array as returned by _theme_build_registry() * @return * The theme registry array stored in memory */ function _theme_set_registry($registry = NULL) { static $theme_registry = NULL; if (isset($registry)) { $theme_registry = $registry; } return $theme_registry; } /** * Get the theme_registry cache from the database; if it doesn't exist, build it. * * @param $theme * The loaded $theme object as returned by list_themes(). * @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_theme_rebuild() { cache_clear_all('theme_registry', 'cache', TRUE); } /** * Process a single implementation of hook_theme(). * * @param $cache * The theme registry that will eventually be cached; It is an associative * array keyed by theme hooks, whose values are associative arrays describing * the hook: * - 'type': The passed in $type. * - 'theme path': The passed in $path. * - 'function': The name of the function generating output for this theme * hook. Either defined explicitly in hook_theme() or, if neither 'function' * nor 'template' is defined, then the default theme function name is used. * The default theme function name is the theme hook prefixed by either * 'theme_' for modules or '$name_' for everything else. If 'function' is * defined, 'template' is not used. * - 'template': The filename of the template generating output for this * theme hook. The template is in the directory defined by the 'path' key of * hook_theme() or defaults to $path. * - 'arguments': The arguments for this theme hook as defined in * hook_theme(). If there is more than one implementation and 'arguments' is * not specified in a later one, then the previous definition is kept. * - 'theme paths': The paths where implementations of a theme hook can be * found. Its definition is similarly inherited like 'arguments'. Each time * _theme_process_registry() is called for this theme hook, either the * 'path' key from hook_theme() (if defined) or $path is added. * - 'preprocess functions': See theme() for detailed documentation. * - 'process functions': See theme() for detailed documentation. * @param $name * The name of the module, theme engine, base theme engine, theme or base * theme implementing hook_theme(). * @param $type * One of 'module', 'theme_engine', 'base_theme_engine', 'theme', or * 'base_theme'. Unlike regular hooks that can only be implemented by modules, * each of these can implement hook_theme(). _theme_process_registry() is * called in aforementioned order and new entries override older ones. For * example, if a theme hook is both defined by a module and a theme, then the * definition in the theme will be used. * @param $theme * The loaded $theme object as returned from list_themes(). * @param $path * The directory where $name is. For example, modules/system or * themes/garland. * * @see theme() * @see _theme_process_registry() * @see hook_theme() * @see list_themes() */ function _theme_process_registry(&$cache, $name, $type, $theme, $path) { $result = array(); $function = $name . '_theme'; // Template functions work in two distinct phases with the process // functions always being executed after the preprocess functions. $template_phases = array( 'preprocess functions' => 'preprocess', 'process functions' => 'process', ); 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 DRUPAL_ROOT . '/' . $result[$hook]['file']; } elseif (isset($info['file']) && isset($info['path'])) { include_once DRUPAL_ROOT . '/' . $info['path'] . '/' . $info['file']; } // 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']; } // The following apply only to theming hooks implemented as templates. if (isset($info['template'])) { // Prepend the current theming path when none is set. if (!isset($info['path'])) { $result[$hook]['template'] = $path . '/' . $info['template']; } // 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; foreach ($template_phases as $phase_key => $template_phase) { // Check for existing variable processors. Ensure arrayness. if (!isset($info[$phase_key]) || !is_array($info[$phase_key])) { $info[$phase_key] = array(); $prefixes = array(); if ($type == 'module') { // Default variable processor prefix. $prefixes[] = 'template'; // Add all modules so they can intervene with their own variable processors. This allows them // to provide variable processors 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 variable processors. $prefixes[] = $name . '_engine'; // The theme engine registers on behalf of the theme using the theme's name. $prefixes[] = $theme; } else { // This applies when the theme manually registers their own variable processors. $prefixes[] = $name; } foreach ($prefixes as $prefix) { if (function_exists($prefix . '_' . $template_phase)) { $info[$phase_key][] = $prefix . '_' . $template_phase; } if (function_exists($prefix . '_' . $template_phase . '_' . $hook)) { $info[$phase_key][] = $prefix . '_' . $template_phase . '_' . $hook; } } } // Check for the override flag and prevent the cached variable processors from being used. // This allows themes or theme engines to remove variable processors set earlier in the registry build. if (!empty($info['override ' . $phase_key])) { // Flag not needed inside the registry. unset($result[$hook]['override ' . $phase_key]); } elseif (isset($cache[$hook][$phase_key]) && is_array($cache[$hook][$phase_key])) { $info[$phase_key] = array_merge($cache[$hook][$phase_key], $info[$phase_key]); } $result[$hook][$phase_key] = $info[$phase_key]; } } } // Merge the newly created theme hooks into the existing cache. $cache = array_merge($cache, $result); } // Let themes have variable processors 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])) { foreach ($template_phases as $phase_key => $template_phase) { if (!isset($info[$phase_key])) { $cache[$hook][$phase_key] = array(); } if (function_exists($name . '_' . $template_phase)) { $cache[$hook][$phase_key][] = $name . '_' . $template_phase; } if (function_exists($name . '_' . $template_phase . '_' . $hook)) { $cache[$hook][$phase_key][] = $name . '_' . $template_phase . '_' . $hook; } // Ensure uniqueness. $cache[$hook][$phase_key] = array_unique($cache[$hook][$phase_key]); } } } } } /** * Rebuild the theme registry cache. * * @param $theme * The loaded $theme object as returned by list_themes(). * @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; } /** * Return a list of all currently available themes. * * Retrieved from the database, if available and the site is not in maintenance * mode; otherwise compiled freshly from the filesystem. * * @param $refresh * Whether to reload the list of themes from the database. Defaults to FALSE. * @return * An associative array of the currently available themes. The keys are the * names of the themes and the values are objects having the following * properties: * - 'filename': The name of the .info file. * - 'name': The name of the theme. * - 'status': 1 for enabled, 0 for disabled themes. * - 'info': The contents of the .info file. * - 'stylesheets': A two dimensional array, using the first key for the * 'media' attribute (e.g. 'all'), the second for the name of the file * (e.g. style.css). The value is a complete filepath * (e.g. themes/garland/style.css). * - 'scripts': An associative array of JavaScripts, using the filename as key * and the complete filepath as value. * - 'engine': The name of the theme engine. * - 'base theme': The name of the base theme. */ 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 = :theme", array(':theme' => 'theme')); foreach ($result as $theme) { 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_get_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 * two distinct phases; "preprocess" and "process" functions. The order it is * listed here is the order in which it will execute. * * - 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. * * - THEME_preprocess(&$variables) * This is for themes that want to alter or provide extra variables. For * example, if a theme 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. * * - THEME_preprocess_HOOK(&$variables) * The same applies from the previous function, but it is called for a * specific hook. * * - template_process(&$variables) * This sets a default set of variables for all template implementations. * * - template_process_HOOK(&$variables) * This is the first processor called specific to the hook; it should be * implemented by the module that registers it. * * - MODULE_process(&$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_process(). * * - MODULE_process_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 * process function of foo_process_node() can be created to intercept * and alter the variable. * * - ENGINE_engine_process(&$variables) * This function should only be implemented by theme engines and exists * so that it can set necessary variables for all hooks. * * - ENGINE_engine_process_HOOK(&$variables) * This is the same as the previous function, but it is called for a single * theming hook. * * - ENGINE_process(&$variables) * This is meant to be used by themes that utilize a theme engine. It is * provided so that the processor 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 processor 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_process_HOOK(&$variables) * The same applies from the previous function, but it is called for a * specific hook. * * - THEME_process(&$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_process(). * * - THEME_process_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 = $info['theme path']; // Include a file if the theme function or variable processor is held elsewhere. if (!empty($info['file'])) { $include_file = $info['file']; if (isset($info['path'])) { $include_file = $info['path'] . '/' . $include_file; } include_once DRUPAL_ROOT . '/' . $include_file; } if (isset($info['function'])) { // The theme call is a function. if (drupal_function_exists($info['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 ($info['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(); } } } // This construct ensures that we can keep a reference through // call_user_func_array. $args = array(&$variables, $hook); // Template functions in two phases. foreach (array('preprocess functions', 'process functions') as $template_phase) { foreach ($info[$template_phase] as $template_function) { if (drupal_function_exists($template_function)) { call_user_func_array($template_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 = $info['template'] . $extension; if (isset($info['path'])) { $template_file = $info['path'] . '/' . $template_file; } } $output = $render_function($template_file, $variables); } // restore path_to_theme() $theme_path = $temp; return $output; } /** * Determine and return which template file will generate the output. * * This helper allows the theme system to pick the template at runtime instead * of build time. * * @see template_page_suggestions() * @see template_preprocess_block() * * @param $paths * The paths where templates can be found. See _theme_process_registry() * 'theme paths' for more information. * @param $suggestions * The possible template names. These are derived from * $variables['template_files'] and $variables['template_file], defined by * preprocess functions. Each file is checked on every path in the order of * precedence defined by theme(). * @return * The filepath to the template that will generate the output. If none is * found, then theme() will use the 'template' as set by * _theme_process_registry(). * * @see _theme_process_registry() * @see theme() */ 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'], ); } } } 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->filepath, str_replace($subtheme_paths, '', $file->filepath)) !== 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->filepath), ); } // 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]->filepath), 'arguments' => $info['arguments'], ); } } } } 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( 'default_logo' => 1, 'logo_path' => '', 'default_favicon' => 1, 'favicon_path' => '', 'main_menu' => 1, 'secondary_menu' => 1, 'toggle_logo' => 1, 'toggle_favicon' => 1, 'toggle_name' => 1, 'toggle_search' => 0, 'toggle_slogan' => 0, 'toggle_node_user_picture' => 0, 'toggle_comment_user_picture' => 0, 'toggle_comment_user_verification' => 1, 'toggle_main_menu' => 1, 'toggle_secondary_menu' => 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 (!defined('MAINTENANCE_MODE') && module_exists('search') && user_access('search content')) { $settings['toggle_search'] = 1; } 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['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 variable processor 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 DRUPAL_ROOT . '/' . $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 THEMENAME_HOOK. For example, for Drupal's default theme (Garland) to * implement the 'table' hook, the phptemplate.engine would find * garland_table(). * * 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. The key for each link is used as its css class. * Each link should be itself an array, with the following keys: * - title: the link text * - href: the link URL. If omitted, the 'title' is shown as a plain text item in the links list. * - html: (optional) set this to TRUE if 'title' is HTML so it will be escaped. * Array items are passed on to the l() function's $options parameter when creating the link. * @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 = '