5 * Helper functions for the Omega base theme.
9 * Retrieve a setting for the current theme or for a given theme.
11 * The final setting is obtained from the last value found in the following
13 * - the default global settings specified in this function
14 * - the default theme-specific settings defined in any base theme's .info file
15 * - the default theme-specific settings defined in the theme's .info file
16 * - the saved values from the global theme settings form
17 * - the saved values from the theme's settings form
18 * To only retrieve the default global theme setting, an empty string should be
21 * @param $setting_name
22 * The name of the setting to be retrieved.
24 * (optional) A default value. Defaults to NULL.
26 * (optional) The name of a given theme. Defaults to the NULL which
27 * evaluates to the current theme.
30 * The value of the requested setting, or the $default value if the setting
33 * @see theme_get_setting().
35 function omega_theme_get_setting($setting_name, $default = NULL
, $theme = NULL
) {
36 $cache = &drupal_static('theme_get_setting', array());
38 // If no key is given, use the current theme if we can determine it.
40 $theme = !empty($GLOBALS['theme_key']) ?
$GLOBALS['theme_key'] : '';
43 if (empty($cache[$theme])) {
44 // If the cache has not been filled yet, invoke theme_get_setting to
45 // retrieve the value. This will populate the cache and make it available
46 // for subsequent requests.
47 if (($setting = theme_get_setting($setting_name, $theme)) !== NULL
) {
48 // Use the default value if the setting does not exist.
52 elseif (isset($cache[$theme][$setting_name])) {
53 // Retrieve the value from the cache.
54 return $cache[$theme][$setting_name];
57 // Use the default value if the setting does not exist.
62 * Builds the full theme trail (deepest base theme first, subtheme last) for a
66 * (Optional) The key (machine-readable name) of a theme. Defaults to the key
67 * of the current theme.
70 * An array of all themes in the trail, keyed by theme key.
72 function omega_theme_trail($theme = NULL
) {
73 $theme = isset($theme) ?
$theme : $GLOBALS['theme_key'];
75 if (($cache = &drupal_static(__FUNCTION__
)) && isset($cache[$theme])) {
76 return $cache[$theme];
79 $cache[$theme] = array();
81 if ($theme == $GLOBALS['theme'] && isset($GLOBALS['theme_info']->base_themes
)) {
82 $cache[$theme] = $GLOBALS['theme_info']->base_themes
;
85 $themes = list_themes();
86 if (empty($cache[$theme]) && isset($themes[$theme]->info
['base theme'])) {
87 $cache[$theme] = system_find_base_themes($themes, $theme);
90 // Add our current subtheme ($key) to that array.
91 $cache[$theme][$theme] = $themes[$theme]->info
['name'];
93 return $cache[$theme];
97 * Helper function for generating a regex from a list of paths.
99 * Generates a single regex from a list of file paths that can be used to match
100 * JS or CSS files using preg_grep() for example in hook_css_alter() or
101 * hook_js_alter(). The '*' (asterisk) character can be used as a wild-card.
104 * An array of file paths.
107 * The generated regex.
109 * @see hook_js_alter()
110 * @see hook_css_alter()
112 function omega_generate_path_regex($paths) {
113 foreach ($paths as
&$item) {
114 // The first segment (everything before the first slash) is the namespace.
115 // This rule only applies to local files... So if the namespace can not be
116 // mapped to a module, profile or theme engine we assume that the we are
117 // trying to target an external file.
118 list($namespace) = explode('/', $item);
120 // Check if the namespace refers to a file residing in the 'misc' folder or
121 // if it is a global wildcard.
122 if ($namespace !== '*' && $namespace !== 'misc') {
123 // Otherwise, check if it refers to a theme, module, profile or theme
125 foreach (array('theme', 'module', 'profile', 'theme_engine') as
$type) {
126 // We can't use drupal_get_path() directly because that uses dirname()
127 // internally which returns '.' if no filename was found.
128 if ($filename = drupal_get_filename($type, $namespace)) {
129 $prefix = dirname($filename);
130 $item = substr_replace($item, $prefix, 0, strlen($namespace));
136 // Escape any regex characters and turn asterisk wildcards into actual regex
138 $item = preg_quote($item, '/');
139 $item = str_replace('\*', '(.*)', $item);
142 return '/^' .
implode('|', $paths) .
'$/';
146 * Helper function for eliminating elements from an array using a simplified
150 * The array of elements that should have some of its items removed.
152 * A regex as generated by omega_generate_path_regex().
154 function omega_exclude_assets(&$elements, $regex) {
155 $mapping = omega_generate_asset_mapping($elements);
157 // Finally, implode the array of items to exclude into a proper regex and
158 // invoke in on the array of files to be excluded.
159 $elements = array_diff_key($elements, preg_grep($regex, $mapping));
163 * Helper function for generating a map of assets based on the data attribute.
165 * We can not rely on the array keys of the JS and CSS file arrays in Drupal
166 * because in case of inline JS or CSS (which uses numerical array keys) and due
167 * to potential overrides of the 'data' attribute which holds the actual,
168 * reliable path of the file. This function returns a single-level array of
169 * reliable JS/CSS file paths using the original array keys as keys. Elements of
170 * type 'inline' or 'setting' are ignored.
173 * An array of JS or CSS files as given in hook_css_alter() or
177 * A map of file paths generated from $elements.
179 * @see hook_js_alter()
180 * @see hook_css_alter()
182 function omega_generate_asset_mapping($elements) {
184 foreach ($elements as
$key => $item) {
185 if ($item['type'] == 'inline' || $item['type'] == 'setting') {
186 // Naturally, in-line CSS is not supported.
190 // We need to build an array containing just the 'data' attribute because
191 // that's the actual path of the file. The array key of the elements can
192 // be something else if someone is sneaky enough to use drupal_add_js() or
193 // drupal_add_css() with a bogus first argument (normally, that is the
194 // path to the file) and then specify the actual path through the 'data'
195 // attribute in the $options array.
196 $mapping[$key] = $item['data'];
203 * Retrieves the array of enabled extensions for a theme. Extensions can be
204 * registered through the .info file. Each extension can define a theme settings
205 * form altering function named
206 * 'THEMENAME_extension_EXTENSION_theme_settings_form_alter()' through a file
207 * named 'THEME_ROOT/includes/EXTENSION/EXTENSION.settings.inc' to have it
208 * automatically included whenever the theme settings form is displayed. Each
209 * extension can also define a
210 * 'THEMENAME_extension_EXTENSION_theme_registry_alter()' function through a
211 * file named 'THEME_ROOT/includes/EXTENSION/EXTENSION.inc' to register custom
212 * hooks with the theme registry.
215 * (Optional) The key (machine-readable name) of a theme. Defaults to the key
216 * of the current theme.
219 * The theme info array of the passed or current theme.
221 * @see _system_default_theme_features()
222 * @see omega_extension_development_theme_settings_form_alter()
223 * @see omega_extension_development_theme_registry_alter()
225 function omega_extensions($theme = NULL
, $reset = FALSE
) {
226 $theme = isset($theme) ?
$theme : $GLOBALS['theme_key'];
229 if (($extensions = &drupal_static(__FUNCTION__
)) && isset($extensions[$theme])) {
230 return $extensions[$theme];
233 if (($cache = cache_get('omega:' .
$theme .
':extensions')) !== FALSE
) {
234 return $extensions[$theme] = $cache->data
;
238 // Extensions can't be hidden.
239 $extensions[$theme] = omega_discovery('extension', $theme);
241 foreach ($extensions[$theme] as
$extension => &$info) {
242 // Make sure that the theme variable is never altered.
244 drupal_alter('omega_extension_info', $info, $context);
246 // Determine if the extension is enabled.
247 $info['enabled'] = omega_theme_get_setting('omega_toggle_extension_' .
$extension, !empty($info['info']['enabled']));
249 // Check if all dependencies are met.
250 $info['errors'] = FALSE
;
251 if (!empty($info['info']['dependencies'])) {
252 foreach ($info['info']['dependencies'] as
$dependency) {
253 $dependency = drupal_parse_dependency($dependency);
255 if ((!$module = system_get_info('module', $dependency['name'])) || omega_check_incompatibility($dependency, $module['version'])) {
256 $info['errors'] = TRUE
;
262 // Write to the cache.
263 cache_set('omega:' .
$theme .
':extensions', $extensions[$theme]);
265 return $extensions[$theme];
269 * Determines if an extension is enabled.
272 * The machine-readable name of an extension.
274 * (Optional) The key (machine-readable name) of a theme. Defaults to the key
275 * of the current theme.
278 * TRUE if the extension is enabled, FALSE otherwise.
280 function omega_extension_enabled($extension, $theme = NULL
) {
281 $theme = isset($theme) ?
$theme : $GLOBALS['theme_key'];
282 if (($extensions = omega_extensions($theme)) && isset($extensions[$extension])) {
283 return empty($extensions[$extension]['errors']) && !empty($extensions[$extension]['enabled']) && variable_get('omega_toggle_extension_' .
$extension, TRUE
);
288 * Looks up the info array of all themes in the theme trail and retrieves a
289 * particular info array element.
291 function omega_theme_trail_info($element, $merge = TRUE
, $theme = NULL
) {
294 // Loop over all themes in the theme trail and look up $element in the .info
296 foreach (omega_theme_trail($theme) as
$key => $name) {
297 $info = omega_theme_info($key);
299 // If $merge is TRUE we combine all the results of all themes in the theme
300 // trail. Otherwise we just return the first occurrence.
301 if (isset($info[$element]) && is_array($info[$element])) {
302 $output = array_merge($info[$element], $output);
305 return array('theme' => $key, 'info' => $output);
314 * Retrieves the full info array of a theme.
317 * (Optional) The key (machine-readable name) of a theme. Defaults to the key
318 * of the current theme.
321 * The theme info array of the passed or current theme.
323 function omega_theme_info($theme = NULL
) {
324 $theme = isset($theme) ?
$theme : $GLOBALS['theme_key'];
326 // If this is the current theme, just load the theme info from the globals.
327 // Note: The global 'theme_key' property is not reliable in this case because
328 // it gets overridden on theme settings pages.
329 if ($theme == $GLOBALS['theme']) {
330 return $GLOBALS['theme_info']->info
;
333 $themes = list_themes();
334 return $themes[$theme]->info
;
338 * Invoke a hook in all themes in the theme trail that implement it.
341 * The name of the hook to invoke.
343 * (Optional) The key (machine-readable name) of a theme. Defaults to the key
344 * of the current theme.
346 * Arguments to pass to the hook.
349 * An array of return values of the hook implementations. If themes return
350 * arrays from their implementations, those are merged into one array.
352 * @see module_invoke_all()
354 function omega_invoke_all($hook, $theme = NULL
) {
355 $theme = isset($theme) ?
$theme : $GLOBALS['theme_key'];
357 $args = func_get_args();
358 // Remove $hook from the arguments.
359 unset($args[0], $args[1]);
362 foreach (omega_theme_trail($theme) as
$key => $name) {
363 $function = $key .
'_' .
$hook;
365 if (function_exists($function)) {
366 $result = call_user_func_array($function, array_merge(array($theme), array_values($args)));
367 if (isset($result) && is_array($result)) {
368 // Append the 'theme' property to each array element.
369 foreach ($result as
&$item) {
370 $item['theme'] = $key;
372 $return = array_merge_recursive($return, $result);
374 elseif (isset($result)) {
383 * Custom implementation of drupal_array_get_nested_value() that also supports
384 * objects instead of just arrays.
387 * The array or object from which to get the value.
389 * An array of parent keys of the value, starting with the outermost key.
391 * (optional) If given, an already defined variable that is altered by
395 * The requested nested value. Possibly NULL if the value is NULL or not all
396 * nested parent keys exist. $key_exists is altered by reference and is a
397 * Boolean that indicates whether all nested parent keys exist (TRUE) or not
398 * (FALSE). This allows to distinguish between the two possibilities when NULL
401 * @see drupal_array_get_nested_value()
403 function omega_get_nested_value(&$object, array $parents, &$key_exists = NULL
) {
405 foreach ($parents as
$parent) {
406 if (is_array($ref) && array_key_exists($parent, $ref)) {
407 $ref = &$ref[$parent];
409 elseif (is_object($ref) && property_exists($ref, $parent)) {
410 $ref = &$ref->$parent;
422 * Retrieves the info array for all available layouts.
425 * An array of available layouts for the given theme.
427 function omega_layouts_info() {
428 if (($layouts = &drupal_static(__FUNCTION__
)) !== NULL
) {
432 // Try to retrieve the layouts definitions from cache.
433 if (($cache = cache_get('omega:layouts')) !== FALSE
) {
434 return $layouts = $cache->data
;
437 // Layouts do not have a specific theme scope.
438 $layouts = omega_discovery('layout', FALSE
);
439 foreach ($layouts as
$layout => &$info) {
440 $info['attached'] = array();
441 $info['template'] = isset($info['info']['template']) ?
$info['info']['template'] : $layout;
442 $root = drupal_get_path('theme', $info['theme']);
444 if (isset($info['info']['stylesheets'])) {
445 foreach ($info['info']['stylesheets'] as
$media => $files) {
446 foreach ($files as
$key => $file) {
447 if (is_file($info['path'] .
'/' .
$file)) {
448 // First, check if the file exists in the layout's path.
449 $path = $info['path'] .
'/' .
$file;
451 elseif (is_file($root .
'/' .
$file)) {
452 // Otherwise, check if the file exists in the theme's path.
453 $path = $root .
'/' .
$file;
456 // The specified file does not exist.
460 $info['attached']['css']["$media:$key"] = array(
463 'group' => CSS_THEME
,
464 'every_page' => TRUE
,
471 // Look up possible CSS and JS file overrides.
472 if (isset($info['info']['scripts'])) {
473 foreach ($info['info']['scripts'] as
$key => $file) {
474 if (is_file($info['path'] .
'/' .
$file)) {
475 // First, check if the file exists in the layout's path.
476 $path = $info['path'] .
'/' .
$file;
478 elseif (is_file($root .
'/' .
$file)) {
479 // Otherwise, check if the file exists in the theme's path.
480 $path = $root .
'/' .
$file;
483 // The specified file does not exist.
487 $info['attached']['js'][$key] = array(
490 'every_page' => TRUE
,
497 // Give modules and themes a chance to alter the layout info array.
498 drupal_alter('omega_layouts_info', $layouts);
500 // Cache the layout definitions in the database.
501 cache_set('omega:layouts', $layouts);
507 * Retrieves the active layout for the current page.
510 * The info array for the active layout or FALSE if the current page does not
511 * use an alternative page layout.
513 function omega_layout() {
514 if (($cache = &drupal_static(__FUNCTION__
)) !== NULL
) {
518 // Load the default layout from the theme settings.
519 $layout = omega_theme_get_setting('omega_layout', 'simple');
520 drupal_alter('omega_layout', $layout);
522 $layouts = omega_layouts_info();
523 $cache = isset($layouts[$layout]) ?
$layouts[$layout] : FALSE
;
529 * Allow themes to easily define libraries.
532 * (Optional) The key (machine-readable name) of a theme. Defaults to the key
533 * of the current theme.
536 * An array of libraries defined by themes in the theme trail of the given
539 function omega_theme_libraries_info($theme = NULL
) {
540 $theme = isset($theme) ?
$theme : $GLOBALS['theme_key'];
542 // Check if the libraries have already been statically cached.
543 if (($libraries = &drupal_static(__FUNCTION__
)) && isset($libraries[$theme])) {
544 return $libraries[$theme];
547 $libraries[$theme] = omega_invoke_all('omega_theme_libraries_info');
550 // Give modules and themes a chance to alter the libraries info array.
551 drupal_alter('omega_theme_libraries_info', $libraries[$theme], $context);
553 return $libraries[$theme];
557 * Helper function for discovering layouts, extensions or other plugins of any
558 * sort in the theme trail.
561 * A theme extension type (e.g. layout or extension).
563 * (Optional) The key (machine-readable name) of a theme. Defaults to the key
564 * of the current theme.
567 * An array containing the discovered definitions.
569 function omega_discovery($type, $theme = NULL
) {
570 $theme = isset($theme) ?
$theme : $GLOBALS['theme_key'];
572 if (($discovery = &drupal_static(__FUNCTION__
, array())) && isset($discovery[$theme][$type])) {
573 return $discovery[$theme][$type];
576 $discovery[$theme][$type] = array();
578 // Retrieve all themes from the theme trail of the given theme.
579 $themes = $theme === FALSE ?
list_themes() : omega_theme_trail($theme);
581 // Collect paths to all sub-themes grouped by base themes. These will be
582 // used for filtering. This allows base themes to have sub-themes in its
583 // folder hierarchy without affecting the base themes template discovery.
585 foreach ($themes as
$key => $info) {
586 $info = system_get_info('theme', $key);
587 if (!empty($info->base_theme
)) {
588 $paths[$info->base_theme
][$info->name
] = dirname($info->filename
);
591 foreach ($paths as
$basetheme => $subthemes) {
592 foreach ($subthemes as
$subtheme => $path) {
593 if (isset($paths[$subtheme])) {
594 $paths[$basetheme] = array_merge($paths[$basetheme], $paths[$subtheme]);
599 $strlen = strlen($type) + 1;
600 foreach ($themes as
$key => $label) {
601 // Retrieve the array of paths that should be ignored for this theme.
602 $ignore = isset($paths[$key]) ?
$paths[$key] : array();
603 $path = drupal_get_path('theme', $key);
605 // Support files without '.inc' extension for backwards compatibility.
606 foreach (file_scan_directory($path, '/\.' .
$type .
'(\.inc)?$/', array('key' => 'name')) as
$name => $file) {
607 // Ignore sub-theme templates for the current theme.
608 if (strpos($file->uri
, str_replace($ignore, '', $file->uri
)) !== 0) {
612 if (substr($name, -$strlen) === '.' .
$type) {
613 $name = substr($name, 0, strlen($name) - $strlen);
616 if ($info = drupal_parse_info_file($file->uri
)) {
617 $discovery[$theme][$type][$name] = array(
619 'path' => dirname($file->uri
),
620 'file' => $file->uri
,
628 return $discovery[$theme][$type];
632 * Checks whether a version is compatible with a given dependency.
634 * This is a wrapper for drupal_check_incompatibility() which strips the core
635 * version and any potential development version suffix from the given string.
638 * The parsed dependency structure from drupal_parse_dependency().
640 * The version to check against (like 4.2).
643 * NULL if compatible, otherwise the original dependency version string that
644 * caused the incompatibility.
646 * @see drupal_check_incompatibility()
647 * @see drupal_parse_dependency()
649 function omega_check_incompatibility($dependency, $current) {
650 // Remove the core version from the version string.
651 $current = preg_replace('/^' . DRUPAL_CORE_COMPATIBILITY .
'-/', '', $current);
652 // Remove any potential development version suffixes from the string.
653 $current = preg_replace('/-dev$/', '', $current);
655 return drupal_check_incompatibility($dependency, $current);