Projet

Général

Profil

Paste
Télécharger (47,4 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / ctools / includes / context.inc @ e4c061ad

1
<?php
2

    
3
/**
4
 * @file
5
 *
6
 * Contains code related to the ctools system of 'context'.
7
 *
8
 * Context, originally from Panels, is a method of packaging objects into
9
 * a more generic bundle and providing a plugin system so that a UI can
10
 * take advantage of them. The idea is that the context objects
11
 * represent 'the context' that a given operation (usually a page view)
12
 * is operating in or on.
13
 *
14
 * For example, when viewing a page, the 'context' is a node object. When
15
 * viewing a user, the 'context' is a user object. Contexts can also
16
 * have related contexts. For example, when viewing a 'node' you may need
17
 * to know something about the node author. Therefore, the node author
18
 * is a related context.
19
 */
20

    
21
/**
22
 * The context object is largely a wrapper around some other object, with
23
 * an interface to finding out what is contained and getting to both
24
 * the object and information about the object.
25
 *
26
 * Each context object has its own information, but some things are very
27
 * common, such as titles, data, keywords, etc. In particulare, the 'type'
28
 * of the context is important.
29
 */
30
class ctools_context {
31
  var $type = NULL;
32
  var $data = NULL;
33
  // The title of this object.
34
  var $title = '';
35
  // The title of the page if this object exists
36
  var $page_title = '';
37
  // The identifier (in the UI) of this object
38
  var $identifier = '';
39
  var $argument = NULL;
40
  var $keyword = '';
41
  var $original_argument = NULL;
42
  var $restrictions = array();
43
  var $empty = FALSE;
44

    
45
  function ctools_context($type = 'none', $data = NULL) {
46
    $this->type  = $type;
47
    $this->data  = $data;
48
    $this->title = t('Unknown context');
49
  }
50

    
51
  function is_type($type) {
52
    if ($type == 'any' || $this->type == 'any') {
53
      return TRUE;
54
    }
55

    
56
    $a = is_array($type) ? $type : array($type);
57
    $b = is_array($this->type) ? $this->type : array($this->type);
58
    return (bool) array_intersect($a, $b);
59
  }
60

    
61
  function get_argument() {
62
    return $this->argument;
63
  }
64

    
65
  function get_original_argument() {
66
    if (!is_null($this->original_argument)) {
67
      return $this->original_argument;
68
    }
69
    return $this->argument;
70
  }
71

    
72
  function get_keyword() {
73
    return $this->keyword;
74
  }
75

    
76
  function get_identifier() {
77
    return $this->identifier;
78
  }
79

    
80
  function get_title() {
81
    return $this->title;
82
  }
83

    
84
  function get_page_title() {
85
    return $this->page_title;
86
  }
87
}
88

    
89
/**
90
 * Used to create a method of comparing if a list of contexts
91
 * match a required context type.
92
 */
93
class ctools_context_required {
94
  var $keywords = '';
95

    
96
  /**
97
   * If set, the title will be used in the selector to identify
98
   * the context. This is very useful when multiple contexts
99
   * are required to inform the user will be used for what.
100
   */
101
  var $title = NULL;
102

    
103
  /**
104
   * Test to see if this context is required.
105
   */
106
  var $required = TRUE;
107

    
108
  /**
109
   * If TRUE, skip the check in ctools_context_required::select()
110
   * for contexts whose names may have changed.
111
   */
112
  var $skip_name_check = FALSE;
113

    
114
  /**
115
   *
116
   * @param $title
117
   *   The first parameter should be the 'title' of the context for use
118
   *   in UYI selectors when multiple contexts qualify.
119
   * @param ...
120
   *   One or more keywords to use for matching which contexts are allowed.
121
   */
122
  function ctools_context_required($title) {
123
    $args = func_get_args();
124
    $this->title = array_shift($args);
125

    
126
    // If we have a boolean value at the end for $skip_name_check, store it
127
    if (is_bool(end($args))) {
128
      $this->skip_name_check = array_pop($args);
129
    }
130

    
131
    // If we were given restrictions at the end, store them.
132
    if (count($args) > 1 && is_array(end($args))) {
133
      $this->restrictions = array_pop($args);
134
    }
135

    
136
    if (count($args) == 1) {
137
      $args = array_shift($args);
138
    }
139
    $this->keywords = $args;
140
  }
141

    
142
  function filter($contexts) {
143
    $result = array();
144

    
145
    // See which of these contexts are valid
146
    foreach ((array) $contexts as $cid => $context) {
147
      if ($context->is_type($this->keywords)) {
148
        // Compare to see if our contexts were met.
149
        if (!empty($this->restrictions) && !empty($context->restrictions)) {
150
          foreach ($this->restrictions as $key => $values) {
151
            // If we have a restriction, the context must either not have that
152
            // restriction listed, which means we simply don't know what it is,
153
            // or there must be an intersection of the restricted values on
154
            // both sides.
155
            if (!is_array($values)) {
156
              $values = array($values);
157
            }
158
            if (!empty($context->restrictions[$key]) && !array_intersect($values, $context->restrictions[$key])) {
159
              continue 2;
160
            }
161
          }
162
        }
163
        $result[$cid] = $context;
164
      }
165
    }
166

    
167
    return $result;
168
  }
169

    
170
  function select($contexts, $context) {
171
    if (!is_array($contexts)) {
172
      if (is_object($contexts) && $contexts instanceof ctools_context) {
173
        $contexts = array($contexts->id => $contexts);
174
      }
175
      else {
176
        $contexts = array($contexts);
177
      }
178
    }
179

    
180
    // If we had requested a $context but that $context doesn't exist
181
    // in our context list, there is a good chance that what happened
182
    // is our context IDs changed. See if there's another context
183
    // that satisfies our requirements.
184
    if (!$this->skip_name_check && !empty($context) && !isset($contexts[$context])) {
185
      $choices = $this->filter($contexts);
186

    
187
      // If we got a hit, take the first one that matches.
188
      if ($choices) {
189
        $keys = array_keys($choices);
190
        $context = reset($keys);
191
      }
192
    }
193

    
194
    if (empty($context) || empty($contexts[$context])) {
195
      return FALSE;
196
    }
197
    return $contexts[$context];
198
  }
199
}
200

    
201
/**
202
 * Used to compare to see if a list of contexts match an optional context. This
203
 * can produce empty contexts to use as placeholders.
204
 */
205
class ctools_context_optional extends ctools_context_required {
206
  var $required = FALSE;
207
  function ctools_context_optional() {
208
    $args = func_get_args();
209
    call_user_func_array(array($this, 'ctools_context_required'), $args);
210
  }
211

    
212
  /**
213
   * Add the 'empty' context which is possible for optional
214
   */
215
  function add_empty(&$contexts) {
216
    $context = new ctools_context('any');
217
    $context->title      = t('No context');
218
    $context->identifier = t('No context');
219
    $contexts = array_merge(array('empty' => $context), $contexts);
220
  }
221

    
222
  function filter($contexts) {
223
    $this->add_empty($contexts);
224
    return parent::filter($contexts);
225
  }
226

    
227
  function select($contexts, $context) {
228
    $this->add_empty($contexts);
229
    if (empty($context)) {
230
      return $contexts['empty'];
231
    }
232

    
233
    $result = parent::select($contexts, $context);
234

    
235
    // Don't flip out if it can't find the context; this is optional, put
236
    // in an empty.
237
    if ($result == FALSE) {
238
      $result = $contexts['empty'];
239
    }
240
    return $result;
241
  }
242
}
243

    
244
/**
245
 * Return a keyed array of context that match the given 'required context'
246
 * filters.
247
 *
248
 * Functions or systems that require contexts of a particular type provide a
249
 * ctools_context_required or ctools_context_optional object. This function
250
 * examines that object and an array of contexts to determine which contexts
251
 * match the filter.
252
 *
253
 * Since multiple contexts can be required, this function will accept either
254
 * an array of all required contexts, or just a single required context object.
255
 *
256
 * @param $contexts
257
 *   A keyed array of all available contexts.
258
 * @param $required
259
 *   A ctools_context_required or ctools_context_optional object, or an array
260
 *   of such objects.
261
 *
262
 * @return
263
 *   A keyed array of contexts that match the filter.
264
 */
265
function ctools_context_filter($contexts, $required) {
266
  if (is_array($required)) {
267
    $result = array();
268
    foreach ($required as $r) {
269
      $result = array_merge($result, _ctools_context_filter($contexts, $r));
270
    }
271
    return $result;
272
  }
273

    
274
  return _ctools_context_filter($contexts, $required);
275
}
276

    
277
function _ctools_context_filter($contexts, $required) {
278
  $result = array();
279

    
280
  if (is_object($required)) {
281
    $result = $required->filter($contexts);
282
  }
283

    
284
  return $result;
285
}
286

    
287
/**
288
 * Create a select box to choose possible contexts.
289
 *
290
 * This only creates a selector if there is actually a choice; if there
291
 * is only one possible context, that one is silently assigned.
292
 *
293
 * If an array of required contexts is provided, one selector will be
294
 * provided for each context.
295
 *
296
 * @param $contexts
297
 *   A keyed array of all available contexts.
298
 * @param $required
299
 *   The required context object or array of objects.
300
 *
301
 * @return
302
 *   A form element, or NULL if there are no contexts that satisfy the
303
 *   requirements.
304
 */
305
function ctools_context_selector($contexts, $required, $default) {
306
  if (is_array($required)) {
307
    $result = array('#tree' => TRUE);
308
    $count = 1;
309
    foreach ($required as $id => $r) {
310
      $result[] = _ctools_context_selector($contexts, $r, isset($default[$id]) ? $default[$id] : '', $count++);
311
    }
312
    return $result;
313
  }
314

    
315
  return _ctools_context_selector($contexts, $required, $default);
316
}
317

    
318
function _ctools_context_selector($contexts, $required, $default, $num = 0) {
319
  $filtered = ctools_context_filter($contexts, $required);
320
  $count = count($filtered);
321

    
322
  $form = array();
323

    
324
  if ($count >= 1) {
325
    // If there's more than one to choose from, create a select widget.
326
    foreach ($filtered as $cid => $context) {
327
      $options[$cid] = $context->get_identifier();
328
    }
329
    if (!empty($required->title)) {
330
      $title = $required->title;
331
    }
332
    else {
333
      $title = $num ? t('Context %count', array('%count' => $num)) : t('Context');
334
    }
335

    
336
    $form = array(
337
      '#type' => 'select',
338
      '#options' => $options,
339
      '#title' => $title,
340
      '#default_value' => $default,
341
    );
342
  }
343

    
344
  return $form;
345
}
346

    
347
/**
348
 * Are there enough contexts for a plugin?
349
 *
350
 * Some plugins can have a 'required contexts' item which can either
351
 * be a context requirement object or an array of them. When contexts
352
 * are required, items that do not have enough contexts should not
353
 * appear. This tests an item to see if it has enough contexts
354
 * to actually appear.
355
 *
356
 * @param $contexts
357
 *   A keyed array of all available contexts.
358
 * @param $required
359
 *   The required context object or array of objects.
360
 *
361
 * @return
362
 *   TRUE if there are enough contexts, FALSE if there are not.
363
 */
364
function ctools_context_match_requirements($contexts, $required) {
365
  if (!is_array($required)) {
366
    $required = array($required);
367
  }
368

    
369
  // Get the keys to avoid bugs in PHP 5.0.8 with keys and loops.
370
  // And use it to remove optional contexts.
371
  $keys = array_keys($required);
372
  foreach ($keys as $key) {
373
    if (empty($required[$key]->required)) {
374
      unset($required[$key]);
375
    }
376
  }
377

    
378
  $count = count($required);
379
  return (count(ctools_context_filter($contexts, $required)) >= $count);
380
}
381

    
382
/**
383
 * Create a select box to choose possible contexts.
384
 *
385
 * This only creates a selector if there is actually a choice; if there
386
 * is only one possible context, that one is silently assigned.
387
 *
388
 * If an array of required contexts is provided, one selector will be
389
 * provided for each context.
390
 *
391
 * @param $contexts
392
 *   A keyed array of all available contexts.
393
 * @param $required
394
 *   The required context object or array of objects.
395
 *
396
 * @return
397
 *   A form element, or NULL if there are no contexts that satisfy the
398
 *   requirements.
399
 */
400
function ctools_context_converter_selector($contexts, $required, $default) {
401
  if (is_array($required)) {
402
    $result = array('#tree' => TRUE);
403
    $count = 1;
404
    foreach ($required as $id => $r) {
405
      $result[] = _ctools_context_converter_selector($contexts, $r, isset($default[$id]) ? $default[$id] : '', $count++);
406
    }
407
    return $result;
408
  }
409

    
410
  return _ctools_context_converter_selector($contexts, $required, $default);
411
}
412

    
413
function _ctools_context_converter_selector($contexts, $required, $default, $num = 0) {
414
  $filtered = ctools_context_filter($contexts, $required);
415
  $count = count($filtered);
416

    
417
  $form = array();
418

    
419
  if ($count > 1) {
420
    // If there's more than one to choose from, create a select widget.
421
    $options = array();
422
    foreach ($filtered as $cid => $context) {
423
      if ($context->type == 'any') {
424
        $options[''] = t('No context');
425
        continue;
426
      }
427
      $key = $context->get_identifier();
428
      if ($converters = ctools_context_get_converters($cid . '.', $context)) {
429
        $options[$key] = $converters;
430
      }
431
    }
432
    if (empty($options)) {
433
      return array(
434
        '#type' => 'value',
435
        '#value' => 'any',
436
      );
437
    }
438
    if (!empty($required->title)) {
439
      $title = $required->title;
440
    }
441
    else {
442
      $title = $num ? t('Context %count', array('%count' => $num)) : t('Context');
443
    }
444

    
445
    return array(
446
      '#type' => 'select',
447
      '#options' => $options,
448
      '#title' => $title,
449
      '#description' => t('Please choose which context and how you would like it converted.'),
450
      '#default_value' => $default,
451
    );
452
  }
453
}
454

    
455
/**
456
 * Get a list of converters available for a given context.
457
 */
458
function ctools_context_get_converters($cid, $context) {
459
  if (empty($context->plugin)) {
460
    return array();
461
  }
462

    
463
  return _ctools_context_get_converters($cid, $context->plugin);
464
}
465

    
466
/**
467
 * Get a list of converters available for a given context.
468
 */
469
function _ctools_context_get_converters($id, $plugin_name) {
470
  $plugin = ctools_get_context($plugin_name);
471
  if (empty($plugin['convert list'])) {
472
    return array();
473
  }
474

    
475
  $converters = array();
476
  if (is_array($plugin['convert list'])) {
477
    $converters = $plugin['convert list'];
478
  }
479
  else if ($function = ctools_plugin_get_function($plugin, 'convert list')) {
480
    $converters = (array) $function($plugin);
481
  }
482

    
483
  foreach (module_implements('ctools_context_convert_list_alter') as $module) {
484
    $function = $module . '_ctools_context_convert_list_alter';
485
    $function($plugin, $converters);
486
  }
487

    
488
  // Now, change them all to include the plugin:
489
  $return = array();
490
  foreach ($converters as $key => $title) {
491
    $return[$id . $key] = $title;
492
  }
493

    
494
  natcasesort($return);
495
  return $return;
496
}
497

    
498
/**
499
 * Get a list of all contexts + converters available.
500
 */
501
function ctools_context_get_all_converters() {
502
  $contexts = ctools_get_contexts();
503
  $converters = array();
504
  foreach ($contexts as $name => $context) {
505
    if (empty($context['no required context ui'])) {
506
      $context_converters = _ctools_context_get_converters($name . '.', $name);
507
      if ($context_converters) {
508
        $converters[$context['title']] = $context_converters;
509
      }
510
    }
511
  }
512

    
513
  return $converters;
514
}
515

    
516
/**
517
 * Let the context convert an argument based upon the converter that was given.
518
 *
519
 * @param $context
520
 *   The context object
521
 * @param $converter
522
 *   The converter to use, which should be a string provided by the converter list.
523
 * @param $converter_options
524
 *   A n array of options to pass on to the generation function. For contexts
525
 *   that use token module, of particular use is 'sanitize' => FALSE which can
526
 *   get raw tokens. This should ONLY be used in values that will later be
527
 *   treated as unsafe user input since these values are by themselves unsafe.
528
 *   It is particularly useful to get raw values from Field API.
529
 */
530
function ctools_context_convert_context($context, $converter, $converter_options = array()) {
531
  // Contexts without plugins might be optional placeholders.
532
  if (empty($context->plugin)) {
533
    return;
534
  }
535

    
536
  $value = $context->argument;
537
  $plugin = ctools_get_context($context->plugin);
538
  if ($function = ctools_plugin_get_function($plugin, 'convert')) {
539
    $value = $function($context, $converter, $converter_options);
540
  }
541

    
542
  foreach (module_implements('ctools_context_converter_alter') as $module) {
543
    $function = $module . '_ctools_context_converter_alter';
544
    $function($context, $converter, $value, $converter_options);
545
  }
546

    
547
  return $value;
548
}
549

    
550
/**
551
 * Choose a context or contexts based upon the selection made via
552
 * ctools_context_filter.
553
 *
554
 * @param $contexts
555
 *   A keyed array of all available contexts
556
 * @param $required
557
 *   The required context object provided by the plugin
558
 * @param $context
559
 *   The selection made using ctools_context_selector
560
 */
561
function ctools_context_select($contexts, $required, $context) {
562
  if (is_array($required)) {
563
    $result = array();
564
    foreach ($required as $id => $r) {
565
      if (empty($required[$id])) {
566
        continue;
567
      }
568

    
569
      if (($result[] = _ctools_context_select($contexts, $r, $context[$id])) === FALSE) {
570
        return FALSE;
571
      }
572
    }
573
    return $result;
574
  }
575

    
576
  return _ctools_context_select($contexts, $required, $context);
577
}
578

    
579
function _ctools_context_select($contexts, $required, $context) {
580
  if (!is_object($required)) {
581
    return FALSE;
582
  }
583

    
584
  return $required->select($contexts, $context);
585
}
586

    
587
/**
588
 * Create a new context object.
589
 *
590
 * @param $type
591
 *   The type of context to create; this loads a plugin.
592
 * @param $data
593
 *   The data to put into the context.
594
 * @param $empty
595
 *   Whether or not this context is specifically empty.
596
 * @param $conf
597
 *   A configuration structure if this context was created via UI.
598
 *
599
 * @return
600
 *   A $context or NULL if one could not be created.
601
 */
602
function ctools_context_create($type, $data = NULL, $conf = FALSE) {
603
  ctools_include('plugins');
604
  $plugin = ctools_get_context($type);
605

    
606
  if ($function = ctools_plugin_get_function($plugin, 'context')) {
607
    return $function(FALSE, $data, $conf, $plugin);
608
  }
609
}
610

    
611
/**
612
 * Create an empty context object.
613
 *
614
 * Empty context objects are primarily used as placeholders in the UI where
615
 * the actual contents of a context object may not be known. It may have
616
 * additional text embedded to give the user clues as to how the context
617
 * is used.
618
 *
619
 * @param $type
620
 *   The type of context to create; this loads a plugin.
621
 *
622
 * @return
623
 *   A $context or NULL if one could not be created.
624
 */
625
function ctools_context_create_empty($type) {
626
  $plugin = ctools_get_context($type);
627
  if ($function = ctools_plugin_get_function($plugin, 'context')) {
628
    $context = $function(TRUE, NULL, FALSE, $plugin);
629
    if (is_object($context)) {
630
      $context->empty = TRUE;
631
    }
632

    
633
    return $context;
634
  }
635
}
636

    
637
/**
638
 * Perform keyword and context substitutions.
639
 */
640
function ctools_context_keyword_substitute($string, $keywords, $contexts, $converter_options = array()) {
641
  // Ensure a default keyword exists:
642
  $keywords['%%'] = '%';
643

    
644
  // Match contexts to the base keywords:
645
  $context_keywords = array();
646
  foreach ($contexts as $context) {
647
    if (isset($context->keyword)) {
648
      $context_keywords[$context->keyword] = $context;
649
    }
650
  }
651

    
652
  // Look for context matches we we only have to convert known matches.
653
  $matches = array();
654
  if (preg_match_all('/%(%|[a-zA-Z0-9_-]+(?:\:[a-zA-Z0-9_-]+)*)/us', $string, $matches)) {
655
    foreach ($matches[1] as $keyword) {
656
      // Ignore anything it finds with %%.
657
      if ($keyword[0] == '%') {
658
        continue;
659
      }
660

    
661
      // If the keyword is already set by something passed in, don't try to
662
      // overwrite it.
663
      if (!empty($keywords['%' . $keyword])) {
664
        continue;
665
      }
666

    
667
      // Figure out our keyword and converter, if specified.
668
      if (strpos($keyword, ':')) {
669
        list($context, $converter) = explode(':', $keyword, 2);
670
      }
671
      else {
672
        $context = $keyword;
673
        if (isset($context_keywords[$keyword])) {
674
          $plugin = ctools_get_context($context_keywords[$context]->plugin);
675

    
676
          // Fall back to a default converter, if specified.
677
          if ($plugin && !empty($plugin['convert default'])) {
678
            $converter = $plugin['convert default'];
679
          }
680
        }
681
      }
682

    
683
      if (empty($context_keywords[$context]) || !empty($context_keywords[$context]->empty)) {
684
        $keywords['%' . $keyword] = '';
685
      }
686
      else if (!empty($converter)) {
687
        $keywords['%' . $keyword] = ctools_context_convert_context($context_keywords[$context], $converter, $converter_options);
688
      }
689
      else {
690
        $keywords['%' . $keyword] = $context_keywords[$keyword]->title;
691
      }
692
    }
693
  }
694
  return strtr($string, $keywords);
695
}
696

    
697
/**
698
 * Determine a unique context ID for a context
699
 *
700
 * Often contexts of many different types will be placed into a list. This
701
 * ensures that even though contexts of multiple types may share IDs, they
702
 * are unique in the final list.
703
 */
704
function ctools_context_id($context, $type = 'context') {
705
  if (!$context['id']) {
706
    $context['id'] = 1;
707
  }
708

    
709
  return $type . '_' . $context['name'] . '_' . $context['id'];
710
}
711

    
712
/**
713
 * Get the next id available given a list of already existing objects.
714
 *
715
 * This finds the next id available for the named object.
716
 *
717
 * @param $objects
718
 *   A list of context descriptor objects, i.e, arguments, relationships, contexts, etc.
719
 * @param $name
720
 *   The name being used.
721
 */
722
function ctools_context_next_id($objects, $name) {
723
  $id = 0;
724
  // Figure out which instance of this argument we're creating
725
  if (!$objects) {
726
    return $id + 1;
727
  }
728

    
729
  foreach ($objects as $object) {
730
    if (isset($object['name']) && $object['name'] == $name) {
731
      if ($object['id'] > $id) {
732
        $id = $object['id'];
733
      }
734
    }
735
  }
736

    
737
  return $id + 1;
738
}
739

    
740

    
741
// ---------------------------------------------------------------------------
742
// Functions related to contexts from arguments.
743

    
744
/**
745
 * Fetch metadata on a specific argument plugin.
746
 *
747
 * @param $argument
748
 *   Name of an argument plugin.
749
 *
750
 * @return
751
 *   An array with information about the requested argument plugin.
752
 */
753
function ctools_get_argument($argument) {
754
  ctools_include('plugins');
755
  return ctools_get_plugins('ctools', 'arguments', $argument);
756
}
757

    
758
/**
759
 * Fetch metadata for all argument plugins.
760
 *
761
 * @return
762
 *   An array of arrays with information about all available argument plugins.
763
 */
764
function ctools_get_arguments() {
765
  ctools_include('plugins');
766
  return ctools_get_plugins('ctools', 'arguments');
767
}
768

    
769
/**
770
 * Get a context from an argument.
771
 *
772
 * @param $argument
773
 *   The configuration of an argument. It must contain the following data:
774
 *   - name: The name of the argument plugin being used.
775
 *   - argument_settings: The configuration based upon the plugin forms.
776
 *   - identifier: The human readable identifier for this argument, usually
777
 *     defined by the UI.
778
 *   - keyword: The keyword used for this argument for substitutions.
779
 *
780
 * @param $arg
781
 *   The actual argument received. This is expected to be a string from a URL but
782
 *   this does not have to be the only source of arguments.
783
 * @param $empty
784
 *   If true, the $arg will not be used to load the context. Instead, an empty
785
 *   placeholder context will be loaded.
786
 *
787
 * @return
788
 *   A context object if one can be loaded.
789
 */
790
function ctools_context_get_context_from_argument($argument, $arg, $empty = FALSE) {
791
  ctools_include('plugins');
792
  if (empty($argument['name'])) {
793
    return;
794
  }
795

    
796
  if ($function = ctools_plugin_load_function('ctools', 'arguments', $argument['name'], 'context')) {
797
    // Backward compatibility: Merge old style settings into new style:
798
    if (!empty($argument['settings'])) {
799
      $argument += $argument['settings'];
800
      unset($argument['settings']);
801
    }
802

    
803
    $context = $function($arg, $argument, $empty);
804

    
805
    if (is_object($context)) {
806
      $context->identifier = $argument['identifier'];
807
      $context->page_title = isset($argument['title']) ? $argument['title'] : '';
808
      $context->keyword    = $argument['keyword'];
809
      $context->id         = ctools_context_id($argument, 'argument');
810
      $context->original_argument = $arg;
811

    
812
      if (!empty($context->empty)) {
813
        $context->placeholder = array(
814
          'type' => 'argument',
815
          'conf' => $argument,
816
        );
817
      }
818
    }
819
    return $context;
820
  }
821
}
822

    
823
/**
824
 * Retrieve a list of empty contexts for all arguments.
825
 */
826
function ctools_context_get_placeholders_from_argument($arguments) {
827
  $contexts = array();
828
  foreach ($arguments as $argument) {
829
    $context = ctools_context_get_context_from_argument($argument, NULL, TRUE);
830
    if ($context) {
831
      $contexts[ctools_context_id($argument, 'argument')] = $context;
832
    }
833
  }
834
  return $contexts;
835
}
836

    
837
/**
838
 * Load the contexts for a given list of arguments.
839
 *
840
 * @param $arguments
841
 *   The array of argument definitions.
842
 * @param &$contexts
843
 *   The array of existing contexts. New contexts will be added to this array.
844
 * @param $args
845
 *   The arguments to load.
846
 *
847
 * @return
848
 *   FALSE if an argument wants to 404.
849
 */
850
function ctools_context_get_context_from_arguments($arguments, &$contexts, $args) {
851
  foreach ($arguments as $argument) {
852
    // pull the argument off the list.
853
    $arg = array_shift($args);
854
    $id = ctools_context_id($argument, 'argument');
855

    
856
    // For % arguments embedded in the URL, our context is already loaded.
857
    // There is no need to go and load it again.
858
    if (empty($contexts[$id])) {
859
      if ($context = ctools_context_get_context_from_argument($argument, $arg)) {
860
        $contexts[$id] = $context;
861
      }
862
    }
863
    else {
864
      $context = $contexts[$id];
865
    }
866

    
867
    if ((empty($context) || empty($context->data)) && !empty($argument['default']) && $argument['default'] == '404') {
868
      return FALSE;
869
    }
870
  }
871
  return TRUE;
872
}
873

    
874
// ---------------------------------------------------------------------------
875
// Functions related to contexts from relationships.
876

    
877
/**
878
 * Fetch metadata on a specific relationship plugin.
879
 *
880
 * @param $content type
881
 *   Name of a panel content type.
882
 *
883
 * @return
884
 *   An array with information about the requested relationship.
885
 */
886
function ctools_get_relationship($relationship) {
887
  ctools_include('plugins');
888
  return ctools_get_plugins('ctools', 'relationships', $relationship);
889
}
890

    
891
/**
892
 * Fetch metadata for all relationship plugins.
893
 *
894
 * @return
895
 *   An array of arrays with information about all available relationships.
896
 */
897
function ctools_get_relationships() {
898
  ctools_include('plugins');
899
  return ctools_get_plugins('ctools', 'relationships');
900
}
901

    
902
/**
903
 *
904
 * @param $relationship
905
 *   The configuration of a relationship. It must contain the following data:
906
 *   - name: The name of the relationship plugin being used.
907
 *   - relationship_settings: The configuration based upon the plugin forms.
908
 *   - identifier: The human readable identifier for this relationship, usually
909
 *     defined by the UI.
910
 *   - keyword: The keyword used for this relationship for substitutions.
911
 *
912
 * @param $source_context
913
 *   The context this relationship is based upon.
914
 *
915
 * @param $placeholders
916
 *   If TRUE, placeholders are acceptable.
917
 *
918
 * @return
919
 *   A context object if one can be loaded.
920
 */
921
function ctools_context_get_context_from_relationship($relationship, $source_context, $placeholders = FALSE) {
922
  ctools_include('plugins');
923
  if ($function = ctools_plugin_load_function('ctools', 'relationships', $relationship['name'], 'context')) {
924
    // Backward compatibility: Merge old style settings into new style:
925
    if (!empty($relationship['relationship_settings'])) {
926
      $relationship += $relationship['relationship_settings'];
927
      unset($relationship['relationship_settings']);
928
    }
929

    
930
    $context = $function($source_context, $relationship, $placeholders);
931
    if ($context) {
932
      $context->identifier = $relationship['identifier'];
933
      $context->page_title = isset($relationship['title']) ? $relationship['title'] : '';
934
      $context->keyword    = $relationship['keyword'];
935
      if (!empty($context->empty)) {
936
        $context->placeholder = array(
937
          'type' => 'relationship',
938
          'conf' => $relationship,
939
        );
940
      }
941
      return $context;
942
    }
943
  }
944
}
945

    
946
/**
947
 * Fetch all relevant relationships.
948
 *
949
 * Relevant relationships are any relationship that can be created based upon
950
 * the list of existing contexts. For example, the 'node author' relationship
951
 * is relevant if there is a 'node' context, but makes no sense if there is
952
 * not one.
953
 *
954
 * @param $contexts
955
 *   An array of contexts used to figure out which relationships are relevant.
956
 *
957
 * @return
958
 *   An array of relationship keys that are relevant for the given set of
959
 *   contexts.
960
 */
961
function ctools_context_get_relevant_relationships($contexts) {
962
  $relevant = array();
963
  $relationships = ctools_get_relationships();
964

    
965
  // Go through each relationship
966
  foreach ($relationships as $rid => $relationship) {
967
    // For each relationship, see if there is a context that satisfies it.
968
    if (empty($relationship['no ui']) && ctools_context_filter($contexts, $relationship['required context'])) {
969
      $relevant[$rid] = $relationship['title'];
970
    }
971
  }
972

    
973
  return $relevant;
974
}
975

    
976
/**
977
 * Fetch all active relationships
978
 *
979
 * @param $relationships
980
 *   An keyed array of relationship data including:
981
 *   - name: name of relationship
982
 *   - context: context id relationship belongs to. This will be used to
983
 *     identify which context in the $contexts array to use to create the
984
 *     relationship context.
985
 *
986
 * @param $contexts
987
 *   A keyed array of contexts used to figure out which relationships
988
 *   are relevant. New contexts will be added to this.
989
 *
990
 * @param $placeholders
991
 *   If TRUE, placeholders are acceptable.
992
 */
993
function ctools_context_get_context_from_relationships($relationships, &$contexts, $placeholders = FALSE) {
994
  $return = array();
995

    
996
  foreach ($relationships as $rdata) {
997
    if (!isset($rdata['context'])) {
998
      continue;
999
    }
1000

    
1001
    if (is_array($rdata['context'])) {
1002
      $rcontexts = array();
1003
      foreach ($rdata['context'] as $cid) {
1004
        if (empty($contexts[$cid])) {
1005
          continue 2;
1006
        }
1007
        $rcontexts[] = $contexts[$cid];
1008
      }
1009
    }
1010
    else {
1011
      if (empty($contexts[$rdata['context']])) {
1012
        continue;
1013
      }
1014
      $rcontexts = $contexts[$rdata['context']];
1015
    }
1016

    
1017
    $cid = ctools_context_id($rdata, 'relationship');
1018
    if ($context = ctools_context_get_context_from_relationship($rdata, $rcontexts)) {
1019
      $contexts[$cid] = $context;
1020
    }
1021
  }
1022
}
1023

    
1024
// ---------------------------------------------------------------------------
1025
// Functions related to loading contexts from simple context definitions.
1026

    
1027
/**
1028
 * Fetch metadata on a specific context plugin.
1029
 *
1030
 * @param $context
1031
 *   Name of a context.
1032
 *
1033
 * @return
1034
 *   An array with information about the requested panel context.
1035
 */
1036
function ctools_get_context($context) {
1037
  static $gate = array();
1038
  ctools_include('plugins');
1039
  $plugin = ctools_get_plugins('ctools', 'contexts', $context);
1040
  if (empty($gate['context']) && !empty($plugin['superceded by'])) {
1041
    // This gate prevents infinite loops.
1042
    $gate[$context] = TRUE;
1043
    $new_plugin = ctools_get_plugins('ctools', 'contexts', $plugin['superceded by']);
1044
    $gate[$context] = FALSE;
1045

    
1046
    // If a new plugin was returned, return it. Otherwise fall through and
1047
    // return the original we fetched.
1048
    if ($new_plugin) {
1049
      return $new_plugin;
1050
    }
1051
  }
1052

    
1053
  return $plugin;
1054
}
1055

    
1056
/**
1057
 * Fetch metadata for all context plugins.
1058
 *
1059
 * @return
1060
 *   An array of arrays with information about all available panel contexts.
1061
 */
1062
function ctools_get_contexts() {
1063
  ctools_include('plugins');
1064
  return ctools_get_plugins('ctools', 'contexts');
1065
}
1066

    
1067
/**
1068
 *
1069
 * @param $context
1070
 *   The configuration of a context. It must contain the following data:
1071
 *   - name: The name of the context plugin being used.
1072
 *   - context_settings: The configuration based upon the plugin forms.
1073
 *   - identifier: The human readable identifier for this context, usually
1074
 *     defined by the UI.
1075
 *   - keyword: The keyword used for this context for substitutions.
1076
 * @param $type
1077
 *   This is either 'context' which indicates the context will be loaded
1078
 *   from data in the settings, or 'required_context' which means the
1079
 *   context must be acquired from an external source. This is the method
1080
 *   used to pass pure contexts from one system to another.
1081
 *
1082
 * @return
1083
 *   A context object if one can be loaded.
1084
 */
1085
function ctools_context_get_context_from_context($context, $type = 'context', $argument = NULL) {
1086
  ctools_include('plugins');
1087
  $plugin = ctools_get_context($context['name']);
1088
  if ($function = ctools_plugin_get_function($plugin, 'context')) {
1089
    // Backward compatibility: Merge old style settings into new style:
1090
    if (!empty($context['context_settings'])) {
1091
      $context += $context['context_settings'];
1092
      unset($context['context_settings']);
1093
    }
1094

    
1095
    if (isset($argument) && isset($plugin['placeholder name'])) {
1096
      $context[$plugin['placeholder name']] = $argument;
1097
    }
1098

    
1099
    $return = $function($type == 'requiredcontext', $context, TRUE, $plugin);
1100
    if ($return) {
1101
      $return->identifier = $context['identifier'];
1102
      $return->page_title = isset($context['title']) ? $context['title'] : '';
1103
      $return->keyword    = $context['keyword'];
1104

    
1105
      if (!empty($context->empty)) {
1106
        $context->placeholder = array(
1107
          'type' => 'context',
1108
          'conf' => $context,
1109
        );
1110
      }
1111

    
1112
      return $return;
1113
    }
1114
  }
1115
}
1116

    
1117
/**
1118
 * Retrieve a list of base contexts based upon a simple 'contexts' definition.
1119
 *
1120
 * For required contexts this will always retrieve placeholders.
1121
 *
1122
 * @param $contexts
1123
 *   The list of contexts defined in the UI.
1124
 * @param $type
1125
 *   Either 'context' or 'requiredcontext', which indicates whether the contexts
1126
 *   are loaded from internal data or copied from an external source.
1127
 * @param $placeholders
1128
 *   If true, placeholders are acceptable.
1129
 */
1130
function ctools_context_get_context_from_contexts($contexts, $type = 'context', $placeholders = FALSE) {
1131
  $return = array();
1132
  foreach ($contexts as $context) {
1133
    $ctext = ctools_context_get_context_from_context($context, $type);
1134
    if ($ctext) {
1135
      if ($placeholders) {
1136
        $ctext->placeholder = TRUE;
1137
      }
1138
      $return[ctools_context_id($context, $type)] = $ctext;
1139
    }
1140
  }
1141
  return $return;
1142
}
1143

    
1144
/**
1145
 * Match up external contexts to our required contexts.
1146
 *
1147
 * This function is used to create a list of contexts with proper
1148
 * IDs based upon a list of required contexts.
1149
 *
1150
 * These contexts passed in should match the numeric positions of the
1151
 * required contexts. The caller must ensure this has already happened
1152
 * correctly as this function will not detect errors here.
1153
 *
1154
 * @param $required
1155
 *   A list of required contexts as defined by the UI.
1156
 * @param $contexts
1157
 *   A list of matching contexts as passed in from the calling system.
1158
 */
1159
function ctools_context_match_required_contexts($required, $contexts) {
1160
  $return = array();
1161
  if (!is_array($required)) {
1162
    return $return;
1163
  }
1164

    
1165
  foreach ($required as $r) {
1166
    $context = clone(array_shift($contexts));
1167
    $context->identifier = $r['identifier'];
1168
    $context->page_title = isset($r['title']) ? $r['title'] : '';
1169
    $context->keyword    = $r['keyword'];
1170
    $return[ctools_context_id($r, 'requiredcontext')] = $context;
1171
  }
1172

    
1173
  return $return;
1174
}
1175

    
1176
/**
1177
 * Load a full array of contexts for an object.
1178
 *
1179
 * Not all of the types need to be supported by this object.
1180
 *
1181
 * This function is not used to load contexts from external data, but may
1182
 * be used to load internal contexts and relationships. Otherwise it can also
1183
 * be used to generate a full set of placeholders for UI purposes.
1184
 *
1185
 * @param $object
1186
 *   An object that contains some or all of the following variables:
1187
 *
1188
 * - requiredcontexts: A list of UI configured contexts that are required
1189
 *   from an external source. Since these require external data, they will
1190
 *   only be added if $placeholders is set to TRUE, and empty contexts will
1191
 *   be created.
1192
 * - arguments: A list of UI configured arguments that will create contexts.
1193
 *   Since these require external data, they will only be added if $placeholders
1194
 *   is set to TRUE.
1195
 * - contexts: A list of UI configured contexts that have no external source,
1196
 *   and are essentially hardcoded. For example, these might configure a
1197
 *   particular node or a particular taxonomy term.
1198
 * - relationships: A list of UI configured contexts to be derived from other
1199
 *   contexts that already exist from other sources. For example, these might
1200
 *   be used to get a user object from a node via the node author relationship.
1201
 * @param $placeholders
1202
 *   If TRUE, this will generate placeholder objects for types this function
1203
 *   cannot load.
1204
 * @param $contexts
1205
 *   An array of pre-existing contexts that will be part of the return value.
1206
 */
1207
function ctools_context_load_contexts($object, $placeholders = TRUE, $contexts = array()) {
1208
  if (!empty($object->base_contexts)) {
1209
    $contexts += $object->base_contexts;
1210
  }
1211

    
1212
  if ($placeholders) {
1213
    // This will load empty contexts as placeholders for arguments that come
1214
    // from external sources. If this isn't set, it's assumed these context
1215
    // will already have been matched up and loaded.
1216
    if (!empty($object->requiredcontexts) && is_array($object->requiredcontexts)) {
1217
      $contexts += ctools_context_get_context_from_contexts($object->requiredcontexts, 'requiredcontext', $placeholders);
1218
    }
1219

    
1220
    if (!empty($object->arguments) && is_array($object->arguments)) {
1221
      $contexts += ctools_context_get_placeholders_from_argument($object->arguments);
1222
    }
1223
  }
1224

    
1225
  if (!empty($object->contexts) && is_array($object->contexts)) {
1226
    $contexts += ctools_context_get_context_from_contexts($object->contexts, 'context', $placeholders);
1227
  }
1228

    
1229
  // add contexts from relationships
1230
  if (!empty($object->relationships) && is_array($object->relationships)) {
1231
    ctools_context_get_context_from_relationships($object->relationships, $contexts, $placeholders);
1232
  }
1233

    
1234
  return $contexts;
1235
}
1236

    
1237
/**
1238
 * Return the first context with a form id from a list of contexts.
1239
 *
1240
 * This function is used to figure out which contexts represents 'the form'
1241
 * from a list of contexts. Only one contexts can actually be 'the form' for
1242
 * a given page, since the @code{<form>} tag can not be embedded within
1243
 * itself.
1244
 */
1245
function ctools_context_get_form($contexts) {
1246
  if (!empty($contexts)) {
1247
    foreach ($contexts as $id => $context) {
1248
      // if a form shows its id as being a 'required context' that means the
1249
      // the context is external to this display and does not count.
1250
      if (!empty($context->form_id) && substr($id, 0, 15) != 'requiredcontext') {
1251
        return $context;
1252
      }
1253
    }
1254
  }
1255
}
1256

    
1257
/**
1258
 * Replace placeholders with real contexts using data extracted from a form
1259
 * for the purposes of previews.
1260
 *
1261
 * @param $contexts
1262
 *   All of the contexts, including the placeholders.
1263
 * @param $arguments
1264
 *   The arguments. These will be acquired from $form_state['values'] and the
1265
 *   keys must match the context IDs.
1266
 *
1267
 * @return
1268
 *   A new $contexts array containing the replaced contexts. Not all contexts
1269
 *   may be replaced if, for example, an argument was unable to be converted
1270
 *   into a context.
1271
 */
1272
function ctools_context_replace_placeholders($contexts, $arguments) {
1273
  foreach ($contexts as $cid => $context) {
1274
    if (empty($context->empty)) {
1275
      continue;
1276
    }
1277

    
1278
    $new_context = NULL;
1279
    switch ($context->placeholder['type']) {
1280
      case 'relationship':
1281
        $relationship = $context->placeholder['conf'];
1282
        if (isset($contexts[$relationship['context']])) {
1283
          $new_context = ctools_context_get_context_from_relationship($relationship, $contexts[$relationship['context']]);
1284
        }
1285
        break;
1286
      case 'argument':
1287
        if (isset($arguments[$cid]) && $arguments[$cid] !== '') {
1288
          $argument = $context->placeholder['conf'];
1289
          $new_context = ctools_context_get_context_from_argument($argument, $arguments[$cid]);
1290
        }
1291
        break;
1292
      case 'context':
1293
        if (!empty($arguments[$cid])) {
1294
          $context_info = $context->placeholder['conf'];
1295
          $new_context = ctools_context_get_context_from_context($context_info, 'requiredcontext', $arguments[$cid]);
1296
        }
1297
        break;
1298
    }
1299

    
1300
    if ($new_context && empty($new_context->empty)) {
1301
      $contexts[$cid] = $new_context;
1302
    }
1303
  }
1304

    
1305
  return $contexts;
1306
}
1307

    
1308
/**
1309
 * Provide a form array for getting data to replace placeholder contexts
1310
 * with real data.
1311
 */
1312
function ctools_context_replace_form(&$form, $contexts) {
1313
  foreach ($contexts as $cid => $context) {
1314
    if (empty($context->empty)) {
1315
      continue;
1316
    }
1317

    
1318
    // Get plugin info from the context which should have been set when the
1319
    // empty context was created.
1320
    $info = NULL;
1321
    $plugin = NULL;
1322
    $settings = NULL;
1323
    switch ($context->placeholder['type']) {
1324
      case 'argument':
1325
        $info = $context->placeholder['conf'];
1326
        $plugin = ctools_get_argument($info['name']);
1327
        break;
1328

    
1329
      case 'context':
1330
        $info = $context->placeholder['conf'];
1331
        $plugin = ctools_get_context($info['name']);
1332
        break;
1333
    }
1334

    
1335
    // Ask the plugin where the form is.
1336
    if ($plugin && isset($plugin['placeholder form'])) {
1337
      if (is_array($plugin['placeholder form'])) {
1338
        $form[$cid] = $plugin['placeholder form'];
1339
      }
1340
      else if (function_exists($plugin['placeholder form'])) {
1341
        $widget = $plugin['placeholder form']($info);
1342
        if ($widget) {
1343
          $form[$cid] = $widget;
1344
        }
1345
      }
1346

    
1347
      if (!empty($form[$cid])) {
1348
        $form[$cid]['#title'] = t('@identifier (@keyword)', array('@keyword' => '%' . $context->keyword, '@identifier' => $context->identifier));
1349
      }
1350
    }
1351
  }
1352
}
1353

    
1354
// ---------------------------------------------------------------------------
1355
// Functions related to loading access control plugins
1356

    
1357
/**
1358
 * Fetch metadata on a specific access control plugin.
1359
 *
1360
 * @param $name
1361
 *   Name of a plugin.
1362
 *
1363
 * @return
1364
 *   An array with information about the requested access control plugin.
1365
 */
1366
function ctools_get_access_plugin($name) {
1367
  ctools_include('plugins');
1368
  return ctools_get_plugins('ctools', 'access', $name);
1369
}
1370

    
1371
/**
1372
 * Fetch metadata for all access control plugins.
1373
 *
1374
 * @return
1375
 *   An array of arrays with information about all available access control plugins.
1376
 */
1377
function ctools_get_access_plugins() {
1378
  ctools_include('plugins');
1379
  return ctools_get_plugins('ctools', 'access');
1380
}
1381

    
1382
/**
1383
 * Fetch a list of access plugins that are available for a given list of
1384
 * contexts.
1385
 *
1386
 * if 'logged-in-user' is not in the list of contexts, it will be added as
1387
 * this is required.
1388
 */
1389
function ctools_get_relevant_access_plugins($contexts) {
1390
  if (!isset($contexts['logged-in-user'])) {
1391
    $contexts['logged-in-user'] = ctools_access_get_loggedin_context();
1392
  }
1393

    
1394
  $all_plugins = ctools_get_access_plugins();
1395
  $plugins = array();
1396
  foreach ($all_plugins as $id => $plugin) {
1397
    if (!empty($plugin['required context']) && !ctools_context_match_requirements($contexts, $plugin['required context'])) {
1398
      continue;
1399
    }
1400
    $plugins[$id] = $plugin;
1401
  }
1402

    
1403
  return $plugins;
1404
}
1405

    
1406
/**
1407
 * Create a context for the logged in user.
1408
 */
1409
function ctools_access_get_loggedin_context() {
1410
  $context = ctools_context_create('entity:user', array('type' => 'current'), TRUE);
1411
  $context->identifier = t('Logged in user');
1412
  $context->keyword    = 'viewer';
1413
  $context->id         = 0;
1414

    
1415
  return $context;
1416
}
1417

    
1418
/**
1419
 * Get a summary of an access plugin's settings.
1420
 */
1421
function ctools_access_summary($plugin, $contexts, $test) {
1422
  if (!isset($contexts['logged-in-user'])) {
1423
    $contexts['logged-in-user'] = ctools_access_get_loggedin_context();
1424
  }
1425

    
1426
  $description = '';
1427
  if ($function = ctools_plugin_get_function($plugin, 'summary')) {
1428
    $required_context = isset($plugin['required context']) ? $plugin['required context'] : array();
1429
    $context          = isset($test['context']) ? $test['context'] : array();
1430
    $description      = $function($test['settings'], ctools_context_select($contexts, $required_context, $context), $plugin);
1431
  }
1432

    
1433
  if (!empty($test['not'])) {
1434
    $description = "NOT ($description)";
1435
  }
1436

    
1437
  return $description;
1438
}
1439

    
1440
/**
1441
 * Get a summary of a group of access plugin's settings.
1442
 */
1443
function ctools_access_group_summary($access, $contexts) {
1444
  if (empty($access['plugins'])) {
1445
    return;
1446
  }
1447

    
1448
  $descriptions = array();
1449
  foreach ($access['plugins'] as $id => $test) {
1450
    $plugin = ctools_get_access_plugin($test['name']);
1451
    $descriptions[] = ctools_access_summary($plugin, $contexts, $test);
1452
  }
1453

    
1454
  $separator = (isset($access['logic']) && $access['logic'] == 'and') ? t(', and ') : t(', or ');
1455
  return implode($separator, $descriptions);
1456
}
1457

    
1458
/**
1459
 * Determine if the current user has access via  plugin.
1460
 *
1461
 * @param $settings
1462
 *   An array of settings theoretically set by the user.
1463
 * @param $contexts
1464
 *   An array of zero or more contexts that may be used to determine if
1465
 *   the user has access.
1466
 *
1467
 * @return
1468
 *   TRUE if access is granted, false if otherwise.
1469
 */
1470
function ctools_access($settings, $contexts = array()) {
1471
  if (empty($settings['plugins'])) {
1472
    return TRUE;
1473
  }
1474

    
1475
  if (!isset($settings['logic'])) {
1476
    $settings['logic'] = 'and';
1477
  }
1478

    
1479
  if (!isset($contexts['logged-in-user'])) {
1480
    $contexts['logged-in-user'] = ctools_access_get_loggedin_context();
1481
  }
1482

    
1483
  foreach ($settings['plugins'] as $test) {
1484
    $pass = FALSE;
1485
    $plugin = ctools_get_access_plugin($test['name']);
1486
    if ($plugin && $function = ctools_plugin_get_function($plugin, 'callback')) {
1487
      // Do we need just some contexts or all of them?
1488
      if (!empty($plugin['all contexts'])) {
1489
        $test_contexts = $contexts;
1490
      }
1491
      else {
1492
        $required_context = isset($plugin['required context']) ? $plugin['required context'] : array();
1493
        $context = isset($test['context']) ? $test['context'] : array();
1494
        $test_contexts = ctools_context_select($contexts, $required_context, $context);
1495
      }
1496

    
1497
      $pass = $function($test['settings'], $test_contexts, $plugin);
1498
      if (!empty($test['not'])) {
1499
        $pass = !$pass;
1500
      }
1501
    }
1502

    
1503
    if ($pass && $settings['logic'] == 'or') {
1504
      // Pass if 'or' and this rule passed.
1505
      return TRUE;
1506
    }
1507
    else if (!$pass && $settings['logic'] == 'and') {
1508
      // Fail if 'and' and htis rule failed.
1509
      return FALSE;
1510
    }
1511
  }
1512

    
1513
  // Return TRUE if logic was and, meaning all rules passed.
1514
  // Return FALSE if logic was or, meaning no rule passed.
1515
  return $settings['logic'] == 'and';
1516
}
1517

    
1518
/**
1519
 * Create default settings for a new access plugin.
1520
 *
1521
 * @param $plugin
1522
 *   The access plugin being used.
1523
 *
1524
 * @return
1525
 *   A default configured test that should be placed in $access['plugins'];
1526
 */
1527
function ctools_access_new_test($plugin) {
1528
  $test = array(
1529
    'name' => $plugin['name'],
1530
    'settings' => array(),
1531
  );
1532

    
1533
  // Set up required context defaults.
1534
  if (isset($plugin['required context'])) {
1535
    if (is_object($plugin['required context'])) {
1536
      $test['context'] = '';
1537
    }
1538
    else {
1539
      $test['context'] = array();
1540
      foreach ($plugin['required context'] as $required) {
1541
        $test['context'][] = '';
1542
      }
1543
    }
1544
  }
1545

    
1546

    
1547
  $default = NULL;
1548
  if (isset($plugin['default'])) {
1549
    $default = $plugin['default'];
1550
  }
1551
  elseif (isset($plugin['defaults'])) {
1552
    $default = $plugin['defaults'];
1553
  }
1554

    
1555
  // Setup plugin defaults.
1556
  if (isset($default)) {
1557
    if (is_array($default)) {
1558
      $test['settings'] = $default;
1559
    }
1560
    else if (function_exists($default)) {
1561
      $test['settings'] = $default();
1562
    }
1563
    else {
1564
      $test['settings'] = array();
1565
    }
1566
  }
1567

    
1568
  return $test;
1569
}
1570

    
1571
/**
1572
 * Apply restrictions to contexts based upon the access control configured.
1573
 *
1574
 * These restrictions allow the UI to not show content that may not
1575
 * be relevant to all types of a particular context.
1576
 */
1577
function ctools_access_add_restrictions($settings, $contexts) {
1578
  if (empty($settings['plugins'])) {
1579
    return;
1580
  }
1581

    
1582
  if (!isset($settings['logic'])) {
1583
    $settings['logic'] = 'and';
1584
  }
1585

    
1586
  // We're not going to try to figure out restrictions on the or.
1587
  if ($settings['logic'] == 'or' && count($settings['plugins']) > 1) {
1588
    return;
1589
  }
1590

    
1591
  foreach ($settings['plugins'] as $test) {
1592
    $plugin = ctools_get_access_plugin($test['name']);
1593
    if ($plugin && $function = ctools_plugin_get_function($plugin, 'restrictions')) {
1594
      $required_context = isset($plugin['required context']) ? $plugin['required context'] : array();
1595
      $context = isset($test['context']) ? $test['context'] : array();
1596
      $contexts = ctools_context_select($contexts, $required_context, $context);
1597
      if ($contexts !== FALSE) {
1598
        $function($test['settings'], $contexts);
1599
      }
1600
    }
1601
  }
1602
}