theme.inc

You are here

  1. 7 includes/theme.inc
  2. 4.6 includes/theme.inc
  3. 4.7 includes/theme.inc
  4. 5 includes/theme.inc
  5. 6 includes/theme.inc
  6. 8 core/modules/views/theme/theme.inc
  7. 8 core/includes/theme.inc

The theme system, which controls the output of Drupal.

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

Functions

Namesort descending Description
drupal_find_base_themes Find all the base themes for the specified theme.
drupal_find_theme_functions Allows themes and/or theme engines to discover overridden theme functions.
drupal_find_theme_templates Allows themes and/or theme engines to easily discover overridden templates.
drupal_theme_access Determines if a theme is available to use.
drupal_theme_initialize Initializes the theme system by loading the theme.
drupal_theme_rebuild Forces the system to rebuild the theme registry.
list_themes Returns a list of all currently available themes.
path_to_theme Returns the path to the current themed element.
template_preprocess Adds a default set of helper variables for variable processors and templates.
template_preprocess_html Preprocess variables for html.tpl.php
template_preprocess_maintenance_page Process variables for maintenance-page.tpl.php.
template_preprocess_page Preprocess variables for page.tpl.php
template_preprocess_region Preprocess variables for region.tpl.php
template_preprocess_username Preprocesses variables for theme_username().
template_process Adds helper variables derived from variables defined during preprocessing.
template_process_html Process variables for html.tpl.php
template_process_maintenance_page Theme process function for theme_maintenance_field().
template_process_page Process variables for page.tpl.php
template_process_username Processes variables for theme_username().
theme Generates themed output.
theme_breadcrumb Returns HTML for a breadcrumb trail.
theme_disable Disables a given list of themes.
theme_enable Enables a given list of themes.
theme_feed_icon Returns HTML for a feed icon.
theme_get_registry Gets the theme registry.
theme_get_setting Retrieves a setting for the current theme or for a given theme.
theme_get_suggestions Generate an array of suggestions from path arguments.
theme_html_tag Returns HTML for a generic HTML tag with attributes.
theme_image Returns HTML for an image.
theme_indentation Returns HTML for an indentation div; used for drag and drop tables.
theme_item_list Returns HTML for a list or nested list of items.
theme_link Returns HTML for a link.
theme_links Returns HTML for a set of links.
theme_mark Returns HTML for a marker for new or updated content.
theme_more_help_link Returns HTML for a "more help" link.
theme_more_link Returns HTML for a "more" link, like those used in blocks.
theme_progress_bar Returns HTML for a progress bar.
theme_render_template Renders a system default template, which is essentially a PHP template.
theme_status_messages Returns HTML for status and/or error messages, grouped by type.
theme_table Returns HTML for a table.
theme_tablesort_indicator Returns HTML for a sort icon.
theme_username Returns HTML for a username, potentially linked to the user's page.
_drupal_theme_access Helper function for determining access to a theme.
_drupal_theme_initialize Initializes the theme system given already loaded information.
_template_preprocess_default_variables Returns hook-independent variables to template_preprocess().
_theme_build_registry Builds the theme registry cache.
_theme_load_registry Gets the theme_registry cache; if it doesn't exist, builds it.
_theme_process_registry Process a single implementation of hook_theme().
_theme_registry_callback Sets the callback that will be used by theme_get_registry().
_theme_save_registry Writes the theme_registry cache into the database.
_theme_table_cell Returns HTML output for a single table cell for theme_table().

Constants

Namesort descending Description
MARK_NEW Mark content as being new.
MARK_READ Mark content as read.
MARK_UPDATED Mark content as being updated.

Classes

Namesort descending Description
ThemeRegistry Builds the run-time theme registry.

File

includes/theme.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * The theme system, which controls the output of Drupal.
  5. *
  6. * The theme system allows for nearly all output of the Drupal system to be
  7. * customized by user themes.
  8. */
  9. /**
  10. * @defgroup content_flags Content markers
  11. * @{
  12. * Markers used by theme_mark() and node_mark() to designate content.
  13. * @see theme_mark(), node_mark()
  14. */
  15. /**
  16. * Mark content as read.
  17. */
  18. define('MARK_READ', 0);
  19. /**
  20. * Mark content as being new.
  21. */
  22. define('MARK_NEW', 1);
  23. /**
  24. * Mark content as being updated.
  25. */
  26. define('MARK_UPDATED', 2);
  27. /**
  28. * @} End of "Content markers".
  29. */
  30. /**
  31. * Determines if a theme is available to use.
  32. *
  33. * @param $theme
  34. * Either the name of a theme or a full theme object.
  35. *
  36. * @return
  37. * Boolean TRUE if the theme is enabled or is the site administration theme;
  38. * FALSE otherwise.
  39. */
  40. function drupal_theme_access($theme) {
  41. if (is_object($theme)) {
  42. return _drupal_theme_access($theme);
  43. }
  44. else {
  45. $themes = list_themes();
  46. return isset($themes[$theme]) && _drupal_theme_access($themes[$theme]);
  47. }
  48. }
  49. /**
  50. * Helper function for determining access to a theme.
  51. *
  52. * @see drupal_theme_access()
  53. */
  54. function _drupal_theme_access($theme) {
  55. $admin_theme = variable_get('admin_theme');
  56. return !empty($theme->status) || ($admin_theme && $theme->name == $admin_theme);
  57. }
  58. /**
  59. * Initializes the theme system by loading the theme.
  60. */
  61. function drupal_theme_initialize() {
  62. global $theme, $user, $theme_key;
  63. // If $theme is already set, assume the others are set, too, and do nothing
  64. if (isset($theme)) {
  65. return;
  66. }
  67. drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);
  68. $themes = list_themes();
  69. // Only select the user selected theme if it is available in the
  70. // list of themes that can be accessed.
  71. $theme = !empty($user->theme) && drupal_theme_access($user->theme) ? $user->theme : variable_get('theme_default', 'bartik');
  72. // Allow modules to override the theme. Validation has already been performed
  73. // inside menu_get_custom_theme(), so we do not need to check it again here.
  74. $custom_theme = menu_get_custom_theme();
  75. $theme = !empty($custom_theme) ? $custom_theme : $theme;
  76. // Store the identifier for retrieving theme settings with.
  77. $theme_key = $theme;
  78. // Find all our ancestor themes and put them in an array.
  79. $base_theme = array();
  80. $ancestor = $theme;
  81. while ($ancestor && isset($themes[$ancestor]->base_theme)) {
  82. $ancestor = $themes[$ancestor]->base_theme;
  83. $base_theme[] = $themes[$ancestor];
  84. }
  85. _drupal_theme_initialize($themes[$theme], array_reverse($base_theme));
  86. // Themes can have alter functions, so reset the drupal_alter() cache.
  87. drupal_static_reset('drupal_alter');
  88. // Provide the page with information about the theme that's used, so that a
  89. // later Ajax request can be rendered using the same theme.
  90. // @see ajax_base_page_theme()
  91. $setting['ajaxPageState'] = array(
  92. 'theme' => $theme_key,
  93. 'theme_token' => drupal_get_token($theme_key),
  94. );
  95. drupal_add_js($setting, 'setting');
  96. }
  97. /**
  98. * Initializes the theme system given already loaded information.
  99. *
  100. * This function is useful to initialize a theme when no database is present.
  101. *
  102. * @param $theme
  103. * An object with the following information:
  104. * filename
  105. * The .info file for this theme. The 'path' to
  106. * the theme will be in this file's directory. (Required)
  107. * owner
  108. * The path to the .theme file or the .engine file to load for
  109. * the theme. (Required)
  110. * stylesheet
  111. * The primary stylesheet for the theme. (Optional)
  112. * engine
  113. * The name of theme engine to use. (Optional)
  114. * @param $base_theme
  115. * An optional array of objects that represent the 'base theme' if the
  116. * theme is meant to be derivative of another theme. It requires
  117. * the same information as the $theme object. It should be in
  118. * 'oldest first' order, meaning the top level of the chain will
  119. * be first.
  120. * @param $registry_callback
  121. * The callback to invoke to set the theme registry.
  122. */
  123. function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callback = '_theme_load_registry') {
  124. global $theme_info, $base_theme_info, $theme_engine, $theme_path;
  125. $theme_info = $theme;
  126. $base_theme_info = $base_theme;
  127. $theme_path = dirname($theme->filename);
  128. // Prepare stylesheets from this theme as well as all ancestor themes.
  129. // We work it this way so that we can have child themes override parent
  130. // theme stylesheets easily.
  131. $final_stylesheets = array();
  132. // Grab stylesheets from base theme
  133. foreach ($base_theme as $base) {
  134. if (!empty($base->stylesheets)) {
  135. foreach ($base->stylesheets as $media => $stylesheets) {
  136. foreach ($stylesheets as $name => $stylesheet) {
  137. $final_stylesheets[$media][$name] = $stylesheet;
  138. }
  139. }
  140. }
  141. }
  142. // Add stylesheets used by this theme.
  143. if (!empty($theme->stylesheets)) {
  144. foreach ($theme->stylesheets as $media => $stylesheets) {
  145. foreach ($stylesheets as $name => $stylesheet) {
  146. $final_stylesheets[$media][$name] = $stylesheet;
  147. }
  148. }
  149. }
  150. // And now add the stylesheets properly
  151. foreach ($final_stylesheets as $media => $stylesheets) {
  152. foreach ($stylesheets as $stylesheet) {
  153. drupal_add_css($stylesheet, array('group' => CSS_THEME, 'every_page' => TRUE, 'media' => $media));
  154. }
  155. }
  156. // Do basically the same as the above for scripts
  157. $final_scripts = array();
  158. // Grab scripts from base theme
  159. foreach ($base_theme as $base) {
  160. if (!empty($base->scripts)) {
  161. foreach ($base->scripts as $name => $script) {
  162. $final_scripts[$name] = $script;
  163. }
  164. }
  165. }
  166. // Add scripts used by this theme.
  167. if (!empty($theme->scripts)) {
  168. foreach ($theme->scripts as $name => $script) {
  169. $final_scripts[$name] = $script;
  170. }
  171. }
  172. // Add scripts used by this theme.
  173. foreach ($final_scripts as $script) {
  174. drupal_add_js($script, array('group' => JS_THEME, 'every_page' => TRUE));
  175. }
  176. $theme_engine = NULL;
  177. // Initialize the theme.
  178. if (isset($theme->engine)) {
  179. // Include the engine.
  180. include_once DRUPAL_ROOT . '/' . $theme->owner;
  181. $theme_engine = $theme->engine;
  182. if (function_exists($theme_engine . '_init')) {
  183. foreach ($base_theme as $base) {
  184. call_user_func($theme_engine . '_init', $base);
  185. }
  186. call_user_func($theme_engine . '_init', $theme);
  187. }
  188. }
  189. else {
  190. // include non-engine theme files
  191. foreach ($base_theme as $base) {
  192. // Include the theme file or the engine.
  193. if (!empty($base->owner)) {
  194. include_once DRUPAL_ROOT . '/' . $base->owner;
  195. }
  196. }
  197. // and our theme gets one too.
  198. if (!empty($theme->owner)) {
  199. include_once DRUPAL_ROOT . '/' . $theme->owner;
  200. }
  201. }
  202. if (isset($registry_callback)) {
  203. _theme_registry_callback($registry_callback, array($theme, $base_theme, $theme_engine));
  204. }
  205. }
  206. /**
  207. * Gets the theme registry.
  208. *
  209. * @param $complete
  210. * Optional boolean to indicate whether to return the complete theme registry
  211. * array or an instance of the ThemeRegistry class. If TRUE, the complete
  212. * theme registry array will be returned. This is useful if you want to
  213. * foreach over the whole registry, use array_* functions or inspect it in a
  214. * debugger. If FALSE, an instance of the ThemeRegistry class will be
  215. * returned, this provides an ArrayObject which allows it to be accessed
  216. * with array syntax and isset(), and should be more lightweight
  217. * than the full registry. Defaults to TRUE.
  218. *
  219. * @return
  220. * The complete theme registry array, or an instance of the ThemeRegistry
  221. * class.
  222. */
  223. function theme_get_registry($complete = TRUE) {
  224. // Use the advanced drupal_static() pattern, since this is called very often.
  225. static $drupal_static_fast;
  226. if (!isset($drupal_static_fast)) {
  227. $drupal_static_fast['registry'] = &drupal_static('theme_get_registry');
  228. }
  229. $theme_registry = &$drupal_static_fast['registry'];
  230. // Initialize the theme, if this is called early in the bootstrap, or after
  231. // static variables have been reset.
  232. if (!is_array($theme_registry)) {
  233. drupal_theme_initialize();
  234. $theme_registry = array();
  235. }
  236. $key = (int) $complete;
  237. if (!isset($theme_registry[$key])) {
  238. list($callback, $arguments) = _theme_registry_callback();
  239. if (!$complete) {
  240. $arguments[] = FALSE;
  241. }
  242. $theme_registry[$key] = call_user_func_array($callback, $arguments);
  243. }
  244. return $theme_registry[$key];
  245. }
  246. /**
  247. * Sets the callback that will be used by theme_get_registry().
  248. *
  249. * @param $callback
  250. * The name of the callback function.
  251. * @param $arguments
  252. * The arguments to pass to the function.
  253. */
  254. function _theme_registry_callback($callback = NULL, array $arguments = array()) {
  255. static $stored;
  256. if (isset($callback)) {
  257. $stored = array($callback, $arguments);
  258. }
  259. return $stored;
  260. }
  261. /**
  262. * Gets the theme_registry cache; if it doesn't exist, builds it.
  263. *
  264. * @param $theme
  265. * The loaded $theme object as returned by list_themes().
  266. * @param $base_theme
  267. * An array of loaded $theme objects representing the ancestor themes in
  268. * oldest first order.
  269. * @param $theme_engine
  270. * The name of the theme engine.
  271. * @param $complete
  272. * Whether to load the complete theme registry or an instance of the
  273. * ThemeRegistry class.
  274. *
  275. * @return
  276. * The theme registry array, or an instance of the ThemeRegistry class.
  277. */
  278. function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL, $complete = TRUE) {
  279. if ($complete) {
  280. // Check the theme registry cache; if it exists, use it.
  281. $cached = cache_get("theme_registry:$theme->name");
  282. if (isset($cached->data)) {
  283. $registry = $cached->data;
  284. }
  285. else {
  286. // If not, build one and cache it.
  287. $registry = _theme_build_registry($theme, $base_theme, $theme_engine);
  288. // Only persist this registry if all modules are loaded. This assures a
  289. // complete set of theme hooks.
  290. if (module_load_all(NULL)) {
  291. _theme_save_registry($theme, $registry);
  292. }
  293. }
  294. return $registry;
  295. }
  296. else {
  297. return new ThemeRegistry('theme_registry:runtime:' . $theme->name, 'cache');
  298. }
  299. }
  300. /**
  301. * Writes the theme_registry cache into the database.
  302. */
  303. function _theme_save_registry($theme, $registry) {
  304. cache_set("theme_registry:$theme->name", $registry);
  305. }
  306. /**
  307. * Forces the system to rebuild the theme registry.
  308. *
  309. * This function should be called when modules are added to the system, or when
  310. * a dynamic system needs to add more theme hooks.
  311. */
  312. function drupal_theme_rebuild() {
  313. drupal_static_reset('theme_get_registry');
  314. cache_clear_all('theme_registry', 'cache', TRUE);
  315. }
  316. /**
  317. * Builds the run-time theme registry.
  318. *
  319. * Extends DrupalCacheArray to allow the theme registry to be accessed as a
  320. * complete registry, while internally caching only the parts of the registry
  321. * that are actually in use on the site. On cache misses the complete
  322. * theme registry is loaded and used to update the run-time cache.
  323. */
  324. class ThemeRegistry Extends DrupalCacheArray {
  325. /**
  326. * Whether the partial registry can be persisted to the cache.
  327. *
  328. * This is only allowed if all modules and the request method is GET. theme()
  329. * should be very rarely called on POST requests and this avoids polluting
  330. * the runtime cache.
  331. */
  332. protected $persistable;
  333. /**
  334. * The complete theme registry array.
  335. */
  336. protected $completeRegistry;
  337. function __construct($cid, $bin) {
  338. $this->cid = $cid;
  339. $this->bin = $bin;
  340. $this->persistable = module_load_all(NULL) && $_SERVER['REQUEST_METHOD'] == 'GET';
  341. $data = array();
  342. if ($this->persistable && $cached = cache_get($this->cid, $this->bin)) {
  343. $data = $cached->data;
  344. }
  345. else {
  346. // If there is no runtime cache stored, fetch the full theme registry,
  347. // but then initialize each value to NULL. This allows offsetExists()
  348. // to function correctly on non-registered theme hooks without triggering
  349. // a call to resolveCacheMiss().
  350. $data = $this->initializeRegistry();
  351. if ($this->persistable) {
  352. $this->set($data);
  353. }
  354. }
  355. $this->storage = $data;
  356. }
  357. /**
  358. * Initializes the full theme registry.
  359. *
  360. * @return
  361. * An array with the keys of the full theme registry, but the values
  362. * initialized to NULL.
  363. */
  364. function initializeRegistry() {
  365. $this->completeRegistry = theme_get_registry();
  366. return array_fill_keys(array_keys($this->completeRegistry), NULL);
  367. }
  368. public function offsetExists($offset) {
  369. // Since the theme registry allows for theme hooks to be requested that
  370. // are not registered, just check the existence of the key in the registry.
  371. // Use array_key_exists() here since a NULL value indicates that the theme
  372. // hook exists but has not yet been requested.
  373. return array_key_exists($offset, $this->storage);
  374. }
  375. public function offsetGet($offset) {
  376. // If the offset is set but empty, it is a registered theme hook that has
  377. // not yet been requested. Offsets that do not exist at all were not
  378. // registered in hook_theme().
  379. if (isset($this->storage[$offset])) {
  380. return $this->storage[$offset];
  381. }
  382. elseif (array_key_exists($offset, $this->storage)) {
  383. return $this->resolveCacheMiss($offset);
  384. }
  385. }
  386. public function resolveCacheMiss($offset) {
  387. if (!isset($this->completeRegistry)) {
  388. $this->completeRegistry = theme_get_registry();
  389. }
  390. $this->storage[$offset] = $this->completeRegistry[$offset];
  391. if ($this->persistable) {
  392. $this->persist($offset);
  393. }
  394. return $this->storage[$offset];
  395. }
  396. public function set($data, $lock = TRUE) {
  397. $lock_name = $this->cid . ':' . $this->bin;
  398. if (!$lock || lock_acquire($lock_name)) {
  399. if ($cached = cache_get($this->cid, $this->bin)) {
  400. // Use array merge instead of union so that filled in values in $data
  401. // overwrite empty values in the current cache.
  402. $data = array_merge($cached->data, $data);
  403. }
  404. else {
  405. $registry = $this->initializeRegistry();
  406. $data = array_merge($registry, $data);
  407. }
  408. cache_set($this->cid, $data, $this->bin);
  409. if ($lock) {
  410. lock_release($lock_name);
  411. }
  412. }
  413. }
  414. }
  415. /**
  416. * Process a single implementation of hook_theme().
  417. *
  418. * @param $cache
  419. * The theme registry that will eventually be cached; It is an associative
  420. * array keyed by theme hooks, whose values are associative arrays describing
  421. * the hook:
  422. * - 'type': The passed-in $type.
  423. * - 'theme path': The passed-in $path.
  424. * - 'function': The name of the function generating output for this theme
  425. * hook. Either defined explicitly in hook_theme() or, if neither 'function'
  426. * nor 'template' is defined, then the default theme function name is used.
  427. * The default theme function name is the theme hook prefixed by either
  428. * 'theme_' for modules or '$name_' for everything else. If 'function' is
  429. * defined, 'template' is not used.
  430. * - 'template': The filename of the template generating output for this
  431. * theme hook. The template is in the directory defined by the 'path' key of
  432. * hook_theme() or defaults to $path.
  433. * - 'variables': The variables for this theme hook as defined in
  434. * hook_theme(). If there is more than one implementation and 'variables' is
  435. * not specified in a later one, then the previous definition is kept.
  436. * - 'render element': The renderable element for this theme hook as defined
  437. * in hook_theme(). If there is more than one implementation and
  438. * 'render element' is not specified in a later one, then the previous
  439. * definition is kept.
  440. * - 'preprocess functions': See theme() for detailed documentation.
  441. * - 'process functions': See theme() for detailed documentation.
  442. * @param $name
  443. * The name of the module, theme engine, base theme engine, theme or base
  444. * theme implementing hook_theme().
  445. * @param $type
  446. * One of 'module', 'theme_engine', 'base_theme_engine', 'theme', or
  447. * 'base_theme'. Unlike regular hooks that can only be implemented by modules,
  448. * each of these can implement hook_theme(). _theme_process_registry() is
  449. * called in aforementioned order and new entries override older ones. For
  450. * example, if a theme hook is both defined by a module and a theme, then the
  451. * definition in the theme will be used.
  452. * @param $theme
  453. * The loaded $theme object as returned from list_themes().
  454. * @param $path
  455. * The directory where $name is. For example, modules/system or
  456. * themes/bartik.
  457. *
  458. * @see theme()
  459. * @see _theme_build_registry()
  460. * @see hook_theme()
  461. * @see list_themes()
  462. */
  463. function _theme_process_registry(&$cache, $name, $type, $theme, $path) {
  464. $result = array();
  465. // Processor functions work in two distinct phases with the process
  466. // functions always being executed after the preprocess functions.
  467. $variable_process_phases = array(
  468. 'preprocess functions' => 'preprocess',
  469. 'process functions' => 'process',
  470. );
  471. $hook_defaults = array(
  472. 'variables' => TRUE,
  473. 'render element' => TRUE,
  474. 'pattern' => TRUE,
  475. 'base hook' => TRUE,
  476. );
  477. // Invoke the hook_theme() implementation, process what is returned, and
  478. // merge it into $cache.
  479. $function = $name . '_theme';
  480. if (function_exists($function)) {
  481. $result = $function($cache, $type, $theme, $path);
  482. foreach ($result as $hook => $info) {
  483. // When a theme or engine overrides a module's theme function
  484. // $result[$hook] will only contain key/value pairs for information being
  485. // overridden. Pull the rest of the information from what was defined by
  486. // an earlier hook.
  487. // Fill in the type and path of the module, theme, or engine that
  488. // implements this theme function.
  489. $result[$hook]['type'] = $type;
  490. $result[$hook]['theme path'] = $path;
  491. // If function and file are omitted, default to standard naming
  492. // conventions.
  493. if (!isset($info['template']) && !isset($info['function'])) {
  494. $result[$hook]['function'] = ($type == 'module' ? 'theme_' : $name . '_') . $hook;
  495. }
  496. if (isset($cache[$hook]['includes'])) {
  497. $result[$hook]['includes'] = $cache[$hook]['includes'];
  498. }
  499. // If the theme implementation defines a file, then also use the path
  500. // that it defined. Otherwise use the default path. This allows
  501. // system.module to declare theme functions on behalf of core .include
  502. // files.
  503. if (isset($info['file'])) {
  504. $include_file = isset($info['path']) ? $info['path'] : $path;
  505. $include_file .= '/' . $info['file'];
  506. include_once DRUPAL_ROOT . '/' . $include_file;
  507. $result[$hook]['includes'][] = $include_file;
  508. }
  509. // If the default keys are not set, use the default values registered
  510. // by the module.
  511. if (isset($cache[$hook])) {
  512. $result[$hook] += array_intersect_key($cache[$hook], $hook_defaults);
  513. }
  514. // The following apply only to theming hooks implemented as templates.
  515. if (isset($info['template'])) {
  516. // Prepend the current theming path when none is set.
  517. if (!isset($info['path'])) {
  518. $result[$hook]['template'] = $path . '/' . $info['template'];
  519. }
  520. }
  521. // Allow variable processors for all theming hooks, whether the hook is
  522. // implemented as a template or as a function.
  523. foreach ($variable_process_phases as $phase_key => $phase) {
  524. // Check for existing variable processors. Ensure arrayness.
  525. if (!isset($info[$phase_key]) || !is_array($info[$phase_key])) {
  526. $info[$phase_key] = array();
  527. $prefixes = array();
  528. if ($type == 'module') {
  529. // Default variable processor prefix.
  530. $prefixes[] = 'template';
  531. // Add all modules so they can intervene with their own variable
  532. // processors. This allows them to provide variable processors even
  533. // if they are not the owner of the current hook.
  534. $prefixes += module_list();
  535. }
  536. elseif ($type == 'theme_engine' || $type == 'base_theme_engine') {
  537. // Theme engines get an extra set that come before the normally
  538. // named variable processors.
  539. $prefixes[] = $name . '_engine';
  540. // The theme engine registers on behalf of the theme using the
  541. // theme's name.
  542. $prefixes[] = $theme;
  543. }
  544. else {
  545. // This applies when the theme manually registers their own variable
  546. // processors.
  547. $prefixes[] = $name;
  548. }
  549. foreach ($prefixes as $prefix) {
  550. // Only use non-hook-specific variable processors for theming hooks
  551. // implemented as templates. See theme().
  552. if (isset($info['template']) && function_exists($prefix . '_' . $phase)) {
  553. $info[$phase_key][] = $prefix . '_' . $phase;
  554. }
  555. if (function_exists($prefix . '_' . $phase . '_' . $hook)) {
  556. $info[$phase_key][] = $prefix . '_' . $phase . '_' . $hook;
  557. }
  558. }
  559. }
  560. // Check for the override flag and prevent the cached variable
  561. // processors from being used. This allows themes or theme engines to
  562. // remove variable processors set earlier in the registry build.
  563. if (!empty($info['override ' . $phase_key])) {
  564. // Flag not needed inside the registry.
  565. unset($result[$hook]['override ' . $phase_key]);
  566. }
  567. elseif (isset($cache[$hook][$phase_key]) && is_array($cache[$hook][$phase_key])) {
  568. $info[$phase_key] = array_merge($cache[$hook][$phase_key], $info[$phase_key]);
  569. }
  570. $result[$hook][$phase_key] = $info[$phase_key];
  571. }
  572. }
  573. // Merge the newly created theme hooks into the existing cache.
  574. $cache = $result + $cache;
  575. }
  576. // Let themes have variable processors even if they didn't register a
  577. // template.
  578. if ($type == 'theme' || $type == 'base_theme') {
  579. foreach ($cache as $hook => $info) {
  580. // Check only if not registered by the theme or engine.
  581. if (empty($result[$hook])) {
  582. foreach ($variable_process_phases as $phase_key => $phase) {
  583. if (!isset($info[$phase_key])) {
  584. $cache[$hook][$phase_key] = array();
  585. }
  586. // Only use non-hook-specific variable processors for theming hooks
  587. // implemented as templates. See theme().
  588. if (isset($info['template']) && function_exists($name . '_' . $phase)) {
  589. $cache[$hook][$phase_key][] = $name . '_' . $phase;
  590. }
  591. if (function_exists($name . '_' . $phase . '_' . $hook)) {
  592. $cache[$hook][$phase_key][] = $name . '_' . $phase . '_' . $hook;
  593. $cache[$hook]['theme path'] = $path;
  594. }
  595. // Ensure uniqueness.
  596. $cache[$hook][$phase_key] = array_unique($cache[$hook][$phase_key]);
  597. }
  598. }
  599. }
  600. }
  601. }
  602. /**
  603. * Builds the theme registry cache.
  604. *
  605. * @param $theme
  606. * The loaded $theme object as returned by list_themes().
  607. * @param $base_theme
  608. * An array of loaded $theme objects representing the ancestor themes in
  609. * oldest first order.
  610. * @param $theme_engine
  611. * The name of the theme engine.
  612. */
  613. function _theme_build_registry($theme, $base_theme, $theme_engine) {
  614. $cache = array();
  615. // First, process the theme hooks advertised by modules. This will
  616. // serve as the basic registry. Since the list of enabled modules is the same
  617. // regardless of the theme used, this is cached in its own entry to save
  618. // building it for every theme.
  619. if ($cached = cache_get('theme_registry:build:modules')) {
  620. $cache = $cached->data;
  621. }
  622. else {
  623. foreach (module_implements('theme') as $module) {
  624. _theme_process_registry($cache, $module, 'module', $module, drupal_get_path('module', $module));
  625. }
  626. // Only cache this registry if all modules are loaded.
  627. if (module_load_all(NULL)) {
  628. cache_set('theme_registry:build:modules', $cache);
  629. }
  630. }
  631. // Process each base theme.
  632. foreach ($base_theme as $base) {
  633. // If the base theme uses a theme engine, process its hooks.
  634. $base_path = dirname($base->filename);
  635. if ($theme_engine) {
  636. _theme_process_registry($cache, $theme_engine, 'base_theme_engine', $base->name, $base_path);
  637. }
  638. _theme_process_registry($cache, $base->name, 'base_theme', $base->name, $base_path);
  639. }
  640. // And then the same thing, but for the theme.
  641. if ($theme_engine) {
  642. _theme_process_registry($cache, $theme_engine, 'theme_engine', $theme->name, dirname($theme->filename));
  643. }
  644. // Finally, hooks provided by the theme itself.
  645. _theme_process_registry($cache, $theme->name, 'theme', $theme->name, dirname($theme->filename));
  646. // Let modules alter the registry.
  647. drupal_alter('theme_registry', $cache);
  648. // Optimize the registry to not have empty arrays for functions.
  649. foreach ($cache as $hook => $info) {
  650. foreach (array('preprocess functions', 'process functions') as $phase) {
  651. if (empty($info[$phase])) {
  652. unset($cache[$hook][$phase]);
  653. }
  654. }
  655. }
  656. return $cache;
  657. }
  658. /**
  659. * Returns a list of all currently available themes.
  660. *
  661. * Retrieved from the database, if available and the site is not in maintenance
  662. * mode; otherwise compiled freshly from the filesystem.
  663. *
  664. * @param $refresh
  665. * Whether to reload the list of themes from the database. Defaults to FALSE.
  666. *
  667. * @return
  668. * An associative array of the currently available themes. The keys are the
  669. * themes' machine names and the values are objects having the following
  670. * properties:
  671. * - filename: The filepath and name of the .info file.
  672. * - name: The machine name of the theme.
  673. * - status: 1 for enabled, 0 for disabled themes.
  674. * - info: The contents of the .info file.
  675. * - stylesheets: A two dimensional array, using the first key for the
  676. * media attribute (e.g. 'all'), the second for the name of the file
  677. * (e.g. style.css). The value is a complete filepath (e.g.
  678. * themes/bartik/style.css). Not set if no stylesheets are defined in the
  679. * .info file.
  680. * - scripts: An associative array of JavaScripts, using the filename as key
  681. * and the complete filepath as value. Not set if no scripts are defined in
  682. * the .info file.
  683. * - prefix: The base theme engine prefix.
  684. * - engine: The machine name of the theme engine.
  685. * - base_theme: If this is a sub-theme, the machine name of the base theme
  686. * defined in the .info file. Otherwise, the element is not set.
  687. * - base_themes: If this is a sub-theme, an associative array of the
  688. * base-theme ancestors of this theme, starting with this theme's base
  689. * theme, then the base theme's own base theme, etc. Each entry has an
  690. * array key equal to the theme's machine name, and a value equal to the
  691. * human-readable theme name; if a theme with matching machine name does
  692. * not exist in the system, the value will instead be NULL (and since the
  693. * system would not know whether that theme itself has a base theme, that
  694. * will end the array of base themes). This is not set if the theme is not
  695. * a sub-theme.
  696. * - sub_themes: An associative array of themes on the system that are
  697. * either direct sub-themes (that is, they declare this theme to be
  698. * their base theme), direct sub-themes of sub-themes, etc. The keys are
  699. * the themes' machine names, and the values are the themes' human-readable
  700. * names. This element is not set if there are no themes on the system that
  701. * declare this theme as their base theme.
  702. */
  703. function list_themes($refresh = FALSE) {
  704. $list = &drupal_static(__FUNCTION__, array());
  705. if ($refresh) {
  706. $list = array();
  707. system_list_reset();
  708. }
  709. if (empty($list)) {
  710. $list = array();
  711. $themes = array();
  712. // Extract from the database only when it is available.
  713. // Also check that the site is not in the middle of an install or update.
  714. if (!defined('MAINTENANCE_MODE')) {
  715. try {
  716. $themes = system_list('theme');
  717. }
  718. catch (Exception $e) {
  719. // If the database is not available, rebuild the theme data.
  720. $themes = _system_rebuild_theme_data();
  721. }
  722. }
  723. else {
  724. // Scan the installation when the database should not be read.
  725. $themes = _system_rebuild_theme_data();
  726. }
  727. foreach ($themes as $theme) {
  728. foreach ($theme->info['stylesheets'] as $media => $stylesheets) {
  729. foreach ($stylesheets as $stylesheet => $path) {
  730. $theme->stylesheets[$media][$stylesheet] = $path;
  731. }
  732. }
  733. foreach ($theme->info['scripts'] as $script => $path) {
  734. $theme->scripts[$script] = $path;
  735. }
  736. if (isset($theme->info['engine'])) {
  737. $theme->engine = $theme->info['engine'];
  738. }
  739. if (isset($theme->info['base theme'])) {
  740. $theme->base_theme = $theme->info['base theme'];
  741. }
  742. // Status is normally retrieved from the database. Add zero values when
  743. // read from the installation directory to prevent notices.
  744. if (!isset($theme->status)) {
  745. $theme->status = 0;
  746. }
  747. $list[$theme->name] = $theme;
  748. }
  749. }
  750. return $list;
  751. }
  752. /**
  753. * Find all the base themes for the specified theme.
  754. *
  755. * Themes can inherit templates and function implementations from earlier themes.
  756. *
  757. * @param $themes
  758. * An array of available themes.
  759. * @param $key
  760. * The name of the theme whose base we are looking for.
  761. * @param $used_keys
  762. * A recursion parameter preventing endless loops.
  763. * @return
  764. * Returns an array of all of the theme's ancestors; the first element's value
  765. * will be NULL if an error occurred.
  766. */
  767. function drupal_find_base_themes($themes, $key, $used_keys = array()) {
  768. $base_key = $themes[$key]->info['base theme'];
  769. // Does the base theme exist?
  770. if (!isset($themes[$base_key])) {
  771. return array($base_key => NULL);
  772. }
  773. $current_base_theme = array($base_key => $themes[$base_key]->info['name']);
  774. // Is the base theme itself a child of another theme?
  775. if (isset($themes[$base_key]->info['base theme'])) {
  776. // Do we already know the base themes of this theme?
  777. if (isset($themes[$base_key]->base_themes)) {
  778. return $themes[$base_key]->base_themes + $current_base_theme;
  779. }
  780. // Prevent loops.
  781. if (!empty($used_keys[$base_key])) {
  782. return array($base_key => NULL);
  783. }
  784. $used_keys[$base_key] = TRUE;
  785. return drupal_find_base_themes($themes, $base_key, $used_keys) + $current_base_theme;
  786. }
  787. // If we get here, then this is our parent theme.
  788. return $current_base_theme;
  789. }
  790. /**
  791. * Generates themed output.
  792. *
  793. * All requests for themed output must go through this function (however,
  794. * calling the theme() function directly is strongly discouraged - see next
  795. * paragraph). It examines the request and routes it to the appropriate
  796. * @link themeable theme function or template @endlink, by checking the theme
  797. * registry.
  798. *
  799. * Avoid calling this function directly. It is preferable to replace direct
  800. * calls to the theme() function with calls to drupal_render() by passing a
  801. * render array with a #theme key to drupal_render(), which in turn calls
  802. * theme().
  803. *
  804. * @section sec_theme_hooks Theme Hooks
  805. * Most commonly, the first argument to this function is the name of the theme
  806. * hook. For instance, to theme a taxonomy term, the theme hook name is
  807. * 'taxonomy_term'. Modules register theme hooks within a hook_theme()
  808. * implementation and provide a default implementation via a function named
  809. * theme_HOOK() (e.g., theme_taxonomy_term()) or via a template file named
  810. * according to the value of the 'template' key registered with the theme hook
  811. * (see hook_theme() for details). Default templates are implemented with the
  812. * PHPTemplate rendering engine and are named the same as the theme hook, with
  813. * underscores changed to hyphens, so for the 'taxonomy_term' theme hook, the
  814. * default template is 'taxonomy-term.tpl.php'.
  815. *
  816. * @subsection sub_overriding_theme_hooks Overriding Theme Hooks
  817. * Themes may also register new theme hooks within a hook_theme()
  818. * implementation, but it is more common for themes to override default
  819. * implementations provided by modules than to register entirely new theme
  820. * hooks. Themes can override a default implementation by implementing a
  821. * function named THEME_HOOK() (for example, the 'bartik' theme overrides the
  822. * default implementation of the 'menu_tree' theme hook by implementing a
  823. * bartik_menu_tree() function), or by adding a template file within its folder
  824. * structure that follows the template naming structure used by the theme's
  825. * rendering engine (for example, since the Bartik theme uses the PHPTemplate
  826. * rendering engine, it overrides the default implementation of the 'page' theme
  827. * hook by containing a 'page.tpl.php' file within its folder structure).
  828. *
  829. * @subsection sub_preprocess_templates Preprocessing for Template Files
  830. * If the implementation is a template file, several functions are called
  831. * before the template file is invoked, to modify the $variables array. These
  832. * fall into the "preprocessing" phase and the "processing" phase, and are
  833. * executed (if they exist), in the following order (note that in the following
  834. * list, HOOK indicates the theme hook name, MODULE indicates a module name,
  835. * THEME indicates a theme name, and ENGINE indicates a theme engine name):
  836. * - template_preprocess(&$variables, $hook): Creates a default set of
  837. * variables for all theme hooks with template implementations.
  838. * - template_preprocess_HOOK(&$variables): Should be implemented by the module
  839. * that registers the theme hook, to set up default variables.
  840. * - MODULE_preprocess(&$variables, $hook): hook_preprocess() is invoked on all
  841. * implementing modules.
  842. * - MODULE_preprocess_HOOK(&$variables): hook_preprocess_HOOK() is invoked on
  843. * all implementing modules, so that modules that didn't define the theme
  844. * hook can alter the variables.
  845. * - ENGINE_engine_preprocess(&$variables, $hook): Allows the theme engine to
  846. * set necessary variables for all theme hooks with template implementations.
  847. * - ENGINE_engine_preprocess_HOOK(&$variables): Allows the theme engine to set
  848. * necessary variables for the particular theme hook.
  849. * - THEME_preprocess(&$variables, $hook): Allows the theme to set necessary
  850. * variables for all theme hooks with template implementations.
  851. * - THEME_preprocess_HOOK(&$variables): Allows the theme to set necessary
  852. * variables specific to the particular theme hook.
  853. * - template_process(&$variables, $hook): Creates an additional set of default
  854. * variables for all theme hooks with template implementations. The variables
  855. * created in this function are derived from ones created by
  856. * template_preprocess(), but potentially altered by the other preprocess
  857. * functions listed above. For example, any preprocess function can add to or
  858. * modify the $variables['attributes_array'] variable, and after all of them
  859. * have finished executing, template_process() flattens it into a
  860. * $variables['attributes'] string for convenient use by templates.
  861. * - template_process_HOOK(&$variables): Should be implemented by the module
  862. * that registers the theme hook, if it needs to perform additional variable
  863. * processing after all preprocess functions have finished.
  864. * - MODULE_process(&$variables, $hook): hook_process() is invoked on all
  865. * implementing modules.
  866. * - MODULE_process_HOOK(&$variables): hook_process_HOOK() is invoked on
  867. * on all implementing modules, so that modules that didn't define the theme
  868. * hook can alter the variables.
  869. * - ENGINE_engine_process(&$variables, $hook): Allows the theme engine to
  870. * process variables for all theme hooks with template implementations.
  871. * - ENGINE_engine_process_HOOK(&$variables): Allows the theme engine to process
  872. * the variables specific to the theme hook.
  873. * - THEME_process(&$variables, $hook): Allows the theme to process the
  874. * variables for all theme hooks with template implementations.
  875. * - THEME_process_HOOK(&$variables): Allows the theme to process the
  876. * variables specific to the theme hook.
  877. *
  878. * @subsection sub_preprocess_theme_funcs Preprocessing for Theme Functions
  879. * If the implementation is a function, only the theme-hook-specific preprocess
  880. * and process functions (the ones ending in _HOOK) are called from the
  881. * list above. This is because theme hooks with function implementations
  882. * need to be fast, and calling the non-theme-hook-specific preprocess and
  883. * process functions for them would incur a noticeable performance penalty.
  884. *
  885. * @subsection sub_alternate_suggestions Suggesting Alternate Hooks
  886. * There are two special variables that these preprocess and process functions
  887. * can set: 'theme_hook_suggestion' and 'theme_hook_suggestions'. These will be
  888. * merged together to form a list of 'suggested' alternate theme hooks to use,
  889. * in reverse order of priority. theme_hook_suggestion will always be a higher
  890. * priority than items in theme_hook_suggestions. theme() will use the
  891. * highest priority implementation that exists. If none exists, theme() will
  892. * use the implementation for the theme hook it was called with. These
  893. * suggestions are similar to and are used for similar reasons as calling
  894. * theme() with an array as the $hook parameter (see below). The difference
  895. * is whether the suggestions are determined by the code that calls theme() or
  896. * by a preprocess or process function.
  897. *
  898. * @param $hook
  899. * The name of the theme hook to call. If the name contains a
  900. * double-underscore ('__') and there isn't an implementation for the full
  901. * name, the part before the '__' is checked. This allows a fallback to a
  902. * more generic implementation. For example, if theme('links__node', ...) is
  903. * called, but there is no implementation of that theme hook, then the
  904. * 'links' implementation is used. This process is iterative, so if
  905. * theme('links__contextual__node', ...) is called, theme() checks for the
  906. * following implementations, and uses the first one that exists:
  907. * - links__contextual__node
  908. * - links__contextual
  909. * - links
  910. * This allows themes to create specific theme implementations for named
  911. * objects and contexts of otherwise generic theme hooks. The $hook parameter
  912. * may also be an array, in which case the first theme hook that has an
  913. * implementation is used. This allows for the code that calls theme() to
  914. * explicitly specify the fallback order in a situation where using the '__'
  915. * convention is not desired or is insufficient.
  916. * @param $variables
  917. * An associative array of variables to merge with defaults from the theme
  918. * registry, pass to preprocess and process functions for modification, and
  919. * finally, pass to the function or template implementing the theme hook.
  920. * Alternatively, this can be a renderable array, in which case, its
  921. * properties are mapped to variables expected by the theme hook
  922. * implementations.
  923. *
  924. * @return
  925. * An HTML string representing the themed output.
  926. *
  927. * @see drupal_render()
  928. * @see themeable
  929. * @see hook_theme()
  930. * @see template_preprocess()
  931. * @see template_process()
  932. */
  933. function theme($hook, $variables = array()) {
  934. // If called before all modules are loaded, we do not necessarily have a full
  935. // theme registry to work with, and therefore cannot process the theme
  936. // request properly. See also _theme_load_registry().
  937. if (!module_load_all(NULL) && !defined('MAINTENANCE_MODE')) {
  938. throw new Exception(t('theme() may not be called until all modules are loaded.'));
  939. }
  940. $hooks = theme_get_registry(FALSE);
  941. // If an array of hook candidates were passed, use the first one that has an
  942. // implementation.
  943. if (is_array($hook)) {
  944. foreach ($hook as $candidate) {
  945. if (isset($hooks[$candidate])) {
  946. break;
  947. }
  948. }
  949. $hook = $candidate;
  950. }
  951. // If there's no implementation, check for more generic fallbacks. If there's
  952. // still no implementation, log an error and return an empty string.
  953. if (!isset($hooks[$hook])) {
  954. // Iteratively strip everything after the last '__' delimiter, until an
  955. // implementation is found.
  956. while ($pos = strrpos($hook, '__')) {
  957. $hook = substr($hook, 0, $pos);
  958. if (isset($hooks[$hook])) {
  959. break;
  960. }
  961. }
  962. if (!isset($hooks[$hook])) {
  963. // Only log a message when not trying theme suggestions ($hook being an
  964. // array).
  965. if (!isset($candidate)) {
  966. watchdog('theme', 'Theme hook %hook not found.', array('%hook' => $hook), WATCHDOG_WARNING);
  967. }
  968. return '';
  969. }
  970. }
  971. $info = $hooks[$hook];
  972. global $theme_path;
  973. $temp = $theme_path;
  974. // point path_to_theme() to the currently used theme path:
  975. $theme_path = $info['theme path'];
  976. // Include a file if the theme function or variable processor is held
  977. // elsewhere.
  978. if (!empty($info['includes'])) {
  979. foreach ($info['includes'] as $include_file) {
  980. include_once DRUPAL_ROOT . '/' . $include_file;
  981. }
  982. }
  983. // If a renderable array is passed as $variables, then set $variables to
  984. // the arguments expected by the theme function.
  985. if (isset($variables['#theme']) || isset($variables['#theme_wrappers'])) {
  986. $element = $variables;
  987. $variables = array();
  988. if (isset($info['variables'])) {
  989. foreach (array_keys($info['variables']) as $name) {
  990. if (isset($element["#$name"])) {
  991. $variables[$name] = $element["#$name"];
  992. }
  993. }
  994. }
  995. else {
  996. $variables[$info['render element']] = $element;
  997. }
  998. }
  999. // Merge in argument defaults.
  1000. if (!empty($info['variables'])) {
  1001. $variables += $info['variables'];
  1002. }
  1003. elseif (!empty($info['render element'])) {
  1004. $variables += array($info['render element'] => array());
  1005. }
  1006. // Invoke the variable processors, if any. The processors may specify
  1007. // alternate suggestions for which hook's template/function to use. If the
  1008. // hook is a suggestion of a base hook, invoke the variable processors of
  1009. // the base hook, but retain the suggestion as a high priority suggestion to
  1010. // be used unless overridden by a variable processor function.
  1011. if (isset($info['base hook'])) {
  1012. $base_hook = $info['base hook'];
  1013. $base_hook_info = $hooks[$base_hook];
  1014. // Include files required by the base hook, since its variable processors
  1015. // might reside there.
  1016. if (!empty($base_hook_info['includes'])) {
  1017. foreach ($base_hook_info['includes'] as $include_file) {
  1018. include_once DRUPAL_ROOT . '/' . $include_file;
  1019. }
  1020. }
  1021. if (isset($base_hook_info['preprocess functions']) || isset($base_hook_info['process functions'])) {
  1022. $variables['theme_hook_suggestion'] = $hook;
  1023. $hook = $base_hook;
  1024. $info = $base_hook_info;
  1025. }
  1026. }
  1027. if (isset($info['preprocess functions']) || isset($info['process functions'])) {
  1028. $variables['theme_hook_suggestions'] = array();
  1029. foreach (array('preprocess functions', 'process functions') as $phase) {
  1030. if (!empty($info[$phase])) {
  1031. foreach ($info[$phase] as $processor_function) {
  1032. if (function_exists($processor_function)) {
  1033. // We don't want a poorly behaved process function changing $hook.
  1034. $hook_clone = $hook;
  1035. $processor_function($variables, $hook_clone);
  1036. }
  1037. }
  1038. }
  1039. }
  1040. // If the preprocess/process functions specified hook suggestions, and the
  1041. // suggestion exists in the theme registry, use it instead of the hook that
  1042. // theme() was called with. This allows the preprocess/process step to
  1043. // route to a more specific theme hook. For example, a function may call
  1044. // theme('node', ...), but a preprocess function can add 'node__article' as
  1045. // a suggestion, enabling a theme to have an alternate template file for
  1046. // article nodes. Suggestions are checked in the following order:
  1047. // - The 'theme_hook_suggestion' variable is checked first. It overrides
  1048. // all others.
  1049. // - The 'theme_hook_suggestions' variable is checked in FILO order, so the
  1050. // last suggestion added to the array takes precedence over suggestions
  1051. // added earlier.
  1052. $suggestions = array();
  1053. if (!empty($variables['theme_hook_suggestions'])) {
  1054. $suggestions = $variables['theme_hook_suggestions'];
  1055. }
  1056. if (!empty($variables['theme_hook_suggestion'])) {
  1057. $suggestions[] = $variables['theme_hook_suggestion'];
  1058. }
  1059. foreach (array_reverse($suggestions) as $suggestion) {
  1060. if (isset($hooks[$suggestion])) {
  1061. $info = $hooks[$suggestion];
  1062. break;
  1063. }
  1064. }
  1065. }
  1066. // Generate the output using either a function or a template.
  1067. $output = '';
  1068. if (isset($info['function'])) {
  1069. if (function_exists($info['function'])) {
  1070. $output = $info['function']($variables);
  1071. }
  1072. }
  1073. else {
  1074. // Default render function and extension.
  1075. $render_function = 'theme_render_template';
  1076. $extension = '.tpl.php';
  1077. // The theme engine may use a different extension and a different renderer.
  1078. global $theme_engine;
  1079. if (isset($theme_engine)) {
  1080. if ($info['type'] != 'module') {
  1081. if (function_exists($theme_engine . '_render_template')) {
  1082. $render_function = $theme_engine . '_render_template';
  1083. }
  1084. $extension_function = $theme_engine . '_extension';
  1085. if (function_exists($extension_function)) {
  1086. $extension = $extension_function();
  1087. }
  1088. }
  1089. }
  1090. // In some cases, a template implementation may not have had
  1091. // template_preprocess() run (for example, if the default implementation is
  1092. // a function, but a template overrides that default implementation). In
  1093. // these cases, a template should still be able to expect to have access to
  1094. // the variables provided by template_preprocess(), so we add them here if
  1095. // they don't already exist. We don't want to run template_preprocess()
  1096. // twice (it would be inefficient and mess up zebra striping), so we use the
  1097. // 'directory' variable to determine if it has already run, which while not
  1098. // completely intuitive, is reasonably safe, and allows us to save on the
  1099. // overhead of adding some new variable to track that.
  1100. if (!isset($variables['directory'])) {
  1101. $default_template_variables = array();
  1102. template_preprocess($default_template_variables, $hook);
  1103. $variables += $default_template_variables;
  1104. }
  1105. // Render the output using the template file.
  1106. $template_file = $info['template'] . $extension;
  1107. if (isset($info['path'])) {
  1108. $template_file = $info['path'] . '/' . $template_file;
  1109. }
  1110. $output = $render_function($template_file, $variables);
  1111. }
  1112. // restore path_to_theme()
  1113. $theme_path = $temp;
  1114. return $output;
  1115. }
  1116. /**
  1117. * Returns the path to the current themed element.
  1118. *
  1119. * It can point to the active theme or the module handling a themed
  1120. * implementation. For example, when invoked within the scope of a theming call
  1121. * it will depend on where the theming function is handled. If implemented from
  1122. * a module, it will point to the module. If implemented from the active theme,
  1123. * it will point to the active theme. When called outside the scope of a
  1124. * theming call, it will always point to the active theme.
  1125. */
  1126. function path_to_theme() {
  1127. global $theme_path;
  1128. if (!isset($theme_path)) {
  1129. drupal_theme_initialize();
  1130. }
  1131. return $theme_path;
  1132. }
  1133. /**
  1134. * Allows themes and/or theme engines to discover overridden theme functions.
  1135. *
  1136. * @param $cache
  1137. * The existing cache of theme hooks to test against.
  1138. * @param $prefixes
  1139. * An array of prefixes to test, in reverse order of importance.
  1140. *
  1141. * @return $implementations
  1142. * The functions found, suitable for returning from hook_theme;
  1143. */
  1144. function drupal_find_theme_functions($cache, $prefixes) {
  1145. $implementations = array();
  1146. $functions = get_defined_functions();
  1147. foreach ($cache as $hook => $info) {
  1148. foreach ($prefixes as $prefix) {
  1149. // Find theme functions that implement possible "suggestion" variants of
  1150. // registered theme hooks and add those as new registered theme hooks.
  1151. // The 'pattern' key defines a common prefix that all suggestions must
  1152. // start with. The default is the name of the hook followed by '__'. An
  1153. // 'base hook' key is added to each entry made for a found suggestion,
  1154. // so that common functionality can be implemented for all suggestions of
  1155. // the same base hook. To keep things simple, deep hierarchy of
  1156. // suggestions is not supported: each suggestion's 'base hook' key
  1157. // refers to a base hook, not to another suggestion, and all suggestions
  1158. // are found using the base hook's pattern, not a pattern from an
  1159. // intermediary suggestion.
  1160. $pattern = isset($info['pattern']) ? $info['pattern'] : ($hook . '__');
  1161. if (!isset($info['base hook']) && !empty($pattern)) {
  1162. $matches = preg_grep('/^' . $prefix . '_' . $pattern . '/', $functions['user']);
  1163. if ($matches) {
  1164. foreach ($matches as $match) {
  1165. $new_hook = substr($match, strlen($prefix) + 1);
  1166. $arg_name = isset($info['variables']) ? 'variables' : 'render element';
  1167. $implementations[$new_hook] = array(
  1168. 'function' => $match,
  1169. $arg_name => $info[$arg_name],
  1170. 'base hook' => $hook,
  1171. );
  1172. }
  1173. }
  1174. }
  1175. // Find theme functions that implement registered theme hooks and include
  1176. // that in what is returned so that the registry knows that the theme has
  1177. // this implementation.
  1178. if (function_exists($prefix . '_' . $hook)) {
  1179. $implementations[$hook] = array(
  1180. 'function' => $prefix . '_' . $hook,
  1181. );
  1182. }
  1183. }
  1184. }
  1185. return $implementations;
  1186. }
  1187. /**
  1188. * Allows themes and/or theme engines to easily discover overridden templates.
  1189. *
  1190. * @param $cache
  1191. * The existing cache of theme hooks to test against.
  1192. * @param $extension
  1193. * The extension that these templates will have.
  1194. * @param $path
  1195. * The path to search.
  1196. */
  1197. function drupal_find_theme_templates($cache, $extension, $path) {
  1198. $implementations = array();
  1199. // Collect paths to all sub-themes grouped by base themes. These will be
  1200. // used for filtering. This allows base themes to have sub-themes in its
  1201. // folder hierarchy without affecting the base themes template discovery.
  1202. $theme_paths = array();
  1203. foreach (list_themes() as $theme_info) {
  1204. if (!empty($theme_info->base_theme)) {
  1205. $theme_paths[$theme_info->base_theme][$theme_info->name] = dirname($theme_info->filename);
  1206. }
  1207. }
  1208. foreach ($theme_paths as $basetheme => $subthemes) {
  1209. foreach ($subthemes as $subtheme => $subtheme_path) {
  1210. if (isset($theme_paths[$subtheme])) {
  1211. $theme_paths[$basetheme] = array_merge($theme_paths[$basetheme], $theme_paths[$subtheme]);
  1212. }
  1213. }
  1214. }
  1215. global $theme;
  1216. $subtheme_paths = isset($theme_paths[$theme]) ? $theme_paths[$theme] : array();
  1217. // Escape the periods in the extension.
  1218. $regex = '/' . str_replace('.', '\.', $extension) . '$/';
  1219. // Get a listing of all template files in the path to search.
  1220. $files = drupal_system_listing($regex, $path, 'name', 0);
  1221. // Find templates that implement registered theme hooks and include that in
  1222. // what is returned so that the registry knows that the theme has this
  1223. // implementation.
  1224. foreach ($files as $template => $file) {
  1225. // Ignore sub-theme templates for the current theme.
  1226. if (strpos($file->uri, str_replace($subtheme_paths, '', $file->uri)) !== 0) {
  1227. continue;
  1228. }
  1229. // Chop off the remaining extensions if there are any. $template already
  1230. // has the rightmost extension removed, but there might still be more,
  1231. // such as with .tpl.php, which still has .tpl in $template at this point.
  1232. if (($pos = strpos($template, '.')) !== FALSE) {
  1233. $template = substr($template, 0, $pos);
  1234. }
  1235. // Transform - in filenames to _ to match function naming scheme
  1236. // for the purposes of searching.
  1237. $hook = strtr($template, '-', '_');
  1238. if (isset($cache[$hook])) {
  1239. $implementations[$hook] = array(
  1240. 'template' => $template,
  1241. 'path' => dirname($file->uri),
  1242. );
  1243. }
  1244. }
  1245. // Find templates that implement possible "suggestion" variants of registered
  1246. // theme hooks and add those as new registered theme hooks. See
  1247. // drupal_find_theme_functions() for more information about suggestions and
  1248. // the use of 'pattern' and 'base hook'.
  1249. $patterns = array_keys($files);
  1250. foreach ($cache as $hook => $info) {
  1251. $pattern = isset($info['pattern']) ? $info['pattern'] : ($hook . '__');
  1252. if (!isset($info['base hook']) && !empty($pattern)) {
  1253. // Transform _ in pattern to - to match file naming scheme
  1254. // for the purposes of searching.
  1255. $pattern = strtr($pattern, '_', '-');
  1256. $matches = preg_grep('/^' . $pattern . '/', $patterns);
  1257. if ($matches) {
  1258. foreach ($matches as $match) {
  1259. $file = substr($match, 0, strpos($match, '.'));
  1260. // Put the underscores back in for the hook name and register this
  1261. // pattern.
  1262. $arg_name = isset($info['variables']) ? 'variables' : 'render element';
  1263. $implementations[strtr($file, '-', '_')] = array(
  1264. 'template' => $file,
  1265. 'path' => dirname($files[$match]->uri),
  1266. $arg_name => $info[$arg_name],
  1267. 'base hook' => $hook,
  1268. );
  1269. }
  1270. }
  1271. }
  1272. }
  1273. return $implementations;
  1274. }
  1275. /**
  1276. * Retrieves a setting for the current theme or for a given theme.
  1277. *
  1278. * The final setting is obtained from the last value found in the following
  1279. * sources:
  1280. * - the default global settings specified in this function
  1281. * - the default theme-specific settings defined in any base theme's .info file
  1282. * - the default theme-specific settings defined in the theme's .info file
  1283. * - the saved values from the global theme settings form
  1284. * - the saved values from the theme's settings form
  1285. * To only retrieve the default global theme setting, an empty string should be
  1286. * given for $theme.
  1287. *
  1288. * @param $setting_name
  1289. * The name of the setting to be retrieved.
  1290. * @param $theme
  1291. * The name of a given theme; defaults to the current theme.
  1292. *
  1293. * @return
  1294. * The value of the requested setting, NULL if the setting does not exist.
  1295. */
  1296. function theme_get_setting($setting_name, $theme = NULL) {
  1297. $cache = &drupal_static(__FUNCTION__, array());
  1298. // If no key is given, use the current theme if we can determine it.
  1299. if (!isset($theme)) {
  1300. $theme = !empty($GLOBALS['theme_key']) ? $GLOBALS['theme_key'] : '';
  1301. }
  1302. if (empty($cache[$theme])) {
  1303. // Set the default values for each global setting.
  1304. // To add new global settings, add their default values below, and then
  1305. // add form elements to system_theme_settings() in system.admin.inc.
  1306. $cache[$theme] = array(
  1307. 'default_logo' => 1,
  1308. 'logo_path' => '',
  1309. 'default_favicon' => 1,
  1310. 'favicon_path' => '',
  1311. // Use the IANA-registered MIME type for ICO files as default.
  1312. 'favicon_mimetype' => 'image/vnd.microsoft.icon',
  1313. );
  1314. // Turn on all default features.
  1315. $features = _system_default_theme_features();
  1316. foreach ($features as $feature) {
  1317. $cache[$theme]['toggle_' . $feature] = 1;
  1318. }
  1319. // Get the values for the theme-specific settings from the .info files of
  1320. // the theme and all its base themes.
  1321. if ($theme) {
  1322. $themes = list_themes();
  1323. $theme_object = $themes[$theme];
  1324. // Create a list which includes the current theme and all its base themes.
  1325. if (isset($theme_object->base_themes)) {
  1326. $theme_keys = array_keys($theme_object->base_themes);
  1327. $theme_keys[] = $theme;
  1328. }
  1329. else {
  1330. $theme_keys = array($theme);
  1331. }
  1332. foreach ($theme_keys as $theme_key) {
  1333. if (!empty($themes[$theme_key]->info['settings'])) {
  1334. $cache[$theme] = array_merge($cache[$theme], $themes[$theme_key]->info['settings']);
  1335. }
  1336. }
  1337. }
  1338. // Get the saved global settings from the database.
  1339. $cache[$theme] = array_merge($cache[$theme], variable_get('theme_settings', array()));
  1340. if ($theme) {
  1341. // Get the saved theme-specific settings from the database.
  1342. $cache[$theme] = array_merge($cache[$theme], variable_get('theme_' . $theme . '_settings', array()));
  1343. // If the theme does not support a particular feature, override the global
  1344. // setting and set the value to NULL.
  1345. if (!empty($theme_object->info['features'])) {
  1346. foreach ($features as $feature) {
  1347. if (!in_array($feature, $theme_object->info['features'])) {
  1348. $cache[$theme]['toggle_' . $feature] = NULL;
  1349. }
  1350. }
  1351. }
  1352. // Generate the path to the logo image.
  1353. if ($cache[$theme]['toggle_logo']) {
  1354. if ($cache[$theme]['default_logo']) {
  1355. $cache[$theme]['logo'] = file_create_url(dirname($theme_object->filename) . '/logo.png');
  1356. }
  1357. elseif ($cache[$theme]['logo_path']) {
  1358. $cache[$theme]['logo'] = file_create_url($cache[$theme]['logo_path']);
  1359. }
  1360. }
  1361. // Generate the path to the favicon.
  1362. if ($cache[$theme]['toggle_favicon']) {
  1363. if ($cache[$theme]['default_favicon']) {
  1364. if (file_exists($favicon = dirname($theme_object->filename) . '/favicon.ico')) {
  1365. $cache[$theme]['favicon'] = file_create_url($favicon);
  1366. }
  1367. else {
  1368. $cache[$theme]['favicon'] = file_create_url('misc/favicon.ico');
  1369. }
  1370. }
  1371. elseif ($cache[$theme]['favicon_path']) {
  1372. $cache[$theme]['favicon'] = file_create_url($cache[$theme]['favicon_path']);
  1373. }
  1374. else {
  1375. $cache[$theme]['toggle_favicon'] = FALSE;
  1376. }
  1377. }
  1378. }
  1379. }
  1380. return isset($cache[$theme][$setting_name]) ? $cache[$theme][$setting_name] : NULL;
  1381. }
  1382. /**
  1383. * Renders a system default template, which is essentially a PHP template.
  1384. *
  1385. * @param $template_file
  1386. * The filename of the template to render.
  1387. * @param $variables
  1388. * A keyed array of variables that will appear in the output.
  1389. *
  1390. * @return
  1391. * The output generated by the template.
  1392. */
  1393. function theme_render_template($template_file, $variables) {
  1394. // Extract the variables to a local namespace
  1395. extract($variables, EXTR_SKIP);
  1396. // Start output buffering
  1397. ob_start();
  1398. // Include the template file
  1399. include DRUPAL_ROOT . '/' . $template_file;
  1400. // End buffering and return its contents
  1401. return ob_get_clean();
  1402. }
  1403. /**
  1404. * Enables a given list of themes.
  1405. *
  1406. * @param $theme_list
  1407. * An array of theme names.
  1408. */
  1409. function theme_enable($theme_list) {
  1410. drupal_clear_css_cache();
  1411. foreach ($theme_list as $key) {
  1412. db_update('system')
  1413. ->fields(array('status' => 1))
  1414. ->condition('type', 'theme')
  1415. ->condition('name', $key)
  1416. ->execute();
  1417. }
  1418. list_themes(TRUE);
  1419. menu_rebuild();
  1420. drupal_theme_rebuild();
  1421. // Invoke hook_themes_enabled() after the themes have been enabled.
  1422. module_invoke_all('themes_enabled', $theme_list);
  1423. }
  1424. /**
  1425. * Disables a given list of themes.
  1426. *
  1427. * @param $theme_list
  1428. * An array of theme names.
  1429. */
  1430. function theme_disable($theme_list) {
  1431. // Don't disable the default theme.
  1432. if ($pos = array_search(variable_get('theme_default', 'bartik'), $theme_list) !== FALSE) {
  1433. unset($theme_list[$pos]);
  1434. if (empty($theme_list)) {
  1435. return;
  1436. }
  1437. }
  1438. drupal_clear_css_cache();
  1439. foreach ($theme_list as $key) {
  1440. db_update('system')
  1441. ->fields(array('status' => 0))
  1442. ->condition('type', 'theme')
  1443. ->condition('name', $key)
  1444. ->execute();
  1445. }
  1446. list_themes(TRUE);
  1447. menu_rebuild();
  1448. drupal_theme_rebuild();
  1449. // Invoke hook_themes_disabled after the themes have been disabled.
  1450. module_invoke_all('themes_disabled', $theme_list);
  1451. }
  1452. /**
  1453. * @addtogroup themeable
  1454. * @{
  1455. */
  1456. /**
  1457. * Returns HTML for status and/or error messages, grouped by type.
  1458. *
  1459. * An invisible heading identifies the messages for assistive technology.
  1460. * Sighted users see a colored box. See http://www.w3.org/TR/WCAG-TECHS/H69.html
  1461. * for info.
  1462. *
  1463. * @param $variables
  1464. * An associative array containing:
  1465. * - display: (optional) Set to 'status' or 'error' to display only messages
  1466. * of that type.
  1467. */
  1468. function theme_status_messages($variables) {
  1469. $display = $variables['display'];
  1470. $output = '';
  1471. $status_heading = array(
  1472. 'status' => t('Status message'),
  1473. 'error' => t('Error message'),
  1474. 'warning' => t('Warning message'),
  1475. );
  1476. foreach (drupal_get_messages($display) as $type => $messages) {
  1477. $output .= "<div class=\"messages $type\">\n";
  1478. if (!empty($status_heading[$type])) {
  1479. $output .= '<h2 class="element-invisible">' . $status_heading[$type] . "</h2>\n";
  1480. }
  1481. if (count($messages) > 1) {
  1482. $output .= " <ul>\n";
  1483. foreach ($messages as $message) {
  1484. $output .= ' <li>' . $message . "</li>\n";
  1485. }
  1486. $output .= " </ul>\n";
  1487. }
  1488. else {
  1489. $output .= $messages[0];
  1490. }
  1491. $output .= "</div>\n";
  1492. }
  1493. return $output;
  1494. }
  1495. /**
  1496. * Returns HTML for a link.
  1497. *
  1498. * All Drupal code that outputs a link should call the l() function. That
  1499. * function performs some initial preprocessing, and then, if necessary, calls
  1500. * theme('link') for rendering the anchor tag.
  1501. *
  1502. * To optimize performance for sites that don't need custom theming of links,
  1503. * the l() function includes an inline copy of this function, and uses that
  1504. * copy if none of the enabled modules or the active theme implement any
  1505. * preprocess or process functions or override this theme implementation.
  1506. *
  1507. * @param $variables
  1508. * An associative array containing the keys 'text', 'path', and 'options'.
  1509. * See the l() function for information about these variables.
  1510. *
  1511. * @see l()
  1512. */
  1513. function theme_link($variables) {
  1514. return '<a href="' . check_plain(url($variables['path'], $variables['options'])) . '"' . drupal_attributes($variables['options']['attributes']) . '>' . ($variables['options']['html'] ? $variables['text'] : check_plain($variables['text'])) . '</a>';
  1515. }
  1516. /**
  1517. * Returns HTML for a set of links.
  1518. *
  1519. * @param $variables
  1520. * An associative array containing:
  1521. * - links: An associative array of links to be themed. The key for each link
  1522. * is used as its CSS class. Each link should be itself an array, with the
  1523. * following elements:
  1524. * - title: The link text.
  1525. * - href: The link URL. If omitted, the 'title' is shown as a plain text
  1526. * item in the links list.
  1527. * - html: (optional) Whether or not 'title' is HTML. If set, the title
  1528. * will not be passed through check_plain().
  1529. * - attributes: (optional) Attributes for the anchor, or for the <span>
  1530. * tag used in its place if no 'href' is supplied. If element 'class' is
  1531. * included, it must be an array of one or more class names.
  1532. * If the 'href' element is supplied, the entire link array is passed to
  1533. * l() as its $options parameter.
  1534. * - attributes: A keyed array of attributes for the UL containing the
  1535. * list of links.
  1536. * - heading: (optional) A heading to precede the links. May be an
  1537. * associative array or a string. If it's an array, it can have the
  1538. * following elements:
  1539. * - text: The heading text.
  1540. * - level: The heading level (e.g. 'h2', 'h3').
  1541. * - class: (optional) An array of the CSS classes for the heading.
  1542. * When using a string it will be used as the text of the heading and the
  1543. * level will default to 'h2'. Headings should be used on navigation menus
  1544. * and any list of links that consistently appears on multiple pages. To
  1545. * make the heading invisible use the 'element-invisible' CSS class. Do not
  1546. * use 'display:none', which removes it from screen-readers and assistive
  1547. * technology. Headings allow screen-reader and keyboard only users to
  1548. * navigate to or skip the links. See
  1549. * http://juicystudio.com/article/screen-readers-display-none.php and
  1550. * http://www.w3.org/TR/WCAG-TECHS/H42.html for more information.
  1551. */
  1552. function theme_links($variables) {
  1553. $links = $variables['links'];
  1554. $attributes = $variables['attributes'];
  1555. $heading = $variables['heading'];
  1556. global $language_url;
  1557. $output = '';
  1558. if (count($links) > 0) {
  1559. $output = '';
  1560. // Treat the heading first if it is present to prepend it to the
  1561. // list of links.
  1562. if (!empty($heading)) {
  1563. if (is_string($heading)) {
  1564. // Prepare the array that will be used when the passed heading
  1565. // is a string.
  1566. $heading = array(
  1567. 'text' => $heading,
  1568. // Set the default level of the heading.
  1569. 'level' => 'h2',
  1570. );
  1571. }
  1572. $output .= '<' . $heading['level'];
  1573. if (!empty($heading['class'])) {
  1574. $output .= drupal_attributes(array('class' => $heading['class']));
  1575. }
  1576. $output .= '>' . check_plain($heading['text']) . '</' . $heading['level'] . '>';
  1577. }
  1578. $output .= '<ul' . drupal_attributes($attributes) . '>';
  1579. $num_links = count($links);
  1580. $i = 1;
  1581. foreach ($links as $key => $link) {
  1582. $class = array($key);
  1583. // Add first, last and active classes to the list of links to help out themers.
  1584. if ($i == 1) {
  1585. $class[] = 'first';
  1586. }
  1587. if ($i == $num_links) {
  1588. $class[] = 'last';
  1589. }
  1590. if (isset($link['href']) && ($link['href'] == $_GET['q'] || ($link['href'] == '<front>' && drupal_is_front_page()))
  1591. && (empty($link['language']) || $link['language']->language == $language_url->language)) {
  1592. $class[] = 'active';
  1593. }
  1594. $output .= '<li' . drupal_attributes(array('class' => $class)) . '>';
  1595. if (isset($link['href'])) {
  1596. // Pass in $link as $options, they share the same keys.
  1597. $output .= l($link['title'], $link['href'], $link);
  1598. }
  1599. elseif (!empty($link['title'])) {
  1600. // Some links are actually not links, but we wrap these in <span> for adding title and class attributes.
  1601. if (empty($link['html'])) {
  1602. $link['title'] = check_plain($link['title']);
  1603. }
  1604. $span_attributes = '';
  1605. if (isset($link['attributes'])) {
  1606. $span_attributes = drupal_attributes($link['attributes']);
  1607. }
  1608. $output .= '<span' . $span_attributes . '>' . $link['title'] . '</span>';
  1609. }
  1610. $i++;
  1611. $output .= "</li>\n";
  1612. }
  1613. $output .= '</ul>';
  1614. }
  1615. return $output;
  1616. }
  1617. /**
  1618. * Returns HTML for an image.
  1619. *
  1620. * @param $variables
  1621. * An associative array containing:
  1622. * - path: Either the path of the image file (relative to base_path()) or a
  1623. * full URL.
  1624. * - width: The width of the image (if known).
  1625. * - height: The height of the image (if known).
  1626. * - alt: The alternative text for text-based browsers. HTML 4 and XHTML 1.0
  1627. * always require an alt attribute. The HTML 5 draft allows the alt
  1628. * attribute to be omitted in some cases. Therefore, this variable defaults
  1629. * to an empty string, but can be set to NULL for the attribute to be
  1630. * omitted. Usually, neither omission nor an empty string satisfies
  1631. * accessibility requirements, so it is strongly encouraged for code
  1632. * calling theme('image') to pass a meaningful value for this variable.
  1633. * - http://www.w3.org/TR/REC-html40/struct/objects.html#h-13.8
  1634. * - http://www.w3.org/TR/xhtml1/dtds.html
  1635. * - http://dev.w3.org/html5/spec/Overview.html#alt
  1636. * - title: The title text is displayed when the image is hovered in some
  1637. * popular browsers.
  1638. * - attributes: Associative array of attributes to be placed in the img tag.
  1639. */
  1640. function theme_image($variables) {
  1641. $attributes = $variables['attributes'];
  1642. $attributes['src'] = file_create_url($variables['path']);
  1643. foreach (array('width', 'height', 'alt', 'title') as $key) {
  1644. if (isset($variables[$key])) {
  1645. $attributes[$key] = $variables[$key];
  1646. }
  1647. }
  1648. return '<img' . drupal_attributes($attributes) . ' />';
  1649. }
  1650. /**
  1651. * Returns HTML for a breadcrumb trail.
  1652. *
  1653. * @param $variables
  1654. * An associative array containing:
  1655. * - breadcrumb: An array containing the breadcrumb links.
  1656. */
  1657. function theme_breadcrumb($variables) {
  1658. $breadcrumb = $variables['breadcrumb'];
  1659. if (!empty($breadcrumb)) {
  1660. // Provide a navigational heading to give context for breadcrumb links to
  1661. // screen-reader users. Make the heading invisible with .element-invisible.
  1662. $output = '<h2 class="element-invisible">' . t('You are here') . '</h2>';
  1663. $output .= '<div class="breadcrumb">' . implode(' » ', $breadcrumb) . '</div>';
  1664. return $output;
  1665. }
  1666. }
  1667. /**
  1668. * Returns HTML for a table.
  1669. *
  1670. * @param $variables
  1671. * An associative array containing:
  1672. * - header: An array containing the table headers. Each element of the array
  1673. * can be either a localized string or an associative array with the
  1674. * following keys:
  1675. * - "data": The localized title of the table column.
  1676. * - "field": The database field represented in the table column (required
  1677. * if user is to be able to sort on this column).
  1678. * - "sort": A default sort order for this column ("asc" or "desc"). Only
  1679. * one column should be given a default sort order because table sorting
  1680. * only applies to one column at a time.
  1681. * - Any HTML attributes, such as "colspan", to apply to the column header
  1682. * cell.
  1683. * - rows: An array of table rows. Every row is an array of cells, or an
  1684. * associative array with the following keys:
  1685. * - "data": an array of cells
  1686. * - Any HTML attributes, such as "class", to apply to the table row.
  1687. * - "no_striping": a boolean indicating that the row should receive no
  1688. * 'even / odd' styling. Defaults to FALSE.
  1689. * Each cell can be either a string or an associative array with the
  1690. * following keys:
  1691. * - "data": The string to display in the table cell.
  1692. * - "header": Indicates this cell is a header.
  1693. * - Any HTML attributes, such as "colspan", to apply to the table cell.
  1694. * Here's an example for $rows:
  1695. * @code
  1696. * $rows = array(
  1697. * // Simple row
  1698. * array(
  1699. * 'Cell 1', 'Cell 2', 'Cell 3'
  1700. * ),
  1701. * // Row with attributes on the row and some of its cells.
  1702. * array(
  1703. * 'data' => array('Cell 1', array('data' => 'Cell 2', 'colspan' => 2)), 'class' => array('funky')
  1704. * )
  1705. * );
  1706. * @endcode
  1707. * - attributes: An array of HTML attributes to apply to the table tag.
  1708. * - caption: A localized string to use for the <caption> tag.
  1709. * - colgroups: An array of column groups. Each element of the array can be
  1710. * either:
  1711. * - An array of columns, each of which is an associative array of HTML
  1712. * attributes applied to the COL element.
  1713. * - An array of attributes applied to the COLGROUP element, which must
  1714. * include a "data" attribute. To add attributes to COL elements, set the
  1715. * "data" attribute with an array of columns, each of which is an
  1716. * associative array of HTML attributes.
  1717. * Here's an example for $colgroup:
  1718. * @code
  1719. * $colgroup = array(
  1720. * // COLGROUP with one COL element.
  1721. * array(
  1722. * array(
  1723. * 'class' => array('funky'), // Attribute for the COL element.
  1724. * ),
  1725. * ),
  1726. * // Colgroup with attributes and inner COL elements.
  1727. * array(
  1728. * 'data' => array(
  1729. * array(
  1730. * 'class' => array('funky'), // Attribute for the COL element.
  1731. * ),
  1732. * ),
  1733. * 'class' => array('jazzy'), // Attribute for the COLGROUP element.
  1734. * ),
  1735. * );
  1736. * @endcode
  1737. * These optional tags are used to group and set properties on columns
  1738. * within a table. For example, one may easily group three columns and
  1739. * apply same background style to all.
  1740. * - sticky: Use a "sticky" table header.
  1741. * - empty: The message to display in an extra row if table does not have any
  1742. * rows.
  1743. */
  1744. function theme_table($variables) {
  1745. $header = $variables['header'];
  1746. $rows = $variables['rows'];
  1747. $attributes = $variables['attributes'];
  1748. $caption = $variables['caption'];
  1749. $colgroups = $variables['colgroups'];
  1750. $sticky = $variables['sticky'];
  1751. $empty = $variables['empty'];
  1752. // Add sticky headers, if applicable.
  1753. if (count($header) && $sticky) {
  1754. drupal_add_js('misc/tableheader.js');
  1755. // Add 'sticky-enabled' class to the table to identify it for JS.
  1756. // This is needed to target tables constructed by this function.
  1757. $attributes['class'][] = 'sticky-enabled';
  1758. }
  1759. $output = '<table' . drupal_attributes($attributes) . ">\n";
  1760. if (isset($caption)) {
  1761. $output .= '<caption>' . $caption . "</caption>\n";
  1762. }
  1763. // Format the table columns:
  1764. if (count($colgroups)) {
  1765. foreach ($colgroups as $number => $colgroup) {
  1766. $attributes = array();
  1767. // Check if we're dealing with a simple or complex column
  1768. if (isset($colgroup['data'])) {
  1769. foreach ($colgroup as $key => $value) {
  1770. if ($key == 'data') {
  1771. $cols = $value;
  1772. }
  1773. else {
  1774. $attributes[$key] = $value;
  1775. }
  1776. }
  1777. }
  1778. else {
  1779. $cols = $colgroup;
  1780. }
  1781. // Build colgroup
  1782. if (is_array($cols) && count($cols)) {
  1783. $output .= ' <colgroup' . drupal_attributes($attributes) . '>';
  1784. $i = 0;
  1785. foreach ($cols as $col) {
  1786. $output .= ' <col' . drupal_attributes($col) . ' />';
  1787. }
  1788. $output .= " </colgroup>\n";
  1789. }
  1790. else {
  1791. $output .= ' <colgroup' . drupal_attributes($attributes) . " />\n";
  1792. }
  1793. }
  1794. }
  1795. // Add the 'empty' row message if available.
  1796. if (!count($rows) && $empty) {
  1797. $header_count = 0;
  1798. foreach ($header as $header_cell) {
  1799. if (is_array($header_cell)) {
  1800. $header_count += isset($header_cell['colspan']) ? $header_cell['colspan'] : 1;
  1801. }
  1802. else {
  1803. $header_count++;
  1804. }
  1805. }
  1806. $rows[] = array(array('data' => $empty, 'colspan' => $header_count, 'class' => array('empty', 'message')));
  1807. }
  1808. // Format the table header:
  1809. if (count($header)) {
  1810. $ts = tablesort_init($header);
  1811. // HTML requires that the thead tag has tr tags in it followed by tbody
  1812. // tags. Using ternary operator to check and see if we have any rows.
  1813. $output .= (count($rows) ? ' <thead><tr>' : ' <tr>');
  1814. foreach ($header as $cell) {
  1815. $cell = tablesort_header($cell, $header, $ts);
  1816. $output .= _theme_table_cell($cell, TRUE);
  1817. }
  1818. // Using ternary operator to close the tags based on whether or not there are rows
  1819. $output .= (count($rows) ? " </tr></thead>\n" : "</tr>\n");
  1820. }
  1821. else {
  1822. $ts = array();
  1823. }
  1824. // Format the table rows:
  1825. if (count($rows)) {
  1826. $output .= "<tbody>\n";
  1827. $flip = array('even' => 'odd', 'odd' => 'even');
  1828. $class = 'even';
  1829. foreach ($rows as $number => $row) {
  1830. // Check if we're dealing with a simple or complex row
  1831. if (isset($row['data'])) {
  1832. $cells = $row['data'];
  1833. $no_striping = isset($row['no_striping']) ? $row['no_striping'] : FALSE;
  1834. // Set the attributes array and exclude 'data' and 'no_striping'.
  1835. $attributes = $row;
  1836. unset($attributes['data']);
  1837. unset($attributes['no_striping']);
  1838. }
  1839. else {
  1840. $cells = $row;
  1841. $attributes = array();
  1842. $no_striping = FALSE;
  1843. }
  1844. if (count($cells)) {
  1845. // Add odd/even class
  1846. if (!$no_striping) {
  1847. $class = $flip[$class];
  1848. $attributes['class'][] = $class;
  1849. }
  1850. // Build row
  1851. $output .= ' <tr' . drupal_attributes($attributes) . '>';
  1852. $i = 0;
  1853. foreach ($cells as $cell) {
  1854. $cell = tablesort_cell($cell, $header, $ts, $i++);
  1855. $output .= _theme_table_cell($cell);
  1856. }
  1857. $output .= " </tr>\n";
  1858. }
  1859. }
  1860. $output .= "</tbody>\n";
  1861. }
  1862. $output .= "</table>\n";
  1863. return $output;
  1864. }
  1865. /**
  1866. * Returns HTML for a sort icon.
  1867. *
  1868. * @param $variables
  1869. * An associative array containing:
  1870. * - style: Set to either 'asc' or 'desc', this determines which icon to
  1871. * show.
  1872. */
  1873. function theme_tablesort_indicator($variables) {
  1874. if ($variables['style'] == "asc") {
  1875. return theme('image', array('path' => 'misc/arrow-asc.png', 'width' => 13, 'height' => 13, 'alt' => t('sort ascending'), 'title' => t('sort ascending')));
  1876. }
  1877. else {
  1878. return theme('image', array('path' => 'misc/arrow-desc.png', 'width' => 13, 'height' => 13, 'alt' => t('sort descending'), 'title' => t('sort descending')));
  1879. }
  1880. }
  1881. /**
  1882. * Returns HTML for a marker for new or updated content.
  1883. *
  1884. * @param $variables
  1885. * An associative array containing:
  1886. * - type: Number representing the marker type to display. See MARK_NEW,
  1887. * MARK_UPDATED, MARK_READ.
  1888. */
  1889. function theme_mark($variables) {
  1890. $type = $variables['type'];
  1891. global $user;
  1892. if ($user->uid) {
  1893. if ($type == MARK_NEW) {
  1894. return ' <span class="marker">' . t('new') . '</span>';
  1895. }
  1896. elseif ($type == MARK_UPDATED) {
  1897. return ' <span class="marker">' . t('updated') . '</span>';
  1898. }
  1899. }
  1900. }
  1901. /**
  1902. * Returns HTML for a list or nested list of items.
  1903. *
  1904. * @param $variables
  1905. * An associative array containing:
  1906. * - items: An array of items to be displayed in the list. If an item is a
  1907. * string, then it is used as is. If an item is an array, then the "data"
  1908. * element of the array is used as the contents of the list item. If an item
  1909. * is an array with a "children" element, those children are displayed in a
  1910. * nested list. All other elements are treated as attributes of the list
  1911. * item element.
  1912. * - title: The title of the list.
  1913. * - type: The type of list to return (e.g. "ul", "ol").
  1914. * - attributes: The attributes applied to the list element.
  1915. */
  1916. function theme_item_list($variables) {
  1917. $items = $variables['items'];
  1918. $title = $variables['title'];
  1919. $type = $variables['type'];
  1920. $attributes = $variables['attributes'];
  1921. // Only output the list container and title, if there are any list items.
  1922. // Check to see whether the block title exists before adding a header.
  1923. // Empty headers are not semantic and present accessibility challenges.
  1924. $output = '<div class="item-list">';
  1925. if (isset($title) && $title !== '') {
  1926. $output .= '<h3>' . $title . '</h3>';
  1927. }
  1928. if (!empty($items)) {
  1929. $output .= "<$type" . drupal_attributes($attributes) . '>';
  1930. $num_items = count($items);
  1931. $i = 0;
  1932. foreach ($items as $item) {
  1933. $attributes = array();
  1934. $children = array();
  1935. $data = '';
  1936. $i++;
  1937. if (is_array($item)) {
  1938. foreach ($item as $key => $value) {
  1939. if ($key == 'data') {
  1940. $data = $value;
  1941. }
  1942. elseif ($key == 'children') {
  1943. $children = $value;
  1944. }
  1945. else {
  1946. $attributes[$key] = $value;
  1947. }
  1948. }
  1949. }
  1950. else {
  1951. $data = $item;
  1952. }
  1953. if (count($children) > 0) {
  1954. // Render nested list.
  1955. $data .= theme_item_list(array('items' => $children, 'title' => NULL, 'type' => $type, 'attributes' => $attributes));
  1956. }
  1957. if ($i == 1) {
  1958. $attributes['class'][] = 'first';
  1959. }
  1960. if ($i == $num_items) {
  1961. $attributes['class'][] = 'last';
  1962. }
  1963. $output .= '<li' . drupal_attributes($attributes) . '>' . $data . "</li>\n";
  1964. }
  1965. $output .= "</$type>";
  1966. }
  1967. $output .= '</div>';
  1968. return $output;
  1969. }
  1970. /**
  1971. * Returns HTML for a "more help" link.
  1972. *
  1973. * @param $variables
  1974. * An associative array containing:
  1975. * - url: The URL for the link.
  1976. */
  1977. function theme_more_help_link($variables) {
  1978. return '<div class="more-help-link">' . l(t('More help'), $variables['url']) . '</div>';
  1979. }
  1980. /**
  1981. * Returns HTML for a feed icon.
  1982. *
  1983. * @param $variables
  1984. * An associative array containing:
  1985. * - url: An internal system path or a fully qualified external URL of the
  1986. * feed.
  1987. * - title: A descriptive title of the feed.
  1988. */
  1989. function theme_feed_icon($variables) {
  1990. $text = t('Subscribe to !feed-title', array('!feed-title' => $variables['title']));
  1991. if ($image = theme('image', array('path' => 'misc/feed.png', 'width' => 16, 'height' => 16, 'alt' => $text))) {
  1992. return l($image, $variables['url'], array('html' => TRUE, 'attributes' => array('class' => array('feed-icon'), 'title' => $text)));
  1993. }
  1994. }
  1995. /**
  1996. * Returns HTML for a generic HTML tag with attributes.
  1997. *
  1998. * @param $variables
  1999. * An associative array containing:
  2000. * - element: An associative array describing the tag:
  2001. * - #tag: The tag name to output. Typical tags added to the HTML HEAD:
  2002. * - meta: To provide meta information, such as a page refresh.
  2003. * - link: To refer to stylesheets and other contextual information.
  2004. * - script: To load JavaScript.
  2005. * - #attributes: (optional) An array of HTML attributes to apply to the
  2006. * tag.
  2007. * - #value: (optional) A string containing tag content, such as inline
  2008. * CSS.
  2009. * - #value_prefix: (optional) A string to prepend to #value, e.g. a CDATA
  2010. * wrapper prefix.
  2011. * - #value_suffix: (optional) A string to append to #value, e.g. a CDATA
  2012. * wrapper suffix.
  2013. */
  2014. function theme_html_tag($variables) {
  2015. $element = $variables['element'];
  2016. $attributes = isset($element['#attributes']) ? drupal_attributes($element['#attributes']) : '';
  2017. if (!isset($element['#value'])) {
  2018. return '<' . $element['#tag'] . $attributes . " />\n";
  2019. }
  2020. else {
  2021. $output = '<' . $element['#tag'] . $attributes . '>';
  2022. if (isset($element['#value_prefix'])) {
  2023. $output .= $element['#value_prefix'];
  2024. }
  2025. $output .= $element['#value'];
  2026. if (isset($element['#value_suffix'])) {
  2027. $output .= $element['#value_suffix'];
  2028. }
  2029. $output .= '</' . $element['#tag'] . ">\n";
  2030. return $output;
  2031. }
  2032. }
  2033. /**
  2034. * Returns HTML for a "more" link, like those used in blocks.
  2035. *
  2036. * @param $variables
  2037. * An associative array containing:
  2038. * - url: The URL of the main page.
  2039. * - title: A descriptive verb for the link, like 'Read more'.
  2040. */
  2041. function theme_more_link($variables) {
  2042. return '<div class="more-link">' . l(t('More'), $variables['url'], array('attributes' => array('title' => $variables['title']))) . '</div>';
  2043. }
  2044. /**
  2045. * Returns HTML for a username, potentially linked to the user's page.
  2046. *
  2047. * @param $variables
  2048. * An associative array containing:
  2049. * - account: The user object to format.
  2050. * - name: The user's name, sanitized.
  2051. * - extra: Additional text to append to the user's name, sanitized.
  2052. * - link_path: The path or URL of the user's profile page, home page, or
  2053. * other desired page to link to for more information about the user.
  2054. * - link_options: An array of options to pass to the l() function's $options
  2055. * parameter if linking the user's name to the user's page.
  2056. * - attributes_array: An array of attributes to pass to the
  2057. * drupal_attributes() function if not linking to the user's page.
  2058. *
  2059. * @see template_preprocess_username()
  2060. * @see template_process_username()
  2061. */
  2062. function theme_username($variables) {
  2063. if (isset($variables['link_path'])) {
  2064. // We have a link path, so we should generate a link using l().
  2065. // Additional classes may be added as array elements like
  2066. // $variables['link_options']['attributes']['class'][] = 'myclass';
  2067. $output = l($variables['name'] . $variables['extra'], $variables['link_path'], $variables['link_options']);
  2068. }
  2069. else {
  2070. // Modules may have added important attributes so they must be included
  2071. // in the output. Additional classes may be added as array elements like
  2072. // $variables['attributes_array']['class'][] = 'myclass';
  2073. $output = '<span' . drupal_attributes($variables['attributes_array']) . '>' . $variables['name'] . $variables['extra'] . '</span>';
  2074. }
  2075. return $output;
  2076. }
  2077. /**
  2078. * Returns HTML for a progress bar.
  2079. *
  2080. * Note that the core Batch API uses this only for non-JavaScript batch jobs.
  2081. *
  2082. * @param $variables
  2083. * An associative array containing:
  2084. * - percent: The percentage of the progress.
  2085. * - message: A string containing information to be displayed.
  2086. */
  2087. function theme_progress_bar($variables) {
  2088. $output = '<div id="progress" class="progress">';
  2089. $output .= '<div class="bar"><div class="filled" style="width: ' . $variables['percent'] . '%"></div></div>';
  2090. $output .= '<div class="percentage">' . $variables['percent'] . '%</div>';
  2091. $output .= '<div class="message">' . $variables['message'] . '</div>';
  2092. $output .= '</div>';
  2093. return $output;
  2094. }
  2095. /**
  2096. * Returns HTML for an indentation div; used for drag and drop tables.
  2097. *
  2098. * @param $variables
  2099. * An associative array containing:
  2100. * - size: Optional. The number of indentations to create.
  2101. */
  2102. function theme_indentation($variables) {
  2103. $output = '';
  2104. for ($n = 0; $n < $variables['size']; $n++) {
  2105. $output .= '<div class="indentation">&nbsp;</div>';
  2106. }
  2107. return $output;
  2108. }
  2109. /**
  2110. * @} End of "addtogroup themeable".
  2111. */
  2112. /**
  2113. * Returns HTML output for a single table cell for theme_table().
  2114. *
  2115. * @param $cell
  2116. * Array of cell information, or string to display in cell.
  2117. * @param bool $header
  2118. * TRUE if this cell is a table header cell, FALSE if it is an ordinary
  2119. * table cell. If $cell is an array with element 'header' set to TRUE, that
  2120. * will override the $header parameter.
  2121. *
  2122. * @return
  2123. * HTML for the cell.
  2124. */
  2125. function _theme_table_cell($cell, $header = FALSE) {
  2126. $attributes = '';
  2127. if (is_array($cell)) {
  2128. $data = isset($cell['data']) ? $cell['data'] : '';
  2129. // Cell's data property can be a string or a renderable array.
  2130. if (is_array($data)) {
  2131. $data = drupal_render($data);
  2132. }
  2133. $header |= isset($cell['header']);
  2134. unset($cell['data']);
  2135. unset($cell['header']);
  2136. $attributes = drupal_attributes($cell);
  2137. }
  2138. else {
  2139. $data = $cell;
  2140. }
  2141. if ($header) {
  2142. $output = "<th$attributes>$data</th>";
  2143. }
  2144. else {
  2145. $output = "<td$attributes>$data</td>";
  2146. }
  2147. return $output;
  2148. }
  2149. /**
  2150. * Adds a default set of helper variables for variable processors and templates.
  2151. *
  2152. * This function is called for theme hooks implemented as templates only, not
  2153. * for theme hooks implemented as functions. This preprocess function is the
  2154. * first in the sequence of preprocessing and processing functions that is
  2155. * called when preparing variables for a template. See theme() for more details
  2156. * about the full sequence.
  2157. *
  2158. * @see theme()
  2159. * @see template_process()
  2160. */
  2161. function template_preprocess(&$variables, $hook) {
  2162. global $user;
  2163. static $count = array();
  2164. // Track run count for each hook to provide zebra striping. See
  2165. // "template_preprocess_block()" which provides the same feature specific to
  2166. // blocks.
  2167. $count[$hook] = isset($count[$hook]) && is_int($count[$hook]) ? $count[$hook] : 1;
  2168. $variables['zebra'] = ($count[$hook] % 2) ? 'odd' : 'even';
  2169. $variables['id'] = $count[$hook]++;
  2170. // Tell all templates where they are located.
  2171. $variables['directory'] = path_to_theme();
  2172. // Initialize html class attribute for the current hook.
  2173. $variables['classes_array'] = array(drupal_html_class($hook));
  2174. // Merge in variables that don't depend on hook and don't change during a
  2175. // single page request.
  2176. // Use the advanced drupal_static() pattern, since this is called very often.
  2177. static $drupal_static_fast;
  2178. if (!isset($drupal_static_fast)) {
  2179. $drupal_static_fast['default_variables'] = &drupal_static(__FUNCTION__);
  2180. }
  2181. $default_variables = &$drupal_static_fast['default_variables'];
  2182. // Global $user object shouldn't change during a page request once rendering
  2183. // has started, but if there's an edge case where it does, re-fetch the
  2184. // variables appropriate for the new user.
  2185. if (!isset($default_variables) || ($user !== $default_variables['user'])) {
  2186. $default_variables = _template_preprocess_default_variables();
  2187. }
  2188. $variables += $default_variables;
  2189. }
  2190. /**
  2191. * Returns hook-independent variables to template_preprocess().
  2192. */
  2193. function _template_preprocess_default_variables() {
  2194. global $user;
  2195. // Variables that don't depend on a database connection.
  2196. $variables = array(
  2197. 'attributes_array' => array(),
  2198. 'title_attributes_array' => array(),
  2199. 'content_attributes_array' => array(),
  2200. 'title_prefix' => array(),
  2201. 'title_suffix' => array(),
  2202. 'user' => $user,
  2203. 'db_is_active' => !defined('MAINTENANCE_MODE'),
  2204. 'is_admin' => FALSE,
  2205. 'logged_in' => FALSE,
  2206. );
  2207. // The user object has no uid property when the database does not exist during
  2208. // install. The user_access() check deals with issues when in maintenance mode
  2209. // as uid is set but the user.module has not been included.
  2210. if (isset($user->uid) && function_exists('user_access')) {
  2211. $variables['is_admin'] = user_access('access administration pages');
  2212. $variables['logged_in'] = ($user->uid > 0);
  2213. }
  2214. // drupal_is_front_page() might throw an exception.
  2215. try {
  2216. $variables['is_front'] = drupal_is_front_page();
  2217. }
  2218. catch (Exception $e) {
  2219. // If the database is not yet available, set default values for these
  2220. // variables.
  2221. $variables['is_front'] = FALSE;
  2222. $variables['db_is_active'] = FALSE;
  2223. }
  2224. return $variables;
  2225. }
  2226. /**
  2227. * Adds helper variables derived from variables defined during preprocessing.
  2228. *
  2229. * When preparing variables for a theme hook implementation, all 'preprocess'
  2230. * functions run first, then all 'process' functions (see theme() for details
  2231. * about the full sequence).
  2232. *
  2233. * This function serializes array variables manipulated during the preprocessing
  2234. * phase into strings for convenient use by templates. As with
  2235. * template_preprocess(), this function does not get called for theme hooks
  2236. * implemented as functions.
  2237. *
  2238. * @see theme()
  2239. * @see template_preprocess()
  2240. */
  2241. function template_process(&$variables, $hook) {
  2242. // Flatten out classes.
  2243. $variables['classes'] = implode(' ', $variables['classes_array']);
  2244. // Flatten out attributes, title_attributes, and content_attributes.
  2245. // Because this function can be called very often, and often with empty
  2246. // attributes, optimize performance by only calling drupal_attributes() if
  2247. // necessary.
  2248. $variables['attributes'] = $variables['attributes_array'] ? drupal_attributes($variables['attributes_array']) : '';
  2249. $variables['title_attributes'] = $variables['title_attributes_array'] ? drupal_attributes($variables['title_attributes_array']) : '';
  2250. $variables['content_attributes'] = $variables['content_attributes_array'] ? drupal_attributes($variables['content_attributes_array']) : '';
  2251. }
  2252. /**
  2253. * Preprocess variables for html.tpl.php
  2254. *
  2255. * @see system_elements()
  2256. * @see html.tpl.php
  2257. */
  2258. function template_preprocess_html(&$variables) {
  2259. // Compile a list of classes that are going to be applied to the body element.
  2260. // This allows advanced theming based on context (home page, node of certain type, etc.).
  2261. // Add a class that tells us whether we're on the front page or not.
  2262. $variables['classes_array'][] = $variables['is_front'] ? 'front' : 'not-front';
  2263. // Add a class that tells us whether the page is viewed by an authenticated user or not.
  2264. $variables['classes_array'][] = $variables['logged_in'] ? 'logged-in' : 'not-logged-in';
  2265. // Add information about the number of sidebars.
  2266. if (!empty($variables['page']['sidebar_first']) && !empty($variables['page']['sidebar_second'])) {
  2267. $variables['classes_array'][] = 'two-sidebars';
  2268. }
  2269. elseif (!empty($variables['page']['sidebar_first'])) {
  2270. $variables['classes_array'][] = 'one-sidebar sidebar-first';
  2271. }
  2272. elseif (!empty($variables['page']['sidebar_second'])) {
  2273. $variables['classes_array'][] = 'one-sidebar sidebar-second';
  2274. }
  2275. else {
  2276. $variables['classes_array'][] = 'no-sidebars';
  2277. }
  2278. // Populate the body classes.
  2279. if ($suggestions = theme_get_suggestions(arg(), 'page', '-')) {
  2280. foreach ($suggestions as $suggestion) {
  2281. if ($suggestion != 'page-front') {
  2282. // Add current suggestion to page classes to make it possible to theme
  2283. // the page depending on the current page type (e.g. node, admin, user,
  2284. // etc.) as well as more specific data like node-12 or node-edit.
  2285. $variables['classes_array'][] = drupal_html_class($suggestion);
  2286. }
  2287. }
  2288. }
  2289. // If on an individual node page, add the node type to body classes.
  2290. if ($node = menu_get_object()) {
  2291. $variables['classes_array'][] = drupal_html_class('node-type-' . $node->type);
  2292. }
  2293. // RDFa allows annotation of XHTML pages with RDF data, while GRDDL provides
  2294. // mechanisms for extraction of this RDF content via XSLT transformation
  2295. // using an associated GRDDL profile.
  2296. $variables['rdf_namespaces'] = drupal_get_rdf_namespaces();
  2297. $variables['grddl_profile'] = 'http://www.w3.org/1999/xhtml/vocab';
  2298. $variables['language'] = $GLOBALS['language'];
  2299. $variables['language']->dir = $GLOBALS['language']->direction ? 'rtl' : 'ltr';
  2300. // Add favicon.
  2301. if (theme_get_setting('toggle_favicon')) {
  2302. $favicon = theme_get_setting('favicon');
  2303. $type = theme_get_setting('favicon_mimetype');
  2304. drupal_add_html_head_link(array('rel' => 'shortcut icon', 'href' => drupal_strip_dangerous_protocols($favicon), 'type' => $type));
  2305. }
  2306. // Construct page title.
  2307. if (drupal_get_title()) {
  2308. $head_title = array(
  2309. 'title' => strip_tags(drupal_get_title()),
  2310. 'name' => check_plain(variable_get('site_name', 'Drupal')),
  2311. );
  2312. }
  2313. else {
  2314. $head_title = array('name' => check_plain(variable_get('site_name', 'Drupal')));
  2315. if (variable_get('site_slogan', '')) {
  2316. $head_title['slogan'] = filter_xss_admin(variable_get('site_slogan', ''));
  2317. }
  2318. }
  2319. $variables['head_title_array'] = $head_title;
  2320. $variables['head_title'] = implode(' | ', $head_title);
  2321. // Populate the page template suggestions.
  2322. if ($suggestions = theme_get_suggestions(arg(), 'html')) {
  2323. $variables['theme_hook_suggestions'] = $suggestions;
  2324. }
  2325. }
  2326. /**
  2327. * Preprocess variables for page.tpl.php
  2328. *
  2329. * Most themes utilize their own copy of page.tpl.php. The default is located
  2330. * inside "modules/system/page.tpl.php". Look in there for the full list of
  2331. * variables.
  2332. *
  2333. * Uses the arg() function to generate a series of page template suggestions
  2334. * based on the current path.
  2335. *
  2336. * Any changes to variables in this preprocessor should also be changed inside
  2337. * template_preprocess_maintenance_page() to keep all of them consistent.
  2338. *
  2339. * @see drupal_render_page()
  2340. * @see template_process_page()
  2341. * @see page.tpl.php
  2342. */
  2343. function template_preprocess_page(&$variables) {
  2344. // Move some variables to the top level for themer convenience and template cleanliness.
  2345. $variables['show_messages'] = $variables['page']['#show_messages'];
  2346. foreach (system_region_list($GLOBALS['theme']) as $region_key => $region_name) {
  2347. if (!isset($variables['page'][$region_key])) {
  2348. $variables['page'][$region_key] = array();
  2349. }
  2350. }
  2351. // Set up layout variable.
  2352. $variables['layout'] = 'none';
  2353. if (!empty($variables['page']['sidebar_first'])) {
  2354. $variables['layout'] = 'first';
  2355. }
  2356. if (!empty($variables['page']['sidebar_second'])) {
  2357. $variables['layout'] = ($variables['layout'] == 'first') ? 'both' : 'second';
  2358. }
  2359. $variables['base_path'] = base_path();
  2360. $variables['front_page'] = url();
  2361. $variables['feed_icons'] = drupal_get_feeds();
  2362. $variables['language'] = $GLOBALS['language'];
  2363. $variables['language']->dir = $GLOBALS['language']->direction ? 'rtl' : 'ltr';
  2364. $variables['logo'] = theme_get_setting('logo');
  2365. $variables['main_menu'] = theme_get_setting('toggle_main_menu') ? menu_main_menu() : array();
  2366. $variables['secondary_menu'] = theme_get_setting('toggle_secondary_menu') ? menu_secondary_menu() : array();
  2367. $variables['action_links'] = menu_local_actions();
  2368. $variables['site_name'] = (theme_get_setting('toggle_name') ? filter_xss_admin(variable_get('site_name', 'Drupal')) : '');
  2369. $variables['site_slogan'] = (theme_get_setting('toggle_slogan') ? filter_xss_admin(variable_get('site_slogan', '')) : '');
  2370. $variables['tabs'] = menu_local_tabs();
  2371. if ($node = menu_get_object()) {
  2372. $variables['node'] = $node;
  2373. }
  2374. // Populate the page template suggestions.
  2375. if ($suggestions = theme_get_suggestions(arg(), 'page')) {
  2376. $variables['theme_hook_suggestions'] = $suggestions;
  2377. }
  2378. }
  2379. /**
  2380. * Process variables for page.tpl.php
  2381. *
  2382. * Perform final addition of variables before passing them into the template.
  2383. * To customize these variables, simply set them in an earlier step.
  2384. *
  2385. * @see template_preprocess_page()
  2386. * @see page.tpl.php
  2387. */
  2388. function template_process_page(&$variables) {
  2389. if (!isset($variables['breadcrumb'])) {
  2390. // Build the breadcrumb last, so as to increase the chance of being able to
  2391. // re-use the cache of an already rendered menu containing the active link
  2392. // for the current page.
  2393. // @see menu_tree_page_data()
  2394. $variables['breadcrumb'] = theme('breadcrumb', array('breadcrumb' => drupal_get_breadcrumb()));
  2395. }
  2396. if (!isset($variables['title'])) {
  2397. $variables['title'] = drupal_get_title();
  2398. }
  2399. // Generate messages last in order to capture as many as possible for the
  2400. // current page.
  2401. if (!isset($variables['messages'])) {
  2402. $variables['messages'] = $variables['show_messages'] ? theme('status_messages') : '';
  2403. }
  2404. }
  2405. /**
  2406. * Process variables for html.tpl.php
  2407. *
  2408. * Perform final addition and modification of variables before passing into
  2409. * the template. To customize these variables, call drupal_render() on elements
  2410. * in $variables['page'] during THEME_preprocess_page().
  2411. *
  2412. * @see template_preprocess_html()
  2413. * @see html.tpl.php
  2414. */
  2415. function template_process_html(&$variables) {
  2416. // Render page_top and page_bottom into top level variables.
  2417. $variables['page_top'] = drupal_render($variables['page']['page_top']);
  2418. $variables['page_bottom'] = drupal_render($variables['page']['page_bottom']);
  2419. // Place the rendered HTML for the page body into a top level variable.
  2420. $variables['page'] = $variables['page']['#children'];
  2421. $variables['page_bottom'] .= drupal_get_js('footer');
  2422. $variables['head'] = drupal_get_html_head();
  2423. $variables['css'] = drupal_add_css();
  2424. $variables['styles'] = drupal_get_css();
  2425. $variables['scripts'] = drupal_get_js();
  2426. }
  2427. /**
  2428. * Generate an array of suggestions from path arguments.
  2429. *
  2430. * This is typically called for adding to the 'theme_hook_suggestions' or
  2431. * 'classes_array' variables from within preprocess functions, when wanting to
  2432. * base the additional suggestions on the path of the current page.
  2433. *
  2434. * @param $args
  2435. * An array of path arguments, such as from function arg().
  2436. * @param $base
  2437. * A string identifying the base 'thing' from which more specific suggestions
  2438. * are derived. For example, 'page' or 'html'.
  2439. * @param $delimiter
  2440. * The string used to delimit increasingly specific information. The default
  2441. * of '__' is appropriate for theme hook suggestions. '-' is appropriate for
  2442. * extra classes.
  2443. *
  2444. * @return
  2445. * An array of suggestions, suitable for adding to
  2446. * $variables['theme_hook_suggestions'] within a preprocess function or to
  2447. * $variables['classes_array'] if the suggestions represent extra CSS classes.
  2448. */
  2449. function theme_get_suggestions($args, $base, $delimiter = '__') {
  2450. // Build a list of suggested theme hooks or body classes in order of
  2451. // specificity. One suggestion is made for every element of the current path,
  2452. // though numeric elements are not carried to subsequent suggestions. For
  2453. // example, for $base='page', http://www.example.com/node/1/edit would result
  2454. // in the following suggestions and body classes:
  2455. //
  2456. // page__node page-node
  2457. // page__node__% page-node-%
  2458. // page__node__1 page-node-1
  2459. // page__node__edit page-node-edit
  2460. $suggestions = array();
  2461. $prefix = $base;
  2462. foreach ($args as $arg) {
  2463. // Remove slashes or null per SA-CORE-2009-003 and change - (hyphen) to _
  2464. // (underscore).
  2465. //
  2466. // When we discover templates in @see drupal_find_theme_templates,
  2467. // hyphens (-) are converted to underscores (_) before the theme hook
  2468. // is registered. We do this because the hyphens used for delimiters
  2469. // in hook suggestions cannot be used in the function names of the
  2470. // associated preprocess functions. Any page templates designed to be used
  2471. // on paths that contain a hyphen are also registered with these hyphens
  2472. // converted to underscores so here we must convert any hyphens in path
  2473. // arguments to underscores here before fetching theme hook suggestions
  2474. // to ensure the templates are appropriately recognized.
  2475. $arg = str_replace(array("/", "\\", "\0", '-'), array('', '', '', '_'), $arg);
  2476. // The percent acts as a wildcard for numeric arguments since
  2477. // asterisks are not valid filename characters on many filesystems.
  2478. if (is_numeric($arg)) {
  2479. $suggestions[] = $prefix . $delimiter . '%';
  2480. }
  2481. $suggestions[] = $prefix . $delimiter . $arg;
  2482. if (!is_numeric($arg)) {
  2483. $prefix .= $delimiter . $arg;
  2484. }
  2485. }
  2486. if (drupal_is_front_page()) {
  2487. // Front templates should be based on root only, not prefixed arguments.
  2488. $suggestions[] = $base . $delimiter . 'front';
  2489. }
  2490. return $suggestions;
  2491. }
  2492. /**
  2493. * Process variables for maintenance-page.tpl.php.
  2494. *
  2495. * The variables array generated here is a mirror of
  2496. * template_preprocess_page(). This preprocessor will run its course when
  2497. * theme_maintenance_page() is invoked. An alternate template file of
  2498. * maintenance-page--offline.tpl.php can be used when the database is offline to
  2499. * hide errors and completely replace the content.
  2500. *
  2501. * The $variables array contains the following arguments:
  2502. * - $content
  2503. *
  2504. * @see maintenance-page.tpl.php
  2505. */
  2506. function template_preprocess_maintenance_page(&$variables) {
  2507. // Add favicon
  2508. if (theme_get_setting('toggle_favicon')) {
  2509. $favicon = theme_get_setting('favicon');
  2510. $type = theme_get_setting('favicon_mimetype');
  2511. drupal_add_html_head_link(array('rel' => 'shortcut icon', 'href' => drupal_strip_dangerous_protocols($favicon), 'type' => $type));
  2512. }
  2513. global $theme;
  2514. // Retrieve the theme data to list all available regions.
  2515. $theme_data = list_themes();
  2516. $regions = $theme_data[$theme]->info['regions'];
  2517. // Get all region content set with drupal_add_region_content().
  2518. foreach (array_keys($regions) as $region) {
  2519. // Assign region to a region variable.
  2520. $region_content = drupal_get_region_content($region);
  2521. isset($variables[$region]) ? $variables[$region] .= $region_content : $variables[$region] = $region_content;
  2522. }
  2523. // Setup layout variable.
  2524. $variables['layout'] = 'none';
  2525. if (!empty($variables['sidebar_first'])) {
  2526. $variables['layout'] = 'first';
  2527. }
  2528. if (!empty($variables['sidebar_second'])) {
  2529. $variables['layout'] = ($variables['layout'] == 'first') ? 'both' : 'second';
  2530. }
  2531. // Construct page title
  2532. if (drupal_get_title()) {
  2533. $head_title = array(
  2534. 'title' => strip_tags(drupal_get_title()),
  2535. 'name' => variable_get('site_name', 'Drupal'),
  2536. );
  2537. }
  2538. else {
  2539. $head_title = array('name' => variable_get('site_name', 'Drupal'));
  2540. if (variable_get('site_slogan', '')) {
  2541. $head_title['slogan'] = variable_get('site_slogan', '');
  2542. }
  2543. }
  2544. // set the default language if necessary
  2545. $language = isset($GLOBALS['language']) ? $GLOBALS['language'] : language_default();
  2546. $variables['head_title_array'] = $head_title;
  2547. $variables['head_title'] = implode(' | ', $head_title);
  2548. $variables['base_path'] = base_path();
  2549. $variables['front_page'] = url();
  2550. $variables['breadcrumb'] = '';
  2551. $variables['feed_icons'] = '';
  2552. $variables['help'] = '';
  2553. $variables['language'] = $language;
  2554. $variables['language']->dir = $language->direction ? 'rtl' : 'ltr';
  2555. $variables['logo'] = theme_get_setting('logo');
  2556. $variables['messages'] = $variables['show_messages'] ? theme('status_messages') : '';
  2557. $variables['main_menu'] = array();
  2558. $variables['secondary_menu'] = array();
  2559. $variables['site_name'] = (theme_get_setting('toggle_name') ? variable_get('site_name', 'Drupal') : '');
  2560. $variables['site_slogan'] = (theme_get_setting('toggle_slogan') ? variable_get('site_slogan', '') : '');
  2561. $variables['tabs'] = '';
  2562. $variables['title'] = drupal_get_title();
  2563. // Compile a list of classes that are going to be applied to the body element.
  2564. $variables['classes_array'][] = 'in-maintenance';
  2565. if (isset($variables['db_is_active']) && !$variables['db_is_active']) {
  2566. $variables['classes_array'][] = 'db-offline';
  2567. }
  2568. if ($variables['layout'] == 'both') {
  2569. $variables['classes_array'][] = 'two-sidebars';
  2570. }
  2571. elseif ($variables['layout'] == 'none') {
  2572. $variables['classes_array'][] = 'no-sidebars';
  2573. }
  2574. else {
  2575. $variables['classes_array'][] = 'one-sidebar sidebar-' . $variables['layout'];
  2576. }
  2577. // Dead databases will show error messages so supplying this template will
  2578. // allow themers to override the page and the content completely.
  2579. if (isset($variables['db_is_active']) && !$variables['db_is_active']) {
  2580. $variables['theme_hook_suggestion'] = 'maintenance_page__offline';
  2581. }
  2582. }
  2583. /**
  2584. * Theme process function for theme_maintenance_field().
  2585. *
  2586. * The variables array generated here is a mirror of template_process_html().
  2587. * This processor will run its course when theme_maintenance_page() is invoked.
  2588. *
  2589. * @see maintenance-page.tpl.php
  2590. * @see template_process_html()
  2591. */
  2592. function template_process_maintenance_page(&$variables) {
  2593. $variables['head'] = drupal_get_html_head();
  2594. $variables['css'] = drupal_add_css();
  2595. $variables['styles'] = drupal_get_css();
  2596. $variables['scripts'] = drupal_get_js();
  2597. }
  2598. /**
  2599. * Preprocess variables for region.tpl.php
  2600. *
  2601. * Prepares the values passed to the theme_region function to be passed into a
  2602. * pluggable template engine. Uses the region name to generate a template file
  2603. * suggestions. If none are found, the default region.tpl.php is used.
  2604. *
  2605. * @see drupal_region_class()
  2606. * @see region.tpl.php
  2607. */
  2608. function template_preprocess_region(&$variables) {
  2609. // Create the $content variable that templates expect.
  2610. $variables['content'] = $variables['elements']['#children'];
  2611. $variables['region'] = $variables['elements']['#region'];
  2612. $variables['classes_array'][] = drupal_region_class($variables['region']);
  2613. $variables['theme_hook_suggestions'][] = 'region__' . $variables['region'];
  2614. }
  2615. /**
  2616. * Preprocesses variables for theme_username().
  2617. *
  2618. * Modules that make any changes to variables like 'name' or 'extra' must insure
  2619. * that the final string is safe to include directly in the output by using
  2620. * check_plain() or filter_xss().
  2621. *
  2622. * @see template_process_username()
  2623. */
  2624. function template_preprocess_username(&$variables) {
  2625. $account = $variables['account'];
  2626. $variables['extra'] = '';
  2627. if (empty($account->uid)) {
  2628. $variables['uid'] = 0;
  2629. if (theme_get_setting('toggle_comment_user_verification')) {
  2630. $variables['extra'] = ' (' . t('not verified') . ')';
  2631. }
  2632. }
  2633. else {
  2634. $variables['uid'] = (int) $account->uid;
  2635. }
  2636. // Set the name to a formatted name that is safe for printing and
  2637. // that won't break tables by being too long. Keep an unshortened,
  2638. // unsanitized version, in case other preprocess functions want to implement
  2639. // their own shortening logic or add markup. If they do so, they must ensure
  2640. // that $variables['name'] is safe for printing.
  2641. $name = $variables['name_raw'] = format_username($account);
  2642. if (drupal_strlen($name) > 20) {
  2643. $name = drupal_substr($name, 0, 15) . '...';
  2644. }
  2645. $variables['name'] = check_plain($name);
  2646. $variables['profile_access'] = user_access('access user profiles');
  2647. $variables['link_attributes'] = array();
  2648. // Populate link path and attributes if appropriate.
  2649. if ($variables['uid'] && $variables['profile_access']) {
  2650. // We are linking to a local user.
  2651. $variables['link_attributes'] = array('title' => t('View user profile.'));
  2652. $variables['link_path'] = 'user/' . $variables['uid'];
  2653. }
  2654. elseif (!empty($account->homepage)) {
  2655. // Like the 'class' attribute, the 'rel' attribute can hold a
  2656. // space-separated set of values, so initialize it as an array to make it
  2657. // easier for other preprocess functions to append to it.
  2658. $variables['link_attributes'] = array('rel' => array('nofollow'));
  2659. $variables['link_path'] = $account->homepage;
  2660. $variables['homepage'] = $account->homepage;
  2661. }
  2662. // We do not want the l() function to check_plain() a second time.
  2663. $variables['link_options']['html'] = TRUE;
  2664. // Set a default class.
  2665. $variables['attributes_array'] = array('class' => array('username'));
  2666. }
  2667. /**
  2668. * Processes variables for theme_username().
  2669. *
  2670. * @see template_preprocess_username()
  2671. */
  2672. function template_process_username(&$variables) {
  2673. // Finalize the link_options array for passing to the l() function.
  2674. // This is done in the process phase so that attributes may be added by
  2675. // modules or the theme during the preprocess phase.
  2676. if (isset($variables['link_path'])) {
  2677. // $variables['attributes_array'] contains attributes that should be applied
  2678. // regardless of whether a link is being rendered or not.
  2679. // $variables['link_attributes'] contains attributes that should only be
  2680. // applied if a link is being rendered. Preprocess functions are encouraged
  2681. // to use the former unless they want to add attributes on the link only.
  2682. // If a link is being rendered, these need to be merged. Some attributes are
  2683. // themselves arrays, so the merging needs to be recursive.
  2684. $variables['link_options']['attributes'] = array_merge_recursive($variables['link_attributes'], $variables['attributes_array']);
  2685. }
  2686. }