diff --git a/plugins/context_condition_context.inc b/plugins/context_condition_context.inc index aca0e6a..31db1f1 100644 --- a/plugins/context_condition_context.inc +++ b/plugins/context_condition_context.inc @@ -5,61 +5,65 @@ */ class context_condition_context extends context_condition_path { function execute() { - if ($this->condition_used()) { - $active_contexts = array_keys(context_active_contexts()); - foreach ($this->get_contexts() as $context) { - if (!in_array($context->name, $active_contexts, TRUE) && $values = $this->fetch_from_context($context, 'values')) { - // Always check against the active contexts. - if ($this->match(array_keys(context_active_contexts()), $values)) { - $this->condition_met($context); - } - } - } - // If the list of active contexts has changed, we need to recurse. - if ($active_contexts != array_keys(context_active_contexts())) { - $this->execute(); + if (!$this->condition_used()) { + return; + } + + // Contexts that were triggered by other conditions. + $activeOld = context_active_contexts(); + + // Get a list of contexts that may be activated by this condition. + $contexts = array(); + // Prepare context values too. + $activateable = array(); + foreach ($this->get_contexts() as $context) { + if (!isset($activeOld[$context->name]) && ($values = $this->fetch_from_context($context, 'values'))) { + $contexts[$context->name] = $context; + $activateable[$context->name] = $values; } } + $activeOld = array_keys($activeOld); + $activeNew = $this->calculateActiveContexts($activeOld, $activateable); + + // Trigger all necessary contexts. + foreach ($activeNew as $name) { + $this->condition_met($contexts[$name]); + } } /** - * Retrieve all context conditions. + * Calculate the list of contexts that we need to trigger. * - * This method is slightly adapted to context_condition::get_contexts() in - * order to ensure that a context that is used as condition in another context - * gets handled before. + * @param array $activeOld + * machine names of contexts that are already active + * @param array $activateable + * associative array of condition values keyed by context name. + * listing all contexts that use this condition. + * @return array + * machine names of all contexts that meet their conditions. */ - function get_contexts($value = NULL) { - $map = context_condition_map(); - $map = isset($map[$this->plugin]) ? $map[$this->plugin] : array(); + protected function calculateActiveContexts($activeOld, $activateable) { + // Keep a list of already visited states. + $knownStates = array(); + $activeNew = array(); + $knownKey = md5(implode('|', $activeNew)); - $contexts = array(); + // Keep going until we hit an already visited state. This means either + // - We found a stable state. + // - We found a circle. + while (!isset($knownStates[$knownKey])) { + $knownStates[$knownKey] = 1; - // Add the contexts that are needed for conditions in the other contexts - // first. Start with the negated ones first, as we can not unset a met - // condition afterwards. - krsort($map); - foreach ($map as $key => $submap) { - // Negated context conditions start with a "~". - if (substr($key, 0, 1) == "~") { - $key = substr($key, 1); - } - if (!isset($contexts[$key])) { - $context = context_load($key); - // Check if context exists. This will fail for wildcards. - if ($context) { - $contexts[$context->name] = $context; + $activeStep = $activeOld + $activeNew; + $activeNew = array(); + foreach ($activateable as $name => $values) { + if ($this->match($activeStep, $values)) { + $activeNew[] = $name; } } + $knownKey = md5(implode('|', $activeNew)); } - foreach ($map as $key => $submap) { - foreach ($submap as $name) { - if (!isset($contexts[$name])) { - $context = context_load($name); - $contexts[$context->name] = $context; - } - } - } - return $contexts; + + return $activeNew; } }