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.
53 // Retrieve the value from the cache.
54 if (isset($cache[$theme][$setting_name])) {
55 return $cache[$theme][$setting_name];
59 // Use the default value if the settingdoes not exist.
64 * Builds the full theme trail (deepest base theme first, subtheme last) for a
68 * (Optional) The key (machine-readable name) of a theme. Defaults to the key
69 * of the current theme.
72 * An array of all themes in the trail, keyed by theme key.
74 function omega_theme_trail($theme = NULL
) {
75 $theme = isset($theme) ?
$theme : $GLOBALS['theme_key'];
76 $cache = &drupal_static(__FUNCTION__
);
78 if (!isset($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'];
94 return $cache[$theme];
98 * Pre-processes CSS files so that CSS files that have 'preprocess_media' set to
99 * TRUE are set to media="all" while having their former media query added to
103 * An array of CSS files as in drupal_pre_render_styles().
106 * An array of preprocessed CSS files.
108 * @see drupal_pre_render_styles()
110 function omega_css_preprocessor($elements) {
111 foreach ($elements['#items'] as
&$item) {
112 if ($item['type'] == 'file' && $item['preprocess'] && $item['media'] != 'all') {
113 $item['data'] = omega_css_cache_media_queries($item);
114 $item['media'] = 'all';
122 * Optimizes CSS aggregation by creating a cached version of each CSS file that,
123 * instead of using the 'media' attribute on the styles tag, writes the media
124 * query into the file itself using the '@media { ... }' syntax.
126 * This prevents unnecessary sprouting of new CSS aggregation.
128 * @see drupal_build_css_cache().
130 function omega_css_cache_media_queries($item) {
131 $map = variable_get('drupal_css_cache_files', array());
132 $key = hash('sha256', serialize($item));
133 $uri = isset($map[$key]) ?
$map[$key] : NULL
;
135 if (empty($uri) || !file_exists($uri)) {
136 // Build the base URL of this CSS file: start with the full URL.
137 $base = file_create_url($item['data']);
138 $base = substr($base, 0, strrpos($base, '/'));
140 if (substr($base, 0, strlen($GLOBALS['base_theme'])) == $GLOBALS['base_theme']) {
141 $base = substr($base, strlen($GLOBALS['base_theme']));
144 _drupal_build_css_path(NULL
, $base .
'/');
146 $data = drupal_load_stylesheet($item['data'], TRUE
);
148 // Anchor all paths in the CSS with its base URL, ignoring external and absolute paths.
149 $data = preg_replace_callback('/url\(\s*[\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\s*\)/i', '_drupal_build_css_path', $data);
150 $data = '@media ' .
$item['media'] .
'{' .
$data .
'}';
152 // Create the css/ within the files folder.
153 $directory = 'public://css';
154 $uri = $directory .
'/css_' .
drupal_hash_base64($data) .
'.css';
156 // Create the CSS file.
157 file_prepare_directory($directory, FILE_CREATE_DIRECTORY
);
158 if (!file_exists($uri) && !file_unmanaged_save_data($data, $uri, FILE_EXISTS_REPLACE
)) {
162 // If CSS gzip compression is enabled, clean URLs are enabled (which means
163 // that rewrite rules are working) and the zlib extension is available then
164 // create a gzipped version of this file. This file is served conditionally
165 // to browsers that accept gzip using .htaccess rules.
166 if (variable_get('css_gzip_compression', TRUE
) && variable_get('clean_url', 0) && extension_loaded('zlib')) {
167 if (!file_exists($uri .
'.gz') && !file_unmanaged_save_data(gzencode($data, 9, FORCE_GZIP
), $uri .
'.gz', FILE_EXISTS_REPLACE
)) {
172 // Save the updated map.
175 // Write the updated map into the variable.
176 variable_set('drupal_css_cache_files', $map);
183 * Helper function for eliminating elements from an array using a simplified
187 * The array of elements that should have some elements nuked.
189 * An array of strings that should be matched against the keys of the array
195 function omega_exclude_assets(&$elements, $exclude) {
196 // For optimization reasons we load the theme trail to check whether a
197 // namespace matches the machine-readable name of one of the themes in the
199 $trail = omega_theme_trail();
201 foreach ($exclude as
$item) {
203 // The first segment (everything before the first slash) is the namespace.
204 list($namespace) = explode('/', $item);
206 // Check if the namespace refers to a file residing in the 'misc' folder.
207 if ($namespace == 'misc') {
208 $path = DRUPAL_ROOT .
'/misc';
210 // Check if the namespace refers to a theme.
211 elseif (array_key_exists($namespace, $trail)) {
212 $path = drupal_get_path('theme', $namespace);
215 // Otherwise, check if it refers to a module, profile or theme engine.
216 foreach (array('module', 'profile', 'theme_engine') as
$type) {
217 if ($path = drupal_get_path($type, $namespace)) {
223 // If a namespace could be identified, use its path as a prefix, otherwise
224 // use the plain file path as provided.
225 $item = $path ?
$path .
'/' .
substr($item, strlen($namespace) + 1) : $item;
226 $item = preg_quote($item, '/');
227 // Turn the * wildcards into actual regex wildcards and make sure that, if
228 // a .css file is targeted directly we are also removing the RTL version of
230 $item = str_replace(array('\*', '\.css'), array('(.*)', '(\.css|-rtl\.css)'), $item);
232 // Look up all elements that match this exclusion pattern.
233 $filtered = preg_grep("/^$item$/", array_keys($elements));
234 $elements = array_diff_key($elements, array_flip($filtered));
239 * Retrieves the array of enabled extensions for a theme. Extensions can be
240 * registered through the .info file. Each extension can define a theme settings
241 * form altering function named
242 * 'THEMENAME_extension_EXTENSION_theme_settings_form_alter()' through a file
243 * named 'THEME_ROOT/includes/EXTENSION/EXTENSION.settings.inc' to have it
244 * automatically included whenever the theme settings form is displayed. Each
245 * extension can also define a
246 * 'THEMENAME_extension_EXTENSION_theme_registry_alter()' function through a
247 * file named 'THEME_ROOT/includes/EXTENSION/EXTENSION.inc' to register custom
248 * hooks with the theme registry.
251 * (Optional) The key (machine-readable name) of a theme. Defaults to the key
252 * of the current theme.
255 * The theme info array of the passed or current theme.
257 * @see _system_default_theme_features()
258 * @see omega_extension_development_theme_settings_form_alter()
259 * @see omega_extension_development_theme_registry_alter()
261 function omega_extensions($theme = NULL
) {
262 $theme = isset($theme) ?
$theme : $GLOBALS['theme_key'];
263 $extensions = drupal_static(__FUNCTION__
);
265 if (!isset($extensions[$theme])) {
266 if (($cache = cache_get('omega_extensions:' .
$theme)) !== FALSE
) {
267 $extensions[$theme] = $cache->data
;
270 // Extensions can't be hidden.
271 $extensions[$theme] = omega_discovery('extension', $theme, TRUE
);
273 foreach ($extensions[$theme] as
$extension => &$info) {
274 // Make sure that the theme variable is never altered.
276 drupal_alter('omega_extension_info', $info, $context);
278 // Determine if the extension is enabled.
279 $info['enabled'] = omega_theme_get_setting('omega_toggle_extension_' .
$extension, !empty($info['info']['enabled']));
281 // Check if all dependencies are met.
282 if ($info['enabled'] && !empty($info['dependencies'])) {
283 foreach ($info['dependencies'] as
$dependency) {
284 $dependency = drupal_parse_dependency($dependency);
286 if ((!$module = system_get_info('module', $dependency['name'])) || drupal_check_incompatibility($dependency, str_replace(DRUPAL_CORE_COMPATIBILITY .
'-', '', $module['version']))) {
287 $info['enabled'] = FALSE
;
293 // Write to the cache.
294 cache_set('omega_extensions:' .
$theme, $extensions[$theme]);
298 return $extensions[$theme];
302 * Determines if an extension is enabled.
305 * The machine-readable name of an extension.
307 * (Optional) The key (machine-readable name) of a theme. Defaults to the key
308 * of the current theme.
311 * TRUE if the extension is enabled, FALSE otherwise.
313 function omega_extension_enabled($extension, $theme = NULL
) {
314 $theme = isset($theme) ?
$theme : $GLOBALS['theme_key'];
315 if (($extensions = omega_extensions($theme)) && isset($extensions[$extension])) {
316 return $extensions[$extension]['enabled'];
321 * Looks up the info array of all themes in the theme trail and retrieves a
322 * particular info array element.
324 function omega_theme_trail_info($element, $merge = TRUE
, $theme = NULL
) {
327 // Loop over all themes in the theme trail and look up $element in the .info
329 foreach (array_reverse(omega_theme_trail($theme)) as
$key => $name) {
330 $info = omega_theme_info($key);
332 // If $merge is TRUE we combine all the results of all themes in the theme
333 // trail. Otherwise we just return the first occurrence.
334 if (isset($info[$element]) && is_array($info[$element])) {
335 $output = array_merge($info[$element], $output);
338 return array('theme' => $key, 'info' => $output);
347 * Retrieves the full info array of a theme.
350 * (Optional) The key (machine-readable name) of a theme. Defaults to the key
351 * of the current theme.
354 * The theme info array of the passed or current theme.
356 function omega_theme_info($theme = NULL
) {
357 $theme = isset($theme) ?
$theme : $GLOBALS['theme_key'];
359 // If this is the current theme, just load the theme info from the globals.
360 // Note: The global 'theme_key' property is not reliable in this case because
361 // it gets overridden on theme settings pages.
362 if ($theme == $GLOBALS['theme']) {
363 return $GLOBALS['theme_info']->info
;
366 $themes = list_themes();
367 return $themes[$theme]->info
;
371 * Invoke a hook in all themes in the theme trail that implement it.
374 * The name of the hook to invoke.
376 * (Optional) The key (machine-readable name) of a theme. Defaults to the key
377 * of the current theme.
379 * Arguments to pass to the hook.
382 * An array of return values of the hook implementations. If themes return
383 * arrays from their implementations, those are merged into one array.
385 * @see module_invoke_all()
387 function omega_invoke_all($hook, $theme = NULL
) {
388 $theme = isset($theme) ?
$theme : $GLOBALS['theme_key'];
390 $args = func_get_args();
391 // Remove $hook from the arguments.
392 unset($args[0], $args[1]);
395 foreach (omega_theme_trail($theme) as
$key => $name) {
396 $function = $key .
'_' .
$hook;
398 if (function_exists($function)) {
399 $result = call_user_func_array($function, array_merge(array($theme), array_values($args)));
400 if (isset($result) && is_array($result)) {
401 // Append the 'theme' property to each array element.
402 foreach ($result as
&$item) {
403 $item['theme'] = $key;
405 $return = array_merge_recursive($return, $result);
407 elseif (isset($result)) {
416 * Custom implementation of drupal_array_get_nested_value() that also supports
417 * objects instead of just arrays.
420 * The array or object from which to get the value.
422 * An array of parent keys of the value, starting with the outermost key.
424 * (optional) If given, an already defined variable that is altered by
428 * The requested nested value. Possibly NULL if the value is NULL or not all
429 * nested parent keys exist. $key_exists is altered by reference and is a
430 * Boolean that indicates whether all nested parent keys exist (TRUE) or not
431 * (FALSE). This allows to distinguish between the two possibilities when NULL
434 * @see drupal_array_get_nested_value()
436 function omega_get_nested_value(&$object, array $parents, &$key_exists = NULL
) {
438 foreach ($parents as
$parent) {
439 if (is_array($ref) && array_key_exists($parent, $ref)) {
440 $ref = &$ref[$parent];
442 elseif (is_object($ref) && property_exists($ref, $parent)) {
443 $ref = &$ref->$parent;
455 * Retrieves the info array for all available layouts.
458 * (Optional) The key (machine-readable name) of a theme. Defaults to the key
459 * of the current theme.
462 * An array of available layouts for the given theme.
464 function omega_layouts_info($theme = NULL
) {
465 $theme = isset($theme) ?
$theme : $GLOBALS['theme_key'];
466 $layouts = drupal_static(__FUNCTION__
);
468 if (!isset($layouts[$theme])) {
469 $layouts[$theme] = omega_discovery('layout', $theme);
471 // A theme or base theme can explicitly restrict the available layouts to
472 // a subset defined through the .info file.
473 if ($filter = omega_theme_trail_info('layouts', TRUE
, $theme)) {
474 $layouts[$theme] = array_intersect_key($layouts[$theme], array_flip($filter));
477 foreach ($layouts[$theme] as
$layout => &$info) {
478 $info['attached'] = array();
480 // Look up possible CSS and JS file overrides.
481 if (isset($info['info']['stylesheets'])) {
482 foreach ($info['info']['stylesheets'] as
$media => $files) {
483 foreach ($files as
$key => $file) {
484 $info['attached']['css'][$key] = array(
486 'data' => $info['path'] .
'/' .
$file,
487 'group' => CSS_THEME
,
488 'every_page' => TRUE
,
495 // Look up possible CSS and JS file overrides.
496 if (isset($info['info']['scripts'])) {
497 foreach ($info['info']['scripts'] as
$key => $file) {
498 $info['attached']['js'][$key] = array(
499 'data' => $info['path'] .
'/' .
$file,
501 'every_page' => TRUE
,
510 // Give modules and themes a chance to alter the layout info array.
511 drupal_alter('omega_layouts_info', $layouts[$theme], $context);
514 return $layouts[$theme];
518 * Retrieves the active layout for the current page.
521 * The info array for the active layout or FALSE if the current page does not
522 * use an alternative page layout.
524 function omega_layout() {
525 $cache = &drupal_static(__FUNCTION__
);
527 if (!isset($cache)) {
529 // Load the default layout from the theme settings.
530 $layout = omega_theme_get_setting('omega_layout', 'epiqo');
531 drupal_alter('omega_layout', $layout);
533 $registry = theme_get_registry();
534 if (isset($registry['page__layout__' .
$layout]['layout'])) {
535 $cache = $registry['page__layout__' .
$layout]['layout'];
543 * Allow themes to easily define libraries.
546 * (Optional) The key (machine-readable name) of a theme. Defaults to the key
547 * of the current theme.
550 * An array of libraries defined by themes in the theme trail of the given
553 function omega_theme_libraries_info($theme = NULL
) {
554 $theme = isset($theme) ?
$theme : $GLOBALS['theme_key'];
555 $libraries = drupal_static(__FUNCTION__
);
557 if (!isset($libraries[$theme])) {
558 $libraries[$theme] = array();
560 foreach (omega_invoke_all('omega_theme_libraries_info') as
$library => $info) {
561 $libraries[$theme][$library] = $info;
566 // Give modules and themes a chance to alter the libraries info array.
567 drupal_alter('omega_theme_libraries_info', $libraries[$theme], $context);
570 return $libraries[$theme];
574 * Helper function for discovering layouts, extensions or other pluggins of any
575 * sort in the theme trail.
578 * A theme extension type (e.g. layout or extension).
580 * (Optional) The key (machine-readable name) of a theme. Defaults to the key
581 * of the current theme.
583 * (Optional) Whether hidden elements should be listed. Defaults to FALSE.
586 * An array containing the discovered definitions.
588 function omega_discovery($type, $theme = NULL
, $all = FALSE
) {
589 $discovery = array();
590 $strlen = strlen($type) + 1;
592 foreach (array_reverse(omega_theme_trail($theme)) as
$key => $label) {
593 $path = drupal_get_path('theme', $key);
595 // Support files without '.inc' extension for backwards compatibilty.
596 foreach (file_scan_directory($path, '/\.' .
$type .
'(\.inc)?$/', array('key' => 'name')) as
$name => $file) {
597 if (substr($name, -$strlen) === '.' .
$type) {
598 $name = substr($name, 0, strlen($name) - $strlen);
601 // The 'hidden' flag is used by starterkits/blueprints.
602 if (($info = drupal_parse_info_file($file->uri
)) && ($all || empty($info['hidden']))) {
603 $discovery[$name] = array(
605 'path' => dirname($file->uri
),