Projet

Général

Profil

Paste
Télécharger (39,1 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / ctools / page_manager / page_manager.module @ 74f6bef0

1
<?php
2

    
3
/**
4
 * @file
5
 * The page manager module provides a UI and API to manage pages.
6
 *
7
 * It defines pages, both for system pages, overrides of system pages, and
8
 * custom pages using Drupal's normal menu system. It allows complex
9
 * manipulations of these pages, their content, and their hierarchy within
10
 * the site. These pages can be exported to code for superior revision
11
 * control.
12
 */
13

    
14
/**
15
 * Bit flag on the 'changed' value to tell us if an item was moved.
16
 */
17
define('PAGE_MANAGER_CHANGED_MOVED', 0x01);
18

    
19
/**
20
 * Bit flag on the 'changed' value to tell us if an item edited or added.
21
 */
22
define('PAGE_MANAGER_CHANGED_CACHED', 0x02);
23

    
24
/**
25
 * Bit flag on the 'changed' value to tell us if an item deleted.
26
 */
27
define('PAGE_MANAGER_CHANGED_DELETED', 0x04);
28

    
29
/**
30
 * Bit flag on the 'changed' value to tell us if an item has had its disabled status changed.
31
 */
32
define('PAGE_MANAGER_CHANGED_STATUS', 0x08);
33

    
34
// --------------------------------------------------------------------------
35
// Drupal hooks
36

    
37
/**
38
 * Implements hook_permission().
39
 */
40
function page_manager_permission() {
41
  return array(
42
    'use page manager' => array(
43
      'title' => t('Use Page Manager'),
44
      'description' => t("Allows users to use most of Page Manager's features, though restricts some of the most powerful, potentially site-damaging features. Note that even the reduced featureset still allows for enormous control over your website."),
45
      'restrict access' => TRUE,
46
    ),
47
    'administer page manager' => array(
48
      'title' => t('Administer Page Manager'),
49
      'description' => t('Allows complete control over Page Manager, i.e., complete control over your site. Grant with extreme caution.'),
50
      'restrict access' => TRUE,
51
    ),
52
  );
53

    
54
}
55

    
56
/**
57
 * Implements hook_ctools_plugin_directory() to let the system know
58
 * where our task and task_handler plugins are.
59
 */
60
function page_manager_ctools_plugin_directory($owner, $plugin_type) {
61
  if ($owner == 'page_manager') {
62
    return 'plugins/' . $plugin_type;
63
  }
64
  if ($owner == 'ctools' && $plugin_type == 'cache') {
65
    return 'plugins/' . $plugin_type;
66
  }
67
}
68

    
69
/**
70
 * Implements hook_ctools_plugin_type() to inform the plugin system that Page
71
 * Manager owns task, task_handler, and page_wizard plugin types.
72
 *
73
 * All of these are empty because the defaults all work.
74
 */
75
function page_manager_ctools_plugin_type() {
76
  return array(
77
    'tasks' => array(),
78
    'task_handlers' => array(),
79
    'page_wizards' => array(),
80
  );
81
}
82

    
83
/**
84
 * Delegated implementation of hook_menu().
85
 */
86
function page_manager_menu() {
87
  // For some reason, some things can activate modules without satisfying
88
  // dependencies. I don't know how, but this helps prevent things from
89
  // whitescreening when this happens.
90
  if (!module_exists('ctools')) {
91
    return;
92
  }
93

    
94
  $items = array();
95
  $base = array(
96
    'access arguments' => array('use page manager'),
97
    'file' => 'page_manager.admin.inc',
98
    'theme callback' => 'ajax_base_page_theme',
99
  );
100

    
101
  $items['admin/structure/pages'] = array(
102
    'title' => 'Pages',
103
    'description' => 'Add, edit and remove overridden system pages and user defined pages from the system.',
104
    'page callback' => 'page_manager_list_page',
105
  ) + $base;
106

    
107
  $items['admin/structure/pages/list'] = array(
108
    'title' => 'List',
109
    'page callback' => 'page_manager_list_page',
110
    'type' => MENU_DEFAULT_LOCAL_TASK,
111
    'weight' => -10,
112
  ) + $base;
113

    
114
  $items['admin/structure/pages/edit/%page_manager_cache'] = array(
115
    'title' => 'Edit',
116
    'page callback' => 'page_manager_edit_page',
117
    'page arguments' => array(4),
118
    'type' => MENU_NORMAL_ITEM,
119
  ) + $base;
120

    
121
  $items['admin/structure/pages/%ctools_js/operation/%page_manager_cache'] = array(
122
    'page callback' => 'page_manager_edit_page_operation',
123
    'page arguments' => array(3, 5),
124
    'type' => MENU_NORMAL_ITEM,
125
  ) + $base;
126

    
127
  $items['admin/structure/pages/%ctools_js/enable/%page_manager_cache'] = array(
128
    'page callback' => 'page_manager_enable_page',
129
    'page arguments' => array(FALSE, 3, 5),
130
    'type' => MENU_CALLBACK,
131
  ) + $base;
132

    
133
  $items['admin/structure/pages/%ctools_js/disable/%page_manager_cache'] = array(
134
    'page callback' => 'page_manager_enable_page',
135
    'page arguments' => array(TRUE, 3, 5),
136
    'type' => MENU_CALLBACK,
137
  ) + $base;
138

    
139
  $tasks = page_manager_get_tasks();
140

    
141
  // Provide menu items for each task.
142
  foreach ($tasks as $task_id => $task) {
143
    // Allow the task to add its own menu items.
144
    if ($function = ctools_plugin_get_function($task, 'hook menu')) {
145
      $function($items, $task);
146
    }
147

    
148
    // And for those that provide subtasks, provide menu items for them, as well.
149
    foreach (page_manager_get_task_subtasks($task) as $subtask_id => $subtask) {
150
      // Allow the task to add its own menu items.
151
      if ($function = ctools_plugin_get_function($task, 'hook menu')) {
152
        $function($items, $subtask);
153
      }
154
    }
155
  }
156

    
157
  return $items;
158
}
159

    
160
function page_manager_admin_paths() {
161
  /* @todo FIX ME this is a major resource suck. */
162
  return;
163

    
164
  $items = array();
165
  ctools_include('page', 'page_manager', 'plugins/tasks');
166
  $pages = page_manager_page_load_all();
167
  foreach ($pages as $page) {
168
    // Make sure the page we're on is set to be an administrative path and that
169
    // it is not set to be a frontpage path.
170
    if ((isset($page->conf['admin_paths']) && $page->conf['admin_paths']) && (!isset($page->make_frontpage) || !$page->make_frontpage)) {
171
      $path_parts = explode('/', $page->path);
172
      foreach ($path_parts as $key => $part) {
173
        if (strpos($part, '%') !== FALSE || strpos($part, '!') !== FALSE) {
174
          $path_parts[$key] = '*';
175
        }
176
      }
177
      $path = implode('/', $path_parts);
178
      if ($page->menu['type'] == 'default tab') {
179
        array_pop($path_parts);
180
        $parent_path = implode('/', $path_parts);
181
        $items[$parent_path] = TRUE;
182
      }
183
      $items[$path] = TRUE;
184
    }
185
  }
186
  return $items;
187
}
188

    
189
/**
190
 * Implements hook_menu_alter.
191
 *
192
 * Get a list of all tasks and delegate to them.
193
 */
194
function page_manager_menu_alter(&$items) {
195
  // For some reason, some things can activate modules without satisfying
196
  // dependencies. I don't know how, but this helps prevent things from
197
  // whitescreening when this happens.
198
  if (!module_exists('ctools')) {
199
    return;
200
  }
201

    
202
  $tasks = page_manager_get_tasks();
203

    
204
  foreach ($tasks as $task) {
205
    if ($function = ctools_plugin_get_function($task, 'hook menu alter')) {
206
      $function($items, $task);
207
    }
208
    // let the subtasks alter the menu items too.
209
    foreach (page_manager_get_task_subtasks($task) as $subtask_id => $subtask) {
210
      if ($function = ctools_plugin_get_function($subtask, 'hook menu alter')) {
211
        $function($items, $subtask);
212
      }
213
    }
214
  }
215

    
216
  return $items;
217
}
218

    
219
/*
220
 * Implements hook_theme()
221
 */
222
function page_manager_theme() {
223
  // For some reason, some things can activate modules without satisfying
224
  // dependencies. I don't know how, but this helps prevent things from
225
  // whitescreening when this happens.
226
  if (!module_exists('ctools')) {
227
    return;
228
  }
229

    
230
  $base = array(
231
    'path' => drupal_get_path('module', 'page_manager') . '/theme',
232
    'file' => 'page_manager.theme.inc',
233
  );
234

    
235
  $items = array(
236
    'page_manager_handler_rearrange' => array(
237
      'render element' => 'form',
238
    ) + $base,
239
    'page_manager_edit_page' => array(
240
      'template' => 'page-manager-edit-page',
241
      'variables' => array('page' => NULL, 'save' => NULL, 'operations' => array(), 'content' => array()),
242
    ) + $base,
243
    'page_manager_lock' => array(
244
      'variables' => array('page' => array()),
245
    ) + $base,
246
    'page_manager_changed' => array(
247
      'variables' => array('text' => NULL, 'description' => NULL),
248
    ) + $base,
249
  );
250

    
251
  // Allow task plugins to have theme registrations by passing through:
252
  $tasks = page_manager_get_tasks();
253

    
254
  // Provide menu items for each task.
255
  foreach ($tasks as $task_id => $task) {
256
    if ($function = ctools_plugin_get_function($task, 'hook theme')) {
257
      $function($items, $task);
258
    }
259
  }
260

    
261
  return $items;
262
}
263

    
264
// --------------------------------------------------------------------------
265
// Page caching
266
//
267
// The page cache is used to store a page temporarily, using the ctools object
268
// cache. When loading from the page cache, it will either load the cached
269
// version, or if there is not one, load the real thing and create a cache
270
// object which can then be easily stored.
271

    
272
/**
273
 * Get the cached changes to a given task handler.
274
 */
275
function page_manager_get_page_cache($task_name) {
276
  $caches = drupal_static(__FUNCTION__, array());
277
  if (!isset($caches[$task_name])) {
278
    ctools_include('object-cache');
279
    $cache = ctools_object_cache_get('page_manager_page', $task_name);
280
    if (!$cache) {
281
      $cache = new stdClass();
282
      $cache->task_name = $task_name;
283
      list($cache->task_id, $cache->subtask_id) = page_manager_get_task_id($cache->task_name);
284

    
285
      $cache->task = page_manager_get_task($cache->task_id);
286
      if (empty($cache->task)) {
287
        return FALSE;
288
      }
289

    
290
      if ($cache->subtask_id) {
291
        $cache->subtask = page_manager_get_task_subtask($cache->task, $cache->subtask_id);
292
        if (empty($cache->subtask)) {
293
          return FALSE;
294
        }
295
      }
296
      else {
297
        $cache->subtask = $cache->task;
298
        $cache->subtask['name'] = '';
299
      }
300

    
301
      $cache->handlers = page_manager_load_sorted_handlers($cache->task, $cache->subtask_id);
302
      $cache->handler_info = array();
303
      foreach ($cache->handlers as $id => $handler) {
304
        $cache->handler_info[$id] = array(
305
          'weight' => $handler->weight,
306
          'changed' => FALSE,
307
          'name' => $id,
308
        );
309
      }
310
    }
311
    else {
312
      // ensure the task is loaded.
313
      page_manager_get_task($cache->task_id);
314
    }
315

    
316
    if ($task_name != '::new') {
317
      $cache->locked = ctools_object_cache_test('page_manager_page', $task_name);
318
    }
319
    else {
320
      $cache->locked = FALSE;
321
    }
322

    
323
    $caches[$task_name] = $cache;
324
  }
325

    
326
  return $caches[$task_name];
327
}
328

    
329
/**
330
 * Store changes to a task handler in the object cache.
331
 */
332
function page_manager_set_page_cache($page) {
333
  if (!empty($page->locked)) {
334
    return;
335
  }
336

    
337
  if (empty($page->task_name)) {
338
    return;
339
  }
340

    
341
  ctools_include('object-cache');
342
  $page->changed = TRUE;
343
  $cache = ctools_object_cache_set('page_manager_page', $page->task_name, $page);
344
}
345

    
346
/**
347
 * Remove an item from the object cache.
348
 */
349
function page_manager_clear_page_cache($name) {
350
  ctools_include('object-cache');
351
  ctools_object_cache_clear('page_manager_page', $name);
352
}
353

    
354
/**
355
 * Write all changes from the page cache and clear it out.
356
 */
357
function page_manager_save_page_cache($cache) {
358
  // Save the subtask:
359
  if ($function = ctools_plugin_get_function($cache->task, 'save subtask callback')) {
360
    $function($cache->subtask, $cache);
361
  }
362

    
363
  // Iterate through handlers and save/delete/update as necessary.
364
  // Go through each of the task handlers, check to see if it needs updating,
365
  // and update it if so.
366
  foreach ($cache->handler_info as $id => $info) {
367
    $handler = &$cache->handlers[$id];
368
    // If it has been marked for deletion, delete it.
369

    
370
    if ($info['changed'] & PAGE_MANAGER_CHANGED_DELETED) {
371
      page_manager_delete_task_handler($handler);
372
    }
373
    // If it has been somehow edited (or added), write the cached version
374
    elseif ($info['changed'] & PAGE_MANAGER_CHANGED_CACHED) {
375
      // Make sure we get updated weight from the form for this.
376
      $handler->weight = $info['weight'];
377
      page_manager_save_task_handler($handler);
378
    }
379
    // Otherwise, check to see if it has moved and, if so, update the weight.
380
    elseif ($info['weight'] != $handler->weight) {
381
      // Theoretically we could only do this for in code objects, but since our
382
      // load mechanism checks for all, this is less database work.
383
      page_manager_update_task_handler_weight($handler, $info['weight']);
384
    }
385

    
386
    // Set enable/disabled status.
387
    if ($info['changed'] & PAGE_MANAGER_CHANGED_STATUS) {
388
      ctools_include('export');
389
      ctools_export_set_object_status($cache->handlers[$id], $info['disabled']);
390
    }
391
  }
392

    
393
  page_manager_clear_page_cache($cache->task_name);
394

    
395
  if (!empty($cache->path_changed) || !empty($cache->new)) {
396
    // Force a menu rebuild to make sure the menu entries are set.
397
    menu_rebuild();
398
  }
399
  cache_clear_all();
400
}
401

    
402
/**
403
 * Menu callback to load a page manager cache object for menu callbacks.
404
 */
405
function page_manager_cache_load($task_name) {
406
  // load context plugin as there may be contexts cached here.
407
  ctools_include('context');
408
  return page_manager_get_page_cache($task_name);
409
}
410

    
411
/**
412
 * Generate a unique name for a task handler.
413
 *
414
 * Task handlers need to be named but they aren't allowed to set their own
415
 * names. Instead, they are named based upon their parent task and type.
416
 */
417
function page_manager_handler_get_name($task_name, $handlers, $handler) {
418
  $base = str_replace('-', '_', $task_name);
419
  // Generate a unique name. Unlike most named objects, we don't let people choose
420
  // names for task handlers because they mostly don't make sense.
421
  $base .= '_' . $handler->handler;
422

    
423
  // Once we have a base, check to see if it is used. If it is, start counting up.
424
  $name = $base;
425
  $count = 1;
426
  // If taken
427
  while (isset($handlers[$name])) {
428
    $name = $base . '_' . ++$count;
429
  }
430

    
431
  return $name;
432
}
433

    
434
/**
435
 * Import a handler into a page.
436
 *
437
 * This is used by both import and clone, since clone just exports the
438
 * handler and immediately imports it.
439
 */
440
function page_manager_handler_add_to_page(&$page, &$handler, $title = NULL) {
441
  $last = end($page->handler_info);
442
  $handler->weight = $last ? $last['weight'] + 1 : 0;
443
  $handler->task = $page->task_id;
444
  $handler->subtask = $page->subtask_id;
445
  $handler->export_type = EXPORT_IN_DATABASE;
446
  $handler->type = t('Normal');
447

    
448
  if ($title) {
449
    $handler->conf['title'] = $title;
450
  }
451

    
452
  $name = page_manager_handler_get_name($page->task_name, $page->handlers, $handler);
453

    
454
  $handler->name = $name;
455

    
456
  $page->handlers[$name] = $handler;
457
  $page->handler_info[$name] = array(
458
    'weight' => $handler->weight,
459
    'name' => $handler->name,
460
    'changed' => PAGE_MANAGER_CHANGED_CACHED,
461
  );
462
}
463

    
464
// --------------------------------------------------------------------------
465
// Database routines
466
//
467
// This includes fetching plugins and plugin info as well as specialized
468
// fetch methods to get groups of task handlers per task.
469

    
470
/**
471
 * Load a single task handler by name.
472
 *
473
 * Handlers can come from multiple sources; either the database or by normal
474
 * export method, which is handled by the ctools library, but handlers can
475
 * also be bundled with task/subtask. We have to check there and perform
476
 * overrides as appropriate.
477
 *
478
 * Handlers bundled with the task are of a higher priority than default
479
 * handlers provided by normal code, and are of a lower priority than
480
 * the database, so we have to check the source of handlers when we have
481
 * multiple to choose from.
482
 */
483
function page_manager_load_task_handler($task, $subtask_id, $name) {
484
  ctools_include('export');
485
  $result = ctools_export_load_object('page_manager_handlers', 'names', array($name));
486
  $handlers = page_manager_get_default_task_handlers($task, $subtask_id);
487
  return page_manager_compare_task_handlers($result, $handlers, $name);
488
}
489

    
490
/**
491
 * Load all task handlers for a given task/subtask.
492
 */
493
function page_manager_load_task_handlers($task, $subtask_id = NULL, $default_handlers = NULL) {
494
  ctools_include('export');
495
  $conditions = array(
496
    'task' => $task['name'],
497
  );
498

    
499
  if (isset($subtask_id)) {
500
    $conditions['subtask'] = $subtask_id;
501
  }
502

    
503
  $handlers = ctools_export_load_object('page_manager_handlers', 'conditions', $conditions);
504
  $defaults = isset($default_handlers) ? $default_handlers : page_manager_get_default_task_handlers($task, $subtask_id);
505
  foreach ($defaults as $name => $default) {
506
    $result = page_manager_compare_task_handlers($handlers, $defaults, $name);
507

    
508
    if ($result) {
509
      $handlers[$name] = $result;
510
      // Ensure task and subtask are correct, because it's easy to change task
511
      // names when editing a default and fail to do it on the associated handlers.
512
      $result->task = $task['name'];
513
      $result->subtask = $subtask_id;
514
    }
515
  }
516

    
517
  // Override weights from the weight table.
518
  if ($handlers) {
519
    $names = array();
520
    $placeholders = array();
521
    foreach ($handlers as $handler) {
522
      $names[] = $handler->name;
523
      $placeholders[] = "'%s'";
524
    }
525

    
526
    $result = db_query('SELECT name, weight FROM {page_manager_weights} WHERE name IN (:names)', array(':names' => $names));
527
    foreach ($result as $weight) {
528
      $handlers[$weight->name]->weight = $weight->weight;
529
    }
530
  }
531

    
532
  return $handlers;
533
}
534

    
535
/**
536
 * Get the default task handlers from a task, if they exist.
537
 *
538
 * Tasks can contain 'default' task handlers which are provided by the
539
 * default task. Because these can come from either the task or the
540
 * subtask, the logic is abstracted to reduce code duplication.
541
 */
542
function page_manager_get_default_task_handlers($task, $subtask_id) {
543
  // Load default handlers that are provied by the task/subtask itself.
544
  $handlers = array();
545
  if ($subtask_id) {
546
    $subtask = page_manager_get_task_subtask($task, $subtask_id);
547
    if (isset($subtask['default handlers'])) {
548
      $handlers = $subtask['default handlers'];
549
    }
550
  }
551
  else if (isset($task['default handlers'])) {
552
    $handlers = $task['default handlers'];
553
  }
554

    
555
  return $handlers;
556
}
557

    
558
/**
559
 * Compare a single task handler from two lists and provide the correct one.
560
 *
561
 * Task handlers can be gotten from multiple sources. As exportable objects,
562
 * they can be provided by default hooks and the database. But also, because
563
 * they are tightly bound to tasks, they can also be provided by default
564
 * tasks. This function reconciles where to pick up a task handler between
565
 * the exportables list and the defaults provided by the task itself.
566
 *
567
 * @param $result
568
 *   A list of handlers provided by export.inc
569
 * @param $handlers
570
 *   A list of handlers provided by the default task.
571
 * @param $name
572
 *   Which handler to compare.
573
 * @return
574
 *   Which handler to use, if any. May be NULL.
575
 */
576
function page_manager_compare_task_handlers($result, $handlers, $name) {
577
  // Compare our special default handler against the actual result, if
578
  // any, and do the right thing.
579
  if (!isset($result[$name]) && isset($handlers[$name])) {
580
    $handlers[$name]->type = t('Default');
581
    $handlers[$name]->export_type = EXPORT_IN_CODE;
582
    return $handlers[$name];
583
  }
584
  else if (isset($result[$name]) && !isset($handlers[$name])) {
585
    return $result[$name];
586
  }
587
  else if (isset($result[$name]) && isset($handlers[$name])) {
588
    if ($result[$name]->export_type & EXPORT_IN_DATABASE) {
589
      $result[$name]->type = t('Overridden');
590
      $result[$name]->export_type = $result[$name]->export_type | EXPORT_IN_CODE;
591
      return $result[$name];
592
    }
593
    else {
594
      // In this case, our default is a higher priority than the standard default.
595
      $handlers[$name]->type = t('Default');
596
      $handlers[$name]->export_type = EXPORT_IN_CODE;
597
      return $handlers[$name];
598
    }
599
  }
600
}
601

    
602
/**
603
 * Load all task handlers for a given task and subtask and sort them.
604
 */
605
function page_manager_load_sorted_handlers($task, $subtask_id = NULL, $enabled = FALSE) {
606
  $handlers = page_manager_load_task_handlers($task, $subtask_id);
607
  if ($enabled) {
608
    foreach ($handlers as $id => $handler) {
609
      if (!empty($handler->disabled)) {
610
        unset($handlers[$id]);
611
      }
612
    }
613
  }
614
  uasort($handlers, 'page_manager_sort_task_handlers');
615
  return $handlers;
616
}
617

    
618
/**
619
 * Callback for uasort to sort task handlers.
620
 *
621
 * Task handlers are sorted by weight then by name.
622
 */
623
function page_manager_sort_task_handlers($a, $b) {
624
  if ($a->weight < $b->weight) {
625
    return -1;
626
  }
627
  elseif ($a->weight > $b->weight) {
628
    return 1;
629
  }
630
  elseif ($a->name < $b->name) {
631
    return -1;
632
  }
633
  elseif ($a->name > $b->name) {
634
    return 1;
635
  }
636

    
637
  return 0;
638
}
639

    
640
/**
641
 * Write a task handler to the database.
642
 */
643
function page_manager_save_task_handler(&$handler) {
644
  $update = (isset($handler->did)) ? array('did') : array();
645
  // Let the task handler respond to saves:
646
  if ($function = ctools_plugin_load_function('page_manager', 'task_handlers', $handler->handler, 'save')) {
647
    $function($handler, $update);
648
  }
649

    
650
  drupal_write_record('page_manager_handlers', $handler, $update);
651
  db_delete('page_manager_weights')
652
    ->condition('name', $handler->name)
653
    ->execute();
654

    
655
  // If this was previously a default handler, we may have to write task handlers.
656
  if (!$update) {
657
    // @todo wtf was I going to do here?
658
  }
659
  return $handler;
660
}
661

    
662
/**
663
 * Remove a task handler.
664
 */
665
function page_manager_delete_task_handler($handler) {
666
  // Let the task handler respond to saves:
667
  if ($function = ctools_plugin_load_function('page_manager', 'task_handlers', $handler->handler, 'delete')) {
668
    $function($handler);
669
  }
670
  db_delete('page_manager_handlers')
671
    ->condition('name', $handler->name)
672
    ->execute();
673
  db_delete('page_manager_weights')
674
    ->condition('name', $handler->name)
675
    ->execute();
676
}
677

    
678
/**
679
 * Export a task handler into code suitable for import or use as a default
680
 * task handler.
681
 */
682
function page_manager_export_task_handler($handler, $indent = '') {
683
  ctools_include('export');
684
  ctools_include('plugins');
685
  $handler = clone $handler;
686

    
687
  $append = '';
688
  if ($function = ctools_plugin_load_function('page_manager', 'task_handlers', $handler->handler, 'export')) {
689
    $append = $function($handler, $indent);
690
  }
691

    
692
  $output = ctools_export_object('page_manager_handlers', $handler, $indent);
693
  $output .= $append;
694

    
695
  return $output;
696
}
697

    
698
/**
699
 * Loads page manager handler for export.
700
 *
701
 * Callback to load page manager handler within ctools_export_crud_load().
702
 *
703
 * @param string $name
704
 *   The name of the handler to load.
705
 *
706
 * @return
707
 *   Loaded page manager handler object, extended with external properties.
708
 */
709
function page_manager_export_task_handler_load($name) {
710
  $table = 'page_manager_handlers';
711
  $schema = ctools_export_get_schema($table);
712
  $export = $schema['export'];
713

    
714
  $result = ctools_export_load_object($table, 'names', array($name));
715
  if (isset($result[$name])) {
716
    $handler = $result[$name];
717

    
718
    // Weight is stored in additional table so that in-code task handlers
719
    // don't need to get written to the database just because they have their
720
    // weight changed. Therefore, handler could have no correspondent database
721
    // entry. Revert will not be performed for this handler and the weight
722
    // will not be reverted. To make possible revert of the weight field
723
    // export_type must simulate that the handler is stored in the database.
724
    $handler->export_type = EXPORT_IN_DATABASE;
725

    
726
    // Also, page manager handler weight should be overriden with correspondent
727
    // weight from page_manager_weights table, if there is one.
728
    $result = db_query('SELECT weight FROM {page_manager_weights} WHERE name = (:names)', array(':names' => $handler->name))->fetchField();
729
    if (is_numeric($result)) {
730
      $handler->weight = $result;
731
    }
732
    return $handler;
733
  }
734
}
735

    
736
/**
737
 * Create a new task handler object.
738
 *
739
 * @param $plugin
740
 *   The plugin this task handler is created from.
741
 */
742
function page_manager_new_task_handler($plugin) {
743
  // Generate a unique name. Unlike most named objects, we don't let people choose
744
  // names for task handlers because they mostly don't make sense.
745

    
746
  // Create a new, empty handler object.
747
  $handler          = new stdClass;
748
  $handler->title   = $plugin['title'];
749
  $handler->task    = NULL;
750
  $handler->subtask = NULL;
751
  $handler->name    = NULL;
752
  $handler->handler = $plugin['name'];
753
  $handler->weight  = 0;
754
  $handler->conf    = array();
755

    
756
  // These are provided by the core export API provided by ctools and we
757
  // set defaults here so that we don't cause notices. Perhaps ctools should
758
  // provide a way to do this for us so we don't have to muck with it.
759
  $handler->export_type = EXPORT_IN_DATABASE;
760
  $handler->type = t('Local');
761

    
762
  if (isset($plugin['default conf'])) {
763
    if (is_array($plugin['default conf'])) {
764
      $handler->conf = $plugin['default conf'];
765
    }
766
    else if (function_exists($plugin['default conf'])) {
767
      $handler->conf = $plugin['default conf']($handler);
768
    }
769
  }
770

    
771
  return $handler;
772
}
773

    
774
/**
775
 * Set an overidden weight for a task handler.
776
 *
777
 * We do this so that in-code task handlers don't need to get written
778
 * to the database just because they have their weight changed.
779
 */
780
function page_manager_update_task_handler_weight($handler, $weight) {
781
  db_delete('page_manager_weights')
782
    ->condition('name', $handler->name)
783
    ->execute();
784
  db_insert('page_manager_weights')
785
    ->fields(array(
786
      'name' => $handler->name,
787
      'weight' => $weight,
788
    ))
789
    ->execute();
790
}
791

    
792

    
793
/**
794
 * Shortcut function to get task plugins.
795
 */
796
function page_manager_get_tasks() {
797
  ctools_include('plugins');
798
  return ctools_get_plugins('page_manager', 'tasks');
799
}
800

    
801
/**
802
 * Shortcut function to get a task plugin.
803
 */
804
function page_manager_get_task($id) {
805
  ctools_include('plugins');
806
  return ctools_get_plugins('page_manager', 'tasks', $id);
807
}
808

    
809
/**
810
 * Get all tasks for a given type.
811
 */
812
function page_manager_get_tasks_by_type($type) {
813
  ctools_include('plugins');
814
  $all_tasks = ctools_get_plugins('page_manager', 'tasks');
815
  $tasks = array();
816
  foreach ($all_tasks as $id => $task) {
817
    if (isset($task['task type']) && $task['task type'] == $type) {
818
      $tasks[$id] = $task;
819
    }
820
  }
821

    
822
  return $tasks;
823
}
824

    
825
/**
826
 * Fetch all subtasks for a page managertask.
827
 *
828
 * @param $task
829
 *   A loaded $task plugin object.
830
 */
831
function page_manager_get_task_subtasks($task) {
832
  if (empty($task['subtasks'])) {
833
    return array();
834
  }
835

    
836
  if ($function = ctools_plugin_get_function($task, 'subtasks callback')) {
837
    $retval = $function($task);
838
    if (is_array($retval)) {
839
      return $retval;
840
    }
841
  }
842

    
843
  return array();
844
}
845

    
846
/**
847
 * Fetch all subtasks for a page managertask.
848
 *
849
 * @param $task
850
 *   A loaded $task plugin object.
851
 * @param $subtask_id
852
 *   The subtask ID to load.
853
 */
854
function page_manager_get_task_subtask($task, $subtask_id) {
855
  if (empty($task['subtasks'])) {
856
    return;
857
  }
858

    
859
  if ($function = ctools_plugin_get_function($task, 'subtask callback')) {
860
    return $function($task, $subtask_id);
861
  }
862
}
863

    
864
/**
865
 * Shortcut function to get task handler plugins.
866
 */
867
function page_manager_get_task_handlers() {
868
  ctools_include('plugins');
869
  return ctools_get_plugins('page_manager', 'task_handlers');
870
}
871

    
872
/**
873
 * Shortcut function to get a task handler plugin.
874
 */
875
function page_manager_get_task_handler($id) {
876
  ctools_include('plugins');
877
  return ctools_get_plugins('page_manager', 'task_handlers', $id);
878
}
879

    
880
/**
881
 * Retrieve a list of all applicable task handlers for a given task.
882
 *
883
 * This looks at the $task['handler type'] and compares that to $task_handler['handler type'].
884
 * If the task has no type, the id of the task is used instead.
885
 */
886
function page_manager_get_task_handler_plugins($task, $all = FALSE) {
887
  $type = isset($task['handler type']) ? $task['handler type'] : $task['name'];
888
  $name = $task['name'];
889

    
890
  $handlers = array();
891
  $task_handlers = page_manager_get_task_handlers();
892
  foreach ($task_handlers as $id => $handler) {
893
    $task_type = is_array($handler['handler type']) ? $handler['handler type'] : array($handler['handler type']);
894
    if (in_array($type, $task_type) || in_array($name, $task_type)) {
895
      if ($all || !empty($handler['visible'])) {
896
        $handlers[$id] = $handler;
897
      }
898
    }
899
  }
900

    
901
  return $handlers;
902
}
903

    
904
/**
905
 * Get the title for a given handler.
906
 *
907
 * If the plugin has no 'admin title' function, the generic title of the
908
 * plugin is used instead.
909
 */
910
function page_manager_get_handler_title($plugin, $handler, $task, $subtask_id) {
911
  $function = ctools_plugin_get_function($plugin, 'admin title');
912
  if ($function) {
913
    return $function($handler, $task, $subtask_id);
914
  }
915
  else {
916
    return $plugin['title'];
917
  }
918
}
919

    
920
/**
921
 * Get the admin summary (additional info) for a given handler.
922
 */
923
function page_manager_get_handler_summary($plugin, $handler, $page, $title = TRUE) {
924
  if ($function = ctools_plugin_get_function($plugin, 'admin summary')) {
925
    return $function($handler, $page->task, $page->subtask, $page, $title);
926
  }
927
}
928

    
929
/**
930
 * Get the admin summary (additional info) for a given page.
931
 */
932
function page_manager_get_page_summary($task, $subtask) {
933
  if ($function = ctools_plugin_get_function($subtask, 'admin summary')) {
934
    return $function($task, $subtask);
935
  }
936
}
937

    
938
/**
939
 * Split a task name into a task id and subtask id, if applicable.
940
 */
941
function page_manager_get_task_id($task_name) {
942
  if (strpos($task_name, '-') !== FALSE) {
943
    return explode('-', $task_name, 2);
944
  }
945
  else {
946
    return array($task_name, NULL);
947
  }
948
}
949

    
950
/**
951
 * Turn a task id + subtask_id into a task name.
952
 */
953
function page_manager_make_task_name($task_id, $subtask_id) {
954
  if ($subtask_id) {
955
    return $task_id . '-' . $subtask_id;
956
  }
957
  else {
958
    return $task_id;
959
  }
960
}
961

    
962
/**
963
 * Get the render function for a handler.
964
 */
965
function page_manager_get_renderer($handler) {
966
  return ctools_plugin_load_function('page_manager', 'task_handlers', $handler->handler, 'render');
967
}
968

    
969
// --------------------------------------------------------------------------
970
// Functions existing on behalf of tasks and task handlers
971

    
972

    
973
/**
974
 * Page manager arg load function because menu system will not load extra
975
 * files for these; they must be in a .module.
976
 */
977
function pm_arg_load($value, $subtask, $argument) {
978
  page_manager_get_task('page');
979
  return _pm_arg_load($value, $subtask, $argument);
980
}
981

    
982
/**
983
 * Special arg_load function to use %menu_tail like functionality to
984
 * get everything after the arg together as a single value.
985
 */
986
function pm_arg_tail_load($value, $subtask, $argument, $map) {
987
  $value = implode('/', array_slice($map, $argument));
988
  page_manager_get_task('page');
989
  return _pm_arg_load($value, $subtask, $argument);
990
}
991

    
992
/**
993
 * Special menu _load() function for the user:uid argument.
994
 *
995
 * This is just the normal page manager argument. It only exists so that
996
 * the to_arg can exist.
997
 */
998
function pm_uid_arg_load($value, $subtask, $argument) {
999
  page_manager_get_task('page');
1000
  return _pm_arg_load($value, $subtask, $argument);
1001
}
1002

    
1003
/**
1004
 * to_arg function for the user:uid argument to provide the arg for the
1005
 * current global user.
1006
 */
1007
function pm_uid_arg_to_arg($arg) {
1008
  return user_uid_optional_to_arg($arg);
1009
}
1010

    
1011
/**
1012
 * Callback for access control ajax form on behalf of page.inc task.
1013
 *
1014
 * Returns the cached access config and contexts used.
1015
 */
1016
function page_manager_page_ctools_access_get($argument) {
1017
  $page = page_manager_get_page_cache($argument);
1018

    
1019
  $contexts = array();
1020

    
1021
  // Load contexts based on argument data:
1022
  if ($arguments = _page_manager_page_get_arguments($page->subtask['subtask'])) {
1023
    $contexts = ctools_context_get_placeholders_from_argument($arguments);
1024
  }
1025

    
1026
  return array($page->subtask['subtask']->access, $contexts);
1027
}
1028

    
1029
/**
1030
 * Callback for access control ajax form on behalf of page.inc task.
1031
 *
1032
 * Writes the changed access to the cache.
1033
 */
1034
function page_manager_page_ctools_access_set($argument, $access) {
1035
  $page = page_manager_get_page_cache($argument);
1036
  $page->subtask['subtask']->access = $access;
1037
  page_manager_set_page_cache($page);
1038
}
1039

    
1040
/**
1041
 * Callback for access control ajax form on behalf of context task handler.
1042
 *
1043
 * Returns the cached access config and contexts used.
1044
 */
1045
function page_manager_task_handler_ctools_access_get($argument) {
1046
  list($task_name, $name) = explode('*', $argument);
1047
  $page = page_manager_get_page_cache($task_name);
1048
  if (empty($name)) {
1049
    $handler = &$page->new_handler;
1050
  }
1051
  else {
1052
    $handler = &$page->handlers[$name];
1053
  }
1054

    
1055
  if (!isset($handler->conf['access'])) {
1056
    $handler->conf['access'] = array();
1057
  }
1058

    
1059
  ctools_include('context-task-handler');
1060

    
1061
  $contexts = ctools_context_handler_get_all_contexts($page->task, $page->subtask, $handler);
1062

    
1063
  return array($handler->conf['access'], $contexts);
1064
}
1065

    
1066
/**
1067
 * Callback for access control ajax form on behalf of context task handler.
1068
 *
1069
 * Writes the changed access to the cache.
1070
 */
1071
function page_manager_task_handler_ctools_access_set($argument, $access) {
1072
  list($task_name, $name) = explode('*', $argument);
1073
  $page = page_manager_get_page_cache($task_name);
1074
  if (empty($name)) {
1075
    $handler = &$page->new_handler;
1076
  }
1077
  else {
1078
    $handler = &$page->handlers[$name];
1079
  }
1080

    
1081
  $handler->conf['access'] = $access;
1082
  page_manager_set_page_cache($page);
1083
}
1084

    
1085
/**
1086
 * Form a URL to edit a given page given the trail.
1087
 */
1088
function page_manager_edit_url($task_name, $trail = array()) {
1089
  if (!is_array($trail)) {
1090
    $trail = array($trail);
1091
  }
1092

    
1093
  if (empty($trail) || $trail == array('summary')) {
1094
    return "admin/structure/pages/edit/$task_name";
1095
  }
1096

    
1097
  return 'admin/structure/pages/nojs/operation/' . $task_name . '/' . implode('/', $trail);
1098
}
1099

    
1100
/**
1101
 * Watch menu links during the menu rebuild, and re-parent things if we need to.
1102
 */
1103
function page_manager_menu_link_alter(&$item) {
1104
  return;
1105
/** -- disabled, concept code --
1106
  static $mlids = array();
1107
  // Keep an array of mlids as links are saved that we can use later.
1108
  if (isset($item['mlid'])) {
1109
    $mlids[$item['path']] = $item['mlid'];
1110
  }
1111

    
1112
  if (isset($item['parent_path'])) {
1113
    if (isset($mlids[$item['parent_path']])) {
1114
      $item['plid'] = $mlids[$item['parent_path']];
1115
    }
1116
    else {
1117
      // Since we didn't already see an mlid, let's check the database for one.
1118
      $mlid = db_query('SELECT mlid FROM {menu_links} WHERE router_path = :path', array('path' => $item['parent_path']))->fetchField();
1119
      if ($mlid) {
1120
        $item['plid'] = $mlid;
1121
      }
1122
    }
1123
  }
1124
 */
1125
}
1126

    
1127
/**
1128
 * Callback to list handlers available for export.
1129
 */
1130
function page_manager_page_manager_handlers_list() {
1131
  $list = $types = array();
1132
  $tasks = page_manager_get_tasks();
1133
  foreach ($tasks as $type => $info) {
1134
    if (empty($info['non-exportable'])) {
1135
      $types[] = $type;
1136
    }
1137
  }
1138

    
1139
  $handlers = ctools_export_load_object('page_manager_handlers');
1140
  foreach ($handlers as $handler) {
1141
    if (in_array($handler->task, $types)) {
1142
      $plugin = page_manager_get_task_handler($handler->handler);
1143
      $title = page_manager_get_handler_title($plugin, $handler, $tasks[$handler->task], $handler->subtask);
1144

    
1145
      if ($title) {
1146
        $list[$handler->name] = check_plain("$handler->task: $title ($handler->name)");
1147
      }
1148
      else {
1149
        $list[$handler->name] = check_plain("$handler->task: ($handler->name)");
1150
      }
1151
    }
1152
  }
1153
  return $list;
1154
}
1155

    
1156
/**
1157
 * Callback to bulk export page manager pages.
1158
 */
1159
function page_manager_page_manager_pages_to_hook_code($names = array(), $name = 'foo') {
1160
  $schema = ctools_export_get_schema('page_manager_pages');
1161
  $export = $schema['export'];
1162
  $objects = ctools_export_load_object('page_manager_pages', 'names', array_values($names));
1163
  if ($objects) {
1164
    $code = "/**\n";
1165
    $code .= " * Implements hook_{$export['default hook']}()\n";
1166
    $code .= " */\n";
1167
    $code .= "function " . $name . "_{$export['default hook']}() {\n";
1168
    foreach ($objects as $object) {
1169
      // Have to implement our own because this export func sig requires it
1170
      $code .= $export['export callback']($object, TRUE, '  ');
1171
      $code .= "  \${$export['identifier']}s['" . check_plain($object->$export['key']) . "'] = \${$export['identifier']};\n\n";
1172
    }
1173
    $code .= "  return \${$export['identifier']}s;\n";
1174
    $code .= "}\n";
1175
    return $code;
1176
  }
1177
}
1178

    
1179
/**
1180
 * Get the current page information.
1181
 *
1182
 * @return $page
1183
 *   An array containing the following information.
1184
 *
1185
 * - 'name': The name of the page as used in the page manager admin UI.
1186
 * - 'task': The plugin for the task in use. If this is a system page it
1187
 *   will contain information about that page, such as what functions
1188
 *   it uses.
1189
 * - 'subtask': The plugin for the subtask. If this is a custom page, this
1190
 *   will contain information about that custom page. See 'subtask' in this
1191
 *   array to get the actual page object.
1192
 * - 'handler': The actual handler object used. If using panels, see
1193
 *   $page['handler']->conf['display'] for the actual panels display
1194
 *   used to render.
1195
 * - 'contexts': The context objects used to render this page.
1196
 * - 'arguments': The raw arguments from the URL used on this page.
1197
 */
1198
function page_manager_get_current_page($page = NULL) {
1199
  static $current = array();
1200
  if (isset($page)) {
1201
    $current = $page;
1202
  }
1203

    
1204
  return $current;
1205
}
1206

    
1207
/**
1208
 * Implementation of hook_panels_dashboard_blocks().
1209
 *
1210
 * Adds page information to the Panels dashboard.
1211
 */
1212
function page_manager_panels_dashboard_blocks(&$vars) {
1213
  $vars['links']['page_manager'] = array(
1214
    'weight' => -100,
1215
    'title' => l(t('Panel page'), 'admin/structure/pages/add'),
1216
    'description' => t('Panel pages can be used as landing pages. They have a URL path, accept arguments and can have menu entries.'),
1217
  );
1218

    
1219
  module_load_include('inc', 'page_manager', 'page_manager.admin');
1220
  $tasks = page_manager_get_tasks_by_type('page');
1221
  $pages = array('operations' => array());
1222

    
1223
  page_manager_get_pages($tasks, $pages);
1224
  $count = 0;
1225
  $rows = array();
1226
  foreach ($pages['rows'] as $id => $info) {
1227
    $rows[] = array(
1228
      'data' => array(
1229
        $info['data']['title'],
1230
        $info['data']['operations'],
1231
      ),
1232
      'class' => $info['class'],
1233
    );
1234

    
1235
    // Only show 10.
1236
    if (++$count >= 10) {
1237
      break;
1238
    }
1239
  }
1240

    
1241
  $vars['blocks']['page_manager'] = array(
1242
    'weight' => -100,
1243
    'title' => t('Manage pages'),
1244
    'link' => l(t('Go to list'), 'admin/structure/pages'),
1245
    'content' => theme('table', array('header' => array(), 'rows' => $rows, 'attributes' => array('class' => 'panels-manage'))),
1246
    'class' => 'dashboard-pages',
1247
    'section' => 'right',
1248
  );
1249
}
1250

    
1251
/**
1252
 * Implement pseudo-hook to fetch addressable content.
1253
 *
1254
 * For Page Manager, the address will be an array. The first
1255
 * element will be the $task and the second element will be the
1256
 * $task_handler. The third elements will be the arguments
1257
 * provided.
1258
 */
1259
function page_manager_addressable_content($address, $type) {
1260
  if (count($address) < 3) {
1261
    return;
1262
  }
1263

    
1264
  $task_name = array_shift($address);
1265
  $subtask_name = array_shift($address);
1266
  $handler_name = array_shift($address);
1267
  if ($address) {
1268
    $arguments = array_shift($address);
1269
  }
1270

    
1271
  // Since $arguments is an array of arbitrary size, we need to implode it:
1272
  if (!empty($arguments)) {
1273
    // The only choices we have for separators since :: is already
1274
    // used involve ., - or _. Since - and _ are more common than .
1275
    // in URLs, let's try .. as an argument separator.
1276
    $arguments = explode('..', $arguments);
1277
  }
1278
  else {
1279
    // implode does not return an empty array on an empty
1280
    // string so do it specifically.
1281
    $arguments = array();
1282
  }
1283

    
1284
  $task = page_manager_get_task($task_name);
1285
  if (!$task) {
1286
    return;
1287
  }
1288

    
1289
  $handler = page_manager_load_task_handler($task, $subtask_name, $handler_name);
1290
  if (!$handler) {
1291
    return;
1292
  }
1293

    
1294
  $handler_plugin = page_manager_get_task_handler($handler->handler);
1295
  if (!$handler_plugin) {
1296
    return;
1297
  }
1298

    
1299
  // Load the contexts for the task.
1300
  ctools_include('context');
1301
  ctools_include('context-task-handler');
1302
  $contexts = ctools_context_handler_get_task_contexts($task, $subtask_name, $arguments);
1303

    
1304
  // With contexts loaded, ensure the task is accessible. Tasks without a callback
1305
  // are automatically accessible.
1306
  $function = ctools_plugin_get_function($task, 'access callback');
1307
  if ($function && !$function($task, $subtask_name, $contexts)) {
1308
    return;
1309
  }
1310

    
1311
  $function = ctools_plugin_get_function($handler_plugin, 'addressable callback');
1312
  if ($function) {
1313
    return $function($task, $subtask_name, $handler, $address, $contexts, $arguments, $type);
1314
  }
1315
}