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