$context) { // context integration against paths if (!empty($context->path) && is_array($context->path)) { $paths = implode("\n", $context->path); $path_match = drupal_match_path($current_path, $paths); if ($current_path != $_GET['q']) { $path_match = $path_match || drupal_match_path($_GET['q'], $paths); } if ($path_match) { context_set($context->namespace, $context->attribute, $context->value); } } // context integration for sitewide context if (!empty($context->sitewide)) { context_set($context->namespace, $context->attribute, $context->value); } } } /** * Implementation of hook_flush_caches(). */ function context_flush_caches() { context_invalidate_cache(); return array(); } /** * L'CRUD FUNCTIONS =================================================== */ /** * Context loader. * * @param $context * The parameters to use for loading this context. Can be an integer * cid or partial context object. * * @return * Returns a fully-loaded context definition. */ function context_load_context($context, $reset = FALSE) { static $cache = array(); // Argument is a cid if (is_numeric($context)) { $cid = $context; if (!isset($cache[$cid])) { $context = db_fetch_object(db_query("SELECT * FROM {context} WHERE cid = %d", $cid)); } else { return $cache[$cid]; } } // Context object has an associated cid else if (is_object($context) && isset($context->cid)) { $context = db_fetch_object(db_query("SELECT * FROM {context} WHERE cid = %d", $context->cid)); } // Context object has no cid -- we'll try to load by ns/attr/val else if (is_object($context) && $context->namespace && $context->attribute && $context->value) { $args = array($context->namespace, $context->attribute, $context->value); $context = db_fetch_object(db_query("SELECT * FROM {context} WHERE namespace = '%s' AND attribute = '%s' AND value = '%s'", $args)); } // Unserialize and populate associations if (!empty($context->data)) { $context = context_unpack_context($context); $cache[$context->cid] = $context; return $context; } return FALSE; } /** * 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($context) { // Insert or update the core context definition if (!isset($context->cid)) { $existing = context_load_context($context, TRUE); if ($existing && $existing->cid) { return FALSE; } else { $packed = context_pack_context($context); drupal_write_record('context', $packed); } } else { $packed = context_pack_context($context); drupal_write_record('context', $packed, 'cid'); } // Invalidate context cache cache_clear_all('context', 'cache'); return TRUE; } /** * Deletes an existing context. * * @param $context * The context object to be deleted. The $context->cid property is * necessary for this operation. * * @return * Returns true on success, false on failure. */ function context_delete_context($context) { if ($context = context_load_context($context, TRUE)) { db_query("DELETE FROM {context} WHERE cid = %d", $context->cid); // Invalidate context cache cache_clear_all('context', 'cache'); return TRUE; } return FALSE; } /** * Helper function to pack a context object to be stored in the database. */ function context_pack_context($context) { $packed = new stdClass(); $packed->cid = $context->cid; $packed->namespace = $context->namespace; $packed->attribute = $context->attribute; $packed->value = $context->value; $serialized = drupal_clone($context); // Clear out non-condition/reaction attributes unset($serialized->cid, $serialized->namespace, $serialized->attribute, $serialized->value, $serialized->type, $serialized->status); // Serialize associations as data array $serialized = (array) $serialized; $packed->data = serialize($serialized); return $packed; } /** * Helper function to unpack a context object that has been retrieved from the database. * @TODO: Remove this once ->conditions and ->reactions issue has been resolved. */ function context_unpack_context($context) { if (!empty($context->data)) { $data = unserialize($context->data); foreach ($data as $k => $v) { $context->{$k} = $v; } unset($context->data); } return $context; } /** * API FUNCTIONS ====================================================== */ /** * Provides an array of all contexts provided by modules and in the database. * * @param $reset * Boolean to clear the static cached array of contexts. * * @return * An array of context objects. */ function context_contexts($reset = FALSE) { static $contexts; if (!$contexts || $reset) { $contexts = array(); foreach (module_implements('context_default_contexts') as $module) { $function = $module .'_context_default_contexts'; $c = call_user_func($function); if (!empty($c)) { foreach ($c as $context) { $context = (object) $context; $context->type = CONTEXT_STORAGE_DEFAULT; $identifier = "{$context->namespace}-{$context->attribute}-{$context->value}"; $contexts[$identifier] = $context; } } } // Allow other modules to alter contexts drupal_alter('context_default_contexts', $contexts); // Collect normal & overridden contexts $result = db_query("SELECT * FROM {context} ORDER BY namespace ASC, attribute ASC, value ASC"); while ($context = db_fetch_object($result)) { $context = context_unpack_context($context); $key = "{$context->namespace}-{$context->attribute}-{$context->value}"; if (isset($contexts[$key])) { $contexts[$key] = $context; $contexts[$key]->type = CONTEXT_STORAGE_OVERRIDDEN; } else { $contexts[$key] = $context; $contexts[$key]->type = CONTEXT_STORAGE_NORMAL; } } // Mark the status of each context foreach ($contexts as $key => $context) { $contexts[$key]->status = context_status($context); } } return $contexts; } /** * 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); } /** * Retrieves all enabled contexts from the cache. */ function context_enabled_contexts($namespace = NULL, $reset = FALSE) { static $enabled; static $namespaces; if (!isset($enabled) || $reset) { $enabled = array(); $cache = context_cache_get('enabled'); if ($cache && !$reset) { $enabled = $cache; } else { $contexts = context_contexts(TRUE); foreach ($contexts as $context) { if (context_status($context) == CONTEXT_STATUS_ENABLED) { $identifier = "{$context->namespace}-{$context->attribute}-{$context->value}"; $enabled[$identifier] = $context; } } // Set the cache context_cache_set('enabled', $enabled); } foreach ($enabled as $identifier => $context) { if (!isset($namespaces[$context->namespace])) { $namespaces[$context->namespace] = array(); } $namespaces[$context->namespace][$identifier] = $context; } } if (!empty($namespace)) { return !empty($namespaces[$namespace]) ? $namespaces[$namespace] : array(); } return $enabled; } /** * 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($reset = FALSE) { static $contexts; if (!isset($contexts) || $reset) { $contexts = array(); $cache = context_enabled_contexts(); foreach ($cache as $context) { if (context_get($context->namespace, $context->attribute) == $context->value) { $identifier = "{$context->namespace}-{$context->attribute}-{$context->value}"; $contexts[$identifier] = $context; } } drupal_alter('context_active_contexts', $contexts); } return $contexts; } /** * Loads an associative array of conditions => context identifiers to allow * contexts to be set by different conditions. Called by context_set_by_condition(). */ function context_condition_map($reset = FALSE) { static $condition_map; if (!isset($condition_map) || $reset) { $cache = context_cache_get('condition_map'); if ($cache && !$reset) { $condition_map = $cache; } else { $enabled = context_enabled_contexts(); foreach (array_keys(context_conditions()) as $condition) { $condition_map[$condition] = array(); foreach ($enabled as $identifier => $context) { if (!empty($context->{$condition})) { if (is_array($context->{$condition})) { foreach ($context->{$condition} as $value) { if (!isset($condition_map[$condition][$value])) { $condition_map[$condition][$value] = array(); } $condition_map[$condition][$value][] = $identifier; } } else if (is_string($context->{$condition})) { $value = $context->{$condition}; if (!isset($condition_map[$condition][$value])) { $condition_map[$condition][$value] = array(); } $condition_map[$condition][$value][] = $identifier; } } } } context_cache_set('condition_map', $condition_map); } } return $condition_map; } /** * Builds an array of values based on the current active contexts and group * by conditions/reactions. * * @param $type * The condition or reaction type to pull values for. * @param $reset * Boolean value for resetting the static cache. * * @return * Returns a keyed array of reactions and values. */ function context_active_values($type = NULL, $reset = FALSE) { static $value_map; if (!isset($value_map) || $reset) { $value_map = array(); // This sucks... @TODO: fix it!!! $keys = array_merge(array_keys(context_reactions()), array_keys(context_conditions())); $keys[] = 'block'; foreach ($keys as $key) { foreach (context_active_contexts() as $identifier => $context) { if (!empty($context->{$key})) { if (!isset($value_map[$key])) { $value_map[$key] = array(); } if (is_array($context->{$key})) { $value_map[$key] = array_merge($value_map[$key], $context->{$key}); } else { $value_map[$key][] = $context->{$key}; } } } } } if (!empty($type)) { return !empty($value_map[$type]) ? $value_map[$type] : array(); } return $value_map; } /** * Returns status for whether a given context definition is enabled. */ function context_status($context) { $status = variable_get('context_status', array()); $identifier = "{$context->namespace}-{$context->attribute}-{$context->value}"; if (isset($status[$identifier]) && !$status[$identifier]) { return CONTEXT_STATUS_DISABLED; } return CONTEXT_STATUS_ENABLED; } /** * Invalidates all context caches(). */ function context_invalidate_cache() { cache_clear_all('context', 'cache'); } /** * Invokes hook_context_conditions() to provide an array of conditions that may be associated with a context. */ function context_conditions($reset = FALSE) { static $conditions; if (!isset($conditions) || $reset) { $cache = context_cache_get('conditions'); if ($cache && !$reset) { $conditions = $cache; } else { $conditions = module_invoke_all('context_conditions'); context_cache_set('conditions', $conditions); } } return $conditions; } /** * Invokes hook_context_reactions() to provide an array of reactions that may be associated with a context. */ function context_reactions($reset = FALSE) { static $reactions; if (!isset($reactions) || $reset) { $cache = context_cache_get('reactions'); if ($cache && !$reset) { $reactions = $cache; } else { $reactions = module_invoke_all('context_reactions'); context_cache_set('reactions', $reactions); } } return $reactions; } /** * Sets a namespace-attribute-value context that has been associated with the provided condition. * * @param $type * The condition type to be matched. * @param $id * A value to match against. * @param $force * Boolean param to force the setting of a context value even if a value for * the found namespace/attribute is already set. Defaults to FALSE. * * @return * True if one or more contexts were set. False if no items/contexts matched. */ function context_set_by_condition($type, $id, $force = FALSE) { $map = context_condition_map(); $set = FALSE; if (!empty($map[$type]) && !empty($map[$type][$id])) { $contexts = context_enabled_contexts(); $identifiers = $map[$type][$id]; foreach ($identifiers as $identifier) { $context = !empty($contexts[$identifier]) ? $contexts[$identifier] : FALSE; if ($context) { // If this context already has a value, don't alter it. if (!context_isset($context->namespace, $context->attribute) || $force) { context_set($context->namespace, $context->attribute, $context->value); $set = TRUE; } } } } return $set; } /** * 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; } /** * Export a variable. */ function context_var_export($var, $prefix = '', $multiple = TRUE) { if (is_array($var)) { if (empty($var)) { $output = 'array()'; } else { $output = "array(\n"; foreach ($var as $key => $value) { $output .= " '$key' => ". context_var_export($value, $prefix . ($multiple? '' : ' ')) .",\n"; } $output .= ')'; } } else if (is_bool($var)) { $output = $var ? 'TRUE' : 'FALSE'; } else { $output = var_export($var, TRUE); } if ($prefix) { $output = str_replace("\n", "\n$prefix", $output); } return $output; }