Project

General

Profile

Paste
Download (27.4 KB) Statistics
| Branch: | Revision:

root / drupal7 / sites / all / modules / ctools / includes / context-admin.inc @ 560c3060

1
<?php
2

    
3
/**
4
 * @file includes/common-context.inc
5
 * Provide API for adding contexts for modules that embed displays.
6
 *
7
 * Note that most of this code was directly copied from Panels 2, and as such
8
 * a lot of this code is crusty. It could probably stand to be rewritten,
9
 * and brought up to date, or at least better commented.
10
 */
11

    
12
/**
13
 * Provide a list of the ways contexts can be embedded.
14
 *
15
 * This provides a full list of context types that the tool understands
16
 * and can let modules utilize.
17
 */
18
function ctools_context_info($type = NULL) {
19
  static $info = NULL;
20

    
21
  // static doesn't work with functions like t().
22
  if (empty($info)) {
23
    $info = array(
24
      'argument' => array(
25
        'title' => t('Arguments'),
26
        'singular title' => t('argument'),
27
        'description' => '', // t("Arguments are parsed from the URL and translated into contexts that may be added to the display via the 'content' tab. These arguments are parsed in the order received, and you may use % in your URL to hold the place of an object; the rest of the arguments will come after the URL. For example, if the URL is node/%/panel and your user visits node/1/panel/foo, the first argument will be 1, and the second argument will be foo."),
28
        'add button' => t('Add argument'),
29
        'context function' => 'ctools_get_argument',
30
        'key' => 'arguments', // the key that data will be stored on an object, eg $panel_page
31
        'sortable' => TRUE,
32
        'settings' => 'argument_settings',
33
      ),
34
      'relationship' => array(
35
        'title' => t('Relationships'),
36
        'singular title' => t('relationship'),
37
        'description' => '', // t('Relationships are contexts that are created from already existing contexts; the add relationship button will only appear once there is another context available. Relationships can load objects based upon how they are related to each other; for example, the author of a node, or a taxonomy term attached to a node, or the vocabulary of a taxonomy term.'),
38
        'add button' => t('Add relationship'),
39
        'context function' => 'ctools_get_relationship',
40
        'key' => 'relationships',
41
        'sortable' => FALSE,
42
        'settings' => 'relationship_settings',
43
      ),
44
      'context' => array(
45
        'title' => t('Contexts'),
46
        'singular title' => t('context'),
47
        'description' => '', // t('Contexts are embedded directly into the panel; you generally must select an object in the panel. For example, you could select node 5, or the term "animals" or the user "administrator"'),
48
        'add button' => t('Add context'),
49
        'context function' => 'ctools_get_context',
50
        'key' => 'contexts',
51
        'sortable' => FALSE,
52
        'settings' => 'context_settings',
53
      ),
54
      'requiredcontext' => array(
55
        'title' => t('Required contexts'),
56
        'singular title' => t('required context'),
57
        'description' => '', // t('Required contexts are passed in from some external source, such as a containing panel. If a mini panel has required contexts, it can only appear when that context is available, and therefore will not show up as a standard Drupal block.'),
58
        'add button' => t('Add required context'),
59
        'context function' => 'ctools_get_context',
60
        'key' => 'requiredcontexts',
61
        'sortable' => FALSE,
62
      ),
63
    );
64
  }
65

    
66
  if ($type === NULL) {
67
    return $info;
68
  }
69

    
70
  return $info[$type];
71
}
72

    
73

    
74
/**
75
 * Get the data belonging to a particular context.
76
 */
77
function ctools_context_get_plugin($type, $name) {
78
  $info = ctools_context_info($type);
79
  if (function_exists($info['context function'])) {
80
    return $info['context function']($name);
81
  }
82
}
83

    
84
/**
85
 * Add the argument table plus gadget plus javascript to the form.
86
 */
87
function ctools_context_add_argument_form($module, &$form, &$form_state, &$form_location, $object, $cache_key = NULL) {
88
  if (empty($cache_key)) {
89
    $cache_key = $object->name;
90
  }
91

    
92
  $form_location = array(
93
    '#prefix' => '<div id="ctools-arguments-table">',
94
    '#suffix' => '</div>',
95
    '#theme' => 'ctools_context_item_form',
96
    '#cache_key' => $cache_key,
97
    '#ctools_context_type' => 'argument',
98
    '#ctools_context_module' => $module,
99
  );
100

    
101
  $args = ctools_get_arguments();
102
  $choices = array();
103
  foreach ($args as $name => $arg) {
104
    if (empty($arg['no ui'])) {
105
      $choices[$name] = $arg['title'];
106
    }
107
  }
108

    
109
  asort($choices);
110

    
111
  if (!empty($choices) || !empty($object->arguments)) {
112
    ctools_context_add_item_table('argument', $form_location, $choices, $object->arguments);
113
  }
114
}
115

    
116
function ctools_context_add_context_form($module, &$form, &$form_state, &$form_location, $object, $cache_key = NULL) {
117
  if (empty($cache_key)) {
118
    $cache_key = $object->name;
119
  }
120

    
121
  $form_location = array(
122
    '#prefix' => '<div id="ctools-contexts-table">',
123
    '#suffix' => '</div>',
124
    '#theme' => 'ctools_context_item_form',
125
    '#cache_key' => $cache_key,
126
    '#ctools_context_type' => 'context',
127
    '#ctools_context_module' => $module,
128
  );
129

    
130
  // Store the order the choices are in so javascript can manipulate it.
131
  $form_location['markup'] = array(
132
    '#markup' => '&nbsp;',
133
  );
134

    
135
  $choices = array();
136
  foreach (ctools_get_contexts() as $name => $arg) {
137
    if (empty($arg['no ui'])) {
138
      $choices[$name] = $arg['title'];
139
    }
140
  }
141

    
142
  asort($choices);
143

    
144
  if (!empty($choices) || !empty($object->contexts)) {
145
    ctools_context_add_item_table('context', $form_location, $choices, $object->contexts);
146
  }
147

    
148
}
149

    
150
function ctools_context_add_required_context_form($module, &$form, &$form_state, &$form_location, $object, $cache_key = NULL) {
151
  if (empty($cache_key)) {
152
    $cache_key = $object->name;
153
  }
154

    
155
  $form_location = array(
156
    '#prefix' => '<div id="ctools-requiredcontexts-table">',
157
    '#suffix' => '</div>',
158
    '#theme' => 'ctools_context_item_form',
159
    '#cache_key' => $cache_key,
160
    '#ctools_context_type' => 'requiredcontext',
161
    '#ctools_context_module' => $module,
162
  );
163

    
164
  // Store the order the choices are in so javascript can manipulate it.
165
  $form_location['markup'] = array(
166
    '#value' => '&nbsp;',
167
  );
168

    
169
  $choices = array();
170
  foreach (ctools_get_contexts() as $name => $arg) {
171
    if (empty($arg['no required context ui'])) {
172
      $choices[$name] = $arg['title'];
173
    }
174
  }
175

    
176
  asort($choices);
177

    
178
  if (!empty($choices) || !empty($object->contexts)) {
179
    ctools_context_add_item_table('requiredcontext', $form_location, $choices, $object->requiredcontexts);
180
  }
181
}
182

    
183
function ctools_context_add_relationship_form($module, &$form, &$form_state, &$form_location, $object, $cache_key = NULL) {
184
  if (empty($cache_key)) {
185
    $cache_key = $object->name;
186
  }
187

    
188
  $form_location = array(
189
    '#prefix' => '<div id="ctools-relationships-table">',
190
    '#suffix' => '</div>',
191
    '#theme' => 'ctools_context_item_form',
192
    '#cache_key' => $cache_key,
193
    '#ctools_context_type' => 'relationship',
194
    '#ctools_context_module' => $module,
195
  );
196

    
197
  // Store the order the choices are in so javascript can manipulate it.
198
  $form_location['markup'] = array(
199
    '#value' => '&nbsp;',
200
  );
201

    
202
  $base_contexts = isset($object->base_contexts) ? $object->base_contexts : array();
203
  $available_relationships = ctools_context_get_relevant_relationships(ctools_context_load_contexts($object, TRUE, $base_contexts));
204

    
205
  ctools_context_add_item_table('relationship', $form_location, $available_relationships, $object->relationships);
206
}
207

    
208
/**
209
 * Include all context administrative include files, css, javascript.
210
 */
211
function ctools_context_admin_includes() {
212
  ctools_include('context');
213
  ctools_include('modal');
214
  ctools_include('ajax');
215
  ctools_include('object-cache');
216
  ctools_modal_add_js();
217
  ctools_modal_add_plugin_js(ctools_get_contexts());
218
  ctools_modal_add_plugin_js(ctools_get_relationships());
219
}
220

    
221
/**
222
 * Add the context table to the page.
223
 */
224
function ctools_context_add_item_table($type, &$form, $available_contexts, $items) {
225
  $form[$type] = array(
226
    '#tree' => TRUE,
227
  );
228

    
229
  $module = $form['#ctools_context_module'];
230
  $cache_key   = $form['#cache_key'];
231

    
232
  if (isset($items) && is_array($items)) {
233
    foreach ($items as $position => $context) {
234
      ctools_context_add_item_to_form($module, $type, $cache_key, $form[$type][$position], $position, $context);
235
    }
236
  }
237

    
238
  $type_info = ctools_context_info($type);
239
  $form['description'] = array(
240
    '#prefix' => '<div class="description">',
241
    '#suffix' => '</div>',
242
    '#markup' => $type_info['description'],
243
  );
244

    
245
  ctools_context_add_item_table_buttons($type, $module, $form, $available_contexts);
246
}
247

    
248
function ctools_context_add_item_table_buttons($type, $module, &$form, $available_contexts) {
249
  drupal_add_library('system', 'drupal.ajax');
250
  $form['buttons'] = array(
251
    '#tree' => TRUE,
252
  );
253

    
254
  if (!empty($available_contexts)) {
255
    $type_info = ctools_context_info($type);
256

    
257
    $module = $form['#ctools_context_module'];
258
    $cache_key   = $form['#cache_key'];
259

    
260
    // The URL for this ajax button
261
    $form['buttons'][$type]['add-url'] = array(
262
      '#attributes' => array('class' => array("ctools-$type-add-url")),
263
      '#type' => 'hidden',
264
      '#value' => url("ctools/context/ajax/add/$module/$type/$cache_key", array('absolute' => TRUE)),
265
    );
266

    
267
    asort($available_contexts);
268
    // This also will be in the URL.
269
    $form['buttons'][$type]['item'] = array(
270
      '#attributes' => array('class' => array("ctools-$type-add-url")),
271
      '#type' => 'select',
272
      '#options' => $available_contexts,
273
      '#required' => FALSE,
274
    );
275

    
276
    $form['buttons'][$type]['add'] = array(
277
      '#type' => 'submit',
278
      '#attributes' => array('class' => array('ctools-use-modal')),
279
      '#id' => "ctools-$type-add",
280
      '#value' => $type_info['add button'],
281
    );
282
  }
283
}
284

    
285
/**
286
 * Add a row to the form. Used both in the main form and by
287
 * the ajax to add an item.
288
 */
289
function ctools_context_add_item_to_form($module, $type, $cache_key, &$form, $position, $item) {
290
  // This is the single function way to load any plugin by variable type.
291
  $info = ctools_context_get_plugin($type, $item['name']);
292
  $form['title'] = array(
293
    '#markup' => check_plain($item['identifier']),
294
  );
295

    
296
  // Relationships not sortable.
297
  $type_info = ctools_context_info($type);
298

    
299
  if (!empty($type_info['sortable'])) {
300
    $form['position'] = array(
301
      '#type' => 'weight',
302
      '#default_value' => $position,
303
      '#attributes' => array('class' => array('drag-position')),
304
    );
305
  }
306

    
307
  $form['remove'] = array(
308
    '#markup' => ctools_ajax_image_button(ctools_image_path('icon-delete.png'), "ctools/context/ajax/delete/$module/$type/$cache_key/$position", t('Remove this item.')),
309
  );
310

    
311
  $form['settings'] = array(
312
    '#markup' => ctools_modal_image_button(ctools_image_path('icon-configure.png'), "ctools/context/ajax/configure/$module/$type/$cache_key/$position", t('Configure settings for this item.')),
313
  );
314
}
315

    
316

    
317
// ---------------------------------------------------------------------------
318
// AJAX forms and stuff.
319

    
320
/**
321
 * Ajax entry point to add an context
322
 */
323
function ctools_context_ajax_item_add($mechanism = NULL, $type = NULL, $cache_key = NULL, $name = NULL, $step = NULL) {
324
  ctools_include('ajax');
325
  ctools_include('modal');
326
  ctools_include('context');
327
  ctools_include('cache');
328
  ctools_include('plugins-admin');
329

    
330
  if (!$name) {
331
    return ctools_ajax_render_error();
332
  }
333

    
334
  // Load stored object from cache.
335
  if (!($object = ctools_cache_get($mechanism, $cache_key))) {
336
    ctools_ajax_render_error(t('Invalid object name.'));
337
  }
338

    
339
  // Get info about what we're adding, i.e, relationship, context, argument, etc.
340
  $plugin_definition = ctools_context_get_plugin($type, $name);
341
  if (empty($plugin_definition)) {
342
    ctools_ajax_render_error(t('Invalid context type'));
343
  }
344

    
345
  // Set up the $conf array for this plugin
346
  if (empty($step) || empty($object->temporary)) {
347
    // Create the basis for our new context.
348
    $conf = ctools_context_get_defaults($plugin_definition, $object, $type);
349
    $object->temporary = &$conf;
350
  }
351
  else {
352
    $conf = &$object->temporary;
353
  }
354

    
355
  // Load the contexts that may be used.
356
  $base_contexts = isset($object->base_contexts) ? $object->base_contexts : array();
357
  $contexts = ctools_context_load_contexts($object, TRUE, $base_contexts);
358

    
359
  $type_info = ctools_context_info($type);
360
  $form_state = array(
361
    'ajax' => TRUE,
362
    'modal' => TRUE,
363
    'modal return' => TRUE,
364
    'object' => &$object,
365
    'conf' => &$conf,
366
    'plugin' => $plugin_definition,
367
    'type' => $type,
368
    'contexts' => $contexts,
369
    'title' => t('Add @type "@context"', array('@type' => $type_info['singular title'], '@context' => $plugin_definition['title'])),
370
    'type info' => $type_info,
371
    'op' => 'add',
372
    'step' => $step,
373
  );
374

    
375
  $form_info = array(
376
    'path' => "ctools/context/ajax/add/$mechanism/$type/$cache_key/$name/%step",
377
    'show cancel' => TRUE,
378
    'default form' => 'ctools_edit_context_form_defaults',
379
    'auto cache' => TRUE,
380
    'cache mechanism' => $mechanism,
381
    'cache key' => $cache_key,
382
    // This is stating what the cache will be referred to in $form_state
383
    'cache location' => 'object',
384
  );
385

    
386
  if ($type == 'requiredcontext') {
387
    $form_info += array(
388
      'add form name' => 'required context add form',
389
      'edit form name' => 'required context edit form',
390
    );
391
  }
392

    
393
  $output = ctools_plugin_configure_form($form_info, $form_state);
394

    
395
  if (!empty($form_state['cancel'])) {
396
    $output = array(ctools_modal_command_dismiss());
397
  }
398
  else if (!empty($form_state['complete'])) {
399
    // Successful submit -- move temporary data to location.
400

    
401
    // Create a reference to the place our context lives. Since this is fairly
402
    // generic, this is the easiest way to get right to the place of the
403
    // object without knowing precisely what data we're poking at.
404
    $ref = &$object->{$type_info['key']};
405

    
406
    // Figure out the position for our new context.
407
    $position = empty($ref) ? 0 : max(array_keys($ref)) + 1;
408

    
409
    $conf['id'] = ctools_context_next_id($ref, $name);
410
    $ref[$position] = $conf;
411

    
412
    if (isset($object->temporary)) {
413
      unset($object->temporary);
414
    }
415

    
416
    ctools_cache_operation($mechanism, $cache_key, 'finalize', $object);
417

    
418
    // Very irritating way to update the form for our contexts.
419
    $arg_form_state = form_state_defaults() + array(
420
      'values' => array(),
421
      'process_input' => FALSE,
422
      'complete form' => array(),
423
    );
424

    
425
    $rel_form_state = $arg_form_state;
426

    
427
    $arg_form = array(
428
      '#post' => array(),
429
      '#programmed' => FALSE,
430
      '#tree' => FALSE,
431
      '#parents' => array(),
432
      '#array_parents' => array(),
433
    );
434

    
435
    // Build a chunk of the form to merge into the displayed form
436
    $arg_form[$type] = array(
437
      '#tree' => TRUE,
438
    );
439
    $arg_form[$type][$position] = array(
440
      '#tree' => TRUE,
441
    );
442

    
443
    ctools_context_add_item_to_form($mechanism, $type, $cache_key, $arg_form[$type][$position], $position, $ref[$position]);
444
    $arg_form = form_builder('ctools_context_form', $arg_form, $arg_form_state);
445

    
446
    // Build the relationships table so we can ajax it in.
447
    // This is an additional thing that goes in here.
448
    $rel_form = array(
449
      '#theme' => 'ctools_context_item_form',
450
      '#cache_key' => $cache_key,
451
      '#ctools_context_type' => 'relationship',
452
      '#ctools_context_module' => $mechanism,
453
      '#only_buttons' => TRUE,
454
      '#post' => array(),
455
      '#programmed' => FALSE,
456
      '#tree' => FALSE,
457
      '#parents' => array(),
458
      '#array_parents' => array(),
459
    );
460

    
461
    $rel_form['relationship'] = array(
462
      '#tree' => TRUE,
463
    );
464

    
465
    // Allow an object to set some 'base' contexts that come from elsewhere.
466
    $rel_contexts = isset($object->base_contexts) ? $object->base_contexts : array();
467
    $all_contexts = ctools_context_load_contexts($object, TRUE, $rel_contexts);
468
    $available_relationships = ctools_context_get_relevant_relationships($all_contexts);
469

    
470
    $output = array();
471
    if (!empty($available_relationships)) {
472
      ctools_context_add_item_table_buttons('relationship', $mechanism, $rel_form, $available_relationships);
473
      $rel_form = form_builder('dummy_form_id', $rel_form, $rel_form_state);
474
      $output[] = ajax_command_replace('div#ctools-relationships-table div.buttons', drupal_render($rel_form));
475
    }
476

    
477
    $theme_vars = array();
478
    $theme_vars['type'] = $type;
479
    $theme_vars['form'] = $arg_form[$type][$position];
480
    $theme_vars['position'] = $position;
481
    $theme_vars['count'] = $position;
482
    $text = theme('ctools_context_item_row', $theme_vars);
483
    $output[] = ajax_command_append('#' . $type . '-table tbody', $text);
484
    $output[] = ajax_command_changed('#' . $type . '-row-' . $position, '.title');
485
    $output[] = ctools_modal_command_dismiss();
486
  }
487
  else {
488
    $output = ctools_modal_form_render($form_state, $output);
489
  }
490
  print ajax_render($output);
491
  exit;
492
}
493

    
494
/**
495
 * Ajax entry point to edit an item
496
 */
497
function ctools_context_ajax_item_edit($mechanism = NULL, $type = NULL, $cache_key = NULL, $position = NULL, $step = NULL) {
498
  ctools_include('ajax');
499
  ctools_include('modal');
500
  ctools_include('context');
501
  ctools_include('cache');
502
  ctools_include('plugins-admin');
503

    
504
  if (!isset($position)) {
505
    return ctools_ajax_render_error();
506
  }
507

    
508
  // Load stored object from cache.
509
  if (!($object = ctools_cache_get($mechanism, $cache_key))) {
510
    ctools_ajax_render_error(t('Invalid object name.'));
511
  }
512

    
513
  $type_info = ctools_context_info($type);
514

    
515
  // Create a reference to the place our context lives. Since this is fairly
516
  // generic, this is the easiest way to get right to the place of the
517
  // object without knowing precisely what data we're poking at.
518
  $ref = &$object->{$type_info['key']};
519

    
520
  if (empty($step) || empty($object->temporary)) {
521
    // Create the basis for our new context.
522
    $conf = $object->{$type_info['key']}[$position];
523
    $object->temporary = &$conf;
524
  }
525
  else {
526
    $conf = &$object->temporary;
527
  }
528

    
529
  $name = $ref[$position]['name'];
530
  if (empty($name)) {
531
    ctools_ajax_render_error();
532
  }
533

    
534
  // load the plugin definition
535
  $plugin_definition = ctools_context_get_plugin($type, $name);
536
  if (empty($plugin_definition)) {
537
    ctools_ajax_render_error(t('Invalid context type'));
538
  }
539

    
540
  // Load the contexts
541
  $base_contexts = isset($object->base_contexts) ? $object->base_contexts : array();
542
  $contexts = ctools_context_load_contexts($object, TRUE, $base_contexts);
543

    
544
  $form_state = array(
545
    'ajax' => TRUE,
546
    'modal' => TRUE,
547
    'modal return' => TRUE,
548
    'object' => &$object,
549
    'conf' => &$conf,
550
    'position' => $position,
551
    'plugin' => $plugin_definition,
552
    'type' => $type,
553
    'contexts' => $contexts,
554
    'title' => t('Edit @type "@context"', array('@type' => $type_info['singular title'], '@context' => $plugin_definition['title'])),
555
    'type info' => $type_info,
556
    'op' => 'add',
557
    'step' => $step,
558
  );
559

    
560
  $form_info = array(
561
    'path' => "ctools/context/ajax/configure/$mechanism/$type/$cache_key/$position/%step",
562
    'show cancel' => TRUE,
563
    'default form' => 'ctools_edit_context_form_defaults',
564
    'auto cache' => TRUE,
565
    'cache mechanism' => $mechanism,
566
    'cache key' => $cache_key,
567
    // This is stating what the cache will be referred to in $form_state
568
    'cache location' => 'object',
569
  );
570

    
571
  if ($type == 'requiredcontext') {
572
    $form_info += array(
573
      'add form name' => 'required context add form',
574
      'edit form name' => 'required context edit form',
575
    );
576
  }
577

    
578
  $output = ctools_plugin_configure_form($form_info, $form_state);
579

    
580
  if (!empty($form_state['cancel'])) {
581
    $output = array(ctools_modal_command_dismiss());
582
  }
583
  else if (!empty($form_state['complete'])) {
584
    // successful submit
585
    $ref[$position] = $conf;
586
    if (isset($object->temporary)) {
587
      unset($object->temporary);
588
    }
589

    
590
    ctools_cache_operation($mechanism, $cache_key, 'finalize', $object);
591

    
592
    $output = array();
593
    $output[] = ctools_modal_command_dismiss();
594

    
595
    $arg_form_state = form_state_defaults() + array(
596
      'values' => array(),
597
      'process_input' => FALSE,
598
      'complete form' => array(),
599
    );
600

    
601
    $arg_form = array(
602
      '#post' => array(),
603
      '#parents' => array(),
604
      '#array_parents' => array(),
605
      '#programmed' => FALSE,
606
      '#tree' => FALSE,
607
    );
608

    
609
    // Build a chunk of the form to merge into the displayed form
610
    $arg_form[$type] = array(
611
      '#tree' => TRUE,
612
    );
613
    $arg_form[$type][$position] = array(
614
      '#tree' => TRUE,
615
    );
616

    
617
    ctools_context_add_item_to_form($mechanism, $type, $cache_key, $arg_form[$type][$position], $position, $ref[$position]);
618
    $arg_form = form_builder('ctools_context_form', $arg_form, $arg_form_state);
619

    
620
    $theme_vars = array();
621
    $theme_vars['type'] = $type;
622
    $theme_vars['form'] = $arg_form[$type][$position];
623
    $theme_vars['position'] = $position;
624
    $theme_vars['count'] = $position;
625
    $output[] = ajax_command_replace('#' . $type . '-row-' . $position, theme('ctools_context_item_row', $theme_vars));
626
    $output[] = ajax_command_changed('#' . $type . '-row-' . $position, '.title');
627
  }
628
  else {
629
    $output = ctools_modal_form_render($form_state, $output);
630
  }
631
  print ajax_render($output);
632
  exit;
633
}
634

    
635
/**
636
 * Get the defaults for a new instance of a context plugin.
637
 *
638
 * @param $plugin_definition
639
 *   The metadata definition of the plugin from ctools_get_plugins().
640
 * @param $object
641
 *   The object the context plugin will be added to.
642
 * @param $type
643
 *   The type of context plugin. i.e, context, requiredcontext, relationship
644
 */
645
function ctools_context_get_defaults($plugin_definition, $object, $type) {
646
  // Fetch the potential id of the plugin so we can append
647
  // title and keyword information for new ones.
648
  $type_info = ctools_context_info($type);
649
  $id = ctools_context_next_id($object->{$type_info['key']}, $plugin_definition['name']);
650

    
651
  $conf = array(
652
    'identifier' => $plugin_definition['title'] . ($id > 1 ? ' ' . $id : ''),
653
    'keyword' => ctools_get_keyword($object, $plugin_definition['keyword']),
654
    'name' => $plugin_definition['name'],
655
  );
656

    
657
  if (isset($plugin_definition['defaults'])) {
658
    $defaults = $plugin_definition['defaults'];
659
  }
660
  else if (isset($subtype['defaults'])) {
661
    $defaults = $subtype['defaults'];
662
  }
663

    
664
  if (isset($defaults)) {
665
    if (is_string($defaults) && function_exists($defaults)) {
666
      if ($settings = $defaults($plugin_definition)) {
667
        $conf += $settings;
668
      }
669
    }
670
    else if (is_array($defaults)) {
671
      $conf += $defaults;
672
    }
673
  }
674

    
675
  return $conf;
676
}
677

    
678
/**
679
 * Form wrapper for the edit context form.
680
 *
681
 * @todo: We should uncombine these.
682
 */
683
function ctools_edit_context_form_defaults($form, &$form_state) {
684
  // Basic values required to orient ourselves
685
  $object            = $form_state['object'];
686
  $plugin_definition = $form_state['plugin'];
687
  $type_info         = $form_state['type info'];
688
  $contexts          = $form_state['contexts'];
689
  $conf             = $form_state['conf'];
690

    
691
  if ($type_info['key'] == 'arguments' && !isset($conf['default'])) {
692
    $conf['default'] = 'ignore';
693
    $conf['title'] = '';
694
  }
695

    
696
  $form['description'] = array(
697
    '#prefix' => '<div class="description">',
698
    '#suffix' => '</div>',
699
    '#markup' => check_plain($plugin_definition['description']),
700
  );
701

    
702
  if ($type_info['key'] == 'relationships') {
703
    $form['context'] = ctools_context_selector($contexts, $plugin_definition['required context'], isset($conf['context']) ? $conf['context'] : '');
704
  }
705
  if ($type_info['key'] == 'arguments') {
706
    $form['default'] = array(
707
      '#type' => 'select',
708
      '#title' => t('Default'),
709
      '#options' => array(
710
        'ignore' => t('Ignore it; content that requires this context will not be available.'),
711
        '404' => t('Display page not found or display nothing at all.'),
712
      ),
713
      '#default_value' => $conf['default'],
714
      '#description' => t('If the argument is missing or is not valid, select how this should behave.'),
715
    );
716

    
717
    $form['title'] = array(
718
      '#type' => 'textfield',
719
      '#title' => t('Title'),
720
      '#default_value' => $conf['title'],
721
      '#description' => t('Enter a title to use when this argument is present. You may use %KEYWORD substitution, where the keyword is specified below.'),
722
    );
723
  }
724

    
725
  $form['identifier'] = array(
726
    '#type' => 'textfield',
727
    '#title' => t('Identifier'),
728
    '#description' => t('Enter a name to identify this !type on administrative screens.', array('!type' => t('context'))),
729
    '#default_value' => $conf['identifier'],
730
  );
731

    
732
  $form['keyword'] = array(
733
    '#type' => 'textfield',
734
    '#title' => t('Keyword'),
735
    '#description' => t('Enter a keyword to use for substitution in titles.'),
736
    '#default_value' => $conf['keyword'],
737
  );
738

    
739
  if ($type_info['key'] == 'requiredcontexts') {
740
    $form['optional'] = array(
741
      '#type' => 'checkbox',
742
      '#title' => t('Context is optional'),
743
      '#default_value' => !empty($form_state['conf']['optional']),
744
      '#description' => t('This context need not be present for the component to function.'),
745
    );
746
  }
747

    
748
  $form['#submit'][] = 'ctools_edit_context_form_defaults_submit';
749

    
750
  return $form;
751
}
752

    
753
/**
754
 * Submit handler to store context identifier and keyword info.
755
 */
756
function ctools_edit_context_form_defaults_submit(&$form, &$form_state) {
757
  if ($form_state['type info']['key'] == 'relationships') {
758
    $form_state['conf']['context'] = $form_state['values']['context'];
759
  }
760
  if ($form_state['type info']['key'] == 'arguments') {
761
    $form_state['conf']['default'] = $form_state['values']['default'];
762
    $form_state['conf']['title'] = $form_state['values']['title'];
763
  }
764
  if ($form_state['type info']['key'] == 'requiredcontexts') {
765
    $form_state['conf']['optional'] = $form_state['values']['optional'];
766
  }
767

    
768
  $form_state['conf']['identifier'] = $form_state['values']['identifier'];
769
  $form_state['conf']['keyword'] = $form_state['values']['keyword'];
770
}
771

    
772
/**
773
 * Ajax entry point to edit an item
774
 */
775
function ctools_context_ajax_item_delete($mechanism = NULL, $type = NULL, $cache_key = NULL, $position = NULL) {
776
  ctools_include('ajax');
777
  ctools_include('context');
778
  ctools_include('cache');
779

    
780
  if (!isset($position)) {
781
    return ctools_ajax_render_error();
782
  }
783

    
784
  // Load stored object from cache.
785
  if (!($object = ctools_cache_get($mechanism, $cache_key))) {
786
    ctools_ajax_render_error(t('Invalid object name.'));
787
  }
788

    
789
  $type_info = ctools_context_info($type);
790

    
791
  // Create a reference to the place our context lives. Since this is fairly
792
  // generic, this is the easiest way to get right to the place of the
793
  // object without knowing precisely what data we're poking at.
794
  $ref = &$object->{$type_info['key']};
795

    
796
  if (!array_key_exists($position, $ref)) {
797
    ctools_ajax_render_error(t('Unable to delete missing item!'));
798
  }
799

    
800
  unset($ref[$position]);
801
  ctools_cache_operation($mechanism, $cache_key, 'finalize', $object);
802

    
803
  $output   = array();
804
  $output[] = ajax_command_replace('#' . $type . '-row-' . $position, '');
805
  $output[] = ajax_command_restripe("#$type-table");
806
  print ajax_render($output);
807
  exit;
808
}
809

    
810
// --- End of contexts
811

    
812
function ctools_save_context($type, &$ref, $form_values) {
813
  $type_info = ctools_context_info($type);
814

    
815
  // Organize arguments
816
  $new = array();
817
  $order = array();
818

    
819
  foreach ($ref as $id => $context) {
820
    $position = $form_values[$type][$id]['position'];
821
    $order[$position] = $id;
822
  }
823

    
824
  ksort($order);
825
  foreach ($order as $id) {
826
    $new[] = $ref[$id];
827
  }
828
  $ref = $new;
829
}
830

    
831
function ctools_get_keyword($page, $word) {
832
  // Create a complete set of keywords
833
  $keywords = array();
834
  foreach (array('arguments', 'relationships', 'contexts', 'requiredcontexts') as $type) {
835
    if (!empty($page->$type) && is_array($page->$type)) {
836
      foreach ($page->$type as $info) {
837
        $keywords[$info['keyword']] = TRUE;
838
      }
839
    }
840
  }
841

    
842
  $keyword = $word;
843
  $count = 1;
844
  while (!empty($keywords[$keyword])) {
845
    $keyword = $word . '_' . ++$count;
846
  }
847
  return $keyword;
848
}
849