array( 'cache' => TRUE, 'use hooks' => TRUE, 'classes' => array('handler'), ), ); } /** * Implementation of hook_context_plugins(). * * This is a ctools plugins hook. */ function context_context_plugins() { module_load_include('inc', 'context', 'context.plugins'); return _context_context_plugins(); } /** * Implementation of hook_context_registry(). */ function context_context_registry() { module_load_include('inc', 'context', 'context.plugins'); return _context_context_registry(); } /** * Implementation of hook_init(). */ function context_init() { if ($plugin = context_get_plugin('condition', 'path')) { $plugin->execute(); } if ($plugin = context_get_plugin('condition', 'language')) { global $language; $plugin->execute($language->language); } if ($plugin = context_get_plugin('condition', 'user')) { global $user; $plugin->execute($user); } } /** * Load & crud functions ============================================== */ /** * Context loader. * * @param $name * The name for this context object. * * @return * Returns a fully-loaded context definition. */ function context_load($name = NULL, $reset = FALSE) { ctools_include('export'); static $contexts; static $altered; if (!isset($contexts) || $reset) { $contexts = $altered = array(); if (!$reset && $contexts = context_cache_get('context')) { // Nothing here. } else { if ($reset) { ctools_export_load_object_reset('context'); } $contexts = ctools_export_load_object('context', 'all'); context_cache_set('context', $contexts); } } if (isset($name)) { // Allow other modules to alter the value just before it's returned. if (isset($contexts[$name]) && !isset($altered[$name])) { $altered[$name] = TRUE; drupal_alter('context_load', $contexts[$name]); } return isset($contexts[$name]) ? $contexts[$name] : FALSE; } return $contexts; } /** * Inserts or updates a context object into the database. * @TODO: should probably return the new cid on success -- make sure * this doesn't break any checks elsewhere. * * @param $context * The context object to be inserted. * * @return * Returns true on success, false on failure. */ function context_save($context) { $existing = context_load($context->name, TRUE); if ($existing && ($existing->export_type & EXPORT_IN_DATABASE)) { drupal_write_record('context', $context, 'name'); } else { drupal_write_record('context', $context); } context_load(NULL, TRUE); context_invalidate_cache(); return TRUE; } /** * Deletes an existing context. * * @param $context * The context object to be deleted. * * @return * Returns true on success, false on failure. */ function context_delete($context) { if (isset($context->name) && ($context->export_type & EXPORT_IN_DATABASE)) { db_query("DELETE FROM {context} WHERE name = '%s'", $context->name); context_invalidate_cache(); return TRUE; } return FALSE; } /** * Exports the specified context. */ function context_export($context, $indent = '') { $output = ctools_export_object('context', $context, $indent); $translatables = array(); foreach (array('description', 'tag') as $key) { if (!empty($context->{$key})) { $translatables[] = $context->{$key}; } } $translatables = array_filter(array_unique($translatables)); if (!empty($translatables)) { $output .= "\n"; $output .= "{$indent}// Translatables\n"; $output .= "{$indent}// Included for use with string extractors like potx.\n"; sort($translatables); foreach ($translatables as $string) { $output .= "{$indent}t(" . ctools_var_export($string) . ");\n"; } } return $output; } /** * API FUNCTIONS ====================================================== */ /** * CTools list callback for bulk export. */ function context_context_list() { $contexts = context_load(NULL, TRUE); $list = array(); foreach ($contexts as $context) { $list[$context->name] = $context->name; } return $list; } /** * Wrapper around cache_get() to make it easier for context to pull different * datastores from a single cache row. */ function context_cache_get($key, $reset = FALSE) { static $cache; if (!isset($cache) || $reset) { $cache = cache_get('context', 'cache'); $cache = $cache ? $cache->data : array(); } return !empty($cache[$key]) ? $cache[$key] : FALSE; } /** * Wrapper around cache_set() to make it easier for context to write different * datastores to a single cache row. */ function context_cache_set($key, $value) { $cache = cache_get('context', 'cache'); $cache = $cache ? $cache->data : array(); $cache[$key] = $value; cache_set('context', $cache); } /** * Wrapper around context_load() that only returns enabled contexts. */ function context_enabled_contexts($reset = FALSE) { $enabled = array(); foreach (context_load(NULL, $reset) as $context) { if (empty($context->disabled)) { $enabled[$context->name] = $context; } } return $enabled; } /** * Queue or activate contexts that have met the specified condition. * * @param $context * The context object to queue or activate. * @param $condition * String. Name for the condition that has been met. * @param $reset * Reset flag for the queue static cache. */ function context_condition_met($context, $condition, $reset = FALSE) { static $queue; if (!isset($queue) || $reset) { $queue = array(); } if (!context_isset('context', $context->name)) { // Context is using AND mode. Queue it. if (isset($context->condition_mode) && $context->condition_mode == CONTEXT_CONDITION_MODE_AND) { $queue[$context->name][$condition] = $condition; // If all conditions have been met. set the context. if (!array_diff(array_keys($context->conditions), $queue[$context->name])) { context_set('context', $context->name, $context); } } // Context is using OR mode. Set it. else { context_set('context', $context->name, $context); } } } /** * Loads any active contexts with associated reactions. This should be run * at a late stage of the page load to ensure that relevant contexts have been set. */ function context_active_contexts() { $contexts = context_get('context'); return !empty($contexts) && is_array($contexts) ? $contexts : array(); } /** * Loads an associative array of conditions => context identifiers to allow * contexts to be set by different conditions. */ function context_condition_map($reset = FALSE) { static $condition_map; if (!isset($condition_map) || $reset) { if (!$reset && $cache = context_cache_get('condition_map')) { $condition_map = $cache; } else { foreach (array_keys(context_conditions()) as $condition) { if ($plugin = context_get_plugin('condition', $condition)) { foreach (context_enabled_contexts() as $context) { $values = $plugin->fetch_from_context($context, 'values'); foreach ($values as $value) { if (!isset($condition_map[$condition][$value])) { $condition_map[$condition][$value] = array(); } $condition_map[$condition][$value][] = $context->name; } } } } context_cache_set('condition_map', $condition_map); } } return $condition_map; } /** * Invalidates all context caches(). * @TODO: Update to use a CTools API function for clearing plugin caches * when/if it becomes available. */ function context_invalidate_cache() { cache_clear_all('context', 'cache', TRUE); cache_clear_all('plugins:context', 'cache', TRUE); } /** * Implementation of hook_flush_caches(). */ function context_flush_caches() { context_invalidate_cache(); } /** * Recursive helper function to determine whether an array and its * children are entirely empty. */ function context_empty($element) { $empty = TRUE; if (is_array($element)) { foreach ($element as $child) { $empty = $empty && context_empty($child); } } else { $empty = $empty && empty($element); } return $empty; } /** * Get a plugin handler. */ function context_get_plugin($type = 'condition', $key, $reset = FALSE) { static $cache = array(); if (!isset($cache[$type][$key]) || $reset) { switch ($type) { case 'condition': $registry = context_conditions(); break; case 'reaction': $registry = context_reactions(); break; } if (isset($registry[$key], $registry[$key]['plugin'])) { ctools_include('plugins'); $info = $registry[$key]; $plugins = ctools_get_plugins('context', 'plugins'); if (isset($plugins[$info['plugin']]) && $class = ctools_plugin_get_class($plugins[$info['plugin']], 'handler')) { // Check that class exists until CTools & registry issues are resolved. if (class_exists($class)) { $cache[$type][$key] = new $class($key, $info); } } } } return isset($cache[$type][$key]) ? $cache[$type][$key] : FALSE; } /** * Get all context conditions. */ function context_conditions($reset = FALSE) { return _context_registry('conditions', $reset); } /** * Get all context reactions. */ function context_reactions($reset = FALSE) { return _context_registry('reactions', $reset); } /** * Retrieves & caches the context registry. */ function _context_registry($key = NULL, $reset = FALSE) { static $registry; if (!isset($registry) || $reset) { if (!$reset && $cache = context_cache_get('registry')) { $registry = $cache; } else { $registry = module_invoke_all('context_registry'); drupal_alter('context_registry', $registry); context_cache_set('registry', $registry); } } if (isset($key)) { return isset($registry[$key]) ? $registry[$key] : array(); } return $registry; }