annotate includes/theme.inc @ 5:2427550111ae 6.2

Drupal 6.2
author Franck Deroche <webmaster@defr.org>
date Tue, 23 Dec 2008 14:30:08 +0100
parents c1f4ac30525a
children fff6d4c8c043
rev   line source
webmaster@1 1 <?php
webmaster@5 2 // $Id: theme.inc,v 1.415.2.2 2008/03/25 11:55:08 goba Exp $
webmaster@1 3
webmaster@1 4 /**
webmaster@1 5 * @file
webmaster@1 6 * The theme system, which controls the output of Drupal.
webmaster@1 7 *
webmaster@1 8 * The theme system allows for nearly all output of the Drupal system to be
webmaster@1 9 * customized by user themes.
webmaster@1 10 *
webmaster@1 11 * @see <a href="http://drupal.org/node/253">Theme system</a>
webmaster@1 12 * @see themeable
webmaster@1 13 */
webmaster@1 14
webmaster@1 15 /**
webmaster@1 16 * @name Content markers
webmaster@1 17 * @{
webmaster@1 18 * Markers used by theme_mark() and node_mark() to designate content.
webmaster@1 19 * @see theme_mark(), node_mark()
webmaster@1 20 */
webmaster@1 21 define('MARK_READ', 0);
webmaster@1 22 define('MARK_NEW', 1);
webmaster@1 23 define('MARK_UPDATED', 2);
webmaster@1 24 /**
webmaster@1 25 * @} End of "Content markers".
webmaster@1 26 */
webmaster@1 27
webmaster@1 28 /**
webmaster@1 29 * Initialize the theme system by loading the theme.
webmaster@1 30 */
webmaster@1 31 function init_theme() {
webmaster@1 32 global $theme, $user, $custom_theme, $theme_key;
webmaster@1 33
webmaster@1 34 // If $theme is already set, assume the others are set, too, and do nothing
webmaster@1 35 if (isset($theme)) {
webmaster@1 36 return;
webmaster@1 37 }
webmaster@1 38
webmaster@1 39 drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);
webmaster@1 40 $themes = list_themes();
webmaster@1 41
webmaster@1 42 // Only select the user selected theme if it is available in the
webmaster@1 43 // list of enabled themes.
webmaster@1 44 $theme = !empty($user->theme) && !empty($themes[$user->theme]->status) ? $user->theme : variable_get('theme_default', 'garland');
webmaster@1 45
webmaster@1 46 // Allow modules to override the present theme... only select custom theme
webmaster@1 47 // if it is available in the list of installed themes.
webmaster@1 48 $theme = $custom_theme && $themes[$custom_theme] ? $custom_theme : $theme;
webmaster@1 49
webmaster@1 50 // Store the identifier for retrieving theme settings with.
webmaster@1 51 $theme_key = $theme;
webmaster@1 52
webmaster@1 53 // Find all our ancestor themes and put them in an array.
webmaster@1 54 $base_theme = array();
webmaster@1 55 $ancestor = $theme;
webmaster@1 56 while ($ancestor && isset($themes[$ancestor]->base_theme)) {
webmaster@1 57 $base_theme[] = $new_base_theme = $themes[$themes[$ancestor]->base_theme];
webmaster@1 58 $ancestor = $themes[$ancestor]->base_theme;
webmaster@1 59 }
webmaster@1 60 _init_theme($themes[$theme], array_reverse($base_theme));
webmaster@1 61 }
webmaster@1 62
webmaster@1 63 /**
webmaster@1 64 * Initialize the theme system given already loaded information. This
webmaster@1 65 * function is useful to initialize a theme when no database is present.
webmaster@1 66 *
webmaster@1 67 * @param $theme
webmaster@1 68 * An object with the following information:
webmaster@1 69 * filename
webmaster@1 70 * The .info file for this theme. The 'path' to
webmaster@1 71 * the theme will be in this file's directory. (Required)
webmaster@1 72 * owner
webmaster@1 73 * The path to the .theme file or the .engine file to load for
webmaster@1 74 * the theme. (Required)
webmaster@1 75 * stylesheet
webmaster@1 76 * The primary stylesheet for the theme. (Optional)
webmaster@1 77 * engine
webmaster@1 78 * The name of theme engine to use. (Optional)
webmaster@1 79 * @param $base_theme
webmaster@1 80 * An optional array of objects that represent the 'base theme' if the
webmaster@1 81 * theme is meant to be derivative of another theme. It requires
webmaster@1 82 * the same information as the $theme object. It should be in
webmaster@1 83 * 'oldest first' order, meaning the top level of the chain will
webmaster@1 84 * be first.
webmaster@1 85 * @param $registry_callback
webmaster@1 86 * The callback to invoke to set the theme registry.
webmaster@1 87 */
webmaster@1 88 function _init_theme($theme, $base_theme = array(), $registry_callback = '_theme_load_registry') {
webmaster@1 89 global $theme_info, $base_theme_info, $theme_engine, $theme_path;
webmaster@1 90 $theme_info = $theme;
webmaster@1 91 $base_theme_info = $base_theme;
webmaster@1 92
webmaster@1 93 $theme_path = dirname($theme->filename);
webmaster@1 94
webmaster@1 95 // Prepare stylesheets from this theme as well as all ancestor themes.
webmaster@1 96 // We work it this way so that we can have child themes override parent
webmaster@1 97 // theme stylesheets easily.
webmaster@1 98 $final_stylesheets = array();
webmaster@1 99
webmaster@1 100 // Grab stylesheets from base theme
webmaster@1 101 foreach ($base_theme as $base) {
webmaster@1 102 if (!empty($base->stylesheets)) {
webmaster@1 103 foreach ($base->stylesheets as $media => $stylesheets) {
webmaster@1 104 foreach ($stylesheets as $name => $stylesheet) {
webmaster@1 105 $final_stylesheets[$media][$name] = $stylesheet;
webmaster@1 106 }
webmaster@1 107 }
webmaster@1 108 }
webmaster@1 109 }
webmaster@1 110
webmaster@1 111 // Add stylesheets used by this theme.
webmaster@1 112 if (!empty($theme->stylesheets)) {
webmaster@1 113 foreach ($theme->stylesheets as $media => $stylesheets) {
webmaster@1 114 foreach ($stylesheets as $name => $stylesheet) {
webmaster@1 115 $final_stylesheets[$media][$name] = $stylesheet;
webmaster@1 116 }
webmaster@1 117 }
webmaster@1 118 }
webmaster@1 119
webmaster@1 120 // And now add the stylesheets properly
webmaster@1 121 foreach ($final_stylesheets as $media => $stylesheets) {
webmaster@1 122 foreach ($stylesheets as $stylesheet) {
webmaster@1 123 drupal_add_css($stylesheet, 'theme', $media);
webmaster@1 124 }
webmaster@1 125 }
webmaster@1 126
webmaster@1 127 // Do basically the same as the above for scripts
webmaster@1 128 $final_scripts = array();
webmaster@1 129
webmaster@1 130 // Grab scripts from base theme
webmaster@1 131 foreach ($base_theme as $base) {
webmaster@1 132 if (!empty($base->scripts)) {
webmaster@1 133 foreach ($base->scripts as $name => $script) {
webmaster@1 134 $final_scripts[$name] = $script;
webmaster@1 135 }
webmaster@1 136 }
webmaster@1 137 }
webmaster@1 138
webmaster@1 139 // Add scripts used by this theme.
webmaster@1 140 if (!empty($theme->scripts)) {
webmaster@1 141 foreach ($theme->scripts as $name => $script) {
webmaster@1 142 $final_scripts[$name] = $script;
webmaster@1 143 }
webmaster@1 144 }
webmaster@1 145
webmaster@1 146 // Add scripts used by this theme.
webmaster@1 147 foreach ($final_scripts as $script) {
webmaster@1 148 drupal_add_js($script, 'theme');
webmaster@1 149 }
webmaster@1 150
webmaster@1 151 $theme_engine = NULL;
webmaster@1 152
webmaster@1 153 // Initialize the theme.
webmaster@1 154 if (isset($theme->engine)) {
webmaster@1 155 // Include the engine.
webmaster@1 156 include_once './'. $theme->owner;
webmaster@1 157
webmaster@1 158 $theme_engine = $theme->engine;
webmaster@1 159 if (function_exists($theme_engine .'_init')) {
webmaster@1 160 foreach ($base_theme as $base) {
webmaster@1 161 call_user_func($theme_engine .'_init', $base);
webmaster@1 162 }
webmaster@1 163 call_user_func($theme_engine .'_init', $theme);
webmaster@1 164 }
webmaster@1 165 }
webmaster@1 166 else {
webmaster@1 167 // include non-engine theme files
webmaster@1 168 foreach ($base_theme as $base) {
webmaster@1 169 // Include the theme file or the engine.
webmaster@1 170 if (!empty($base->owner)) {
webmaster@1 171 include_once './'. $base->owner;
webmaster@1 172 }
webmaster@1 173 }
webmaster@1 174 // and our theme gets one too.
webmaster@1 175 if (!empty($theme->owner)) {
webmaster@1 176 include_once './'. $theme->owner;
webmaster@1 177 }
webmaster@1 178 }
webmaster@1 179
webmaster@1 180 $registry_callback($theme, $base_theme, $theme_engine);
webmaster@1 181 }
webmaster@1 182
webmaster@1 183 /**
webmaster@1 184 * Retrieve the stored theme registry. If the theme registry is already
webmaster@1 185 * in memory it will be returned; otherwise it will attempt to load the
webmaster@1 186 * registry from cache. If this fails, it will construct the registry and
webmaster@1 187 * cache it.
webmaster@1 188 */
webmaster@1 189 function theme_get_registry($registry = NULL) {
webmaster@1 190 static $theme_registry = NULL;
webmaster@1 191 if (isset($registry)) {
webmaster@1 192 $theme_registry = $registry;
webmaster@1 193 }
webmaster@1 194
webmaster@1 195 return $theme_registry;
webmaster@1 196 }
webmaster@1 197
webmaster@1 198 /**
webmaster@1 199 * Store the theme registry in memory.
webmaster@1 200 */
webmaster@1 201 function _theme_set_registry($registry) {
webmaster@1 202 // Pass through for setting of static variable.
webmaster@1 203 return theme_get_registry($registry);
webmaster@1 204 }
webmaster@1 205
webmaster@1 206 /**
webmaster@1 207 * Get the theme_registry cache from the database; if it doesn't exist, build
webmaster@1 208 * it.
webmaster@1 209 *
webmaster@1 210 * @param $theme
webmaster@1 211 * The loaded $theme object.
webmaster@1 212 * @param $base_theme
webmaster@1 213 * An array of loaded $theme objects representing the ancestor themes in
webmaster@1 214 * oldest first order.
webmaster@1 215 * @param theme_engine
webmaster@1 216 * The name of the theme engine.
webmaster@1 217 */
webmaster@1 218 function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL) {
webmaster@1 219 // Check the theme registry cache; if it exists, use it.
webmaster@1 220 $cache = cache_get("theme_registry:$theme->name", 'cache');
webmaster@1 221 if (isset($cache->data)) {
webmaster@1 222 $registry = $cache->data;
webmaster@1 223 }
webmaster@1 224 else {
webmaster@1 225 // If not, build one and cache it.
webmaster@1 226 $registry = _theme_build_registry($theme, $base_theme, $theme_engine);
webmaster@1 227 _theme_save_registry($theme, $registry);
webmaster@1 228 }
webmaster@1 229 _theme_set_registry($registry);
webmaster@1 230 }
webmaster@1 231
webmaster@1 232 /**
webmaster@1 233 * Write the theme_registry cache into the database.
webmaster@1 234 */
webmaster@1 235 function _theme_save_registry($theme, $registry) {
webmaster@1 236 cache_set("theme_registry:$theme->name", $registry);
webmaster@1 237 }
webmaster@1 238
webmaster@1 239 /**
webmaster@1 240 * Force the system to rebuild the theme registry; this should be called
webmaster@1 241 * when modules are added to the system, or when a dynamic system needs
webmaster@1 242 * to add more theme hooks.
webmaster@1 243 */
webmaster@1 244 function drupal_rebuild_theme_registry() {
webmaster@1 245 cache_clear_all('theme_registry', 'cache', TRUE);
webmaster@1 246 }
webmaster@1 247
webmaster@1 248 /**
webmaster@1 249 * Process a single invocation of the theme hook. $type will be one
webmaster@1 250 * of 'module', 'theme_engine' or 'theme' and it tells us some
webmaster@1 251 * important information.
webmaster@1 252 *
webmaster@1 253 * Because $cache is a reference, the cache will be continually
webmaster@1 254 * expanded upon; new entries will replace old entries in the
webmaster@1 255 * array_merge, but we are careful to ensure some data is carried
webmaster@1 256 * forward, such as the arguments a theme hook needs.
webmaster@1 257 *
webmaster@1 258 * An override flag can be set for preprocess functions. When detected the
webmaster@1 259 * cached preprocessors for the hook will not be merged with the newly set.
webmaster@1 260 * This can be useful to themes and theme engines by giving them more control
webmaster@1 261 * over how and when the preprocess functions are run.
webmaster@1 262 */
webmaster@1 263 function _theme_process_registry(&$cache, $name, $type, $theme, $path) {
webmaster@1 264 $function = $name .'_theme';
webmaster@1 265 if (function_exists($function)) {
webmaster@1 266 $result = $function($cache, $type, $theme, $path);
webmaster@1 267
webmaster@1 268 foreach ($result as $hook => $info) {
webmaster@1 269 $result[$hook]['type'] = $type;
webmaster@1 270 $result[$hook]['theme path'] = $path;
webmaster@1 271 // if function and file are left out, default to standard naming
webmaster@1 272 // conventions.
webmaster@1 273 if (!isset($info['template']) && !isset($info['function'])) {
webmaster@1 274 $result[$hook]['function'] = ($type == 'module' ? 'theme_' : $name .'_') . $hook;
webmaster@1 275 }
webmaster@1 276 // If a path is set in the info, use what was set. Otherwise use the
webmaster@1 277 // default path. This is mostly so system.module can declare theme
webmaster@1 278 // functions on behalf of core .include files.
webmaster@1 279 // All files are included to be safe. Conditionally included
webmaster@1 280 // files can prevent them from getting registered.
webmaster@1 281 if (isset($info['file']) && !isset($info['path'])) {
webmaster@1 282 $result[$hook]['file'] = $path .'/'. $info['file'];
webmaster@1 283 include_once($result[$hook]['file']);
webmaster@1 284 }
webmaster@1 285 elseif (isset($info['file']) && isset($info['path'])) {
webmaster@1 286 include_once($info['path'] .'/'. $info['file']);
webmaster@1 287 }
webmaster@1 288
webmaster@1 289 if (isset($info['template']) && !isset($info['path'])) {
webmaster@1 290 $result[$hook]['template'] = $path .'/'. $info['template'];
webmaster@1 291 }
webmaster@1 292 // If 'arguments' have been defined previously, carry them forward.
webmaster@1 293 // This should happen if a theme overrides a Drupal defined theme
webmaster@1 294 // function, for example.
webmaster@1 295 if (!isset($info['arguments']) && isset($cache[$hook])) {
webmaster@1 296 $result[$hook]['arguments'] = $cache[$hook]['arguments'];
webmaster@1 297 }
webmaster@1 298 // Likewise with theme paths. These are used for template naming suggestions.
webmaster@1 299 // Theme implementations can occur in multiple paths. Suggestions should follow.
webmaster@1 300 if (!isset($info['theme paths']) && isset($cache[$hook])) {
webmaster@1 301 $result[$hook]['theme paths'] = $cache[$hook]['theme paths'];
webmaster@1 302 }
webmaster@1 303 // Check for sub-directories.
webmaster@1 304 $result[$hook]['theme paths'][] = isset($info['path']) ? $info['path'] : $path;
webmaster@1 305
webmaster@1 306 // Check for default _preprocess_ functions. Ensure arrayness.
webmaster@1 307 if (!isset($info['preprocess functions']) || !is_array($info['preprocess functions'])) {
webmaster@1 308 $info['preprocess functions'] = array();
webmaster@1 309 $prefixes = array();
webmaster@1 310 if ($type == 'module') {
webmaster@1 311 // Default preprocessor prefix.
webmaster@1 312 $prefixes[] = 'template';
webmaster@1 313 // Add all modules so they can intervene with their own preprocessors. This allows them
webmaster@1 314 // to provide preprocess functions even if they are not the owner of the current hook.
webmaster@1 315 $prefixes += module_list();
webmaster@1 316 }
webmaster@1 317 elseif ($type == 'theme_engine') {
webmaster@1 318 // Theme engines get an extra set that come before the normally named preprocessors.
webmaster@1 319 $prefixes[] = $name .'_engine';
webmaster@1 320 // The theme engine also registers on behalf of the theme. The theme or engine name can be used.
webmaster@1 321 $prefixes[] = $name;
webmaster@1 322 $prefixes[] = $theme;
webmaster@1 323 }
webmaster@1 324 else {
webmaster@1 325 // This applies when the theme manually registers their own preprocessors.
webmaster@1 326 $prefixes[] = $name;
webmaster@1 327 }
webmaster@1 328
webmaster@1 329 foreach ($prefixes as $prefix) {
webmaster@1 330 if (function_exists($prefix .'_preprocess')) {
webmaster@1 331 $info['preprocess functions'][] = $prefix .'_preprocess';
webmaster@1 332 }
webmaster@1 333 if (function_exists($prefix .'_preprocess_'. $hook)) {
webmaster@1 334 $info['preprocess functions'][] = $prefix .'_preprocess_'. $hook;
webmaster@1 335 }
webmaster@1 336 }
webmaster@1 337 }
webmaster@1 338 // Check for the override flag and prevent the cached preprocess functions from being used.
webmaster@1 339 // This allows themes or theme engines to remove preprocessors set earlier in the registry build.
webmaster@1 340 if (!empty($info['override preprocess functions'])) {
webmaster@1 341 // Flag not needed inside the registry.
webmaster@1 342 unset($result[$hook]['override preprocess functions']);
webmaster@1 343 }
webmaster@1 344 elseif (isset($cache[$hook]['preprocess functions']) && is_array($cache[$hook]['preprocess functions'])) {
webmaster@1 345 $info['preprocess functions'] = array_merge($cache[$hook]['preprocess functions'], $info['preprocess functions']);
webmaster@1 346 }
webmaster@1 347 $result[$hook]['preprocess functions'] = $info['preprocess functions'];
webmaster@1 348 }
webmaster@1 349
webmaster@1 350 // Merge the newly created theme hooks into the existing cache.
webmaster@1 351 $cache = array_merge($cache, $result);
webmaster@1 352 }
webmaster@1 353 }
webmaster@1 354
webmaster@1 355 /**
webmaster@1 356 * Rebuild the hook theme_registry cache.
webmaster@1 357 *
webmaster@1 358 * @param $theme
webmaster@1 359 * The loaded $theme object.
webmaster@1 360 * @param $base_theme
webmaster@1 361 * An array of loaded $theme objects representing the ancestor themes in
webmaster@1 362 * oldest first order.
webmaster@1 363 * @param theme_engine
webmaster@1 364 * The name of the theme engine.
webmaster@1 365 */
webmaster@1 366 function _theme_build_registry($theme, $base_theme, $theme_engine) {
webmaster@1 367 $cache = array();
webmaster@1 368 // First, process the theme hooks advertised by modules. This will
webmaster@1 369 // serve as the basic registry.
webmaster@1 370 foreach (module_implements('theme') as $module) {
webmaster@1 371 _theme_process_registry($cache, $module, 'module', $module, drupal_get_path('module', $module));
webmaster@1 372 }
webmaster@1 373
webmaster@1 374 // Process each base theme.
webmaster@1 375 foreach ($base_theme as $base) {
webmaster@1 376 // If the theme uses a theme engine, process its hooks.
webmaster@1 377 $base_path = dirname($base->filename);
webmaster@1 378 if ($theme_engine) {
webmaster@1 379 _theme_process_registry($cache, $theme_engine, 'base_theme_engine', $base->name, $base_path);
webmaster@1 380 }
webmaster@1 381 _theme_process_registry($cache, $base->name, 'base_theme', $base->name, $base_path);
webmaster@1 382 }
webmaster@1 383
webmaster@1 384 // And then the same thing, but for the theme.
webmaster@1 385 if ($theme_engine) {
webmaster@1 386 _theme_process_registry($cache, $theme_engine, 'theme_engine', $theme->name, dirname($theme->filename));
webmaster@1 387 }
webmaster@1 388
webmaster@1 389 // Finally, hooks provided by the theme itself.
webmaster@1 390 _theme_process_registry($cache, $theme->name, 'theme', $theme->name, dirname($theme->filename));
webmaster@1 391
webmaster@1 392 // Let modules alter the registry
webmaster@1 393 drupal_alter('theme_registry', $cache);
webmaster@1 394 return $cache;
webmaster@1 395 }
webmaster@1 396
webmaster@1 397 /**
webmaster@1 398 * Provides a list of currently available themes.
webmaster@1 399 *
webmaster@1 400 * If the database is active then it will be retrieved from the database.
webmaster@1 401 * Otherwise it will retrieve a new list.
webmaster@1 402 *
webmaster@1 403 * @param $refresh
webmaster@1 404 * Whether to reload the list of themes from the database.
webmaster@1 405 * @return
webmaster@1 406 * An array of the currently available themes.
webmaster@1 407 */
webmaster@1 408 function list_themes($refresh = FALSE) {
webmaster@1 409 static $list = array();
webmaster@1 410
webmaster@1 411 if ($refresh) {
webmaster@1 412 $list = array();
webmaster@1 413 }
webmaster@1 414
webmaster@1 415 if (empty($list)) {
webmaster@1 416 $list = array();
webmaster@1 417 $themes = array();
webmaster@1 418 // Extract from the database only when it is available.
webmaster@1 419 // Also check that the site is not in the middle of an install or update.
webmaster@1 420 if (db_is_active() && !defined('MAINTENANCE_MODE')) {
webmaster@1 421 $result = db_query("SELECT * FROM {system} WHERE type = '%s'", 'theme');
webmaster@1 422 while ($theme = db_fetch_object($result)) {
webmaster@1 423 if (file_exists($theme->filename)) {
webmaster@1 424 $theme->info = unserialize($theme->info);
webmaster@1 425 $themes[] = $theme;
webmaster@1 426 }
webmaster@1 427 }
webmaster@1 428 }
webmaster@1 429 else {
webmaster@1 430 // Scan the installation when the database should not be read.
webmaster@1 431 $themes = _system_theme_data();
webmaster@1 432 }
webmaster@1 433
webmaster@1 434 foreach ($themes as $theme) {
webmaster@1 435 foreach ($theme->info['stylesheets'] as $media => $stylesheets) {
webmaster@1 436 foreach ($stylesheets as $stylesheet => $path) {
webmaster@1 437 if (file_exists($path)) {
webmaster@1 438 $theme->stylesheets[$media][$stylesheet] = $path;
webmaster@1 439 }
webmaster@1 440 }
webmaster@1 441 }
webmaster@1 442 foreach ($theme->info['scripts'] as $script => $path) {
webmaster@1 443 if (file_exists($path)) {
webmaster@1 444 $theme->scripts[$script] = $path;
webmaster@1 445 }
webmaster@1 446 }
webmaster@1 447 if (isset($theme->info['engine'])) {
webmaster@1 448 $theme->engine = $theme->info['engine'];
webmaster@1 449 }
webmaster@1 450 if (isset($theme->info['base theme'])) {
webmaster@1 451 $theme->base_theme = $theme->info['base theme'];
webmaster@1 452 }
webmaster@1 453 // Status is normally retrieved from the database. Add zero values when
webmaster@1 454 // read from the installation directory to prevent notices.
webmaster@1 455 if (!isset($theme->status)) {
webmaster@1 456 $theme->status = 0;
webmaster@1 457 }
webmaster@1 458 $list[$theme->name] = $theme;
webmaster@1 459 }
webmaster@1 460 }
webmaster@1 461
webmaster@1 462 return $list;
webmaster@1 463 }
webmaster@1 464
webmaster@1 465 /**
webmaster@1 466 * Generate the themed output.
webmaster@1 467 *
webmaster@1 468 * All requests for theme hooks must go through this function. It examines
webmaster@1 469 * the request and routes it to the appropriate theme function. The theme
webmaster@1 470 * registry is checked to determine which implementation to use, which may
webmaster@1 471 * be a function or a template.
webmaster@1 472 *
webmaster@1 473 * If the implementation is a function, it is executed and its return value
webmaster@1 474 * passed along.
webmaster@1 475 *
webmaster@1 476 * If the implementation is a template, the arguments are converted to a
webmaster@1 477 * $variables array. This array is then modified by the module implementing
webmaster@1 478 * the hook, theme engine (if applicable) and the theme. The following
webmaster@1 479 * functions may be used to modify the $variables array. They are processed in
webmaster@1 480 * this order when available:
webmaster@1 481 *
webmaster@1 482 * - template_preprocess(&$variables)
webmaster@1 483 * This sets a default set of variables for all template implementations.
webmaster@1 484 *
webmaster@1 485 * - template_preprocess_HOOK(&$variables)
webmaster@1 486 * This is the first preprocessor called specific to the hook; it should be
webmaster@1 487 * implemented by the module that registers it.
webmaster@1 488 *
webmaster@1 489 * - MODULE_preprocess(&$variables)
webmaster@1 490 * This will be called for all templates; it should only be used if there
webmaster@1 491 * is a real need. It's purpose is similar to template_preprocess().
webmaster@1 492 *
webmaster@1 493 * - MODULE_preprocess_HOOK(&$variables)
webmaster@1 494 * This is for modules that want to alter or provide extra variables for
webmaster@1 495 * theming hooks not registered to itself. For example, if a module named
webmaster@1 496 * "foo" wanted to alter the $submitted variable for the hook "node" a
webmaster@1 497 * preprocess function of foo_preprocess_node() can be created to intercept
webmaster@1 498 * and alter the variable.
webmaster@1 499 *
webmaster@1 500 * - ENGINE_engine_preprocess(&$variables)
webmaster@1 501 * This function should only be implemented by theme engines and exists
webmaster@1 502 * so that it can set necessary variables for all hooks.
webmaster@1 503 *
webmaster@1 504 * - ENGINE_engine_preprocess_HOOK(&$variables)
webmaster@1 505 * This is the same as the previous function, but it is called for a single
webmaster@1 506 * theming hook.
webmaster@1 507 *
webmaster@1 508 * - ENGINE_preprocess(&$variables)
webmaster@1 509 * This is meant to be used by themes that utilize a theme engine. It is
webmaster@1 510 * provided so that the preprocessor is not locked into a specific theme.
webmaster@1 511 * This makes it easy to share and transport code but theme authors must be
webmaster@1 512 * careful to prevent fatal re-declaration errors when using sub-themes that
webmaster@1 513 * have their own preprocessor named exactly the same as its base theme. In
webmaster@1 514 * the default theme engine (PHPTemplate), sub-themes will load their own
webmaster@1 515 * template.php file in addition to the one used for its parent theme. This
webmaster@1 516 * increases the risk for these errors. A good practice is to use the engine
webmaster@1 517 * name for the base theme and the theme name for the sub-themes to minimize
webmaster@1 518 * this possibility.
webmaster@1 519 *
webmaster@1 520 * - ENGINE_preprocess_HOOK(&$variables)
webmaster@1 521 * The same applies from the previous function, but it is called for a
webmaster@1 522 * specific hook.
webmaster@1 523 *
webmaster@1 524 * - THEME_preprocess(&$variables)
webmaster@1 525 * These functions are based upon the raw theme; they should primarily be
webmaster@1 526 * used by themes that do not use an engine or by sub-themes. It serves the
webmaster@1 527 * same purpose as ENGINE_preprocess().
webmaster@1 528 *
webmaster@1 529 * - THEME_preprocess_HOOK(&$variables)
webmaster@1 530 * The same applies from the previous function, but it is called for a
webmaster@1 531 * specific hook.
webmaster@1 532 *
webmaster@1 533 * There are two special variables that these hooks can set:
webmaster@1 534 * 'template_file' and 'template_files'. These will be merged together
webmaster@1 535 * to form a list of 'suggested' alternate template files to use, in
webmaster@1 536 * reverse order of priority. template_file will always be a higher
webmaster@1 537 * priority than items in template_files. theme() will then look for these
webmaster@1 538 * files, one at a time, and use the first one
webmaster@1 539 * that exists.
webmaster@1 540 * @param $hook
webmaster@1 541 * The name of the theme function to call. May be an array, in which
webmaster@1 542 * case the first hook that actually has an implementation registered
webmaster@1 543 * will be used. This can be used to choose 'fallback' theme implementations,
webmaster@1 544 * so that if the specific theme hook isn't implemented anywhere, a more
webmaster@1 545 * generic one will be used. This can allow themes to create specific theme
webmaster@1 546 * implementations for named objects.
webmaster@1 547 * @param ...
webmaster@1 548 * Additional arguments to pass along to the theme function.
webmaster@1 549 * @return
webmaster@1 550 * An HTML string that generates the themed output.
webmaster@1 551 */
webmaster@1 552 function theme() {
webmaster@1 553 $args = func_get_args();
webmaster@1 554 $hook = array_shift($args);
webmaster@1 555
webmaster@1 556 static $hooks = NULL;
webmaster@1 557 if (!isset($hooks)) {
webmaster@1 558 init_theme();
webmaster@1 559 $hooks = theme_get_registry();
webmaster@1 560 }
webmaster@1 561
webmaster@1 562 if (is_array($hook)) {
webmaster@1 563 foreach ($hook as $candidate) {
webmaster@1 564 if (isset($hooks[$candidate])) {
webmaster@1 565 break;
webmaster@1 566 }
webmaster@1 567 }
webmaster@1 568 $hook = $candidate;
webmaster@1 569 }
webmaster@1 570
webmaster@1 571 if (!isset($hooks[$hook])) {
webmaster@1 572 return;
webmaster@1 573 }
webmaster@1 574
webmaster@1 575 $info = $hooks[$hook];
webmaster@1 576 global $theme_path;
webmaster@1 577 $temp = $theme_path;
webmaster@1 578 // point path_to_theme() to the currently used theme path:
webmaster@1 579 $theme_path = $hooks[$hook]['theme path'];
webmaster@1 580
webmaster@1 581 // Include a file if the theme function or preprocess function is held elsewhere.
webmaster@1 582 if (!empty($info['file'])) {
webmaster@1 583 $include_file = $info['file'];
webmaster@1 584 if (isset($info['path'])) {
webmaster@1 585 $include_file = $info['path'] .'/'. $include_file;
webmaster@1 586 }
webmaster@1 587 include_once($include_file);
webmaster@1 588 }
webmaster@1 589 if (isset($info['function'])) {
webmaster@1 590 // The theme call is a function.
webmaster@1 591 $output = call_user_func_array($info['function'], $args);
webmaster@1 592 }
webmaster@1 593 else {
webmaster@1 594 // The theme call is a template.
webmaster@1 595 $variables = array(
webmaster@1 596 'template_files' => array()
webmaster@1 597 );
webmaster@1 598 if (!empty($info['arguments'])) {
webmaster@1 599 $count = 0;
webmaster@1 600 foreach ($info['arguments'] as $name => $default) {
webmaster@1 601 $variables[$name] = isset($args[$count]) ? $args[$count] : $default;
webmaster@1 602 $count++;
webmaster@1 603 }
webmaster@1 604 }
webmaster@1 605
webmaster@1 606 // default render function and extension.
webmaster@1 607 $render_function = 'theme_render_template';
webmaster@1 608 $extension = '.tpl.php';
webmaster@1 609
webmaster@1 610 // Run through the theme engine variables, if necessary
webmaster@1 611 global $theme_engine;
webmaster@1 612 if (isset($theme_engine)) {
webmaster@1 613 // If theme or theme engine is implementing this, it may have
webmaster@1 614 // a different extension and a different renderer.
webmaster@1 615 if ($hooks[$hook]['type'] != 'module') {
webmaster@1 616 if (function_exists($theme_engine .'_render_template')) {
webmaster@1 617 $render_function = $theme_engine .'_render_template';
webmaster@1 618 }
webmaster@1 619 $extension_function = $theme_engine .'_extension';
webmaster@1 620 if (function_exists($extension_function)) {
webmaster@1 621 $extension = $extension_function();
webmaster@1 622 }
webmaster@1 623 }
webmaster@1 624 }
webmaster@1 625
webmaster@1 626 if (isset($info['preprocess functions']) && is_array($info['preprocess functions'])) {
webmaster@1 627 // This construct ensures that we can keep a reference through
webmaster@1 628 // call_user_func_array.
webmaster@1 629 $args = array(&$variables, $hook);
webmaster@1 630 foreach ($info['preprocess functions'] as $preprocess_function) {
webmaster@1 631 if (function_exists($preprocess_function)) {
webmaster@1 632 call_user_func_array($preprocess_function, $args);
webmaster@1 633 }
webmaster@1 634 }
webmaster@1 635 }
webmaster@1 636
webmaster@1 637 // Get suggestions for alternate templates out of the variables
webmaster@1 638 // that were set. This lets us dynamically choose a template
webmaster@1 639 // from a list. The order is FILO, so this array is ordered from
webmaster@1 640 // least appropriate first to most appropriate last.
webmaster@1 641 $suggestions = array();
webmaster@1 642
webmaster@1 643 if (isset($variables['template_files'])) {
webmaster@1 644 $suggestions = $variables['template_files'];
webmaster@1 645 }
webmaster@1 646 if (isset($variables['template_file'])) {
webmaster@1 647 $suggestions[] = $variables['template_file'];
webmaster@1 648 }
webmaster@1 649
webmaster@1 650 if ($suggestions) {
webmaster@1 651 $template_file = drupal_discover_template($info['theme paths'], $suggestions, $extension);
webmaster@1 652 }
webmaster@1 653
webmaster@1 654 if (empty($template_file)) {
webmaster@1 655 $template_file = $hooks[$hook]['template'] . $extension;
webmaster@1 656 if (isset($hooks[$hook]['path'])) {
webmaster@1 657 $template_file = $hooks[$hook]['path'] .'/'. $template_file;
webmaster@1 658 }
webmaster@1 659 }
webmaster@1 660 $output = $render_function($template_file, $variables);
webmaster@1 661 }
webmaster@1 662 // restore path_to_theme()
webmaster@1 663 $theme_path = $temp;
webmaster@1 664 return $output;
webmaster@1 665 }
webmaster@1 666
webmaster@1 667 /**
webmaster@1 668 * Choose which template file to actually render. These are all suggested
webmaster@1 669 * templates from themes and modules. Theming implementations can occur on
webmaster@1 670 * multiple levels. All paths are checked to account for this.
webmaster@1 671 */
webmaster@1 672 function drupal_discover_template($paths, $suggestions, $extension = '.tpl.php') {
webmaster@1 673 global $theme_engine;
webmaster@1 674
webmaster@1 675 // Loop through all paths and suggestions in FIFO order.
webmaster@1 676 $suggestions = array_reverse($suggestions);
webmaster@1 677 $paths = array_reverse($paths);
webmaster@1 678 foreach ($suggestions as $suggestion) {
webmaster@1 679 if (!empty($suggestion)) {
webmaster@1 680 foreach ($paths as $path) {
webmaster@1 681 if (file_exists($file = $path .'/'. $suggestion . $extension)) {
webmaster@1 682 return $file;
webmaster@1 683 }
webmaster@1 684 }
webmaster@1 685 }
webmaster@1 686 }
webmaster@1 687 }
webmaster@1 688
webmaster@1 689 /**
webmaster@1 690 * Return the path to the currently selected theme.
webmaster@1 691 */
webmaster@1 692 function path_to_theme() {
webmaster@1 693 global $theme_path;
webmaster@1 694
webmaster@1 695 if (!isset($theme_path)) {
webmaster@1 696 init_theme();
webmaster@1 697 }
webmaster@1 698
webmaster@1 699 return $theme_path;
webmaster@1 700 }
webmaster@1 701
webmaster@1 702 /**
webmaster@1 703 * Find overridden theme functions. Called by themes and/or theme engines to
webmaster@1 704 * easily discover theme functions.
webmaster@1 705 *
webmaster@1 706 * @param $cache
webmaster@1 707 * The existing cache of theme hooks to test against.
webmaster@1 708 * @param $prefixes
webmaster@1 709 * An array of prefixes to test, in reverse order of importance.
webmaster@1 710 *
webmaster@1 711 * @return $templates
webmaster@1 712 * The functions found, suitable for returning from hook_theme;
webmaster@1 713 */
webmaster@1 714 function drupal_find_theme_functions($cache, $prefixes) {
webmaster@1 715 $templates = array();
webmaster@1 716 $functions = get_defined_functions();
webmaster@1 717
webmaster@1 718 foreach ($cache as $hook => $info) {
webmaster@1 719 foreach ($prefixes as $prefix) {
webmaster@1 720 if (!empty($info['pattern'])) {
webmaster@1 721 $matches = preg_grep('/^'. $prefix .'_'. $info['pattern'] .'/', $functions['user']);
webmaster@1 722 if ($matches) {
webmaster@1 723 foreach ($matches as $match) {
webmaster@1 724 $new_hook = str_replace($prefix .'_', '', $match);
webmaster@1 725 $templates[$new_hook] = array(
webmaster@1 726 'function' => $match,
webmaster@1 727 'arguments' => $info['arguments'],
webmaster@1 728 );
webmaster@1 729 }
webmaster@1 730 }
webmaster@1 731 }
webmaster@1 732 if (function_exists($prefix .'_'. $hook)) {
webmaster@1 733 $templates[$hook] = array(
webmaster@1 734 'function' => $prefix .'_'. $hook,
webmaster@1 735 );
webmaster@1 736 }
webmaster@1 737 }
webmaster@1 738 }
webmaster@1 739
webmaster@1 740 return $templates;
webmaster@1 741 }
webmaster@1 742
webmaster@1 743 /**
webmaster@1 744 * Find overridden theme templates. Called by themes and/or theme engines to
webmaster@1 745 * easily discover templates.
webmaster@1 746 *
webmaster@1 747 * @param $cache
webmaster@1 748 * The existing cache of theme hooks to test against.
webmaster@1 749 * @param $extension
webmaster@1 750 * The extension that these templates will have.
webmaster@1 751 * @param $path
webmaster@1 752 * The path to search.
webmaster@1 753 */
webmaster@1 754 function drupal_find_theme_templates($cache, $extension, $path) {
webmaster@1 755 $templates = array();
webmaster@1 756
webmaster@1 757 // Collect paths to all sub-themes grouped by base themes. These will be
webmaster@1 758 // used for filtering. This allows base themes to have sub-themes in its
webmaster@1 759 // folder hierarchy without affecting the base themes template discovery.
webmaster@1 760 $theme_paths = array();
webmaster@1 761 foreach (list_themes() as $theme_info) {
webmaster@1 762 if (!empty($theme_info->base_theme)) {
webmaster@1 763 $theme_paths[$theme_info->base_theme][$theme_info->name] = dirname($theme_info->filename);
webmaster@1 764 }
webmaster@1 765 }
webmaster@1 766 foreach ($theme_paths as $basetheme => $subthemes) {
webmaster@1 767 foreach ($subthemes as $subtheme => $subtheme_path) {
webmaster@1 768 if (isset($theme_paths[$subtheme])) {
webmaster@1 769 $theme_paths[$basetheme] = array_merge($theme_paths[$basetheme], $theme_paths[$subtheme]);
webmaster@1 770 }
webmaster@1 771 }
webmaster@1 772 }
webmaster@1 773 global $theme;
webmaster@1 774 $subtheme_paths = isset($theme_paths[$theme]) ? $theme_paths[$theme] : array();
webmaster@1 775
webmaster@1 776 // Escape the periods in the extension.
webmaster@1 777 $regex = str_replace('.', '\.', $extension) .'$';
webmaster@1 778 // Because drupal_system_listing works the way it does, we check for real
webmaster@1 779 // templates separately from checking for patterns.
webmaster@1 780 $files = drupal_system_listing($regex, $path, 'name', 0);
webmaster@1 781 foreach ($files as $template => $file) {
webmaster@1 782 // Ignore sub-theme templates for the current theme.
webmaster@1 783 if (strpos($file->filename, str_replace($subtheme_paths, '', $file->filename)) !== 0) {
webmaster@1 784 continue;
webmaster@1 785 }
webmaster@1 786 // Chop off the remaining extensions if there are any. $template already
webmaster@1 787 // has the rightmost extension removed, but there might still be more,
webmaster@1 788 // such as with .tpl.php, which still has .tpl in $template at this point.
webmaster@1 789 if (($pos = strpos($template, '.')) !== FALSE) {
webmaster@1 790 $template = substr($template, 0, $pos);
webmaster@1 791 }
webmaster@1 792 // Transform - in filenames to _ to match function naming scheme
webmaster@1 793 // for the purposes of searching.
webmaster@1 794 $hook = strtr($template, '-', '_');
webmaster@1 795 if (isset($cache[$hook])) {
webmaster@1 796 $templates[$hook] = array(
webmaster@1 797 'template' => $template,
webmaster@1 798 'path' => dirname($file->filename),
webmaster@1 799 );
webmaster@1 800 }
webmaster@1 801 }
webmaster@1 802
webmaster@1 803 $patterns = array_keys($files);
webmaster@1 804
webmaster@1 805 foreach ($cache as $hook => $info) {
webmaster@1 806 if (!empty($info['pattern'])) {
webmaster@1 807 // Transform _ in pattern to - to match file naming scheme
webmaster@1 808 // for the purposes of searching.
webmaster@1 809 $pattern = strtr($info['pattern'], '_', '-');
webmaster@1 810
webmaster@1 811 $matches = preg_grep('/^'. $pattern .'/', $patterns);
webmaster@1 812 if ($matches) {
webmaster@1 813 foreach ($matches as $match) {
webmaster@1 814 $file = substr($match, 0, strpos($match, '.'));
webmaster@1 815 // Put the underscores back in for the hook name and register this pattern.
webmaster@1 816 $templates[strtr($file, '-', '_')] = array(
webmaster@1 817 'template' => $file,
webmaster@1 818 'path' => dirname($files[$match]->filename),
webmaster@1 819 'arguments' => $info['arguments'],
webmaster@1 820 );
webmaster@1 821 }
webmaster@1 822 }
webmaster@1 823 }
webmaster@1 824 }
webmaster@1 825 return $templates;
webmaster@1 826 }
webmaster@1 827
webmaster@1 828 /**
webmaster@1 829 * Retrieve an associative array containing the settings for a theme.
webmaster@1 830 *
webmaster@1 831 * The final settings are arrived at by merging the default settings,
webmaster@1 832 * the site-wide settings, and the settings defined for the specific theme.
webmaster@1 833 * If no $key was specified, only the site-wide theme defaults are retrieved.
webmaster@1 834 *
webmaster@1 835 * The default values for each of settings are also defined in this function.
webmaster@1 836 * To add new settings, add their default values here, and then add form elements
webmaster@1 837 * to system_theme_settings() in system.module.
webmaster@1 838 *
webmaster@1 839 * @param $key
webmaster@1 840 * The template/style value for a given theme.
webmaster@1 841 *
webmaster@1 842 * @return
webmaster@1 843 * An associative array containing theme settings.
webmaster@1 844 */
webmaster@1 845 function theme_get_settings($key = NULL) {
webmaster@1 846 $defaults = array(
webmaster@1 847 'mission' => '',
webmaster@1 848 'default_logo' => 1,
webmaster@1 849 'logo_path' => '',
webmaster@1 850 'default_favicon' => 1,
webmaster@1 851 'favicon_path' => '',
webmaster@1 852 'primary_links' => 1,
webmaster@1 853 'secondary_links' => 1,
webmaster@1 854 'toggle_logo' => 1,
webmaster@1 855 'toggle_favicon' => 1,
webmaster@1 856 'toggle_name' => 1,
webmaster@1 857 'toggle_search' => 1,
webmaster@1 858 'toggle_slogan' => 0,
webmaster@1 859 'toggle_mission' => 1,
webmaster@1 860 'toggle_node_user_picture' => 0,
webmaster@1 861 'toggle_comment_user_picture' => 0,
webmaster@1 862 'toggle_primary_links' => 1,
webmaster@1 863 'toggle_secondary_links' => 1,
webmaster@1 864 );
webmaster@1 865
webmaster@1 866 if (module_exists('node')) {
webmaster@1 867 foreach (node_get_types() as $type => $name) {
webmaster@1 868 $defaults['toggle_node_info_'. $type] = 1;
webmaster@1 869 }
webmaster@1 870 }
webmaster@1 871 $settings = array_merge($defaults, variable_get('theme_settings', array()));
webmaster@1 872
webmaster@1 873 if ($key) {
webmaster@1 874 $settings = array_merge($settings, variable_get(str_replace('/', '_', 'theme_'. $key .'_settings'), array()));
webmaster@1 875 }
webmaster@1 876
webmaster@1 877 // Only offer search box if search.module is enabled.
webmaster@1 878 if (!module_exists('search') || !user_access('search content')) {
webmaster@1 879 $settings['toggle_search'] = 0;
webmaster@1 880 }
webmaster@1 881
webmaster@1 882 return $settings;
webmaster@1 883 }
webmaster@1 884
webmaster@1 885 /**
webmaster@1 886 * Retrieve a setting for the current theme.
webmaster@1 887 * This function is designed for use from within themes & engines
webmaster@1 888 * to determine theme settings made in the admin interface.
webmaster@1 889 *
webmaster@1 890 * Caches values for speed (use $refresh = TRUE to refresh cache)
webmaster@1 891 *
webmaster@1 892 * @param $setting_name
webmaster@1 893 * The name of the setting to be retrieved.
webmaster@1 894 *
webmaster@1 895 * @param $refresh
webmaster@1 896 * Whether to reload the cache of settings.
webmaster@1 897 *
webmaster@1 898 * @return
webmaster@1 899 * The value of the requested setting, NULL if the setting does not exist.
webmaster@1 900 */
webmaster@1 901 function theme_get_setting($setting_name, $refresh = FALSE) {
webmaster@1 902 global $theme_key;
webmaster@1 903 static $settings;
webmaster@1 904
webmaster@1 905 if (empty($settings) || $refresh) {
webmaster@1 906 $settings = theme_get_settings($theme_key);
webmaster@1 907
webmaster@1 908 $themes = list_themes();
webmaster@1 909 $theme_object = $themes[$theme_key];
webmaster@1 910
webmaster@1 911 if ($settings['mission'] == '') {
webmaster@1 912 $settings['mission'] = variable_get('site_mission', '');
webmaster@1 913 }
webmaster@1 914
webmaster@1 915 if (!$settings['toggle_mission']) {
webmaster@1 916 $settings['mission'] = '';
webmaster@1 917 }
webmaster@1 918
webmaster@1 919 if ($settings['toggle_logo']) {
webmaster@1 920 if ($settings['default_logo']) {
webmaster@1 921 $settings['logo'] = base_path() . dirname($theme_object->filename) .'/logo.png';
webmaster@1 922 }
webmaster@1 923 elseif ($settings['logo_path']) {
webmaster@1 924 $settings['logo'] = base_path() . $settings['logo_path'];
webmaster@1 925 }
webmaster@1 926 }
webmaster@1 927
webmaster@1 928 if ($settings['toggle_favicon']) {
webmaster@1 929 if ($settings['default_favicon']) {
webmaster@1 930 if (file_exists($favicon = dirname($theme_object->filename) .'/favicon.ico')) {
webmaster@1 931 $settings['favicon'] = base_path() . $favicon;
webmaster@1 932 }
webmaster@1 933 else {
webmaster@1 934 $settings['favicon'] = base_path() .'misc/favicon.ico';
webmaster@1 935 }
webmaster@1 936 }
webmaster@1 937 elseif ($settings['favicon_path']) {
webmaster@1 938 $settings['favicon'] = base_path() . $settings['favicon_path'];
webmaster@1 939 }
webmaster@1 940 else {
webmaster@1 941 $settings['toggle_favicon'] = FALSE;
webmaster@1 942 }
webmaster@1 943 }
webmaster@1 944 }
webmaster@1 945
webmaster@1 946 return isset($settings[$setting_name]) ? $settings[$setting_name] : NULL;
webmaster@1 947 }
webmaster@1 948
webmaster@1 949 /**
webmaster@1 950 * Render a system default template, which is essentially a PHP template.
webmaster@1 951 *
webmaster@1 952 * @param $file
webmaster@1 953 * The filename of the template to render.
webmaster@1 954 * @param $variables
webmaster@1 955 * A keyed array of variables that will appear in the output.
webmaster@1 956 *
webmaster@1 957 * @return
webmaster@1 958 * The output generated by the template.
webmaster@1 959 */
webmaster@1 960 function theme_render_template($file, $variables) {
webmaster@1 961 extract($variables, EXTR_SKIP); // Extract the variables to a local namespace
webmaster@1 962 ob_start(); // Start output buffering
webmaster@1 963 include "./$file"; // Include the file
webmaster@1 964 $contents = ob_get_contents(); // Get the contents of the buffer
webmaster@1 965 ob_end_clean(); // End buffering and discard
webmaster@1 966 return $contents; // Return the contents
webmaster@1 967 }
webmaster@1 968
webmaster@1 969 /**
webmaster@1 970 * @defgroup themeable Default theme implementations
webmaster@1 971 * @{
webmaster@1 972 * Functions and templates that present output to the user, and can be
webmaster@1 973 * implemented by themes.
webmaster@1 974 *
webmaster@1 975 * Drupal's presentation layer is a pluggable system known as the theme
webmaster@1 976 * layer. Each theme can take control over most of Drupal's output, and
webmaster@1 977 * has complete control over the CSS.
webmaster@1 978 *
webmaster@1 979 * Inside Drupal, the theme layer is utilized by the use of the theme()
webmaster@1 980 * function, which is passed the name of a component (the theme hook)
webmaster@1 981 * and several arguments. For example, theme('table', $header, $rows);
webmaster@1 982 * Additionally, the theme() function can take an array of theme
webmaster@1 983 * hooks, which can be used to provide 'fallback' implementations to
webmaster@1 984 * allow for more specific control of output. For example, the function:
webmaster@1 985 * theme(array('table__foo', 'table'), $header, $rows) would look to see if
webmaster@1 986 * 'table__foo' is registered anywhere; if it is not, it would 'fall back'
webmaster@1 987 * to the generic 'table' implementation. This can be used to attach specific
webmaster@1 988 * theme functions to named objects, allowing the themer more control over
webmaster@1 989 * specific types of output.
webmaster@1 990 *
webmaster@1 991 * As of Drupal 6, every theme hook is required to be registered by the
webmaster@1 992 * module that owns it, so that Drupal can tell what to do with it and
webmaster@1 993 * to make it simple for themes to identify and override the behavior
webmaster@1 994 * for these calls.
webmaster@1 995 *
webmaster@1 996 * The theme hooks are registered via hook_theme(), which returns an
webmaster@1 997 * array of arrays with information about the hook. It describes the
webmaster@1 998 * arguments the function or template will need, and provides
webmaster@1 999 * defaults for the template in case they are not filled in. If the default
webmaster@1 1000 * implementation is a function, by convention it is named theme_HOOK().
webmaster@1 1001 *
webmaster@1 1002 * Each module should provide a default implementation for themes that
webmaster@1 1003 * it registers. This implementation may be either a function or a template;
webmaster@1 1004 * if it is a function it must be specified via hook_theme(). By convention,
webmaster@1 1005 * default implementations of theme hooks are named theme_HOOK. Default
webmaster@1 1006 * template implementations are stored in the module directory.
webmaster@1 1007 *
webmaster@1 1008 * Drupal's default template renderer is a simple PHP parsing engine that
webmaster@1 1009 * includes the template and stores the output. Drupal's theme engines
webmaster@1 1010 * can provide alternate template engines, such as XTemplate, Smarty and
webmaster@1 1011 * PHPTal. The most common template engine is PHPTemplate (included with
webmaster@1 1012 * Drupal and implemented in phptemplate.engine, which uses Drupal's default
webmaster@1 1013 * template renderer.
webmaster@1 1014 *
webmaster@1 1015 * In order to create theme-specific implementations of these hooks,
webmaster@1 1016 * themes can implement their own version of theme hooks, either as functions
webmaster@1 1017 * or templates. These implementations will be used instead of the default
webmaster@1 1018 * implementation. If using a pure .theme without an engine, the .theme is
webmaster@1 1019 * required to implement its own version of hook_theme() to tell Drupal what
webmaster@1 1020 * it is implementing; themes utilizing an engine will have their well-named
webmaster@1 1021 * theming functions automatically registered for them. While this can vary
webmaster@1 1022 * based upon the theme engine, the standard set by phptemplate is that theme
webmaster@1 1023 * functions should be named either phptemplate_HOOK or THEMENAME_HOOK. For
webmaster@1 1024 * example, for Drupal's default theme (Garland) to implement the 'table' hook,
webmaster@1 1025 * the phptemplate.engine would find phptemplate_table() or garland_table().
webmaster@1 1026 * The ENGINE_HOOK() syntax is preferred, as this can be used by sub-themes
webmaster@1 1027 * (which are themes that share code but use different stylesheets).
webmaster@1 1028 *
webmaster@1 1029 * The theme system is described and defined in theme.inc.
webmaster@1 1030 *
webmaster@1 1031 * @see theme()
webmaster@1 1032 * @see hook_theme()
webmaster@1 1033 */
webmaster@1 1034
webmaster@1 1035 /**
webmaster@1 1036 * Formats text for emphasized display in a placeholder inside a sentence.
webmaster@1 1037 * Used automatically by t().
webmaster@1 1038 *
webmaster@1 1039 * @param $text
webmaster@1 1040 * The text to format (plain-text).
webmaster@1 1041 * @return
webmaster@1 1042 * The formatted text (html).
webmaster@1 1043 */
webmaster@1 1044 function theme_placeholder($text) {
webmaster@1 1045 return '<em>'. check_plain($text) .'</em>';
webmaster@1 1046 }
webmaster@1 1047
webmaster@1 1048 /**
webmaster@1 1049 * Return a themed set of status and/or error messages. The messages are grouped
webmaster@1 1050 * by type.
webmaster@1 1051 *
webmaster@1 1052 * @param $display
webmaster@1 1053 * (optional) Set to 'status' or 'error' to display only messages of that type.
webmaster@1 1054 *
webmaster@1 1055 * @return
webmaster@1 1056 * A string containing the messages.
webmaster@1 1057 */
webmaster@1 1058 function theme_status_messages($display = NULL) {
webmaster@1 1059 $output = '';
webmaster@1 1060 foreach (drupal_get_messages($display) as $type => $messages) {
webmaster@1 1061 $output .= "<div class=\"messages $type\">\n";
webmaster@1 1062 if (count($messages) > 1) {
webmaster@1 1063 $output .= " <ul>\n";
webmaster@1 1064 foreach ($messages as $message) {
webmaster@1 1065 $output .= ' <li>'. $message ."</li>\n";
webmaster@1 1066 }
webmaster@1 1067 $output .= " </ul>\n";
webmaster@1 1068 }
webmaster@1 1069 else {
webmaster@1 1070 $output .= $messages[0];
webmaster@1 1071 }
webmaster@1 1072 $output .= "</div>\n";
webmaster@1 1073 }
webmaster@1 1074 return $output;
webmaster@1 1075 }
webmaster@1 1076
webmaster@1 1077 /**
webmaster@1 1078 * Return a themed set of links.
webmaster@1 1079 *
webmaster@1 1080 * @param $links
webmaster@1 1081 * A keyed array of links to be themed.
webmaster@1 1082 * @param $attributes
webmaster@1 1083 * A keyed array of attributes
webmaster@1 1084 * @return
webmaster@1 1085 * A string containing an unordered list of links.
webmaster@1 1086 */
webmaster@1 1087 function theme_links($links, $attributes = array('class' => 'links')) {
webmaster@1 1088 $output = '';
webmaster@1 1089
webmaster@1 1090 if (count($links) > 0) {
webmaster@1 1091 $output = '<ul'. drupal_attributes($attributes) .'>';
webmaster@1 1092
webmaster@1 1093 $num_links = count($links);
webmaster@1 1094 $i = 1;
webmaster@1 1095
webmaster@1 1096 foreach ($links as $key => $link) {
webmaster@1 1097 $class = $key;
webmaster@1 1098
webmaster@1 1099 // Add first, last and active classes to the list of links to help out themers.
webmaster@1 1100 if ($i == 1) {
webmaster@1 1101 $class .= ' first';
webmaster@1 1102 }
webmaster@1 1103 if ($i == $num_links) {
webmaster@1 1104 $class .= ' last';
webmaster@1 1105 }
webmaster@5 1106 if (isset($link['href']) && ($link['href'] == $_GET['q'] || ($link['href'] == '<front>' && drupal_is_front_page()))) {
webmaster@1 1107 $class .= ' active';
webmaster@1 1108 }
webmaster@1 1109 $output .= '<li class="'. $class .'">';
webmaster@1 1110
webmaster@1 1111 if (isset($link['href'])) {
webmaster@1 1112 // Pass in $link as $options, they share the same keys.
webmaster@1 1113 $output .= l($link['title'], $link['href'], $link);
webmaster@1 1114 }
webmaster@1 1115 else if (!empty($link['title'])) {
webmaster@1 1116 // Some links are actually not links, but we wrap these in <span> for adding title and class attributes
webmaster@1 1117 if (empty($link['html'])) {
webmaster@1 1118 $link['title'] = check_plain($link['title']);
webmaster@1 1119 }
webmaster@1 1120 $span_attributes = '';
webmaster@1 1121 if (isset($link['attributes'])) {
webmaster@1 1122 $span_attributes = drupal_attributes($link['attributes']);
webmaster@1 1123 }
webmaster@1 1124 $output .= '<span'. $span_attributes .'>'. $link['title'] .'</span>';
webmaster@1 1125 }
webmaster@1 1126
webmaster@1 1127 $i++;
webmaster@1 1128 $output .= "</li>\n";
webmaster@1 1129 }
webmaster@1 1130
webmaster@1 1131 $output .= '</ul>';
webmaster@1 1132 }
webmaster@1 1133
webmaster@1 1134 return $output;
webmaster@1 1135 }
webmaster@1 1136
webmaster@1 1137 /**
webmaster@1 1138 * Return a themed image.
webmaster@1 1139 *
webmaster@1 1140 * @param $path
webmaster@1 1141 * Either the path of the image file (relative to base_path()) or a full URL.
webmaster@1 1142 * @param $alt
webmaster@1 1143 * The alternative text for text-based browsers.
webmaster@1 1144 * @param $title
webmaster@1 1145 * The title text is displayed when the image is hovered in some popular browsers.
webmaster@1 1146 * @param $attributes
webmaster@1 1147 * Associative array of attributes to be placed in the img tag.
webmaster@1 1148 * @param $getsize
webmaster@1 1149 * If set to TRUE, the image's dimension are fetched and added as width/height attributes.
webmaster@1 1150 * @return
webmaster@1 1151 * A string containing the image tag.
webmaster@1 1152 */
webmaster@1 1153 function theme_image($path, $alt = '', $title = '', $attributes = NULL, $getsize = TRUE) {
webmaster@1 1154 if (!$getsize || (is_file($path) && (list($width, $height, $type, $image_attributes) = @getimagesize($path)))) {
webmaster@1 1155 $attributes = drupal_attributes($attributes);
webmaster@1 1156 $url = (url($path) == $path) ? $path : (base_path() . $path);
webmaster@1 1157 return '<img src="'. check_url($url) .'" alt="'. check_plain($alt) .'" title="'. check_plain($title) .'" '. (isset($image_attributes) ? $image_attributes : '') . $attributes .' />';
webmaster@1 1158 }
webmaster@1 1159 }
webmaster@1 1160
webmaster@1 1161 /**
webmaster@1 1162 * Return a themed breadcrumb trail.
webmaster@1 1163 *
webmaster@1 1164 * @param $breadcrumb
webmaster@1 1165 * An array containing the breadcrumb links.
webmaster@1 1166 * @return a string containing the breadcrumb output.
webmaster@1 1167 */
webmaster@1 1168 function theme_breadcrumb($breadcrumb) {
webmaster@1 1169 if (!empty($breadcrumb)) {
webmaster@1 1170 return '<div class="breadcrumb">'. implode(' » ', $breadcrumb) .'</div>';
webmaster@1 1171 }
webmaster@1 1172 }
webmaster@1 1173
webmaster@1 1174 /**
webmaster@1 1175 * Return a themed help message.
webmaster@1 1176 *
webmaster@1 1177 * @return a string containing the helptext for the current page.
webmaster@1 1178 */
webmaster@1 1179 function theme_help() {
webmaster@1 1180 if ($help = menu_get_active_help()) {
webmaster@1 1181 return '<div class="help">'. $help .'</div>';
webmaster@1 1182 }
webmaster@1 1183 }
webmaster@1 1184
webmaster@1 1185 /**
webmaster@1 1186 * Return a themed submenu, typically displayed under the tabs.
webmaster@1 1187 *
webmaster@1 1188 * @param $links
webmaster@1 1189 * An array of links.
webmaster@1 1190 */
webmaster@1 1191 function theme_submenu($links) {
webmaster@1 1192 return '<div class="submenu">'. implode(' | ', $links) .'</div>';
webmaster@1 1193 }
webmaster@1 1194
webmaster@1 1195 /**
webmaster@1 1196 * Return a themed table.
webmaster@1 1197 *
webmaster@1 1198 * @param $header
webmaster@1 1199 * An array containing the table headers. Each element of the array can be
webmaster@1 1200 * either a localized string or an associative array with the following keys:
webmaster@1 1201 * - "data": The localized title of the table column.
webmaster@1 1202 * - "field": The database field represented in the table column (required if
webmaster@1 1203 * user is to be able to sort on this column).
webmaster@1 1204 * - "sort": A default sort order for this column ("asc" or "desc").
webmaster@1 1205 * - Any HTML attributes, such as "colspan", to apply to the column header cell.
webmaster@1 1206 * @param $rows
webmaster@1 1207 * An array of table rows. Every row is an array of cells, or an associative
webmaster@1 1208 * array with the following keys:
webmaster@1 1209 * - "data": an array of cells
webmaster@1 1210 * - Any HTML attributes, such as "class", to apply to the table row.
webmaster@1 1211 *
webmaster@1 1212 * Each cell can be either a string or an associative array with the following keys:
webmaster@1 1213 * - "data": The string to display in the table cell.
webmaster@1 1214 * - "header": Indicates this cell is a header.
webmaster@1 1215 * - Any HTML attributes, such as "colspan", to apply to the table cell.
webmaster@1 1216 *
webmaster@1 1217 * Here's an example for $rows:
webmaster@1 1218 * @verbatim
webmaster@1 1219 * $rows = array(
webmaster@1 1220 * // Simple row
webmaster@1 1221 * array(
webmaster@1 1222 * 'Cell 1', 'Cell 2', 'Cell 3'
webmaster@1 1223 * ),
webmaster@1 1224 * // Row with attributes on the row and some of its cells.
webmaster@1 1225 * array(
webmaster@1 1226 * 'data' => array('Cell 1', array('data' => 'Cell 2', 'colspan' => 2)), 'class' => 'funky'
webmaster@1 1227 * )
webmaster@1 1228 * );
webmaster@1 1229 * @endverbatim
webmaster@1 1230 *
webmaster@1 1231 * @param $attributes
webmaster@1 1232 * An array of HTML attributes to apply to the table tag.
webmaster@1 1233 * @param $caption
webmaster@1 1234 * A localized string to use for the <caption> tag.
webmaster@1 1235 * @return
webmaster@1 1236 * An HTML string representing the table.
webmaster@1 1237 */
webmaster@1 1238 function theme_table($header, $rows, $attributes = array(), $caption = NULL) {
webmaster@1 1239
webmaster@1 1240 // Add sticky headers, if applicable.
webmaster@1 1241 if (count($header)) {
webmaster@1 1242 drupal_add_js('misc/tableheader.js');
webmaster@1 1243 // Add 'sticky-enabled' class to the table to identify it for JS.
webmaster@1 1244 // This is needed to target tables constructed by this function.
webmaster@1 1245 $attributes['class'] = empty($attributes['class']) ? 'sticky-enabled' : ($attributes['class'] .' sticky-enabled');
webmaster@1 1246 }
webmaster@1 1247
webmaster@1 1248 $output = '<table'. drupal_attributes($attributes) .">\n";
webmaster@1 1249
webmaster@1 1250 if (isset($caption)) {
webmaster@1 1251 $output .= '<caption>'. $caption ."</caption>\n";
webmaster@1 1252 }
webmaster@1 1253
webmaster@1 1254 // Format the table header:
webmaster@1 1255 if (count($header)) {
webmaster@1 1256 $ts = tablesort_init($header);
webmaster@1 1257 // HTML requires that the thead tag has tr tags in it follwed by tbody
webmaster@1 1258 // tags. Using ternary operator to check and see if we have any rows.
webmaster@1 1259 $output .= (count($rows) ? ' <thead><tr>' : ' <tr>');
webmaster@1 1260 foreach ($header as $cell) {
webmaster@1 1261 $cell = tablesort_header($cell, $header, $ts);
webmaster@1 1262 $output .= _theme_table_cell($cell, TRUE);
webmaster@1 1263 }
webmaster@1 1264 // Using ternary operator to close the tags based on whether or not there are rows
webmaster@1 1265 $output .= (count($rows) ? " </tr></thead>\n" : "</tr>\n");
webmaster@1 1266 }
webmaster@1 1267 else {
webmaster@1 1268 $ts = array();
webmaster@1 1269 }
webmaster@1 1270
webmaster@1 1271 // Format the table rows:
webmaster@1 1272 if (count($rows)) {
webmaster@1 1273 $output .= "<tbody>\n";
webmaster@1 1274 $flip = array('even' => 'odd', 'odd' => 'even');
webmaster@1 1275 $class = 'even';
webmaster@1 1276 foreach ($rows as $number => $row) {
webmaster@1 1277 $attributes = array();
webmaster@1 1278
webmaster@1 1279 // Check if we're dealing with a simple or complex row
webmaster@1 1280 if (isset($row['data'])) {
webmaster@1 1281 foreach ($row as $key => $value) {
webmaster@1 1282 if ($key == 'data') {
webmaster@1 1283 $cells = $value;
webmaster@1 1284 }
webmaster@1 1285 else {
webmaster@1 1286 $attributes[$key] = $value;
webmaster@1 1287 }
webmaster@1 1288 }
webmaster@1 1289 }
webmaster@1 1290 else {
webmaster@1 1291 $cells = $row;
webmaster@1 1292 }
webmaster@1 1293 if (count($cells)) {
webmaster@1 1294 // Add odd/even class
webmaster@1 1295 $class = $flip[$class];
webmaster@1 1296 if (isset($attributes['class'])) {
webmaster@1 1297 $attributes['class'] .= ' '. $class;
webmaster@1 1298 }
webmaster@1 1299 else {
webmaster@1 1300 $attributes['class'] = $class;
webmaster@1 1301 }
webmaster@1 1302
webmaster@1 1303 // Build row
webmaster@1 1304 $output .= ' <tr'. drupal_attributes($attributes) .'>';
webmaster@1 1305 $i = 0;
webmaster@1 1306 foreach ($cells as $cell) {
webmaster@1 1307 $cell = tablesort_cell($cell, $header, $ts, $i++);
webmaster@1 1308 $output .= _theme_table_cell($cell);
webmaster@1 1309 }
webmaster@1 1310 $output .= " </tr>\n";
webmaster@1 1311 }
webmaster@1 1312 }
webmaster@1 1313 $output .= "</tbody>\n";
webmaster@1 1314 }
webmaster@1 1315
webmaster@1 1316 $output .= "</table>\n";
webmaster@1 1317 return $output;
webmaster@1 1318 }
webmaster@1 1319
webmaster@1 1320 /**
webmaster@1 1321 * Returns a header cell for tables that have a select all functionality.
webmaster@1 1322 */
webmaster@1 1323 function theme_table_select_header_cell() {
webmaster@1 1324 drupal_add_js('misc/tableselect.js');
webmaster@1 1325
webmaster@1 1326 return array('class' => 'select-all');
webmaster@1 1327 }
webmaster@1 1328
webmaster@1 1329 /**
webmaster@1 1330 * Return a themed sort icon.
webmaster@1 1331 *
webmaster@1 1332 * @param $style
webmaster@1 1333 * Set to either asc or desc. This sets which icon to show.
webmaster@1 1334 * @return
webmaster@1 1335 * A themed sort icon.
webmaster@1 1336 */
webmaster@1 1337 function theme_tablesort_indicator($style) {
webmaster@1 1338 if ($style == "asc") {
webmaster@1 1339 return theme('image', 'misc/arrow-asc.png', t('sort icon'), t('sort ascending'));
webmaster@1 1340 }
webmaster@1 1341 else {
webmaster@1 1342 return theme('image', 'misc/arrow-desc.png', t('sort icon'), t('sort descending'));
webmaster@1 1343 }
webmaster@1 1344 }
webmaster@1 1345
webmaster@1 1346 /**
webmaster@1 1347 * Return a themed box.
webmaster@1 1348 *
webmaster@1 1349 * @param $title
webmaster@1 1350 * The subject of the box.
webmaster@1 1351 * @param $content
webmaster@1 1352 * The content of the box.
webmaster@1 1353 * @param $region
webmaster@1 1354 * The region in which the box is displayed.
webmaster@1 1355 * @return
webmaster@1 1356 * A string containing the box output.
webmaster@1 1357 */
webmaster@1 1358 function theme_box($title, $content, $region = 'main') {
webmaster@1 1359 $output = '<h2 class="title">'. $title .'</h2><div>'. $content .'</div>';
webmaster@1 1360 return $output;
webmaster@1 1361 }
webmaster@1 1362
webmaster@1 1363 /**
webmaster@1 1364 * Return a themed marker, useful for marking new or updated
webmaster@1 1365 * content.
webmaster@1 1366 *
webmaster@1 1367 * @param $type
webmaster@1 1368 * Number representing the marker type to display
webmaster@1 1369 * @see MARK_NEW, MARK_UPDATED, MARK_READ
webmaster@1 1370 * @return
webmaster@1 1371 * A string containing the marker.
webmaster@1 1372 */
webmaster@1 1373 function theme_mark($type = MARK_NEW) {
webmaster@1 1374 global $user;
webmaster@1 1375 if ($user->uid) {
webmaster@1 1376 if ($type == MARK_NEW) {
webmaster@1 1377 return ' <span class="marker">'. t('new') .'</span>';
webmaster@1 1378 }
webmaster@1 1379 else if ($type == MARK_UPDATED) {
webmaster@1 1380 return ' <span class="marker">'. t('updated') .'</span>';
webmaster@1 1381 }
webmaster@1 1382 }
webmaster@1 1383 }
webmaster@1 1384
webmaster@1 1385 /**
webmaster@1 1386 * Return a themed list of items.
webmaster@1 1387 *
webmaster@1 1388 * @param $items
webmaster@1 1389 * An array of items to be displayed in the list. If an item is a string,
webmaster@1 1390 * then it is used as is. If an item is an array, then the "data" element of
webmaster@1 1391 * the array is used as the contents of the list item. If an item is an array
webmaster@1 1392 * with a "children" element, those children are displayed in a nested list.
webmaster@1 1393 * All other elements are treated as attributes of the list item element.
webmaster@1 1394 * @param $title
webmaster@1 1395 * The title of the list.
webmaster@1 1396 * @param $attributes
webmaster@1 1397 * The attributes applied to the list element.
webmaster@1 1398 * @param $type
webmaster@1 1399 * The type of list to return (e.g. "ul", "ol")
webmaster@1 1400 * @return
webmaster@1 1401 * A string containing the list output.
webmaster@1 1402 */
webmaster@1 1403 function theme_item_list($items = array(), $title = NULL, $type = 'ul', $attributes = NULL) {
webmaster@1 1404 $output = '<div class="item-list">';
webmaster@1 1405 if (isset($title)) {
webmaster@1 1406 $output .= '<h3>'. $title .'</h3>';
webmaster@1 1407 }
webmaster@1 1408
webmaster@1 1409 if (!empty($items)) {
webmaster@1 1410 $output .= "<$type". drupal_attributes($attributes) .'>';
webmaster@1 1411 $num_items = count($items);
webmaster@1 1412 foreach ($items as $i => $item) {
webmaster@1 1413 $attributes = array();
webmaster@1 1414 $children = array();
webmaster@1 1415 if (is_array($item)) {
webmaster@1 1416 foreach ($item as $key => $value) {
webmaster@1 1417 if ($key == 'data') {
webmaster@1 1418 $data = $value;
webmaster@1 1419 }
webmaster@1 1420 elseif ($key == 'children') {
webmaster@1 1421 $children = $value;
webmaster@1 1422 }
webmaster@1 1423 else {
webmaster@1 1424 $attributes[$key] = $value;
webmaster@1 1425 }
webmaster@1 1426 }
webmaster@1 1427 }
webmaster@1 1428 else {
webmaster@1 1429 $data = $item;
webmaster@1 1430 }
webmaster@1 1431 if (count($children) > 0) {
webmaster@1 1432 $data .= theme_item_list($children, NULL, $type, $attributes); // Render nested list
webmaster@1 1433 }
webmaster@1 1434 if ($i == 0) {
webmaster@1 1435 $attributes['class'] = empty($attributes['class']) ? 'first' : ($attributes['class'] .' first');
webmaster@1 1436 }
webmaster@1 1437 if ($i == $num_items - 1) {
webmaster@1 1438 $attributes['class'] = empty($attributes['class']) ? 'last' : ($attributes['class'] .' last');
webmaster@1 1439 }
webmaster@1 1440 $output .= '<li'. drupal_attributes($attributes) .'>'. $data ."</li>\n";
webmaster@1 1441 }
webmaster@1 1442 $output .= "</$type>";
webmaster@1 1443 }
webmaster@1 1444 $output .= '</div>';
webmaster@1 1445 return $output;
webmaster@1 1446 }
webmaster@1 1447
webmaster@1 1448 /**
webmaster@1 1449 * Returns code that emits the 'more help'-link.
webmaster@1 1450 */
webmaster@1 1451 function theme_more_help_link($url) {
webmaster@1 1452 return '<div class="more-help-link">'. t('[<a href="@link">more help...</a>]', array('@link' => check_url($url))) .'</div>';
webmaster@1 1453 }
webmaster@1 1454
webmaster@1 1455 /**
webmaster@1 1456 * Return code that emits an XML icon.
webmaster@1 1457 *
webmaster@1 1458 * For most use cases, this function has been superseded by theme_feed_icon().
webmaster@1 1459 *
webmaster@1 1460 * @see theme_feed_icon()
webmaster@1 1461 * @param $url
webmaster@1 1462 * The url of the feed.
webmaster@1 1463 */
webmaster@1 1464 function theme_xml_icon($url) {
webmaster@1 1465 if ($image = theme('image', 'misc/xml.png', t('XML feed'), t('XML feed'))) {
webmaster@1 1466 return '<a href="'. check_url($url) .'" class="xml-icon">'. $image .'</a>';
webmaster@1 1467 }
webmaster@1 1468 }
webmaster@1 1469
webmaster@1 1470 /**
webmaster@1 1471 * Return code that emits an feed icon.
webmaster@1 1472 *
webmaster@1 1473 * @param $url
webmaster@1 1474 * The url of the feed.
webmaster@1 1475 * @param $title
webmaster@1 1476 * A descriptive title of the feed.
webmaster@1 1477 */
webmaster@1 1478 function theme_feed_icon($url, $title) {
webmaster@1 1479 if ($image = theme('image', 'misc/feed.png', t('Syndicate content'), $title)) {
webmaster@1 1480 return '<a href="'. check_url($url) .'" class="feed-icon">'. $image .'</a>';
webmaster@1 1481 }
webmaster@1 1482 }
webmaster@1 1483
webmaster@1 1484 /**
webmaster@1 1485 * Returns code that emits the 'more' link used on blocks.
webmaster@1 1486 *
webmaster@1 1487 * @param $url
webmaster@1 1488 * The url of the main page
webmaster@1 1489 * @param $title
webmaster@1 1490 * A descriptive verb for the link, like 'Read more'
webmaster@1 1491 */
webmaster@1 1492 function theme_more_link($url, $title) {
webmaster@1 1493 return '<div class="more-link">'. t('<a href="@link" title="@title">more</a>', array('@link' => check_url($url), '@title' => $title)) .'</div>';
webmaster@1 1494 }
webmaster@1 1495
webmaster@1 1496 /**
webmaster@1 1497 * Execute hook_footer() which is run at the end of the page right before the
webmaster@1 1498 * close of the body tag.
webmaster@1 1499 *
webmaster@1 1500 * @param $main (optional)
webmaster@1 1501 * Whether the current page is the front page of the site.
webmaster@1 1502 * @return
webmaster@1 1503 * A string containing the results of the hook_footer() calls.
webmaster@1 1504 */
webmaster@1 1505 function theme_closure($main = 0) {
webmaster@1 1506 $footer = module_invoke_all('footer', $main);
webmaster@1 1507 return implode("\n", $footer) . drupal_get_js('footer');
webmaster@1 1508 }
webmaster@1 1509
webmaster@1 1510 /**
webmaster@1 1511 * Return a set of blocks available for the current user.
webmaster@1 1512 *
webmaster@1 1513 * @param $region
webmaster@1 1514 * Which set of blocks to retrieve.
webmaster@1 1515 * @return
webmaster@1 1516 * A string containing the themed blocks for this region.
webmaster@1 1517 */
webmaster@1 1518 function theme_blocks($region) {
webmaster@1 1519 $output = '';
webmaster@1 1520
webmaster@1 1521 if ($list = block_list($region)) {
webmaster@1 1522 foreach ($list as $key => $block) {
webmaster@1 1523 // $key == <i>module</i>_<i>delta</i>
webmaster@1 1524 $output .= theme('block', $block);
webmaster@1 1525 }
webmaster@1 1526 }
webmaster@1 1527
webmaster@1 1528 // Add any content assigned to this region through drupal_set_content() calls.
webmaster@1 1529 $output .= drupal_get_content($region);
webmaster@1 1530
webmaster@1 1531 return $output;
webmaster@1 1532 }
webmaster@1 1533
webmaster@1 1534 /**
webmaster@1 1535 * Format a username.
webmaster@1 1536 *
webmaster@1 1537 * @param $object
webmaster@1 1538 * The user object to format, usually returned from user_load().
webmaster@1 1539 * @return
webmaster@1 1540 * A string containing an HTML link to the user's page if the passed object
webmaster@1 1541 * suggests that this is a site user. Otherwise, only the username is returned.
webmaster@1 1542 */
webmaster@1 1543 function theme_username($object) {
webmaster@1 1544
webmaster@1 1545 if ($object->uid && $object->name) {
webmaster@1 1546 // Shorten the name when it is too long or it will break many tables.
webmaster@1 1547 if (drupal_strlen($object->name) > 20) {
webmaster@1 1548 $name = drupal_substr($object->name, 0, 15) .'...';
webmaster@1 1549 }
webmaster@1 1550 else {
webmaster@1 1551 $name = $object->name;
webmaster@1 1552 }
webmaster@1 1553
webmaster@1 1554 if (user_access('access user profiles')) {
webmaster@5 1555 $output = l($name, 'user/'. $object->uid, array('attributes' => array('title' => t('View user profile.'))));
webmaster@1 1556 }
webmaster@1 1557 else {
webmaster@1 1558 $output = check_plain($name);
webmaster@1 1559 }
webmaster@1 1560 }
webmaster@1 1561 else if ($object->name) {
webmaster@1 1562 // Sometimes modules display content composed by people who are
webmaster@1 1563 // not registered members of the site (e.g. mailing list or news
webmaster@1 1564 // aggregator modules). This clause enables modules to display
webmaster@1 1565 // the true author of the content.
webmaster@1 1566 if (!empty($object->homepage)) {
webmaster@1 1567 $output = l($object->name, $object->homepage, array('rel' => 'nofollow'));
webmaster@1 1568 }
webmaster@1 1569 else {
webmaster@1 1570 $output = check_plain($object->name);
webmaster@1 1571 }
webmaster@1 1572
webmaster@1 1573 $output .= ' ('. t('not verified') .')';
webmaster@1 1574 }
webmaster@1 1575 else {
webmaster@1 1576 $output = variable_get('anonymous', t('Anonymous'));
webmaster@1 1577 }
webmaster@1 1578
webmaster@1 1579 return $output;
webmaster@1 1580 }
webmaster@1 1581
webmaster@1 1582 /**
webmaster@1 1583 * Return a themed progress bar.
webmaster@1 1584 *
webmaster@1 1585 * @param $percent
webmaster@1 1586 * The percentage of the progress.
webmaster@1 1587 * @param $message
webmaster@1 1588 * A string containing information to be displayed.
webmaster@1 1589 * @return
webmaster@1 1590 * A themed HTML string representing the progress bar.
webmaster@1 1591 */
webmaster@1 1592 function theme_progress_bar($percent, $message) {
webmaster@1 1593 $output = '<div id="progress" class="progress">';
webmaster@1 1594 $output .= '<div class="bar"><div class="filled" style="width: '. $percent .'%"></div></div>';
webmaster@1 1595 $output .= '<div class="percentage">'. $percent .'%</div>';
webmaster@1 1596 $output .= '<div class="message">'. $message .'</div>';
webmaster@1 1597 $output .= '</div>';
webmaster@1 1598
webmaster@1 1599 return $output;
webmaster@1 1600 }
webmaster@1 1601
webmaster@1 1602 /**
webmaster@1 1603 * Create a standard indentation div. Used for drag and drop tables.
webmaster@1 1604 *
webmaster@1 1605 * @param $size
webmaster@1 1606 * Optional. The number of indentations to create.
webmaster@1 1607 * @return
webmaster@1 1608 * A string containing indentations.
webmaster@1 1609 */
webmaster@1 1610 function theme_indentation($size = 1) {
webmaster@1 1611 $output = '';
webmaster@1 1612 for ($n = 0; $n < $size; $n++) {
webmaster@1 1613 $output .= '<div class="indentation">&nbsp;</div>';
webmaster@1 1614 }
webmaster@1 1615 return $output;
webmaster@1 1616 }
webmaster@1 1617
webmaster@1 1618 /**
webmaster@1 1619 * @} End of "defgroup themeable".
webmaster@1 1620 */
webmaster@1 1621
webmaster@1 1622 function _theme_table_cell($cell, $header = FALSE) {
webmaster@1 1623 $attributes = '';
webmaster@1 1624
webmaster@1 1625 if (is_array($cell)) {
webmaster@1 1626 $data = isset($cell['data']) ? $cell['data'] : '';
webmaster@1 1627 $header |= isset($cell['header']);
webmaster@1 1628 unset($cell['data']);
webmaster@1 1629 unset($cell['header']);
webmaster@1 1630 $attributes = drupal_attributes($cell);
webmaster@1 1631 }
webmaster@1 1632 else {
webmaster@1 1633 $data = $cell;
webmaster@1 1634 }
webmaster@1 1635
webmaster@1 1636 if ($header) {
webmaster@1 1637 $output = "<th$attributes>$data</th>";
webmaster@1 1638 }
webmaster@1 1639 else {
webmaster@1 1640 $output = "<td$attributes>$data</td>";
webmaster@1 1641 }
webmaster@1 1642
webmaster@1 1643 return $output;
webmaster@1 1644 }
webmaster@1 1645
webmaster@1 1646 /**
webmaster@1 1647 * Adds a default set of helper variables for preprocess functions and
webmaster@1 1648 * templates. This comes in before any other preprocess function which makes
webmaster@1 1649 * it possible to be used in default theme implementations (non-overriden
webmaster@1 1650 * theme functions).
webmaster@1 1651 */
webmaster@1 1652 function template_preprocess(&$variables, $hook) {
webmaster@1 1653 global $user;
webmaster@1 1654 static $count = array();
webmaster@1 1655
webmaster@1 1656 // Track run count for each hook to provide zebra striping.
webmaster@1 1657 // See "template_preprocess_block()" which provides the same feature specific to blocks.
webmaster@1 1658 $count[$hook] = isset($count[$hook]) && is_int($count[$hook]) ? $count[$hook] : 1;
webmaster@1 1659 $variables['zebra'] = ($count[$hook] % 2) ? 'odd' : 'even';
webmaster@1 1660 $variables['id'] = $count[$hook]++;
webmaster@1 1661
webmaster@1 1662 // Tell all templates where they are located.
webmaster@1 1663 $variables['directory'] = path_to_theme();
webmaster@1 1664
webmaster@1 1665 // Set default variables that depend on the database.
webmaster@1 1666 $variables['is_admin'] = FALSE;
webmaster@1 1667 $variables['is_front'] = FALSE;
webmaster@1 1668 $variables['logged_in'] = FALSE;
webmaster@1 1669 if ($variables['db_is_active'] = db_is_active() && !defined('MAINTENANCE_MODE')) {
webmaster@1 1670 // Check for administrators.
webmaster@1 1671 if (user_access('access administration pages')) {
webmaster@1 1672 $variables['is_admin'] = TRUE;
webmaster@1 1673 }
webmaster@1 1674 // Flag front page status.
webmaster@1 1675 $variables['is_front'] = drupal_is_front_page();
webmaster@1 1676 // Tell all templates by which kind of user they're viewed.
webmaster@1 1677 $variables['logged_in'] = ($user->uid > 0);
webmaster@1 1678 // Provide user object to all templates
webmaster@1 1679 $variables['user'] = $user;
webmaster@1 1680 }
webmaster@1 1681 }
webmaster@1 1682
webmaster@1 1683 /**
webmaster@1 1684 * Process variables for page.tpl.php
webmaster@1 1685 *
webmaster@1 1686 * Most themes utilize their own copy of page.tpl.php. The default is located
webmaster@1 1687 * inside "modules/system/page.tpl.php". Look in there for the full list of
webmaster@1 1688 * variables.
webmaster@1 1689 *
webmaster@1 1690 * Uses the arg() function to generate a series of page template suggestions
webmaster@1 1691 * based on the current path.
webmaster@1 1692 *
webmaster@1 1693 * Any changes to variables in this preprocessor should also be changed inside
webmaster@1 1694 * template_preprocess_maintenance_page() to keep all them consistent.
webmaster@1 1695 *
webmaster@1 1696 * The $variables array contains the following arguments:
webmaster@1 1697 * - $content
webmaster@1 1698 * - $show_blocks
webmaster@1 1699 *
webmaster@1 1700 * @see page.tpl.php
webmaster@1 1701 */
webmaster@1 1702 function template_preprocess_page(&$variables) {
webmaster@1 1703 // Add favicon
webmaster@1 1704 if (theme_get_setting('toggle_favicon')) {
webmaster@1 1705 drupal_set_html_head('<link rel="shortcut icon" href="'. check_url(theme_get_setting('favicon')) .'" type="image/x-icon" />');
webmaster@1 1706 }
webmaster@1 1707
webmaster@1 1708 global $theme;
webmaster@1 1709 // Populate all block regions.
webmaster@1 1710 $regions = system_region_list($theme);
webmaster@1 1711 // Load all region content assigned via blocks.
webmaster@1 1712 foreach (array_keys($regions) as $region) {
webmaster@1 1713 // Prevent left and right regions from rendering blocks when 'show_blocks' == FALSE.
webmaster@1 1714 if (!(!$variables['show_blocks'] && ($region == 'left' || $region == 'right'))) {
webmaster@1 1715 $blocks = theme('blocks', $region);
webmaster@1 1716 }
webmaster@1 1717 else {
webmaster@1 1718 $blocks = '';
webmaster@1 1719 }
webmaster@1 1720 // Assign region to a region variable.
webmaster@1 1721 isset($variables[$region]) ? $variables[$region] .= $blocks : $variables[$region] = $blocks;
webmaster@1 1722 }
webmaster@1 1723
webmaster@1 1724 // Set up layout variable.
webmaster@1 1725 $variables['layout'] = 'none';
webmaster@1 1726 if (!empty($variables['left'])) {
webmaster@1 1727 $variables['layout'] = 'left';
webmaster@1 1728 }
webmaster@1 1729 if (!empty($variables['right'])) {
webmaster@1 1730 $variables['layout'] = ($variables['layout'] == 'left') ? 'both' : 'right';
webmaster@1 1731 }
webmaster@1 1732
webmaster@1 1733 // Set mission when viewing the frontpage.
webmaster@1 1734 if (drupal_is_front_page()) {
webmaster@1 1735 $mission = filter_xss_admin(theme_get_setting('mission'));
webmaster@1 1736 }
webmaster@1 1737
webmaster@1 1738 // Construct page title
webmaster@1 1739 if (drupal_get_title()) {
webmaster@1 1740 $head_title = array(strip_tags(drupal_get_title()), variable_get('site_name', 'Drupal'));
webmaster@1 1741 }
webmaster@1 1742 else {
webmaster@1 1743 $head_title = array(variable_get('site_name', 'Drupal'));
webmaster@1 1744 if (variable_get('site_slogan', '')) {
webmaster@1 1745 $head_title[] = variable_get('site_slogan', '');
webmaster@1 1746 }
webmaster@1 1747 }
webmaster@1 1748 $variables['head_title'] = implode(' | ', $head_title);
webmaster@1 1749 $variables['base_path'] = base_path();
webmaster@1 1750 $variables['front_page'] = url();
webmaster@1 1751 $variables['breadcrumb'] = theme('breadcrumb', drupal_get_breadcrumb());
webmaster@1 1752 $variables['feed_icons'] = drupal_get_feeds();
webmaster@1 1753 $variables['footer_message'] = filter_xss_admin(variable_get('site_footer', FALSE));
webmaster@1 1754 $variables['head'] = drupal_get_html_head();
webmaster@1 1755 $variables['help'] = theme('help');
webmaster@1 1756 $variables['language'] = $GLOBALS['language'];
webmaster@1 1757 $variables['language']->dir = $GLOBALS['language']->direction ? 'rtl' : 'ltr';
webmaster@1 1758 $variables['logo'] = theme_get_setting('logo');
webmaster@1 1759 $variables['messages'] = $variables['show_messages'] ? theme('status_messages') : '';
webmaster@1 1760 $variables['mission'] = isset($mission) ? $mission : '';
webmaster@1 1761 $variables['primary_links'] = theme_get_setting('toggle_primary_links') ? menu_primary_links() : array();
webmaster@1 1762 $variables['secondary_links'] = theme_get_setting('toggle_secondary_links') ? menu_secondary_links() : array();
webmaster@1 1763 $variables['search_box'] = (theme_get_setting('toggle_search') ? drupal_get_form('search_theme_form') : '');
webmaster@1 1764 $variables['site_name'] = (theme_get_setting('toggle_name') ? variable_get('site_name', 'Drupal') : '');
webmaster@1 1765 $variables['site_slogan'] = (theme_get_setting('toggle_slogan') ? variable_get('site_slogan', '') : '');
webmaster@1 1766 $variables['css'] = drupal_add_css();
webmaster@1 1767 $variables['styles'] = drupal_get_css();
webmaster@1 1768 $variables['scripts'] = drupal_get_js();
webmaster@1 1769 $variables['tabs'] = theme('menu_local_tasks');
webmaster@1 1770 $variables['title'] = drupal_get_title();
webmaster@1 1771 // Closure should be filled last.
webmaster@1 1772 $variables['closure'] = theme('closure');
webmaster@1 1773
webmaster@1 1774 if ($node = menu_get_object()) {
webmaster@1 1775 $variables['node'] = $node;
webmaster@1 1776 }
webmaster@1 1777
webmaster@1 1778 // Compile a list of classes that are going to be applied to the body element.
webmaster@1 1779 // This allows advanced theming based on context (home page, node of certain type, etc.).
webmaster@1 1780 $body_classes = array();
webmaster@1 1781 // Add a class that tells us whether we're on the front page or not.
webmaster@1 1782 $body_classes[] = $variables['is_front'] ? 'front' : 'not-front';
webmaster@1 1783 // Add a class that tells us whether the page is viewed by an authenticated user or not.
webmaster@1 1784 $body_classes[] = $variables['logged_in'] ? 'logged-in' : 'not-logged-in';
webmaster@1 1785 // Add arg(0) to make it possible to theme the page depending on the current page
webmaster@1 1786 // type (e.g. node, admin, user, etc.). To avoid illegal characters in the class,
webmaster@1 1787 // we're removing everything disallowed. We are not using 'a-z' as that might leave
webmaster@1 1788 // in certain international characters (e.g. German umlauts).
webmaster@1 1789 $body_classes[] = preg_replace('![^abcdefghijklmnopqrstuvwxyz0-9-_]+!s', '', 'page-'. form_clean_id(drupal_strtolower(arg(0))));
webmaster@1 1790 // If on an individual node page, add the node type.
webmaster@1 1791 if (isset($variables['node']) && $variables['node']->type) {
webmaster@1 1792 $body_classes[] = 'node-type-'. form_clean_id($variables['node']->type);
webmaster@1 1793 }
webmaster@1 1794 // Add information about the number of sidebars.
webmaster@1 1795 if ($variables['layout'] == 'both') {
webmaster@1 1796 $body_classes[] = 'two-sidebars';
webmaster@1 1797 }
webmaster@1 1798 elseif ($variables['layout'] == 'none') {
webmaster@1 1799 $body_classes[] = 'no-sidebars';
webmaster@1 1800 }
webmaster@1 1801 else {
webmaster@1 1802 $body_classes[] = 'one-sidebar sidebar-'. $variables['layout'];
webmaster@1 1803 }
webmaster@1 1804 // Implode with spaces.
webmaster@1 1805 $variables['body_classes'] = implode(' ', $body_classes);
webmaster@1 1806
webmaster@1 1807 // Build a list of suggested template files in order of specificity. One
webmaster@1 1808 // suggestion is made for every element of the current path, though
webmaster@1 1809 // numeric elements are not carried to subsequent suggestions. For example,
webmaster@1 1810 // http://www.example.com/node/1/edit would result in the following
webmaster@1 1811 // suggestions:
webmaster@1 1812 //
webmaster@1 1813 // page-node-edit.tpl.php
webmaster@1 1814 // page-node-1.tpl.php
webmaster@1 1815 // page-node.tpl.php
webmaster@1 1816 // page.tpl.php
webmaster@1 1817 $i = 0;
webmaster@1 1818 $suggestion = 'page';
webmaster@1 1819 $suggestions = array();
webmaster@1 1820 while ($arg = arg($i++)) {
webmaster@1 1821 $suggestions[] = $suggestion .'-'. $arg;
webmaster@1 1822 if (!is_numeric($arg)) {
webmaster@1 1823 $suggestion .= '-'. $arg;
webmaster@1 1824 }
webmaster@1 1825 }
webmaster@1 1826 if (drupal_is_front_page()) {
webmaster@1 1827 $suggestions[] = 'page-front';
webmaster@1 1828 }
webmaster@1 1829
webmaster@1 1830 if ($suggestions) {
webmaster@1 1831 $variables['template_files'] = $suggestions;
webmaster@1 1832 }
webmaster@1 1833 }
webmaster@1 1834
webmaster@1 1835 /**
webmaster@1 1836 * Process variables for node.tpl.php
webmaster@1 1837 *
webmaster@1 1838 * Most themes utilize their own copy of node.tpl.php. The default is located
webmaster@1 1839 * inside "modules/node/node.tpl.php". Look in there for the full list of
webmaster@1 1840 * variables.
webmaster@1 1841 *
webmaster@1 1842 * The $variables array contains the following arguments:
webmaster@1 1843 * - $node
webmaster@1 1844 * - $teaser
webmaster@1 1845 * - $page
webmaster@1 1846 *
webmaster@1 1847 * @see node.tpl.php
webmaster@1 1848 */
webmaster@1 1849 function template_preprocess_node(&$variables) {
webmaster@1 1850 $node = $variables['node'];
webmaster@1 1851 if (module_exists('taxonomy')) {
webmaster@1 1852 $variables['taxonomy'] = taxonomy_link('taxonomy terms', $node);
webmaster@1 1853 }
webmaster@1 1854 else {
webmaster@1 1855 $variables['taxonomy'] = array();
webmaster@1 1856 }
webmaster@1 1857
webmaster@1 1858 if ($variables['teaser'] && $node->teaser) {
webmaster@1 1859 $variables['content'] = $node->teaser;
webmaster@1 1860 }
webmaster@1 1861 elseif (isset($node->body)) {
webmaster@1 1862 $variables['content'] = $node->body;
webmaster@1 1863 }
webmaster@1 1864 else {
webmaster@1 1865 $variables['content'] = '';
webmaster@1 1866 }
webmaster@1 1867
webmaster@1 1868 $variables['date'] = format_date($node->created);
webmaster@1 1869 $variables['links'] = !empty($node->links) ? theme('links', $node->links, array('class' => 'links inline')) : '';
webmaster@1 1870 $variables['name'] = theme('username', $node);
webmaster@1 1871 $variables['node_url'] = url('node/'. $node->nid);
webmaster@1 1872 $variables['terms'] = theme('links', $variables['taxonomy'], array('class' => 'links inline'));
webmaster@1 1873 $variables['title'] = check_plain($node->title);
webmaster@1 1874
webmaster@1 1875 // Flatten the node object's member fields.
webmaster@1 1876 $variables = array_merge((array)$node, $variables);
webmaster@1 1877
webmaster@1 1878 // Display info only on certain node types.
webmaster@1 1879 if (theme_get_setting('toggle_node_info_'. $node->type)) {
webmaster@1 1880 $variables['submitted'] = theme('node_submitted', $node);
webmaster@1 1881 $variables['picture'] = theme_get_setting('toggle_node_user_picture') ? theme('user_picture', $node) : '';
webmaster@1 1882 }
webmaster@1 1883 else {
webmaster@1 1884 $variables['submitted'] = '';
webmaster@1 1885 $variables['picture'] = '';
webmaster@1 1886 }
webmaster@1 1887 // Clean up name so there are no underscores.
webmaster@1 1888 $variables['template_files'][] = 'node-'. $node->type;
webmaster@1 1889 }
webmaster@1 1890
webmaster@1 1891 /**
webmaster@1 1892 * Process variables for block.tpl.php
webmaster@1 1893 *
webmaster@1 1894 * Prepare the values passed to the theme_block function to be passed
webmaster@1 1895 * into a pluggable template engine. Uses block properties to generate a
webmaster@1 1896 * series of template file suggestions. If none are found, the default
webmaster@1 1897 * block.tpl.php is used.
webmaster@1 1898 *
webmaster@1 1899 * Most themes utilize their own copy of block.tpl.php. The default is located
webmaster@1 1900 * inside "modules/system/block.tpl.php". Look in there for the full list of
webmaster@1 1901 * variables.
webmaster@1 1902 *
webmaster@1 1903 * The $variables array contains the following arguments:
webmaster@1 1904 * - $block
webmaster@1 1905 *
webmaster@1 1906 * @see block.tpl.php
webmaster@1 1907 */
webmaster@1 1908 function template_preprocess_block(&$variables) {
webmaster@1 1909 static $block_counter = array();
webmaster@1 1910 // All blocks get an independent counter for each region.
webmaster@1 1911 if (!isset($block_counter[$variables['block']->region])) {
webmaster@1 1912 $block_counter[$variables['block']->region] = 1;
webmaster@1 1913 }
webmaster@1 1914 // Same with zebra striping.
webmaster@1 1915 $variables['block_zebra'] = ($block_counter[$variables['block']->region] % 2) ? 'odd' : 'even';
webmaster@1 1916 $variables['block_id'] = $block_counter[$variables['block']->region]++;
webmaster@1 1917
webmaster@1 1918 $variables['template_files'][] = 'block-'. $variables['block']->region;
webmaster@1 1919 $variables['template_files'][] = 'block-'. $variables['block']->module;
webmaster@1 1920 $variables['template_files'][] = 'block-'. $variables['block']->module .'-'. $variables['block']->delta;
webmaster@1 1921 }
webmaster@1 1922