Theme system * @see themeable */ /** * @name Content markers * @{ * Markers used by theme_mark() and node_mark() to designate content. * @see theme_mark(), node_mark() */ define('MARK_READ', 0); define('MARK_NEW', 1); define('MARK_UPDATED', 2); /** * @} End of "Content markers". */ /** * Initialize the theme system by loading the theme. * */ function init_theme() { global $theme, $user, $custom_theme, $theme_engine, $theme_key; // If $theme is already set, assume the others are set, too, and do nothing if (isset($theme)) { return; } drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE); $themes = list_themes(); // Only select the user selected theme if it is available in the // list of enabled themes. $theme = !empty($user->theme) && $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; // If we're using a style, load its appropriate theme, // which is stored in the style's description field. // Also add the stylesheet using drupal_add_css(). // Otherwise, load the theme. if (strpos($themes[$theme]->filename, '.css')) { // File is a style; loads its CSS. // Set theme to its template/theme drupal_add_css($themes[$theme]->filename, 'theme'); $theme = basename(dirname($themes[$theme]->owner)); } else { // File is a template/theme // Load its CSS, if it exists if (file_exists($stylesheet = dirname($themes[$theme]->filename) .'/style.css')) { drupal_add_css($stylesheet, 'theme'); } } if (strpos($themes[$theme]->filename, '.theme')) { // file is a theme; include it include_once './'. $themes[$theme]->filename; _theme_load_registry($theme); } elseif (strpos($themes[$theme]->owner, '.engine')) { // file is a template; include its engine include_once './'. $themes[$theme]->owner; $theme_engine = basename($themes[$theme]->owner, '.engine'); if (function_exists($theme_engine .'_init')) { call_user_func($theme_engine .'_init', $themes[$theme]); } _theme_load_registry($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. */ function _theme_load_registry($theme, $theme_engine = NULL) { $cache = cache_get("theme_registry:$theme", 'cache'); if (isset($cache->data)) { $registry = unserialize($cache->data); } else { $registry = _theme_build_registry($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", serialize($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. */ function _theme_process_registry(&$cache, $name, $type) { $function = $name .'_theme'; if (function_exists($function)) { $result = $function($cache); // Automatically find paths $path = drupal_get_path($type, $name); foreach ($result as $hook => $info) { $result[$hook]['type'] = $type; // if function and file are left out, default to standard naming // conventions. if (!isset($info['file']) && !isset($info['function'])) { $result[$hook]['function'] = ($type == 'module' ? 'theme_' : $name .'_') . $hook; } if (isset($info['file']) && !isset($info['path'])) { $result[$hook]['file'] = $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']; } } $cache = array_merge($cache, $result); } } /** * Rebuild the hook theme_registry cache. */ function _theme_build_registry($theme, $theme_engine) { $cache = array(); foreach (module_implements('theme') as $module) { _theme_process_registry($cache, $module, 'module'); } if ($theme_engine) { _theme_process_registry($cache, $theme_engine, 'theme_engine'); } _theme_process_registry($cache, $theme, 'theme'); return $cache; } /** * Provides a list of currently available themes. * * @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(); $result = db_query("SELECT * FROM {system} WHERE type = 'theme'"); while ($theme = db_fetch_object($result)) { if (file_exists($theme->filename)) { $theme->info = unserialize($theme->info); $list[$theme->name] = $theme; } } } return $list; } /** * Provides a list of currently available theme engines * * @param $refresh * Whether to reload the list of themes from the database. * @return * An array of the currently available theme engines. */ function list_theme_engines($refresh = FALSE) { static $list; if ($refresh) { unset($list); } if (!$list) { $list = array(); $result = db_query("SELECT * FROM {system} WHERE type = 'theme_engine' AND status = '1' ORDER BY name"); while ($engine = db_fetch_object($result)) { if (file_exists($engine->filename)) { $engine->info = unserialize($engine->info); $list[$engine->name] = $engine; } } } 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 theme engine (if * applicable) and the theme. The following functions may be used to modify * the $variables array: * * ENGINE_engine_variables(&$variables) * This function should only be implemented by theme engines and is exists * so that the theme engine can set necessary variables. It is commonly * used to set global variables such as $directory and $is_front_page. * ENGINE_engine_variables_HOOK(&$variables) * This is the same as the previous function, but is called only per hook. * ENGINE_variables_HOOK(&$variables) * ENGINE_variables(&$variables) * This is meant to be used by themes that utilize a theme engine; as it is * good practice for these themes to use the theme engine's name for * their functions so that they may share code. In PHPTemplate, these * functions will appear in template.php * THEME_variables_HOOK(&$variables) * THEME_variables(&$variables) * These functions are based upon the raw theme; they should primarily be * used by themes that do not use an engine or by themes that need small * changes to what has already been established in the theme engine version * of the function. * * 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. * @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 (!isset($hooks[$hook])) { return; } $info = $hooks[$hook]; if (isset($info['function'])) { // The theme call is a function. // Include a file if this theme function is held elsewhere. if (!empty($info['file'])) { include_once($info['file']); } return 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'; $variables_list = array(); // Run through the theme engine variables, if necessary global $theme_engine; if (isset($theme_engine)) { // Call each of our variable override functions. We allow // several to create cleaner code. $variables_list[] = $theme_engine .'_engine_variables'; $variables_list[] = $theme_engine .'_engine_variables_'. $hook; $variables_list[] = $theme_engine .'_variables'; $variables_list[] = $theme_engine .'_variables_'. $hook; // 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(); } } } // Add theme specific variable substitution: global $theme; $variables_list[] = $theme .'_variables'; $variables_list[] = $theme .'_variables_'. $hook; // This construct ensures that we can keep a reference through // call_user_func_array. $args = array(&$variables, $hook); foreach ($variables_list as $variables_function) { if (function_exists($variables_function)) { call_user_func_array($variables_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($suggestions, $extension); } if (empty($template_file)) { $template_file = $hooks[$hook]['file'] . $extension; if (isset($hooks[$hook]['path'])) { $template_file = $hooks[$hook]['path'] .'/'. $template_file; } } return $render_function($template_file, $variables); } } /** * Choose which template file to actually render; these are all * suggested templates from the theme. */ function drupal_discover_template($suggestions, $extension = '.tpl.php') { global $theme_engine; // Loop through any suggestions in FIFO order. $suggestions = array_reverse($suggestions); foreach ($suggestions as $suggestion) { if (!empty($suggestion) && file_exists($file = path_to_theme() .'/'. $suggestion . $extension)) { return $file; } } } /** * Return the path to the currently selected theme. */ function path_to_theme() { global $theme; if (!isset($theme)) { init_theme(); } $themes = list_themes(); return dirname($themes[$theme]->filename); } /** * Return the path to the currently selected engine. */ function path_to_engine() { global $theme, $theme_engine; if (!isset($theme)) { init_theme(); } $engines = list_theme_engines(); return dirname($engines[$theme_engine]->filename); } /** * 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' => '', '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, ); 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']; } } } return isset($settings[$setting_name]) ? $settings[$setting_name] : NULL; } /** * Render a system default template, which is essentially a PHP template. * * @param $file * The filename of the template to render. * @param $variables * A keyed array of variables that will appear in the output. * * @return * The output generated by the template. */ function theme_render_template($file, $variables) { extract($variables, EXTR_SKIP); // Extract the variables to a local namespace ob_start(); // Start output buffering include "./$file"; // Include the 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); * * 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 themes 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 an entire Drupal page displaying the supplied content. * * @param $content * A string to display in the main content area of the page. * @return * A string containing the entire HTML page. */ function theme_page($content) { // Get blocks before so that they can alter the header (JavaScript, Stylesheets etc.) $blocks = theme('blocks', 'all'); $output = "\n"; $output .= ''; $output .= ''; $output .= ' '. (drupal_get_title() ? strip_tags(drupal_get_title()) : variable_get('site_name', 'Drupal')) .''; $output .= drupal_get_html_head(); $output .= drupal_get_css(); $output .= drupal_get_js(); $output .= ' '; $output .= ' '; $output .= '
'; $output .= $blocks; $output .= ''; $output .= theme('breadcrumb', drupal_get_breadcrumb()); $output .= '

'. drupal_get_title() .'

'; if ($tabs = theme('menu_local_tasks')) { $output .= $tabs; } $output .= theme('help'); $output .= theme('status_messages'); $output .= "\n\n"; $output .= $content; $output .= drupal_get_feeds(); $output .= "\n\n"; $output .= '
'; $output .= theme('closure'); $output .= ''; return $output; } /** * Generate a themed maintenance page. * * Note: this function is not themable. * * @param $content * The page content to show. * @param $messages * Whether to output status and error messages. */ function theme_maintenance_page($content, $messages = TRUE) { // Set required headers. drupal_set_header('Content-Type: text/html; charset=utf-8'); drupal_set_html_head(''); drupal_set_html_head(''); drupal_set_html_head(''); drupal_set_html_head(''); // Prepare variables. $variables = array( 'head_title' => strip_tags(drupal_get_title()), 'head' => drupal_get_html_head(), 'styles' => '', 'scripts' => drupal_get_js(), 'sidebar_left' => drupal_get_content('left'), 'sidebar_right' => drupal_get_content('right'), 'base_path' => base_path(), 'path_to_theme' => base_path() .'themes/garland/minnelli', 'logo' => base_path() .'themes/garland/minnelli/logo.png', 'site_title' => t('Drupal'), 'title' => drupal_get_title(), 'messages' => theme('status_messages'), 'content' => $content, ); $output = theme_render_template('misc/maintenance.tpl.php', $variables); return $output; } /** * Generate a themed installation page. * * Note: this function is not themable. * * @param $content * The page content to show. */ function theme_install_page($content) { drupal_set_header('Content-Type: text/html; charset=utf-8'); drupal_add_css('misc/maintenance.css', 'module', 'all', FALSE); drupal_set_html_head(''); $variables = array( 'head_title' => strip_tags(drupal_get_title()), 'head' => drupal_get_html_head(), 'styles' => drupal_get_css(), 'scripts' => drupal_get_js(), 'sidebar_left' => drupal_get_content('left'), 'sidebar_right' => drupal_get_content('right'), 'base_path' => base_path(), 'path_to_theme' => base_path() .'themes/garland/minnelli', 'logo' => base_path() .'themes/garland/minnelli/logo.png', 'site_title' => st('Drupal Installation'), 'title' => drupal_get_title(), 'messages' => '', 'content' => $content, ); // Special handling of error messages $messages = drupal_set_message(); if (isset($messages['error'])) { $title = count($messages['error']) > 1 ? st('The following errors must be resolved before you can continue the installation process') : st('The following error must be resolved before you can continue the installation process'); $variables['messages'] .= '

'. $title .':

'; $variables['messages'] .= theme('status_messages', 'error'); } // Special handling of status messages if (isset($messages['status'])) { $warnings = count($messages['status']) > 1 ? st('The following installation warnings should be carefully reviewed, but in most cases may be safely ignored') : st('The following installation warning should be carefully reviewed, but in most cases may be safely ignored'); $variables['messages'] .= '

'. $title .':

'; $variables['messages'] .= theme('status_messages', 'status'); } return theme_render_template('misc/maintenance.tpl.php', $variables); } /** * Return a themed list of maintenance tasks to perform. * * Note: this function is not themable. */ function theme_task_list($items, $active = NULL) { $done = isset($items[$active]) || $active == NULL; $output = '
    '; foreach ($items as $k => $item) { if ($active == $k) { $class = 'active'; $done = false; } else { $class = $done ? 'done' : ''; } $output .= '
  1. '. $item .'
  2. '; } $output .= '
'; return $output; } /** * 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"; if (count($messages) > 1) { $output .= " \n"; } else { $output .= $messages[0]; } $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')) { $output = ''; if (count($links) > 0) { $output = ''; $num_links = count($links); $i = 1; foreach ($links as $key => $link) { $class = ''; // Automatically add a class to each link and also to each LI if (isset($link['attributes']) && isset($link['attributes']['class'])) { $link['attributes']['class'] .= ' '. $key; $class = $key; } else { $link['attributes']['class'] = $key; $class = $key; } // Add first and last classes to the list of links to help out themers. $extra_class = ''; if ($i == 1) { $extra_class .= 'first '; } if ($i == $num_links) { $extra_class .= 'last '; } $output .= '
  • '; if (isset($link['href'])) { // Pass in $link as $options, they share the same keys. $output .= l($link['title'], $link['href'], $link); } else if (!empty($link['title'])) { // Some links are actually not links, but we wrap these in for adding title and class attributes if (empty($link['html'])) { $link['title'] = check_plain($link['title']); } $output .= ''. $link['title'] .''; } $i++; $output .= "
  • \n"; } $output .= ''; } return $output; } /** * Return a themed image. * * @param $path * Either the path of the image file (relative to base_path()) or a full URL. * @param $alt * The alternative text for text-based browsers. * @param $title * The title text is displayed when the image is hovered in some popular browsers. * @param $attributes * Associative array of attributes to be placed in the img tag. * @param $getsize * If set to TRUE, the image's dimension are fetched and added as width/height attributes. * @return * A string containing the image tag. */ function theme_image($path, $alt = '', $title = '', $attributes = NULL, $getsize = TRUE) { if (!$getsize || (is_file($path) && (list($width, $height, $type, $image_attributes) = @getimagesize($path)))) { $attributes = drupal_attributes($attributes); $url = (url($path) == $path) ? $path : (base_path() . $path); return ''. check_plain($alt) .''; } } /** * Return a themed breadcrumb trail. * * @param $breadcrumb * An array containing the breadcrumb links. * @return a string containing the breadcrumb output. */ function theme_breadcrumb($breadcrumb) { if (!empty($breadcrumb)) { return ''; } } /** * Return a themed help message. * * @return a string containing the helptext for the current page. */ function theme_help() { if ($help = menu_get_active_help()) { return '
    '. $help .'
    '; } } /** * Return a themed node. * * @param $node * An object providing all relevant information for displaying a node: * - $node->nid: The ID of the node. * - $node->type: The content type (story, blog, forum...). * - $node->title: The title of the node. * - $node->created: The creation date, as a UNIX timestamp. * - $node->teaser: A shortened version of the node body. * - $node->body: The entire node contents. * - $node->changed: The last modification date, as a UNIX timestamp. * - $node->uid: The ID of the author. * - $node->username: The username of the author. * @param $teaser * Whether to display the teaser only, as on the main page. * @param $page * Whether to display the node as a standalone page. If TRUE, do not display * the title because it will be provided by the menu system. * @return * A string containing the node output. */ function theme_node($node, $teaser = FALSE, $page = FALSE) { if (!$node->status) { $output = '
    '; } if (module_exists('taxonomy')) { $terms = taxonomy_link('taxonomy terms', $node); } if ($page == 0) { $output .= t('!title by !name', array('!title' => '

    '. check_plain($node->title) .'

    ', '!name' => theme('username', $node))); } else { $output .= t('by !name', array('!name' => theme('username', $node))); } if (count($terms)) { $output .= ' ('. theme('links', $terms) .')
    '; } if ($teaser && $node->teaser) { $output .= $node->teaser; } else { $output .= $node->body; } if ($node->links) { $output .= ''; } if (!$node->status) { $output .= '
    '; } return $output; } /** * Return a themed submenu, typically displayed under the tabs. * * @param $links * An array of links. */ function theme_submenu($links) { return ''; } /** * Return a themed table. * * @param $header * An array containing the table headers. Each element of the array can be * either a localized string or an associative array with the following keys: * - "data": The localized title of the table column. * - "field": The database field represented in the table column (required if * user is to be able to sort on this column). * - "sort": A default sort order for this column ("asc" or "desc"). * - Any HTML attributes, such as "colspan", to apply to the column header cell. * @param $rows * An array of table rows. Every row is an array of cells, or an associative * array with the following keys: * - "data": an array of cells * - Any HTML attributes, such as "class", to apply to the table row. * * Each cell can be either a string or an associative array with the following keys: * - "data": The string to display in the table cell. * - "header": Indicates this cell is a header. * - Any HTML attributes, such as "colspan", to apply to the table cell. * * Here's an example for $rows: * @verbatim * $rows = array( * // Simple row * array( * 'Cell 1', 'Cell 2', 'Cell 3' * ), * // Row with attributes on the row and some of its cells. * array( * 'data' => array('Cell 1', array('data' => 'Cell 2', 'colspan' => 2)), 'class' => 'funky' * ) * ); * @endverbatim * * @param $attributes * An array of HTML attributes to apply to the table tag. * @param $caption * A localized string to use for the tag. * @return * An HTML string representing the table. */ function theme_table($header, $rows, $attributes = array(), $caption = NULL) { $output = '\n"; if (isset($caption)) { $output .= ''. $caption ."\n"; } // Format the table header: if (count($header)) { // Include JS for sticky headers. drupal_add_js('misc/tableheader.js'); $ts = tablesort_init($header); $output .= ' '; foreach ($header as $cell) { $cell = tablesort_header($cell, $header, $ts); $output .= _theme_table_cell($cell, TRUE); } $output .= " \n"; } else { $ts = array(); } // Format the table rows: $output .= "\n"; if (count($rows)) { $flip = array('even' => 'odd', 'odd' => 'even'); $class = 'even'; foreach ($rows as $number => $row) { $attributes = array(); // Check if we're dealing with a simple or complex row if (isset($row['data'])) { foreach ($row as $key => $value) { if ($key == 'data') { $cells = $value; } else { $attributes[$key] = $value; } } } else { $cells = $row; } // Add odd/even class $class = $flip[$class]; if (isset($attributes['class'])) { $attributes['class'] .= ' '. $class; } else { $attributes['class'] = $class; } // Build row $output .= ' '; $i = 0; foreach ($cells as $cell) { $cell = tablesort_cell($cell, $header, $ts, $i++); $output .= _theme_table_cell($cell); } $output .= " \n"; } } $output .= "\n"; return $output; } /** * Returns a header cell for tables that have a select all functionality. */ function theme_table_select_header_cell() { drupal_add_js(array('tableSelect' => array('selectAll' => t('Select all rows in this table'), 'selectNone' => t('Deselect all rows in this table'))), 'setting'); drupal_add_js('misc/tableselect.js'); return array('class' => 'select-all'); } /** * Return a themed sort icon. * * @param $style * Set to either asc or desc. This sets which icon to show. * @return * A themed sort icon. */ function theme_tablesort_indicator($style) { if ($style == "asc") { return theme('image', 'misc/arrow-asc.png', t('sort icon'), t('sort ascending')); } else { return theme('image', 'misc/arrow-desc.png', t('sort icon'), t('sort descending')); } } /** * Return a themed box. * * @param $title * The subject of the box. * @param $content * The content of the box. * @param $region * The region in which the box is displayed. * @return * A string containing the box output. */ function theme_box($title, $content, $region = 'main') { $output = '

    '. $title .'

    '. $content .'
    '; return $output; } /** * Return a themed block. * * You can style your blocks by defining .block (all blocks), * .block-module (all blocks of module module), and * \#block-module-delta (specific block of module module * with delta delta) in your theme's CSS. * * @param $block * An object populated with fields from the "blocks" database table * ($block->module, $block->delta ...) and fields returned by * module_block('view') ($block->subject, $block->content, ...). * @return * A string containing the block output. */ function theme_block($block) { $output = "
    module\" id=\"block-$block->module-$block->delta\">\n"; $output .= "

    $block->subject

    \n"; $output .= "
    $block->content
    \n"; $output .= "
    \n"; return $output; } /** * Return a themed marker, useful for marking new or updated * content. * * @param $type * Number representing the marker type to display * @see MARK_NEW, MARK_UPDATED, MARK_READ * @return * A string containing the marker. */ function theme_mark($type = MARK_NEW) { global $user; if ($user->uid) { if ($type == MARK_NEW) { return ' '. t('new') .''; } else if ($type == MARK_UPDATED) { return ' '. t('updated') .''; } } } /** * Return a themed list of items. * * @param $items * An array of items to be displayed in the list. If an item is a string, * then it is used as is. If an item is an array, then the "data" element of * the array is used as the contents of the list item. If an item is an array * with a "children" element, those children are displayed in a nested list. * All other elements are treated as attributes of the list item element. * @param $title * The title of the list. * @param $attributes * The attributes applied to the list element. * @param $type * The type of list to return (e.g. "ul", "ol") * @return * A string containing the list output. */ function theme_item_list($items = array(), $title = NULL, $type = 'ul', $attributes = NULL) { $output = '
    '; if (isset($title)) { $output .= '

    '. $title .'

    '; } if (!empty($items)) { $output .= "<$type". drupal_attributes($attributes) .'>'; foreach ($items as $item) { $attributes = array(); $children = array(); if (is_array($item)) { foreach ($item as $key => $value) { if ($key == 'data') { $data = $value; } elseif ($key == 'children') { $children = $value; } else { $attributes[$key] = $value; } } } else { $data = $item; } if (count($children) > 0) { $data .= theme_item_list($children, NULL, $type, $attributes); // Render nested list } $output .= ''. $data .''; } $output .= ""; } $output .= '
    '; return $output; } /** * Returns code that emits the 'more help'-link. */ function theme_more_help_link($url) { return ''; } /** * Return code that emits an XML icon. */ function theme_xml_icon($url) { if ($image = theme('image', 'misc/xml.png', t('XML feed'), t('XML feed'))) { return ''. $image .''; } } /** * Return code that emits an feed icon. */ function theme_feed_icon($url) { if ($image = theme('image', 'misc/feed.png', t('Syndicate content'), t('Syndicate content'))) { return ''. $image .''; } } /** * Execute hook_footer() which is run at the end of the page right before the * close of the body tag. * * @param $main (optional) * Whether the current page is the front page of the site. * @return * A string containing the results of the hook_footer() calls. */ function theme_closure($main = 0) { $footer = module_invoke_all('footer', $main); return implode("\n", $footer) . drupal_get_js('footer'); } /** * Return a set of blocks available for the current user. * * @param $region * Which set of blocks to retrieve. * @return * A string containing the themed blocks for this region. */ function theme_blocks($region) { $output = ''; if ($list = block_list($region)) { foreach ($list as $key => $block) { // $key == module_delta $output .= theme('block', $block); } } // Add any content assigned to this region through drupal_set_content() calls. $output .= drupal_get_content($region); return $output; } /** * Format a username. * * @param $object * The user object to format, usually returned from user_load(). * @return * A string containing an HTML link to the user's page if the passed object * suggests that this is a site user. Otherwise, only the username is returned. */ function theme_username($object) { if ($object->uid && $object->name) { // Shorten the name when it is too long or it will break many tables. if (drupal_strlen($object->name) > 20) { $name = drupal_substr($object->name, 0, 15) .'...'; } else { $name = $object->name; } if (user_access('access user profiles')) { $output = l($name, 'user/'. $object->uid, array('title' => t('View user profile.'))); } else { $output = check_plain($name); } } else if ($object->name) { // Sometimes modules display content composed by people who are // not registered members of the site (e.g. mailing list or news // aggregator modules). This clause enables modules to display // the true author of the content. if ($object->homepage) { $output = l($object->name, $object->homepage); } else { $output = check_plain($object->name); } $output .= ' ('. t('not verified') .')'; } else { $output = variable_get('anonymous', t('Anonymous')); } return $output; } function theme_progress_bar($percent, $message) { $output = '
    '; $output .= '
    '. $percent .'%
    '; $output .= '
    '. $message .'
    '; $output .= '
    '; $output .= '
    '; return $output; } /** * @} End of "defgroup themeable". */ function _theme_table_cell($cell, $header = FALSE) { $attributes = ''; if (is_array($cell)) { $data = isset($cell['data']) ? $cell['data'] : ''; $header |= isset($cell['header']); unset($cell['data']); unset($cell['header']); $attributes = drupal_attributes($cell); } else { $data = $cell; } if ($header) { $output = "$data"; } else { $output = "$data"; } return $output; }