upgrade omega again
[indybay:drupal.git] / docs / sites / all / modules / contrib / context / context.module
1 <?php
2
3 require('context.core.inc');
4
5 define('CONTEXT_GET', 0);
6 define('CONTEXT_SET', 1);
7 define('CONTEXT_ISSET', 2);
8 define('CONTEXT_CLEAR', 3);
9
10 define('CONTEXT_CONDITION_MODE_OR', 0);
11 define('CONTEXT_CONDITION_MODE_AND', 1);
12
13 /**
14  * Master context function. Avoid calling this directly -- use one of the helper functions below.
15  *
16  * @param $op
17  *   The operation to perform - handled by the context helper functions. Use them.
18  * @param $namespace
19  *   A string to be used as the namespace for the context information.
20  * @param $attribute
21  *   Usually a string to be used as a key to set/retrieve context information. An array can
22  *   also be used when setting context to establish an entire context namespace at once.
23  *   (At some point objects may also be accepted, but currently functionaliy isn't complete.)
24  * @param $value
25  *   A value to set for the provided key. If omitted the value will be set to true.
26  *
27  * @return
28  *   Either the requested value, or false if the operation fails.
29  */
30 function context_context($op = CONTEXT_GET, $namespace = NULL, $attribute = NULL, $value = NULL) {
31   static $context;
32   $context = !$context ? array() : $context;
33   switch ($op) {
34     case CONTEXT_GET:
35       // return entire context
36       if (!$namespace) {
37         return $context;
38       }
39       // return entire space if set
40       elseif (isset($context[(string) $namespace])) {
41         // return val of key from space
42         if (is_array($context[(string) $namespace]) && isset($context[(string) $namespace][(string) $attribute])) {
43           return $context[(string) $namespace][(string) $attribute];
44         }
45         elseif (!$attribute) {
46           return $context[(string) $namespace];
47         }
48       }
49       break;
50     case CONTEXT_SET:
51       // bail if invalid space is specified or context is already set
52       if (is_string($namespace) || is_int($namespace)) {
53         // initialize namespace if no key is specified
54         if (!$attribute) {
55           $context[(string) $namespace] = array();
56           return TRUE;
57         }
58         // set to true if key is a usable identifier. otherwise, allow a key or object to be inserted
59         if ($value === NULL) {
60           if (is_string($attribute) || is_int($attribute)) {
61             $context[(string) $namespace][(string) $attribute] = TRUE;
62             return TRUE;
63           }
64           elseif (is_array($attribute) || is_object($attribute)) {
65             $context[(string) $namespace] = $attribute;
66             return TRUE;
67           }
68         }
69         // set value if key is valid
70         if ((is_string($attribute) || is_int($attribute)) && $value !== NULL) {
71           $context[$namespace][$attribute] = $value;
72           return TRUE;
73         }
74       }
75       break;
76     case CONTEXT_ISSET:
77       // return entire context
78       if (!$namespace) return FALSE;
79       if (!$attribute) {
80         // return entire space if set
81         return isset($context[$namespace]);
82       }
83       // return val of key from space
84       return isset($context[$namespace][$attribute]);
85     case CONTEXT_CLEAR:
86       $context = array();
87       return TRUE;
88   }
89   return FALSE;
90 }
91
92 /**
93  * Sets a context by namespace + attribute.
94  */
95 function context_set($namespace, $attribute = NULL, $value = NULL) {
96   return context_context(CONTEXT_SET, $namespace, $attribute, $value);
97 }
98
99 /**
100  * Retrieves a context by namespace + (optional) attribute.
101  */
102 function context_get($namespace = NULL, $attribute = NULL) {
103   return context_context(CONTEXT_GET, $namespace, $attribute, NULL);
104 }
105
106 /**
107  * Returns a boolean for whether a context namespace + attribute have been set.
108  */
109 function context_isset($namespace = NULL, $attribute = NULL) {
110   return context_context(CONTEXT_ISSET, $namespace, $attribute, NULL);
111 }
112
113 /**
114  * Deprecated context_exists() function. Retained for backwards
115  * compatibility -- please use context_isset() instead.
116  */
117 function context_exists($namespace = NULL, $attribute = NULL) {
118   return context_context(CONTEXT_ISSET, $namespace, $attribute, NULL);
119 }
120
121 /**
122  * Clears static context array() -- meant only for testing
123  */
124 function context_clear() {
125   return context_context(CONTEXT_CLEAR);
126 }
127
128 /**
129  * Implemented hooks ==================================================
130  */
131
132 /**
133  * Implementation of hook_ctools_plugin_type().
134  */
135 function context_ctools_plugin_type() {
136   return array(
137     'plugins' => array(
138       'cache' => TRUE,
139       'use hooks' => TRUE,
140       'classes' => array('handler'),
141     ),
142   );
143 }
144
145 /**
146  * Implementation of hook_context_plugins().
147  *
148  * This is a ctools plugins hook.
149  */
150 function context_context_plugins() {
151   module_load_include('inc', 'context', 'context.plugins');
152   return _context_context_plugins();
153 }
154
155
156 /**
157  * Implementation of hook_context_registry().
158  */
159 function context_context_registry() {
160   module_load_include('inc', 'context', 'context.plugins');
161   return _context_context_registry();
162 }
163
164 /**
165  * Implementation of hook_init().
166  */
167 function context_init() {
168   if ($plugin = context_get_plugin('condition', 'path')) {
169     $plugin->execute();
170   }
171   if ($plugin = context_get_plugin('condition', 'language')) {
172     global $language;
173     $plugin->execute($language->language);
174   }
175   if ($plugin = context_get_plugin('condition', 'user')) {
176     global $user;
177     $plugin->execute($user);
178   }
179 }
180
181 /**
182  * Implementation of hook_preprocess_menu_link().
183  *
184  * This allows menus that are not primary/secondary menus to get
185  * the "active" class assigned to them. This assumes they are using
186  * theme('menu_link') for the menu rendering to html.
187  */
188 function context_preprocess_menu_link(&$variables) {
189   if($contexts = context_active_contexts()){
190     foreach($contexts as $context){
191       if((isset($context->reactions['menu']))){
192         if ($variables['element']['#href'] == $context->reactions['menu']) {
193           $variables['element']['#localized_options']['attributes']['class'][] = "active";
194         }
195       }
196     }
197   }
198 }
199
200 /**
201  * Load & crud functions ==============================================
202  */
203
204 /**
205  * Context loader.
206  *
207  * @param $name
208  *   The name for this context object.
209  *
210  * @return
211  *   Returns a fully-loaded context definition.
212  */
213 function context_load($name = NULL, $reset = FALSE) {
214   ctools_include('export');
215   static $contexts;
216   static $altered;
217   if (!isset($contexts) || $reset) {
218     $contexts = $altered = array();
219     if (!$reset && $contexts = context_cache_get('context')) {
220       // Nothing here.
221     }
222     else {
223       if ($reset) {
224         ctools_export_load_object_reset('context');
225       }
226       $contexts = ctools_export_load_object('context', 'all');
227       context_cache_set('context', $contexts);
228     }
229   }
230   if (isset($name)) {
231     // Allow other modules to alter the value just before it's returned.
232     if (isset($contexts[$name]) && !isset($altered[$name])) {
233       $altered[$name] = TRUE;
234       drupal_alter('context_load', $contexts[$name]);
235     }
236     return isset($contexts[$name]) ? $contexts[$name] : FALSE;
237   }
238   return $contexts;
239 }
240
241 /**
242  * Inserts or updates a context object into the database.
243  * @TODO: should probably return the new cid on success -- make sure
244  * this doesn't break any checks elsewhere.
245  *
246  * @param $context
247  *   The context object to be inserted.
248  *
249  * @return
250  *   Returns true on success, false on failure.
251  */
252 function context_save($context) {
253   $existing = context_load($context->name, TRUE);
254   if ($existing && ($existing->export_type & EXPORT_IN_DATABASE)) {
255     drupal_write_record('context', $context, 'name');
256   }
257   else {
258     drupal_write_record('context', $context);
259   }
260   context_load(NULL, TRUE);
261   context_invalidate_cache();
262   return TRUE;
263 }
264
265 /**
266  * Deletes an existing context.
267  *
268  * @param $context
269  *   The context object to be deleted.
270  *
271  * @return
272  *   Returns true on success, false on failure.
273  */
274 function context_delete($context) {
275   if (isset($context->name) && ($context->export_type & EXPORT_IN_DATABASE)) {
276     db_query("DELETE FROM {context} WHERE name = :name", array(':name' => $context->name));
277     context_invalidate_cache();
278     return TRUE;
279   }
280   return FALSE;
281 }
282
283 /**
284  * Exports the specified context.
285  */
286 function context_export($context, $indent = '') {
287   $output = ctools_export_object('context', $context, $indent);
288   $translatables = array();
289   foreach (array('description', 'tag') as $key) {
290     if (!empty($context->{$key})) {
291       $translatables[] = $context->{$key};
292     }
293   }
294   $translatables = array_filter(array_unique($translatables));
295   if (!empty($translatables)) {
296     $output .= "\n";
297     $output .= "{$indent}// Translatables\n";
298     $output .= "{$indent}// Included for use with string extractors like potx.\n";
299     sort($translatables);
300     foreach ($translatables as $string) {
301       $output .= "{$indent}t(" . ctools_var_export($string) . ");\n";
302     }
303   }
304   return $output;
305 }
306
307 /**
308  * API FUNCTIONS ======================================================
309  */
310
311 /**
312  * CTools list callback for bulk export.
313  */
314 function context_context_list() {
315   $contexts = context_load(NULL, TRUE);
316   $list = array();
317   foreach ($contexts as $context) {
318     $list[$context->name] = $context->name;
319   }
320   return $list;
321 }
322
323 /**
324  * Wrapper around cache_get() to make it easier for context to pull different
325  * datastores from a single cache row.
326  */
327 function context_cache_get($key, $reset = FALSE) {
328   static $cache;
329   if (!isset($cache) || $reset) {
330     $cache = cache_get('context', 'cache');
331     $cache = $cache ? $cache->data : array();
332   }
333   return !empty($cache[$key]) ? $cache[$key] : FALSE;
334 }
335
336 /**
337  * Wrapper around cache_set() to make it easier for context to write different
338  * datastores to a single cache row.
339  */
340 function context_cache_set($key, $value) {
341   $cache = cache_get('context', 'cache');
342   $cache = $cache ? $cache->data : array();
343   $cache[$key] = $value;
344   cache_set('context', $cache);
345 }
346
347 /**
348  * Wrapper around context_load() that only returns enabled contexts.
349  */
350 function context_enabled_contexts($reset = FALSE) {
351   $enabled = array();
352   foreach (context_load(NULL, $reset) as $context) {
353     if (empty($context->disabled)) {
354       $enabled[$context->name] = $context;
355     }
356   }
357   return $enabled;
358 }
359
360 /**
361  * Queue or activate contexts that have met the specified condition.
362  *
363  * @param $context
364  *   The context object to queue or activate.
365  * @param $condition
366  *   String. Name for the condition that has been met.
367  * @param $reset
368  *   Reset flag for the queue static cache.
369  */
370 function context_condition_met($context, $condition, $reset = FALSE) {
371   static $queue;
372   if (!isset($queue) || $reset) {
373     $queue = array();
374   }
375   if (!context_isset('context', $context->name)) {
376     // Context is using AND mode. Queue it.
377     if (isset($context->condition_mode) && $context->condition_mode == CONTEXT_CONDITION_MODE_AND) {
378       $queue[$context->name][$condition] = $condition;
379
380       // If all conditions have been met. set the context.
381       if (!array_diff(array_keys($context->conditions), $queue[$context->name])) {
382         context_set('context', $context->name, $context);
383       }
384     }
385     // Context is using OR mode. Set it.
386     else {
387       context_set('context', $context->name, $context);
388     }
389   }
390 }
391
392 /**
393  * Loads any active contexts with associated reactions. This should be run
394  * at a late stage of the page load to ensure that relevant contexts have been set.
395  */
396 function context_active_contexts() {
397   $contexts = context_get('context');
398   return !empty($contexts) && is_array($contexts) ? $contexts : array();
399 }
400
401 /**
402  * Loads an associative array of conditions => context identifiers to allow
403  * contexts to be set by different conditions.
404  */
405 function context_condition_map($reset = FALSE) {
406   static $condition_map;
407   if (!isset($condition_map) || $reset) {
408     if (!$reset && $cache = context_cache_get('condition_map')) {
409       $condition_map = $cache;
410     }
411     else {
412       $condition_map = array();
413       foreach (array_keys(context_conditions()) as $condition) {
414         if ($plugin = context_get_plugin('condition', $condition)) {
415           foreach (context_enabled_contexts() as $context) {
416             $values = $plugin->fetch_from_context($context, 'values');
417             foreach ($values as $value) {
418               if (!isset($condition_map[$condition][$value])) {
419                 $condition_map[$condition][$value] = array();
420               }
421               $condition_map[$condition][$value][] = $context->name;
422             }
423           }
424         }
425       }
426       context_cache_set('condition_map', $condition_map);
427     }
428   }
429   return $condition_map;
430 }
431
432 /**
433  * Invalidates all context caches().
434  * @TODO: Update to use a CTools API function for clearing plugin caches
435  * when/if it becomes available.
436  */
437 function context_invalidate_cache() {
438   cache_clear_all('context', 'cache', TRUE);
439   cache_clear_all('plugins:context', 'cache', TRUE);
440 }
441
442 /**
443  * Implementation of hook_flush_caches().
444  */
445 function context_flush_caches() {
446   context_invalidate_cache();
447 }
448
449 /**
450  * Recursive helper function to determine whether an array and its
451  * children are entirely empty.
452  */
453 function context_empty($element) {
454   $empty = TRUE;
455   if (is_array($element)) {
456     foreach ($element as $child) {
457       $empty = $empty && context_empty($child);
458     }
459   }
460   else {
461     $empty = $empty && empty($element);
462   }
463   return $empty;
464 }
465
466 /**
467  * Get a plugin handler.
468  */
469 function context_get_plugin($type = 'condition', $key, $reset = FALSE) {
470   static $cache = array();
471   if (!isset($cache[$type][$key]) || $reset) {
472     switch ($type) {
473       case 'condition':
474         $registry = context_conditions();
475         break;
476       case 'reaction':
477         $registry = context_reactions();
478         break;
479     }
480     if (isset($registry[$key], $registry[$key]['plugin'])) {
481       ctools_include('plugins');
482       $info = $registry[$key];
483       $plugins = ctools_get_plugins('context', 'plugins');
484       if (isset($plugins[$info['plugin']]) && $class = ctools_plugin_get_class($plugins[$info['plugin']], 'handler')) {
485         // Check that class exists until CTools & registry issues are resolved.
486         if (class_exists($class)) {
487           $cache[$type][$key] = new $class($key, $info);
488         }
489       }
490     }
491   }
492   return isset($cache[$type][$key]) ? $cache[$type][$key] : FALSE;
493 }
494
495 /**
496  * Get all context conditions.
497  */
498 function context_conditions($reset = FALSE) {
499   return _context_registry('conditions', $reset);
500 }
501
502 /**
503  * Get all context reactions.
504  */
505 function context_reactions($reset = FALSE) {
506   return _context_registry('reactions', $reset);
507 }
508
509 /**
510  * Retrieves & caches the context registry.
511  */
512 function _context_registry($key = NULL, $reset = FALSE) {
513   static $registry;
514   if (!isset($registry) || $reset) {
515     if (!$reset && $cache = context_cache_get('registry')) {
516       $registry = $cache;
517     }
518     else {
519       $registry = module_invoke_all('context_registry');
520       drupal_alter('context_registry', $registry);
521       context_cache_set('registry', $registry);
522     }
523   }
524   if (isset($key)) {
525     return isset($registry[$key]) ? $registry[$key] : array();
526   }
527   return $registry;
528 }
529
530 /**
531  * hook_block_view_alter - if the context editor block is on this page,
532  * ensure that all blocks have some content so that empty blocks are
533  * not dropped
534  */
535 function context_block_view_alter(&$data, $block) {
536   if (context_isset('context_ui', 'context_ui_editor_present') && empty($data['content'])) {
537     $data['content']['#markup'] = "<div class='context-block-empty-content'>" . t('This block appears empty when displayed on this page.') . "</div>";
538     $data['context_block_hidden'] = TRUE;
539   }
540 }
541
542 /**
543  * implement hook_page_alter()
544  *
545  * used for region context
546  */
547 function context_page_alter(&$page) {
548   if ($plugin = context_get_plugin('reaction', 'region')) {
549     $plugin->execute($page);
550   }
551 }
552
553 /**
554  * hook_block_view_alter - if the context editor block is on this page,
555  * ensure that all blocks have some content so that empty blocks are
556  * not dropped
557  */
558 function context_preprocess_block(&$vars) {
559   if (isset($vars['block']->context_block_hidden)) {
560     $vars['classes_array'][] = 'context-block-hidden';
561     $vars['classes_array'][] = 'context-block-empty';
562   }
563 }