Same filename and directory in other branches
  1. 4.6.x includes/theme.inc
  2. 4.7.x includes/theme.inc
  3. 6.x includes/theme.inc
  4. 7.x includes/theme.inc

The theme system, which controls the output of Drupal.

The theme system allows for nearly all output of the Drupal system to be customized by user themes.

See also

<a href="http://drupal.org/node/171179">Theme guide</a>

Themeable functions

File

includes/theme.inc
View source
<?php

/**
 * @file
 * The theme system, which controls the output of Drupal.
 *
 * The theme system allows for nearly all output of the Drupal system to be
 * customized by user themes.
 *
 * @see <a href="http://drupal.org/node/171179">Theme guide</a>
 * @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) && !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;

  // 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]->description));
  }
  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;
  }
  elseif (strpos($themes[$theme]->description, '.engine')) {

    // file is a template; include its engine
    include_once './' . $themes[$theme]->description;
    $theme_engine = basename($themes[$theme]->description, '.engine');
    if (function_exists($theme_engine . '_init')) {
      call_user_func($theme_engine . '_init', $themes[$theme]);
    }
  }
}

/**
 * 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;
  if ($refresh) {
    unset($list);
  }
  if (!$list) {
    $list = array();
    $result = db_query("SELECT * FROM {system} WHERE type = 'theme'");
    while ($theme = db_fetch_object($result)) {
      if (file_exists($theme->filename)) {
        $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)) {
        $list[$engine->name] = $engine;
      }
    }
  }
  return $list;
}

/**
 * Generate the themed representation of a Drupal object.
 *
 * All requests for themed functions must go through this function. It examines
 * the request and routes it to the appropriate theme function. If the current
 * theme does not implement the requested function, then the current theme
 * engine is checked. If neither the engine nor theme implement the requested
 * function, then the base theme function is called.
 *
 * For example, to retrieve the HTML that is output by theme_page($output), a
 * module should call theme('page', $output).
 *
 * @param $function
 *   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() {
  static $functions;
  $args = func_get_args();
  $function = array_shift($args);
  if (!isset($functions[$function])) {
    $functions[$function] = theme_get_function($function);
  }
  if ($functions[$function]) {
    $output = call_user_func_array($functions[$function], $args);

    // Add final markup to the full page.
    if ($function == 'page' || $function == 'book_export_html') {
      $output = drupal_final_markup($output);
    }
    return $output;
  }
}

/**
 * Determine if a theme function exists, and if so return which one was found.
 *
 * @param $function
 *   The name of the theme function to test.
 * @return
 *   The name of the theme function that should be used, or FALSE if no function exists.
 */
function theme_get_function($function) {
  global $theme, $theme_engine;

  // Because theme() is called a lot, calling init_theme() only to have it
  // smartly return is a noticeable performance hit. Don't do it.
  if (!isset($theme)) {
    init_theme();
  }
  if ($theme != '' && function_exists($theme . '_' . $function)) {

    // call theme function
    return $theme . '_' . $function;
  }
  elseif ($theme != '' && isset($theme_engine) && function_exists($theme_engine . '_' . $function)) {

    // call engine function
    return $theme_engine . '_' . $function;
  }
  elseif (function_exists('theme_' . $function)) {

    // call Drupal function
    return 'theme_' . $function;
  }
  return FALSE;
}

/**
 * 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'];
      }
      else {
        $settings['toggle_favicon'] = FALSE;
      }
    }
  }
  return isset($settings[$setting_name]) ? $settings[$setting_name] : NULL;
}

/**
 * @defgroup themeable Themeable functions
 * @{
 * Functions that display HTML, and which can be customized by themes.
 *
 * All functions that produce HTML for display should be themeable. This means
 * that they should be named with the theme_ prefix, and invoked using theme()
 * rather than being called directly. This allows themes to override the display
 * of any Drupal object.
 *
 * The theme system is described and defined in theme.inc.
 */

/**
 * 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 '<em>' . check_plain($text) . '</em>';
}

/**
 * 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 = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
  $output .= '<html xmlns="http://www.w3.org/1999/xhtml">';
  $output .= '<head>';
  $output .= ' <title>' . (drupal_get_title() ? strip_tags(drupal_get_title()) : variable_get('site_name', 'Drupal')) . '</title>';
  $output .= drupal_get_html_head();
  $output .= drupal_get_css();
  $output .= drupal_get_js();
  $output .= ' </head>';
  $output .= ' <body style="background-color: #fff; color: #000;">';
  $output .= '<table border="0" cellspacing="4" cellpadding="4"><tr><td style="vertical-align: top; width: 170px;">';
  $output .= $blocks;
  $output .= '</td><td style="vertical-align: top;">';
  $output .= theme('breadcrumb', drupal_get_breadcrumb());
  $output .= '<h1>' . drupal_get_title() . '</h1>';
  if ($tabs = theme('menu_local_tasks')) {
    $output .= $tabs;
  }
  $output .= theme('help');
  $output .= theme('status_messages');
  $output .= "\n<!-- begin content -->\n";
  $output .= $content;
  $output .= drupal_get_feeds();
  $output .= "\n<!-- end content -->\n";
  $output .= '</td></tr></table>';
  $output .= theme('closure');
  $output .= '</body></html>';
  return $output;
}
function theme_maintenance_page($content, $messages = TRUE, $partial = FALSE) {
  drupal_set_header('Content-Type: text/html; charset=utf-8');
  drupal_set_html_head('<style type="text/css" media="all">@import "' . base_path() . 'misc/maintenance.css";</style>');
  drupal_set_html_head('<style type="text/css" media="all">@import "' . base_path() . drupal_get_path('module', 'system') . '/defaults.css";</style>');
  drupal_set_html_head('<style type="text/css" media="all">@import "' . base_path() . drupal_get_path('module', 'system') . '/system.css";</style>');
  drupal_set_html_head('<link rel="shortcut icon" href="' . base_path() . 'misc/favicon.ico" type="image/x-icon" />');
  $output = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
  $output .= '<html xmlns="http://www.w3.org/1999/xhtml">';
  $output .= '<head>';
  $output .= ' <title>' . strip_tags(drupal_get_title()) . '</title>';
  $output .= drupal_get_html_head();
  $output .= drupal_get_js();
  $output .= '</head>';
  $output .= '<body>';
  $output .= '<h1>' . drupal_get_title() . '</h1>';
  if ($messages) {
    $output .= theme('status_messages');
  }
  $output .= "\n<!-- begin content -->\n";
  $output .= $content;
  $output .= "\n<!-- end content -->\n";
  if (!$partial) {
    $output .= '</body></html>';
  }
  return $output;
}
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('<link rel="shortcut icon" href="' . base_path() . 'misc/favicon.ico" type="image/x-icon" />');
  $output = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
  $output .= '<html xmlns="http://www.w3.org/1999/xhtml">';
  $output .= '<head>';
  $output .= ' <title>' . strip_tags(drupal_get_title()) . '</title>';
  $output .= drupal_get_html_head();
  $output .= drupal_get_css();
  $output .= drupal_get_js();
  $output .= '</head>';
  $output .= '<body>';
  $output .= '<h1>' . drupal_get_title() . '</h1>';
  $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');
    $output .= '<h3>' . $title . ':</h3>';
    $output .= theme('status_messages', 'error');
  }
  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');
    $output .= '<h4>' . $title . ':</h4>';
    $output .= theme('status_messages', 'status');
  }
  $output .= "\n<!-- begin content -->\n";
  $output .= $content;
  $output .= "\n<!-- end content -->\n";
  $output .= '</body></html>';
  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 .= "<div class=\"messages {$type}\">\n";
    if (count($messages) > 1) {
      $output .= " <ul>\n";
      foreach ($messages as $message) {
        $output .= '  <li>' . $message . "</li>\n";
      }
      $output .= " </ul>\n";
    }
    else {
      $output .= $messages[0];
    }
    $output .= "</div>\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 = '<ul' . drupal_attributes($attributes) . '>';
    $num_links = count($links);
    $i = 1;
    foreach ($links as $key => $link) {
      $class = $key;

      // Automatically add a class to each link and also to each LI
      if (isset($link['attributes']) && isset($link['attributes']['class'])) {
        $link['attributes']['class'] .= ' ' . $key;
      }
      else {
        $link['attributes']['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 .= '<li ' . drupal_attributes(array(
        'class' => $extra_class . $class,
      )) . '>';

      // Is the title HTML?
      $html = isset($link['html']) && $link['html'];

      // Initialize fragment and query variables.
      $link['query'] = isset($link['query']) ? $link['query'] : NULL;
      $link['fragment'] = isset($link['fragment']) ? $link['fragment'] : NULL;
      if (isset($link['href'])) {
        $output .= l($link['title'], $link['href'], $link['attributes'], $link['query'], $link['fragment'], FALSE, $html);
      }
      else {
        if ($link['title']) {

          //Some links are actually not links, but we wrap these in <span> for adding title and class attributes
          if (!$html) {
            $link['title'] = check_plain($link['title']);
          }
          $output .= '<span' . drupal_attributes($link['attributes']) . '>' . $link['title'] . '</span>';
        }
      }
      $i++;
      $output .= "</li>\n";
    }
    $output .= '</ul>';
  }
  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 '<img src="' . check_url($url) . '" alt="' . check_plain($alt) . '" title="' . check_plain($title) . '" ' . $image_attributes . $attributes . ' />';
  }
}

/**
 * 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 '<div class="breadcrumb">' . implode(' » ', $breadcrumb) . '</div>';
  }
}

/**
 * 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 '<div class="help">' . $help . '</div>';
  }
}

/**
 * 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 = '<div class="node-unpublished">';
  }
  if (module_exists('taxonomy')) {
    $terms = taxonomy_link('taxonomy terms', $node);
  }
  if ($page == 0) {
    $output .= t('!title by !name', array(
      '!title' => '<h2 class="title">' . check_plain($node->title) . '</h2>',
      '!name' => theme('username', $node),
    ));
  }
  else {
    $output .= t('by !name', array(
      '!name' => theme('username', $node),
    ));
  }
  if (count($terms)) {
    $output .= ' <small>(' . theme('links', $terms) . ')</small><br />';
  }
  if ($teaser && $node->teaser) {
    $output .= $node->teaser;
  }
  else {
    $output .= $node->body;
  }
  if ($node->links) {
    $output .= '<div class="links">' . theme('links', $node->links) . '</div>';
  }
  if (!$node->status) {
    $output .= '</div>';
  }
  return $output;
}

/**
 * Return a themed submenu, typically displayed under the tabs.
 *
 * @param $links
 *   An array of links.
 */
function theme_submenu($links) {
  return '<div class="submenu">' . implode(' | ', $links) . '</div>';
}

/**
 * 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 <caption> tag.
 * @return
 *   An HTML string representing the table.
 */
function theme_table($header, $rows, $attributes = array(), $caption = NULL) {
  $output = '<table' . drupal_attributes($attributes) . ">\n";
  if (isset($caption)) {
    $output .= '<caption>' . $caption . "</caption>\n";
  }

  // Format the table header:
  if (count($header)) {
    $ts = tablesort_init($header);

    // HTML requires that the thead tag has tr tags in it follwed by tbody
    // tags. Using ternary operator to check and see if we have any rows.
    $output .= count($rows) ? ' <thead><tr>' : ' <tr>';
    foreach ($header as $cell) {
      $cell = tablesort_header($cell, $header, $ts);
      $output .= _theme_table_cell($cell, TRUE);
    }

    // Using ternary operator to close the tags based on whether or not there are rows
    $output .= count($rows) ? " </tr></thead>\n" : "</tr>\n";
  }

  // Format the table rows:
  if (count($rows)) {
    $output .= "<tbody>\n";
    $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 .= ' <tr' . drupal_attributes($attributes) . '>';
      $i = 0;
      foreach ($cells as $cell) {
        $cell = tablesort_cell($cell, $header, $ts, $i++);
        $output .= _theme_table_cell($cell);
      }
      $output .= " </tr>\n";
    }
    $output .= "</tbody>\n";
  }
  $output .= "</table>\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 = '<h2 class="title">' . $title . '</h2><div>' . $content . '</div>';
  return $output;
}

/**
 * Return a themed block.
 *
 * You can style your blocks by defining .block (all blocks),
 * .block-<i>module</i> (all blocks of module <i>module</i>), and
 * \#block-<i>module</i>-<i>delta</i> (specific block of module <i>module</i>
 * with delta <i>delta</i>) 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
 *   <i>module</i>_block('view') ($block->subject, $block->content, ...).
 * @return
 *   A string containing the block output.
 */
function theme_block($block) {
  $output = "<div class=\"block block-{$block->module}\" id=\"block-{$block->module}-{$block->delta}\">\n";
  $output .= " <h2 class=\"title\">{$block->subject}</h2>\n";
  $output .= " <div class=\"content\">{$block->content}</div>\n";
  $output .= "</div>\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 ' <span class="marker">' . t('new') . '</span>';
    }
    else {
      if ($type == MARK_UPDATED) {
        return ' <span class="marker">' . t('updated') . '</span>';
      }
    }
  }
}

/**
 * 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 $type
 *   The type of list to return (e.g. "ul", "ol")
 * @param $attributes
 *   The attributes applied to the list element.
 * @return
 *   A string containing the list output.
 */
function theme_item_list($items = array(), $title = NULL, $type = 'ul', $attributes = NULL) {
  $output = '<div class="item-list">';
  if (isset($title)) {
    $output .= '<h3>' . $title . '</h3>';
  }
  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 .= '<li' . drupal_attributes($attributes) . '>' . $data . '</li>';
    }
    $output .= "</{$type}>";
  }
  $output .= '</div>';
  return $output;
}

/**
 * Returns code that emits the 'more help'-link.
 */
function theme_more_help_link($url) {
  return '<div class="more-help-link">' . t('[<a href="@link">more help...</a>]', array(
    '@link' => check_url($url),
  )) . '</div>';
}

/**
 * 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 '<a href="' . check_url($url) . '" class="xml-icon">' . $image . '</a>';
  }
}

/**
 * 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 '<a href="' . check_url($url) . '" class="feed-icon">' . $image . '</a>';
  }
}

/**
 * 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 == <i>module</i>_<i>delta</i>
      $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 = '<div id="progress" class="progress">';
  $output .= '<div class="percentage">' . $percent . '%</div>';
  $output .= '<div class="status">' . $message . '</div>';
  $output .= '<div class="bar"><div class="filled" style="width: ' . $percent . '%"></div></div>';
  $output .= '</div>';
  return $output;
}

/**
 * @} End of "defgroup themeable".
 */
function _theme_table_cell($cell, $header = FALSE) {
  $attributes = '';
  if (is_array($cell)) {
    $data = $cell['data'];
    $header |= isset($cell['header']);
    unset($cell['data']);
    unset($cell['header']);
    $attributes = drupal_attributes($cell);
  }
  else {
    $data = $cell;
  }
  if ($header) {
    $output = "<th{$attributes}>{$data}</th>";
  }
  else {
    $output = "<td{$attributes}>{$data}</td>";
  }
  return $output;
}

Functions

Namesort ascending Description
_theme_table_cell End of "defgroup themeable".
theme_xml_icon Return code that emits an XML icon.
theme_username Format a username.
theme_table_select_header_cell Returns a header cell for tables that have a select all functionality.
theme_tablesort_indicator Return a themed sort icon.
theme_table Return a themed table.
theme_submenu Return a themed submenu, typically displayed under the tabs.
theme_status_messages Return a themed set of status and/or error messages. The messages are grouped by type.
theme_progress_bar
theme_placeholder Formats text for emphasized display in a placeholder inside a sentence. Used automatically by t().
theme_page Return an entire Drupal page displaying the supplied content.
theme_node Return a themed node.
theme_more_help_link Returns code that emits the 'more help'-link.
theme_mark Return a themed marker, useful for marking new or updated content.
theme_maintenance_page
theme_links Return a themed set of links.
theme_item_list Return a themed list of items.
theme_install_page
theme_image Return a themed image.
theme_help Return a themed help message.
theme_get_settings Retrieve an associative array containing the settings for a theme.
theme_get_setting 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.
theme_get_function Determine if a theme function exists, and if so return which one was found.
theme_feed_icon Return code that emits an feed icon.
theme_closure Execute hook_footer() which is run at the end of the page right before the close of the body tag.
theme_breadcrumb Return a themed breadcrumb trail.
theme_box Return a themed box.
theme_blocks Return a set of blocks available for the current user.
theme_block Return a themed block.
theme Generate the themed representation of a Drupal object.
path_to_theme Return the path to the currently selected theme.
path_to_engine Return the path to the currently selected engine.
list_theme_engines Provides a list of currently available theme engines
list_themes Provides a list of currently available themes.
init_theme Initialize the theme system by loading the theme.

Constants

Namesort ascending Description
MARK_UPDATED
MARK_READ @name Content markers
MARK_NEW