summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEarl Miles2009-07-09 00:07:05 (GMT)
committer Earl Miles2009-07-09 00:07:05 (GMT)
commit5e8dada09be08f1b446b1c82930a3484637d9038 (patch)
treed44da547560a1ee05490cefc78b5afd28c5e597e
parente6d992411cb5c0f375f723dd1fc0627810af9647 (diff)
Remove the delegator module, introduce the page manager module. Anything that previously relied on delegator module must now change its code to rely on page manager instead!
-rw-r--r--ctools.module15
-rw-r--r--delegator/css/page-task.css22
-rw-r--r--delegator/css/task-handlers.css84
-rw-r--r--delegator/delegator.admin.inc1369
-rw-r--r--delegator/delegator.info5
-rw-r--r--delegator/delegator.module888
-rw-r--r--delegator/images/configure.pngbin765 -> 0 bytes
-rw-r--r--delegator/images/delete.pngbin877 -> 0 bytes
-rw-r--r--delegator/js/task-handlers.js87
-rw-r--r--delegator/plugins/task_types/page.admin.inc120
-rw-r--r--delegator/plugins/task_types/page.inc58
-rw-r--r--delegator/plugins/tasks/page.admin.inc1622
-rw-r--r--delegator/plugins/tasks/page.inc513
-rw-r--r--includes/ajax.inc2
-rw-r--r--includes/context-task-handler.inc62
-rw-r--r--includes/export.inc36
-rw-r--r--includes/wizard.inc176
-rw-r--r--js/ajax-responder.js6
-rw-r--r--page_manager/css/page-manager.css245
-rw-r--r--page_manager/help/api-task-handler.html44
-rw-r--r--page_manager/help/api-task-type.html3
-rw-r--r--page_manager/help/api-task.html39
-rw-r--r--page_manager/help/page-task-type.html4
-rw-r--r--page_manager/images/arrow-active.pngbin0 -> 313 bytes
-rw-r--r--page_manager/images/locked.pngbin0 -> 273 bytes
-rw-r--r--page_manager/page_manager.admin.inc1440
-rw-r--r--page_manager/page_manager.info5
-rw-r--r--page_manager/page_manager.install217
-rw-r--r--page_manager/page_manager.module884
-rw-r--r--page_manager/plugins/tasks/node_edit.inc (renamed from delegator/plugins/tasks/node_edit.inc)48
-rw-r--r--page_manager/plugins/tasks/node_view.inc (renamed from delegator/plugins/tasks/node_view.inc)48
-rw-r--r--page_manager/plugins/tasks/page.admin.inc1439
-rw-r--r--page_manager/plugins/tasks/page.inc540
-rw-r--r--page_manager/plugins/tasks/term_view.inc (renamed from delegator/plugins/tasks/term_view.inc)91
-rw-r--r--page_manager/plugins/tasks/user_view.inc (renamed from delegator/plugins/tasks/user_view.inc)42
-rw-r--r--page_manager/theme/page-manager-edit-page.tpl.php49
-rw-r--r--page_manager/theme/page_manager.theme.inc137
37 files changed, 5330 insertions, 5010 deletions
diff --git a/ctools.module b/ctools.module
index c9649fb..4a8a6cf 100644
--- a/ctools.module
+++ b/ctools.module
@@ -345,3 +345,18 @@ function ctools_flush_caches() {
ctools_include('css');
ctools_css_flush_caches();
}
+
+/**
+ * Check to see if the incoming menu item is js capable or not.
+ *
+ * This can be used as %ctools_js as part of a path in hook menu. CTools
+ * ajax functions will automatically change the phrase 'nojs' to 'ajax'
+ * when it attaches ajax to a link. This can be used to autodetect if
+ * that happened.
+ */
+function ctools_js_load($js) {
+ if ($js == 'ajax') {
+ return TRUE;
+ }
+ return 0;
+}
diff --git a/delegator/css/page-task.css b/delegator/css/page-task.css
deleted file mode 100644
index e36d4f3..0000000
--- a/delegator/css/page-task.css
+++ /dev/null
@@ -1,22 +0,0 @@
-/* $Id$ */
-
-.delegator-page-operations .ctools-dropdown-link-wrapper,
-th.delegator-page-operations {
- text-align: right;
-}
-
-td.delegator-page-operations {
- width: 175px;
-}
-
-.delegator-disabled {
- color: #ccc;
-}
-
-.delegator-page-title {
- width: 20%;
-}
-
-.delegator-page-path {
- width: 20%;
-}
diff --git a/delegator/css/task-handlers.css b/delegator/css/task-handlers.css
deleted file mode 100644
index f90eed9..0000000
--- a/delegator/css/task-handlers.css
+++ /dev/null
@@ -1,84 +0,0 @@
-/* $Id$ */
-
-.delegator-operations {
- text-align: right;
-}
-
-.delegator-breadcrumb {
- font-size: 120%;
-}
-
-.delegator-current {
- font-weight: bold;
-}
-
-.delegator-locked {
- color: red;
- border: 1px solid red;
- padding: 1em;
-}
-
-.delegator-owns-lock {
- background: #FFFFDD none repeat scroll 0 0;
- border: 1px solid #F0C020;
- padding: 1em;
-}
-
-.delegator-operations div {
- display: inline;
-}
-
-.delegator-disabled {
- color: #ccc;
-}
-
-.delegator-operations select {
- width: 10em;
-}
-
-.delegator-changed-col {
- width: .1em;
- white-space: nowrap;
- margin: 0;
-}
-
-.delegator-changed-col .tabledrag-changed {
- position: relative;
- top: .2em;
- font-size: 150%;
-}
-
-#delegator-task-list-arrange a.tabledrag-handle {
- padding-right: .75em;
-}
-
-#delegator-task-list-arrange td {
- vertical-align: top;
-}
-
-#delegator-task-list-arrange tr.delegator-collapsible td {
- padding-left: 4em;
-}
-
-#delegator-task-list-arrange tr.draggable .ctools-collapsible-content {
- display: none;
-}
-
-/*
-#delegator-task-list-arrange tr.delegator-collapsible div.ctools-collapsible-content {
- display: block;
-}
-*/
-
-html.js .delegator-operations {
- width: 170px;
-}
-
-div.delegator-task-handler-operations {
- display: none;
-}
-
-html.js div.delegator-task-handler-operations {
- display: block;
-}
-
diff --git a/delegator/delegator.admin.inc b/delegator/delegator.admin.inc
index af0536d..e69de29 100644
--- a/delegator/delegator.admin.inc
+++ b/delegator/delegator.admin.inc
@@ -1,1369 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Administrative pages for delegator module.
- */
-
-/**
- * Bit flag on the 'changed' value to tell us if an item was moved.
- */
-define('DGA_CHANGED_MOVED', 0x01);
-
-/**
- * Bit flag on the 'changed' value to tell us if an item edited or added.
- */
-define('DGA_CHANGED_CACHED', 0x02);
-
-/**
- * Bit flag on the 'changed' value to tell us if an item edited or added.
- */
-define('DGA_CHANGED_DELETED', 0x04);
-
-/**
- * Bit flag on the 'changed' value to tell us if an item has had its disabled status changed.
- */
-define('DGA_CHANGED_STATUS', 0x08);
-
-/**
- * Get the cached changes to a group of task handlers for
- * a given task.
- *
- * This cache stores the current weights for each task, and
- * it also stores a record of whether or not each task handler
- * has been changed so that we can update the display when
- * it is drawn.
- */
-function delegator_admin_get_task_cache($task, $subtask_id, $task_handlers = NULL) {
- $key = delegator_make_task_name($task['name'], $subtask_id);
- ctools_include('object-cache');
- $cache = ctools_object_cache_get('delegator_handlers', $key);
-
- if (!$cache) {
- // If no cache found, create one. We don't write this, though,
- // because we only want to create this object when something
- // actually changes.
- if (!isset($task_handlers)) {
- $task_handlers = delegator_load_task_handlers($task, $subtask_id);
- }
-
- $cache = new stdClass;
- $cache->handlers = array();
- foreach ($task_handlers as $id => $handler) {
- $cache->handlers[$id]['name'] = $id;
- $cache->handlers[$id]['weight'] = $handler->weight;
- $cache->handlers[$id]['changed'] = FALSE;
- $cache->handlers[$id]['disabled'] = !empty($handler->disabled);
- }
- $cache->locked = ctools_object_cache_test('delegator_handlers', $key);
- }
-
- // Sort the new cache.
- uasort($cache->handlers, '_delegator_admin_task_cache_sort');
-
- return $cache;
-}
-
-/**
- * Store information about task handlers in the object cache.
- *
- * This object should have already been retrieved or created by
- * delegator_admin_get_task_cache().
- */
-function delegator_admin_set_task_cache($task, $subtask_id, $cache) {
- if ($cache->locked) {
- drupal_set_message(t('Unable to update task due to lock.'), 'error');
- return;
- }
-
- // We only bother if something has been marked changed. This keeps us from
- // locking when we should not.
- $changed = FALSE;
- if (!empty($cache->working)) {
- $changed = TRUE;
- }
- else {
- foreach ($cache->handlers as $handler) {
- if (!empty($handler['changed'])) {
- $changed = TRUE;
- break;
- }
- }
- }
-
- if (!$changed) {
- // We may have cancelled a working copy. We'll actually clear cache in this
- // instance.
- delegator_admin_clear_task_cache($task, $subtask_id);
- return;
- }
-
- // First, sort the cache object.
- uasort($cache->handlers, '_delegator_admin_task_cache_sort');
-
- // Then write it.
- $key = delegator_make_task_name($task['name'], $subtask_id);
- ctools_include('object-cache');
-
- $cache->changed = TRUE;
- $cache = ctools_object_cache_set('delegator_handlers', $key, $cache);
-}
-
-/**
- * Reset information about the task handlers for a given task.
- */
-function delegator_admin_clear_task_cache($task, $subtask_id) {
- ctools_include('object-cache');
- $key = delegator_make_task_name($task['name'], $subtask_id);
- ctools_object_cache_clear('delegator_handlers', $key);
-}
-
-/**
- * Get the cached changes to a given task handler.
- */
-function delegator_admin_get_task_handler_cache($name) {
- ctools_include('object-cache');
- return ctools_object_cache_get('delegator_task_handler', $name);
-}
-
-/**
- * Store changes to a task handler in the object cache.
- */
-function delegator_admin_set_task_handler_cache($handler, $working = FALSE) {
- $name = $handler->name;
-
- if ($working) {
- $name = delegator_make_task_name($handler->task, $handler->subtask) . '-working';
- }
-
- ctools_include('object-cache');
- $cache = ctools_object_cache_set('delegator_task_handler', $name, $handler);
-}
-
-/**
- * Remove an item from the object cache.
- */
-function delegator_admin_clear_task_handler_cache($name) {
- ctools_include('object-cache');
- ctools_object_cache_clear('delegator_task_handler', $name);
-}
-
-/**
- * Write a new handler to the database.
- */
-function delegator_admin_new_task_handler($handler, $task_name, $task, $subtask_id, $cache, $plugin) {
- // Store the new handler.
- if (!$cache->locked) {
- delegator_admin_set_task_handler_cache($handler, TRUE);
- }
-
- $cache->handlers[$handler->name] = array(
- 'name' => $handler->name,
- 'weight' => $handler->weight,
- 'changed' => DGA_CHANGED_CACHED,
- 'disabled' => FALSE,
- );
- $cache->working = $handler->name;
-
- // Store the changed task handler list.
- delegator_admin_set_task_cache($task, $subtask_id, $cache);
-
- // If the task handler plugin specifies an add form, set a redirect.
- if (isset($plugin['add forms'])) {
- // Get the beginning of the array.
- reset($plugin['add forms']);
- list($id, $title) = each($plugin['add forms']);
- return "admin/build/delegator/$task_name/add/$handler->name/$id";
- }
- else {
- return "admin/build/delegator/$task_name";
- }
-}
-
-/**
- * Used as a callback to uasort to sort the task cache by weight.
- *
- * The 'name' field is used as a backup when weights are the same, which
- * can happen when multiple modules put items out there at the same
- * weight.
- */
-function _delegator_admin_task_cache_sort($a, $b) {
- if ($a['weight'] < $b['weight']) {
- return -1;
- }
- elseif ($a['weight'] > $b['weight']) {
- return 1;
- }
- elseif ($a['name'] < $b['name']) {
- return -1;
- }
- elseif ($a['name'] > $b['name']) {
- return 1;
- }
-
- return 0;
-}
-
-/**
- * Find the right handler to use for an id during the edit process.
- *
- * When editing, a handler may be stored in cache. It may also be
- * reverted and unsaved, which can cause issues all their own. This
- * function can be used to find the right handler to use in these cases.
- */
-function delegator_admin_find_handler($id, $cache, $task_name, $task_handlers = array()) {
- // Use the one from the database or an updated one in cache?
- if (isset($cache->working) && $cache->working == $id) {
- $handler = delegator_admin_get_task_handler_cache($task_name . '-working');
- }
- else if ($cache->handlers[$id]['changed'] & DGA_CHANGED_CACHED) {
- $handler = delegator_admin_get_task_handler_cache($id);
- }
- else {
- // Special case: Reverted handlers get their defaults back.
- if ($cache->handlers[$id]['changed'] & DGA_CHANGED_DELETED) {
- ctools_include('export');
- $handler = ctools_get_default_object('delegator_handlers', $id);
- }
- else if (!empty($task_handlers)) {
- $handler = $task_handlers[$id];
- }
- else {
- list($task_id, $subtask_id) = delegator_get_task_id($task_name);
- $task = delegator_get_task($task_id);
- $handler = delegator_load_task_handler($task, $subtask_id, $id);
- }
- }
-
- return $handler;
-}
-
-/**
- * Page callback to administer a particular task.
- */
-function delegator_administer_task($task_name) {
- list($task_id, $subtask_id) = delegator_get_task_id($task_name);
-
- $task = delegator_get_task($task_id);
- if (!$task) {
- return drupal_not_found();
- }
-
- $subtask = delegator_get_task_subtask($task, $subtask_id);
-
- // Subtasks marked as single tasks won't see this page, so redirect
- // to wherever the subtask wants to be administered from.
- if (!empty($subtask['single task'])) {
- $task_type = delegator_get_task_type($task['task type']);
- return drupal_goto($task_type['admin path']);
- }
-
- $task_handlers = delegator_load_task_handlers($task, $subtask_id);
- delegator_set_trail($task);
-
- $form_state = array(
- 'task_name' => $task_name,
- 'task_id' => $task_id,
- 'task' => $task,
- 'subtask' => $subtask,
- 'subtask_id' => $subtask_id,
- 'task_handlers' => $task_handlers,
- 'cache' => delegator_admin_get_task_cache($task, $subtask_id, $task_handlers),
- );
-
- ctools_include('form');
- return ctools_build_form('delegator_admin_list_form', $form_state);
-}
-
-/**
- * Form to administer task handlers assigned to a task.
- */
-function delegator_admin_list_form(&$form_state) {
- $task = &$form_state['task'];
- $subtask = &$form_state['subtask'];
- $task_handlers = &$form_state['task_handlers'];
- $cache = &$form_state['cache'];
-
- // Get a list of possible task handlers for this task.
- $task_handler_plugins = delegator_get_task_handler_plugins($task);
-
- if (isset($task['description'])) {
- $form['description'] = array(
- '#prefix' => '<div class="description">',
- '#value' => $task['description'],
- '#suffix' => '</div>',
- );
- }
-
- if ($subtask && isset($subtask['description'])) {
- $form['subtask_description'] = array(
- '#prefix' => '<div class="description">',
- '#value' => $subtask['description'],
- '#suffix' => '</div>',
- );
- }
-
- $options = array('' => t('Choose'));
- foreach ($task_handler_plugins as $id => $plugin) {
- $options[$id] = $plugin['title'];
- }
-
- $form['handlers'] = array('#tree' => TRUE);
- $form['#changed'] = FALSE;
-
- // Create data for a table for all of the task handlers.
- foreach ($cache->handlers as $id => $info) {
- // Skip deleted items.
- $handler = delegator_admin_find_handler($id, $form_state['cache'], $form_state['task_name'], $task_handlers);
-
- if (!$handler || ($info['changed'] & DGA_CHANGED_DELETED && !($handler->export_type & EXPORT_IN_CODE))) {
- $form['#changed'] = TRUE;
- continue;
- }
-
- if ($info['changed']) {
- $form['#changed'] = TRUE;
- }
-
- if (isset($task_handler_plugins[$handler->handler])) {
- $plugin = $task_handler_plugins[$handler->handler];
-
- $title = delegator_get_handler_title($plugin, $handler, $task, $form_state['subtask_id']);
- $summary = delegator_get_handler_summary($plugin, $handler, $task, $form_state['subtask_id']);
- }
- else {
- $title = t('Broken/missing handler plugin "@plugin"', array('@plugin' => $handler->handler));
- $summary = '';
- }
-
- $form['handlers'][$id]['title'] = array(
- '#value' => $title,
- );
-
- $form['handlers'][$id]['summary'] = array(
- '#value' => $summary,
- );
-
- $form['handlers'][$id]['weight'] = array(
- '#type' => 'weight',
- '#default_value' => $info['weight'],
- );
-
- $form['handlers'][$id]['changed'] = array(
- '#type' => 'value',
- '#value' => $info['changed'],
- );
-
- // Make a list of possible actions.
- $actions = array(
- '' => t('Actions...'),
- );
-
- if ($info['disabled']) {
- $actions['enable'] = t('Enable');
- }
- // For enabled handlers.
- else {
- // Make all of the edit items under the Edit optgroup.
- if (!empty($plugin['edit forms'])) {
- foreach ($plugin['edit forms'] as $edit_id => $title) {
- if ($title) {
- $actions[t('Edit')]['edit-' . $edit_id] = $title;
- }
- }
- }
-
- if (isset($plugin)) {
- $actions['clone'] = t('Clone');
- $actions['export'] = t('Export');
- }
-
- if ($handler->export_type == (EXPORT_IN_CODE | EXPORT_IN_DATABASE)) {
- $actions['delete'] = t('Revert');
- }
- else if ($handler->export_type == EXPORT_IN_CODE) {
- $actions['disable'] = t('Disable');
- }
- else {
- $actions['delete'] = t('Delete');
- }
- }
-
- $form['handlers'][$id]['dropdown'] = array(
- '#value' => theme('ctools_dropdown', t('Operations'), delegator_admin_make_dropdown_links($actions), 'delegator-task-handler-operations'),
- );
-
- $form['handlers'][$id]['action'] = array(
- '#type' => 'select',
- '#options' => $actions,
- );
-
- $form['handlers'][$id]['config'] = array(
- // image buttons must not have a #value or they will not be properly detected.
- '#type' => 'image_button',
- '#src' => drupal_get_path('module', 'delegator') . '/images/configure.png',
- '#handler' => $id, // so the submit handler can tell which one this is
- '#submit' => array('delegator_admin_list_form_action'),
- );
-
- $type = $handler->type;
-
- // Adjust type for this scenario: They have reverted a handler to the in code
- // version and have not modified it again.
- if ($type == t('Overridden') && $info['changed'] &= DGA_CHANGED_DELETED && !($info['changed'] &= DGA_CHANGED_CACHED)) {
- $type = t('Default');
- }
-
- $class = 'draggable';
- if ($type == t('Overridden')) {
- $class .= ' delegator-overridden';
- }
- else if ($type == t('Default')) {
- $class .= ' delegator-default';
- if ($info['disabled']) {
- $class .= ' delegator-disabled';
- }
- }
-
- $form['handlers'][$id]['class'] = array(
- '#value' => $class,
- );
-
- if ($info['disabled']) {
- $type .= ', ' . t('Disabled');
- }
-
- $form['handlers'][$id]['type'] = array(
- '#value' => $type,
- );
-
- // This sets the tabledrag last dragged class so that the most recently
- // touched row will show up yellow. This is a nice reminder after adding
- // or editing a row which one was touched.
- if (isset($cache->last_touched) && $cache->last_touched == $handler->name) {
- $form['handlers'][$id]['class']['#value'] .= ' delegator-changed';
- }
- }
-
- $form['handler'] = array(
- '#type' => 'select',
- '#options' => $options,
- '#default_value' => '',
- );
- $form['add_handler'] = array(
- '#type' => 'submit',
- '#value' => t('Add new handler'),
- '#validate' => array('delegator_admin_list_form_validate_add'),
- '#submit' => array('delegator_admin_list_form_add'),
- );
-
- $form['import_handler'] = array(
- '#type' => 'submit',
- '#value' => t('Import task handler'),
- '#submit' => array('delegator_admin_list_form_import'),
- );
-
- $form['save'] = array(
- '#type' => 'submit',
- '#value' => t('Save'),
- '#submit' => array('delegator_admin_list_form_submit'),
- );
- $form['cancel'] = array(
- '#type' => 'submit',
- '#value' => t('Cancel'),
- '#submit' => array('delegator_admin_list_form_cancel'),
- );
-
- if (!empty($cache->locked)) {
- $form['warning'] = array('#value' => theme('delegator_admin_lock', $cache->locked, $form_state['task_name']));
- }
- else if (!empty($cache->changed)) {
- $form['warning'] = array('#value' => theme('delegator_admin_changed'));
- }
-
- // Set up a list of callbacks for our actions. This method allows
- // clever form_alter uses to add more actions easily.
-
- // Bear in mind that any action will be split on a '-' so don't use it
- // in your name. This is how 'edit' can edit multiple forms, i.e,
- // edit-settings, edit-context, edit-foobarbaz.
- $form['#actions'] = array(
- 'edit' => 'delegator_admin_list_form_action_edit',
- 'delete' => 'delegator_admin_list_form_action_delete',
- 'enable' => 'delegator_admin_list_form_action_enable',
- 'disable' => 'delegator_admin_list_form_action_disable',
- 'clone' => 'delegator_admin_list_form_action_clone',
- 'export' => 'delegator_admin_list_form_action_export',
- );
-
- if ($function = ctools_plugin_get_function($task, 'task admin')) {
- $function($form, $form_state);
- }
-
- return $form;
-}
-
-/**
- * Make a set of links out of the actions array.
- *
- * because this can have embedded arrays, this is a function so it can
- * use recursion.
- */
-function delegator_admin_make_dropdown_links($actions) {
- $links = array();
- // Take the actions and make a dropdown for those of us with javascript.
- foreach ($actions as $id => $text) {
- if (!$id) {
- continue;
- }
-
- if (is_array($text)) {
- $links[] = array(
- 'title' => '<span class="text">' . $id . '</span>' . theme('links', delegator_admin_make_dropdown_links($text)),
- 'html' => TRUE,
- );
- }
- else {
- $links[] = array(
- 'title' => $text,
- 'href' => $id,
- );
- }
- }
-
- return $links;
-}
-
-/**
- * Draw the "this task is locked from editing" box.
- */
-function theme_delegator_admin_lock($lock, $task_name) {
- $account = user_load($lock->uid);
- $name = theme('username', $account);
- $lock_age = format_interval(time() - $lock->updated);
- $break = url('admin/build/delegator/' . $task_name . '/break-lock', array('query' => array('destination' => $_GET['q'], 'cancel' => $_GET['q'])));
-
- ctools_add_css('ctools');
- $output = '<div class="ctools-locked">';
- $output .= t('This task handler is being edited by user !user, and is therefore locked from editing by others. This lock is !age old. Click here to <a href="!break">break this lock</a>.', array('!user' => $name, '!age' => $lock_age, '!break' => $break));
- $output .= '</div>';
- return $output;
-}
-
-/**
- * Draw the "you have unsaved changes and this task is locked." message.
- */
-function theme_delegator_admin_changed() {
- ctools_add_css('ctools');
- $output = '<div class="ctools-owns-lock">';
- $output .= t('You have modified this task handler but the changes have not yet been permanently saved. It will be locked for editing by others until you save or cancel these changes.');
- $output .= '</div>';
-
- return $output;
-}
-
-/**
- * Theme the form so it has a table.
- */
-function theme_delegator_admin_list_form($form) {
- $output = '';
-
- if (isset($form['warning'])) {
- $output .= drupal_render($form['warning']);
- }
- if (isset($form['description'])) {
- $output .= drupal_render($form['description']);
- }
- if (isset($form['subtask_description'])) {
- $output .= drupal_render($form['subtask_description']);
- }
-
- // Assemble the data for a table from everything in $form['handlers']
- foreach (element_children($form['handlers']) as $id) {
- // provide a reference shortcut.
- $element = &$form['handlers'][$id];
- if (isset($element['title'])) {
- $row = array();
-
- $changed_text = '';
- // Add a visible 'changed' flag if necessary.
- if ($element['changed']['#value']) {
- $changed_text = '<span class="warning tabledrag-changed">*</span>';
- }
-
- $row[] = array(
- 'data' => $changed_text,
- 'class' => 'delegator-changed-col',
- );
-
- if ($summary = drupal_render($element['summary'])) {
- $title = theme('ctools_collapsible', drupal_render($element['title']), $summary, TRUE);
- }
- else {
- $title = drupal_render($element['title']);
- }
-
- $row[] = array(
- 'data' => $title,
- 'class' => 'delegator-handler',
- );
-
- $row[] = array(
- 'data' => drupal_render($element['type']),
- 'class' => 'delegator-type',
- );
-
- $element['weight']['#attributes']['class'] = 'weight';
- $row[] = drupal_render($element['weight']);
-
- $operations = '';
- $operations .= drupal_render($element['dropdown']);
- $operations .= drupal_render($element['action']);
- $operations .= drupal_render($element['config']);
- $row[] = array(
- 'data' => $operations,
- 'class' => 'delegator-operations',
- );
-
- $class = drupal_render($element['class']);
-
- $rows[] = array('data' => $row, 'class' => $class, 'id' => 'delegator-row-' . $id);
- }
- }
-
- if (empty($rows)) {
- $rows[] = array(array('data' => t('No task handlers are defined for this task.'), 'colspan' => '5'));
- }
-
- $header = array(
- array('data' => '', 'class' => 'delegator-changed-col'),
- array('data' => t('Task handler'), 'class' => 'delegator-handler'),
- array('data' => t('Type'), 'class' => 'delegator-type'),
- t('Weight'),
- array('data' => t('Operations'), 'class' => 'delegator-operations'),
- );
-
- drupal_add_tabledrag('delegator-task-list-arrange', 'order', 'sibling', 'weight');
-
- $attributes = array('id' => 'delegator-task-list-arrange');
- if ($form['#changed']) {
- $attributes['class'] = 'changed';
- }
-
- $output .= theme('table', $header, $rows, $attributes);
-
- // Render the add button + select box as a table too.
- $left = '<div class="container-inline">' . drupal_render($form['handler']) . drupal_render($form['add_handler']) . '</div>';
- $output .= theme('table', array(), array(array($left, drupal_render($form['import_handler']))));
-
- $path = drupal_get_path('module', 'delegator');
- drupal_add_js("$path/js/task-handlers.js");
- drupal_add_css("$path/css/task-handlers.css");
-
- $output .= drupal_render($form);
- return $output;
-}
-
-/**
- * Don't let them submit the 'please choose' button.
- */
-function delegator_admin_list_form_validate_add($form, &$form_state) {
- if (!$form_state['values']['handler']) {
- form_error($form['handler'], t('Please choose a task handler to add.'));
- }
-}
-
-/**
- * Update the weights from the form.
- *
- * Since we're looping, we determine the highest weight + 1 and return that.
- */
-function delegator_admin_update_weights(&$form_state) {
- // Go through our cache and check weights.
- $handlers = &$form_state['cache']->handlers;
- foreach ($handlers as $id => $info) {
- // update weights from form.
- if (isset($form_state['values']['handlers'][$id]['weight']) && $form_state['values']['handlers'][$id]['weight'] != $info['weight']) {
- $handlers[$id]['weight'] = $info['weight'] = $form_state['values']['handlers'][$id]['weight'];
- $handlers[$id]['changed'] |= DGA_CHANGED_MOVED;
- }
-
- // Record the highest weight we've seen so we know what to set our addition.
- if (!isset($weight) || $info['weight'] >= $weight) {
- $weight = $info['weight'] + 1;
- }
-
- // Unset any 'last touched' flag and let whatever handler is updating the
- // weights do that if it wants to.
- unset($form_state['cache']->last_touched);
- }
-
- // if weight stubbornly continues to not be set (meaning the cache was empty)
- // make it 0.
- if (!isset($weight)) {
- $weight = 0;
- }
-
- return $weight;
-}
-
-/**
- * Add a new task handler.
- *
- * This submit handler creates a new task handler and stores it in the
- * cache, then if there is a settings page, redirects to the proper
- * settings page. If there isn't one it simply redirects to the
- * back to itself.
- */
-function delegator_admin_list_form_add($form, &$form_state) {
- $plugin = delegator_get_task_handler($form_state['values']['handler']);
-
- // Update the weights from the form.
- $weight = delegator_admin_update_weights($form_state);
-
- $handler = delegator_new_task_handler($form_state['task'], $form_state['subtask_id'], $plugin, $weight, $form_state['cache']);
-
- $form_state['redirect'] = delegator_admin_new_task_handler($handler, $form_state['task_name'], $form_state['task'], $form_state['subtask_id'], $form_state['cache'], $plugin);
-}
-
-/**
- * Import a new task handler.
- */
-function delegator_admin_list_form_import($form, &$form_state) {
- // Update the weights from the form.
- delegator_admin_update_weights($form_state);
-
- // Store the changed task handler list.
- delegator_admin_set_task_cache($form_state['task'], $form_state['subtask_id'], $form_state['cache']);
-
- $form_state['redirect'] = 'admin/build/delegator/' . $form_state['task_name'] . '/import';
-}
-
-/**
- * Save all changes to the task handler list.
- */
-function delegator_admin_list_form_submit($form, &$form_state) {
- // Update the weights from the form.
- $form_state['redirect'] = $_GET['q'];
-
- // If there's an admin path, go there instead.
- $task_type = delegator_get_task_type($form_state['task']['task type']);
- if (isset($task_type['admin path'])) {
- $form_state['redirect'] = $task_type['admin path'];
- }
-
- delegator_admin_update_weights($form_state);
-
- $cache = &$form_state['cache'];
- if ($cache->locked) {
- drupal_set_message(t('Unable to update task due to lock.'), 'error');
- return;
- }
-
- // Go through each of the task handlers, check to see if it needs updating,
- // and update it if so.
- foreach ($cache->handlers as $id => $info) {
- // If it has been marked for deletion, delete it.
- if ($info['changed'] & DGA_CHANGED_DELETED) {
- if (isset($form_state['task_handlers'][$id])) {
- delegator_delete_task_handler($form_state['task_handlers'][$id]);
- }
- }
- // If it has been somehow edited (or added), write the cached version
- if ($info['changed'] & DGA_CHANGED_CACHED) {
- // load and write the cached version.
- $handler = delegator_admin_get_task_handler_cache($id);
- // Make sure we get updated weight from the form for this.
- $handler->weight = $info['weight'];
- delegator_save_task_handler($handler);
-
- // Now that we've written it, remove it from cache.
- delegator_admin_clear_task_handler_cache($id);
-
- // @todo -- do we need to clear the handler weight here?
- }
- // Otherwise, check to see if it has moved and, if so, update the weight.
- elseif (isset($form_state['task_handlers']) && $info['weight'] != $form_state['task_handlers'][$id]->weight) {
- // Theoretically we could only do this for in code objects, but since our
- // load mechanism checks for all, this is less database work.
- delegator_update_task_handler_weight($form_state['task_handlers'][$id], $info['weight']);
- }
-
- // Set enable/disabled status.
- if ($info['changed'] & DGA_CHANGED_STATUS) {
- ctools_include('export');
- ctools_export_set_status('delegator_handlers', $id, $info['disabled']);
- }
- }
-
- drupal_set_message(t('All changes have been saved.'));
-
- // Clear the cache
- delegator_admin_clear_task_cache($form_state['task'], $form_state['subtask_id']);
- delegator_admin_clear_task_handler_cache($form_state['task_name'] . '-working');
-}
-
-/**
- * Cancel all changes to the task handler list.
- */
-function delegator_admin_list_form_cancel($form, &$form_state) {
- drupal_set_message(t('All changes have been discarded.'));
- foreach ($form_state['cache']->handlers as $id => $info) {
- if ($info['changed'] & DGA_CHANGED_CACHED) {
- // clear cached version.
- delegator_admin_clear_task_handler_cache($id . '-working');
- }
- }
-
- delegator_admin_clear_task_cache($form_state['task'], $form_state['subtask_id']);
- delegator_admin_clear_task_handler_cache($form_state['task_name'] . '-working');
-
- // If there's an admin path, go there instead.
- $task_type = delegator_get_task_type($form_state['task']['task type']);
- if (isset($task_type['admin path'])) {
- $form_state['redirect'] = $task_type['admin path'];
- }
-}
-
-/**
- * Submit handler for item action.
- *
- * This is attached to every delete button; it uses $form_state['clicked_value']
- * to know which delete button was pressed. In the form, we set #handler => $id
- * to that this information could be easily retrieved.
- *
- * The actual action to call will be in the 'action' setting for the handler.
- */
-function delegator_admin_list_form_action($form, &$form_state) {
- // Update the weights from the form.
- delegator_admin_update_weights($form_state);
-
- $id = $form_state['clicked_button']['#handler'];
- $action = $form_state['values']['handlers'][$id]['action'];
-
- // Set this now, that way handlers can override it to go elsewhere if they
- // want.
- $form_state['redirect'] = $_GET['q'];
-
- // Break up our
- if (strpos($action, '-') !== FALSE) {
- list($action, $argument) = explode('-', $action, 2);
- }
- else {
- $action = $action;
- $argument = NULL;
- }
-
- if (!empty($form['#actions'][$action]) && function_exists($form['#actions'][$action])) {
- $form['#actions'][$action]($form, $form_state, $id, $action, $argument);
- }
-
- delegator_admin_set_task_cache($form_state['task'], $form_state['subtask_id'], $form_state['cache']);
- return;
-}
-
-/**
- * Delegated submit handler to delete an item.
- */
-function delegator_admin_list_form_action_delete($form, &$form_state, $id, $action, $argument) {
- // This overwrites 'moved' and 'cached' states.
- if ($form_state['cache']->handlers[$id]['changed'] & DGA_CHANGED_CACHED && !$form_state['cache']->locked) {
- // clear cached version.
- delegator_admin_clear_task_handler_cache($id);
- }
- $form_state['cache']->handlers[$id]['changed'] = DGA_CHANGED_DELETED;
-}
-
-/**
- * Delegated submit handler to edit an item.
- *
- * Which form to go to will be specified by $argument.
- */
-function delegator_admin_list_form_action_edit($form, &$form_state, $id, $action, $argument) {
- // Use the one from the database or an updated one in cache?
- $handler = delegator_admin_find_handler($id, $form_state['cache'], $form_state['task_name'], $form_state['task_handlers']);
-
- $name = $form_state['task_name'];
- $form_state['redirect'] = "admin/build/delegator/$name/$handler->handler/$id/$argument";
-}
-
-/**
- * Clone an existing task handler into a new handler.
- */
-function delegator_admin_list_form_action_clone($form, &$form_state, $id, $action, $argument) {
- // Use the one from the database or an updated one in cache?
- $handler = delegator_admin_find_handler($id, $form_state['cache'], $form_state['task_name'], $form_state['task_handlers']);
-
- // Get the next weight from the form
- $handler->weight = delegator_admin_update_weights($form_state);
-
- $handler->export_type = EXPORT_IN_DATABASE;
- $handler->type = t('Normal');
-
- // Generate a unique name. Unlike most named objects, we don't let people choose
- // names for task handlers because they mostly don't make sense.
- $base = $form_state['task']['name'];
- if ($form_state['subtask_id']) {
- $base .= '_' . $form_state['subtask_id'];
- }
- $base .= '_' . $handler->handler;
-
- // Once we have a base, check to see if it is used. If it is, start counting up.
- $name = $base;
- $count = 1;
- // If taken
- while (isset($form_state['cache']->handlers[$name])) {
- $name = $base . '_' . ++$count;
- }
-
- $handler->name = $name;
- unset($handler->did);
-
- if ($function = ctools_plugin_load_function('delegator', 'task_handlers', $handler->handler, 'clone')) {
- $function($handler);
- }
-
- // Store the new handler.
- if (!$form_state['cache']->locked) {
- delegator_admin_set_task_handler_cache($handler);
- }
-
- $form_state['cache']->handlers[$handler->name] = array(
- 'name' => $handler->name,
- 'weight' => $handler->weight,
- 'changed' => DGA_CHANGED_CACHED,
- 'disabled' => FALSE,
- );
- $form_state['cache']->last_touched = $handler->name;
-}
-
-/**
- * Export a task handler.
- */
-function delegator_admin_list_form_action_export($form, &$form_state, $id, $action, $argument) {
- // Redirect to the export page.
- $name = $form_state['task_name'];
- $form_state['redirect'] = "admin/build/delegator/$name/export/$id";
-}
-
-/**
- * Enable a task handler.
- */
-function delegator_admin_list_form_action_enable($form, &$form_state, $id, $action, $argument) {
- $form_state['cache']->handlers[$id]['changed'] |= DGA_CHANGED_STATUS;
- $form_state['cache']->handlers[$id]['disabled'] = FALSE;
-}
-
-/**
- * Enable a task handler.
- */
-function delegator_admin_list_form_action_disable($form, &$form_state, $id, $action, $argument) {
- $form_state['cache']->handlers[$id]['changed'] |= DGA_CHANGED_STATUS;
- $form_state['cache']->handlers[$id]['disabled'] = TRUE;
-}
-
-/**
- * Entry point to export a task handler.
- */
-function delegator_administer_task_handler_export($task_name, $name) {
- list($task_id, $subtask_id) = delegator_get_task_id($task_name);
- $task = delegator_get_task($task_id);
-
- $handler = delegator_admin_get_task_handler_cache($name);
- if (!$handler) {
- $handler = delegator_load_task_handler($task, $subtask_id, $name);
- }
-
- if (!$handler) {
- return drupal_not_found();
- }
-
- $plugin = delegator_get_task_handler($handler->handler);
-
- $title = delegator_get_handler_title($plugin, $handler, $task, $subtask_id);
- drupal_set_title(t('Export task handler "@title"', array('@title' => $title)));
-
- ctools_include('export');
- delegator_set_trail($task, $task_name);
- return drupal_get_form('ctools_export_form', delegator_export_task_handler($handler), $title);
-}
-
-/**
- * Basic form info for the wizard to multi-step edit a task handler.
- */
-function delegator_admin_task_handler_form_info($task_name) {
- return array(
- 'id' => 'delegator_task_handler',
- 'show back' => TRUE,
- 'show cancel' => TRUE,
- 'return path' => "admin/build/delegator/$task_name",
- 'next callback' => 'delegator_admin_edit_task_handler_next',
- 'finish callback' => 'delegator_admin_edit_task_handler_finish',
- 'return callback' => 'delegator_admin_edit_task_handler_finish',
- 'cancel callback' => 'delegator_admin_edit_task_handler_cancel',
- );
-}
-
-/**
- * Entry point to edit a task handler.
- */
-function delegator_administer_task_handler_edit($task_name, $handler_id, $name, $step) {
- list($task_id, $subtask_id) = delegator_get_task_id($task_name);
-
- $task = delegator_get_task($task_id);
- $subtask = delegator_get_task_subtask($task, $subtask_id);
- $plugin = delegator_get_task_handler($handler_id);
-
- $cache = delegator_admin_get_task_cache($task, $subtask_id);
-
- $handler = delegator_admin_find_handler($name, $cache, $task_name);
-
- if (!$handler) {
- return drupal_not_found();
- }
-
- // Prevent silliness of using some other handler type's tabs for this
- // particular handler, or of somehow having invalid tasks or task handlers.
- if ($handler_id != $handler->handler ||
- !$task ||
- !$plugin ||
- !isset($plugin['forms'][$step]) ||
- !isset($plugin['edit forms'][$step])) {
- return drupal_not_found();
- }
-
- $form_info = delegator_admin_task_handler_form_info($task_name);
- if (!empty($subtask['single task'])) {
- $task_type = delegator_get_task_type($task['task type']);
- $form_info['return path'] = $task_type['admin path'];
- }
- $form_info['order'] = $plugin['edit forms'];
- $form_info['forms'] = $plugin['forms'];
- $form_info['path'] = "admin/build/delegator/$task_name/$handler_id/$name/%step";
- if (empty($plugin['forms'][$step]['no return'])) {
- $form_info['show return'] = TRUE;
- }
-
- $title = delegator_get_handler_title($plugin, $handler, $task, $subtask_id);
- drupal_set_title(t('Edit task handler "@title"', array('@title' => $title)));
-
- delegator_set_trail($task, $task_name);
- $form_state = array(
- 'step' => $step,
- 'task_name' => $task_name,
- 'task_id' => $task_id,
- 'task' => $task,
- 'subtask_id' => $subtask_id,
- 'subtask' => $subtask,
- 'plugin' => $plugin,
- 'handler' => $handler,
- 'type' => 'edit',
- 'cache' => $cache,
- );
-
- ctools_include('wizard');
- $output = ctools_wizard_multistep_form($form_info, $step, $form_state);
-
- if (!empty($cache->locked)) {
- $output = theme('delegator_admin_lock', $cache->locked, $task_name) . $output;
- }
- else if (!empty($cache->changed)) {
- $output = theme('delegator_admin_changed') . $output;
- }
-
- drupal_add_css(drupal_get_path('module', 'delegator') . '/css/task-handlers.css');
- if (!empty($plugin['forms'][$step]['no blocks'])) {
- print theme('page', $output, FALSE);
- }
- else {
- return $output;
- }
-}
-
-/**
- * Entry point to add a task handler.
- */
-function delegator_administer_task_handler_add($task_name, $name, $step) {
- list($task_id, $subtask_id) = delegator_get_task_id($task_name);
-
- $handler = delegator_admin_get_task_handler_cache($task_name . '-working');
-
- if (!$handler) {
- return drupal_not_found();
- }
-
- $task = delegator_get_task($task_id);
- $subtask = delegator_get_task_subtask($task, $subtask_id);
- $plugin = delegator_get_task_handler($handler->handler);
-
- // Prevent silliness of using some other handler type's tabs for this
- // particular handler, or of somehow having invalid tasks or task handlers.
- if (!$task ||
- !$plugin ||
- !isset($plugin['forms'][$step]) ||
- !isset($plugin['add forms'][$step])) {
- return drupal_not_found();
- }
-
- $cache = delegator_admin_get_task_cache($task, $subtask_id);
- $form_info = delegator_admin_task_handler_form_info($task_name);
- // Single tasks will return to a different location, this will help that.
- if (!empty($subtask['single task'])) {
- $task_type = delegator_get_task_type($task['task type']);
- $form_info['return path'] = $task_type['admin path'];
- }
-
- $form_info['order'] = $plugin['add forms'];
- $form_info['forms'] = $plugin['forms'];
- $form_info['path'] = "admin/build/delegator/$task_name/add/$handler->name/%step";
- $form_info['show trail'] = TRUE;
-
- $title = isset($subtask['admin title']) ? $subtask['admin title'] : $task['admin title'];
- drupal_set_title(t('Add task handler for %task', array('%task' => $title)));
-
- delegator_set_trail($task);
- $form_state = array(
- 'step' => $step,
- 'task_name' => $task_name,
- 'task_id' => $task_id,
- 'task' => $task,
- 'subtask_id' => $subtask_id,
- 'subtask' => $subtask,
- 'plugin' => $plugin,
- 'handler' => $handler,
- 'type' => 'add',
- 'cache' => $cache,
- );
-
- ctools_include('wizard');
- $output = ctools_wizard_multistep_form($form_info, $step, $form_state);
-
- drupal_add_css(drupal_get_path('module', 'delegator') . '/css/task-handlers.css');
- if (!empty($plugin['forms'][$step]['no blocks'])) {
- print theme('page', $output, FALSE);
- }
- else {
- return $output;
- }
-}
-
-/**
- * Form wizard 'next' handler -- update the cache.
- */
-function delegator_admin_edit_task_handler_next(&$form_state) {
- if ($form_state['cache']->locked) {
- drupal_set_message(t('Unable to update task due to lock.'), 'error');
- return;
- }
-
- $handler = &$form_state['handler'];
-
- // This updates the working cache, since we're not finished.
- delegator_admin_set_task_handler_cache($handler, TRUE);
-
- // Make sure the cache is aware that we're editing this item:
- if (!isset($form_state['cache']->working) || $form_state['cache']->working != $handler->name) {
- $form_state['cache']->working = $handler->name;
- delegator_admin_set_task_cache($form_state['task'], $form_state['subtask_id'], $form_state['cache']);
- }
-}
-
-/**
- * Form wizard finish handler; called to update everything the wizard touched.
- *
- * This transfers the working cache to the task handler cache and
- * updates the cache to mark this item as having changed.
- */
-function delegator_admin_edit_task_handler_finish(&$form_state) {
- $handler = &$form_state['handler'];
- $cache = &$form_state['cache'];
-
- if ($cache->locked) {
- drupal_set_message(t('Unable to update task due to lock.'), 'error');
- return;
- }
-
-
- // Set status of our handler
- $cache->handlers[$handler->name]['changed'] |= DGA_CHANGED_CACHED;
- $cache->last_touched = $handler->name;
- unset($cache->working);
-
- delegator_admin_set_task_cache($form_state['task'], $form_state['subtask_id'], $cache);
-
- if (isset($form_state['values']['conf']) && is_array($form_state['values']['conf'])) {
- // Merge whatever is in the form values with the existing configuration.
- $handler->conf = array_merge($handler->conf, $form_state['values']['conf']);
- }
-
- // Write to cache.
- delegator_admin_set_task_handler_cache($handler);
-
- // Remove working copy.
- delegator_admin_clear_task_handler_cache($form_state['task_name'] . '-working');
-
- // If the task or subtask is defined as containing a single handler, then just save
- // the whole cache here since they won't get a chance to later.
- if (!empty($form_state['subtask']['single task']) || !empty($form_state['task']['single task'])) {
- delegator_admin_list_form_submit(array(), $form_state);
- }
-}
-
-/**
- * Wizard cancel handler for the task handler edit. Clear the working
- * copy from cache.
- */
-function delegator_admin_edit_task_handler_cancel(&$form_state) {
- // Remove working copy.
- delegator_admin_clear_task_handler_cache($form_state['task_name'] . '-working');
- unset($form_state['cache']->working);
- delegator_admin_set_task_cache($form_state['task'], $form_state['subtask_id'], $form_state['cache']);
-}
-
-/**
- * Form to break a lock on a delegator task.
- */
-function delegator_administer_break_lock(&$form_state, $task_name) {
- list($task_id, $subtask_id) = delegator_get_task_id($task_name);
-
- $form_state['task_name'] = $task_name;
-
- ctools_include('object-cache');
- $lock = ctools_object_cache_test('delegator_handlers', $form_state['task_name']);
- $form = array();
-
- $task = delegator_get_task($task_id);
- delegator_set_trail($task, $task_name);
-
- // @todo put task title here, but also needs subtask support.
- if (empty($lock)) {
- return array('message' => array('#value' => t('There is no lock on this task to break.')));
- }
-
- $cancel = 'admin/build/delegator/' . $task_name;
- if (!empty($_REQUEST['cancel'])) {
- $cancel = $_REQUEST['cancel'];
- }
-
- $account = user_load($lock->uid);
- return confirm_form($form,
- t('Are you sure you want to break this lock?'),
- $cancel,
- t('By breaking this lock, any unsaved changes made by !user will be lost!', array('!user' => theme('username', $account))),
- t('Break lock'),
- t('Cancel')
- );
-}
-
-/**
- * Submit handler to break_lock a view.
- */
-function delegator_administer_break_lock_submit(&$form, &$form_state) {
- ctools_object_cache_clear_all('delegator_handlers', $form_state['task_name']);
- drupal_set_message(t('The lock has been broken and you may now edit this task.'));
- $form_state['redirect'] = 'admin/build/delegator/' . $form_state['task_name'];
-}
-
-/**
- * Import a task handler from cut & paste
- */
-function delegator_admin_import_task_handler(&$form_state, $task_name) {
- drupal_set_title(t('Import task handler'));
-
- $form_state['task_name'] = $task_name;
- list($form_state['task_id'], $form_state['subtask_id']) = delegator_get_task_id($task_name);
- $form_state['task'] = delegator_get_task($form_state['task_id']);
-// $form_state['subtask'] = delegator_get_task_subtask($task, $subtask_id);
-
- delegator_set_trail($form_state['task']);
-
- $form['object'] = array(
- '#type' => 'textarea',
- '#title' => t('Paste task handler code here'),
- '#rows' => 15,
- );
-
- $form['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Import'),
- );
- return $form;
-}
-
-/**
- * Ensure we got a valid task handler.
- */
-function delegator_admin_import_task_handler_validate($form, &$form_state) {
- ob_start();
- eval($form_state['values']['object']);
- ob_end_clean();
-
- if (!isset($handler) || !is_object($handler)) {
- form_error($form['object'], t('Unable to interpret task handler code.'));
- }
-
- $old_name = $handler->task;
- if (!empty($handler->subtask)) {
- $old_name .= '_' . $handler->subtask;
- }
-
- $new_name = $form_state['task_id'];
- if (!empty($form_state['subtask_id'])) {
- $new_name .= '_' . $form_state['subtask_id'];
- }
-
- $handler->name = preg_replace('/^' . $old_name . '/', $new_name, $handler->name);
-
- $handler->task = $form_state['task_id'];
- $handler->subtask = $form_state['subtask_id'];
-
- // See if the task is already locked.
- $cache = delegator_admin_get_task_cache($form_state['task'], $handler->subtask);
- if ($cache->locked) {
- $account = user_load($cache->locked->uid);
- $name = theme('username', $account);
- $lock_age = format_interval(time() - $cache->locked->updated);
- $break = url('admin/build/delegator/' . $handler->task . '/break-lock', array('query' => array('destination' => $_GET['q'], 'cancel' => $_GET['q'])));
-
- form_error($form['object'], t('Unable to import task handler because the task is being edited by user !user, and is therefore locked from editing by others. This lock is !age old. Click here to <a href="!break">break this lock</a>.', array('!user' => $name, '!age' => $lock_age, '!break' => $break)));
- }
-
- $handler->type = t('Normal');
-
- ctools_include('export');
- $handler->export_type = EXPORT_IN_DATABASE;
-
- if (isset($cache->handlers[$handler->name])) {
- drupal_set_message(t('Warning: The handler you are importing already exists and this operation will overwrite an existing handler. If this is not what you intend, you may Cancel this. You should then modify the <code>$handler-&gt;name</code> field of your import to have a unique name.'), 'warning');
-
- $old_handler = delegator_admin_find_handler($handler->name, $cache, $form_state['task_name']);
- $handler->export_type = $old_handler->export_type | EXPORT_IN_DATABASE;
- }
-
- $form_state['cache'] = $cache;
- $form_state['handler'] = $handler;
-}
-
-/**
- * Submit handler to import an existing page.
- */
-function delegator_admin_import_task_handler_submit($form, &$form_state) {
- // Use the one from the database or an updated one in cache?
- $handler = &$form_state['handler'];
- $cache = &$form_state['cache'];
-
- delegator_admin_set_task_handler_cache($handler);
-
- $cache->handlers[$handler->name] = array(
- 'name' => $handler->name,
- 'weight' => $handler->weight,
- 'changed' => DGA_CHANGED_CACHED,
- 'disabled' => FALSE,
- );
- $cache->last_touched = $handler->name;
-
- delegator_admin_set_task_cache($form_state['task'], $handler->subtask, $cache);
- $form_state['redirect'] = 'admin/build/delegator/' . $form_state['task_name'];
-}
-
diff --git a/delegator/delegator.info b/delegator/delegator.info
deleted file mode 100644
index a677330..0000000
--- a/delegator/delegator.info
+++ /dev/null
@@ -1,5 +0,0 @@
-; $Id$
-name = Delegator
-description = Provides a UI and API to select which handlers will perform tasks.
-core = 6.x
-dependencies[] = ctools
diff --git a/delegator/delegator.module b/delegator/delegator.module
index 317ea3c..e69de29 100644
--- a/delegator/delegator.module
+++ b/delegator/delegator.module
@@ -1,888 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- *
- * The delegator handles two plugins and provides a UI to link them together.
- * The 'task' plugin defines a particular task, and it provides menu access
- * to those tasks so that they can respond to page requests.
- *
- * 'task_handlers' are then chosen to handle these tasks, arranged in
- * order of priority in the UI. Task handlers can choose whether or
- * not to handle a task. The first one that does handles it.
- *
- * This system was initially designed to provide a nice way to override
- * existing menu items and provide alternate paths based on the data,
- * particularly for node views, but tasks are not limited to this
- * kind of task. The UI is modular and can be embedded within another
- * module's UI to allow specific tasks to have chosen handlers.
- */
-
-/**
- * Implementation of hook_perm().
- */
-function delegator_perm() {
- return array('administer delegator');
-}
-
-/**
- * Implementation of hook_theme().
- */
-function delegator_theme() {
- $items = array(
- 'delegator_admin_list_form' => array(
- 'arguments' => array('form' => NULL),
- 'file' => 'delegator.admin.inc',
- ),
- 'delegator_admin_lock' => array(
- 'arguments' => array('lock' => array(), 'task_name' => NULL),
- 'file' => 'delegator.admin.inc',
- ),
- 'delegator_admin_changed' => array(
- 'arguments' => array(),
- 'file' => 'delegator.admin.inc',
- ),
- );
-
- // Allow task plugins to have theme registrations by passing through:
- $tasks = delegator_get_tasks();
-
- // Provide menu items for each task.
- foreach ($tasks as $task_id => $task) {
- if ($function = ctools_plugin_get_function($task, 'hook theme')) {
- $function($items, $task);
- }
- }
-
- return $items;
-}
-
-/**
- * Implementation of hook_ctools_plugin_dierctory() to let the system know
- * we implement task and task_handler plugins.
- */
-function delegator_ctools_plugin_directory($module, $plugin) {
- if ($module == 'delegator') {
- return 'plugins/' . $plugin;
- }
-}
-
-/**
- * Implementation of hook_menu.
- *
- * Get a list of all tasks and delegate to them.
- */
-function delegator_menu() {
- $items = array();
-
- // Task types get menu entries so they can set up their own administrative
- // areas.
- $task_types = delegator_get_task_types();
-
- foreach ($task_types as $id => $task_type) {
- if ($function = ctools_plugin_get_function($task_type, 'hook menu')) {
- $function($items, $task_type);
- }
- }
-
- $tasks = delegator_get_tasks();
-
- // Provide menu items for each task.
- foreach ($tasks as $task_id => $task) {
- $handlers = delegator_get_task_handler_plugins($task);
- delegator_menu_task_items($items, $task_id, $task, $handlers);
-
- // And for those that provide subtasks, provide menu items for them, as well.
- foreach (delegator_get_task_subtasks($task) as $subtask_id => $subtask) {
- delegator_menu_task_items($items, $task_id, $subtask, $handlers, $subtask_id);
- }
- }
-
- return $items;
-}
-
-/**
- * Implementation of hook_menu_alter.
- *
- * Get a list of all tasks and delegate to them.
- */
-function delegator_menu_alter(&$items) {
- $tasks = delegator_get_tasks();
-
- foreach ($tasks as $task) {
- if ($function = ctools_plugin_get_function($task, 'hook menu alter')) {
- $function($items, $task);
- }
- // let the subtasks alter the menu items too.
- foreach (delegator_get_task_subtasks($task) as $subtask_id => $subtask) {
- if ($function = ctools_plugin_get_function($subtask, 'hook menu alter')) {
- $function($items, $subtask);
- }
- }
- }
-
- return $items;
-}
-
-/**
- * Generate a menu item for a given task and subtask combination.
- */
-function delegator_menu_task_items(&$items, $task_id, $task, $handlers, $subtask_id = NULL) {
- // If using a subtask, attach the subtask id to the name we use.
- $task_name = $task_id . ($subtask_id ? '-' . $subtask_id : '');
-
- // Allow the task to add its own menu items.
- if ($function = ctools_plugin_get_function($task, 'hook menu')) {
- $function($items, $task);
- }
-
- // Set up access permissions.
- $access_callback = isset($task['admin access callback']) ? $task['admin access callback'] : 'user_access';
- $access_arguments = isset($task['admin access arguments']) ? $task['admin access arguments'] : array('administer delegator');
-
- $base = array(
- 'access callback' => $access_callback,
- 'access arguments' => $access_arguments,
- 'file' => 'delegator.admin.inc',
- );
-
- if (isset($task['admin title'])) {
- $items['admin/build/delegator/' . $task_name] = array(
- 'title' => $task['admin title'],
- 'description' => isset($task['admin description']) ? $task['admin description'] : '',
- 'page callback' => 'delegator_administer_task',
- 'page arguments' => array($task_name),
- 'type' => MENU_CALLBACK,
- ) + $base;
- }
-
- // Form to add new items to this task.
- $items['admin/build/delegator/' . $task_name . '/add/%/%'] = array(
- 'page callback' => 'delegator_administer_task_handler_add',
- 'page arguments' => array($task_name, 5, 6),
- ) + $base;
-
- // Form to add export a handler
- $items['admin/build/delegator/' . $task_name . '/export/%'] = array(
- 'page callback' => 'delegator_administer_task_handler_export',
- 'page arguments' => array($task_name, 5),
- ) + $base;
-
- // Form to break a lock on this task.
- $items['admin/build/delegator/' . $task_name . '/break-lock'] = array(
- 'title' => 'Break lock',
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('delegator_administer_break_lock', $task_name),
- 'type' => MENU_CALLBACK,
- ) + $base;
-
- // Form to import a task handler for this task.
- $items['admin/build/delegator/' . $task_name . '/import'] = array(
- 'title' => 'Import',
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('delegator_admin_import_task_handler', $task_name),
- 'type' => MENU_CALLBACK,
- ) + $base;
-
- foreach ($handlers as $handler_id => $handler) {
- if (isset($handler['edit forms'])) {
- $weight = 0;
- $default_task = FALSE;
- foreach ($handler['edit forms'] as $form_id => $form_title) {
- if (!$default_task) {
- $default_task = TRUE;
- // The first edit form is the default for tabs, so it gets a bit
- // of special treatment here. It gets the parent menu declaration
- // which is just a callback, and it gets its main entry declared
- // as the default local task.
- $items["admin/build/delegator/$task_name/$handler_id/%"] = array(
- 'title' => t('Edit'),
- 'page callback' => 'delegator_administer_task_handler_edit',
- 'page arguments' => array($task_name, $handler_id, 5, $form_id),
- 'type' => MENU_CALLBACK,
- ) + $base;
- $type = MENU_DEFAULT_LOCAL_TASK;
- }
- else {
- $type = $form_title ? MENU_LOCAL_TASK : MENU_CALLBACK;
- }
- // Handler to edit delegator task handlers. May exist in its own UI.
- $items["admin/build/delegator/$task_name/$handler_id/%/$form_id"] = array(
- 'title' => $form_title,
- 'page callback' => 'delegator_administer_task_handler_edit',
- 'page arguments' => array($task_name, $handler_id, 5, $form_id),
- // This allows an empty form title to provide a hidden form
- // which is useful for doing more branch-like multi-step
- // functionality.
- 'type' => $type,
- 'weight' => $weight++,
- ) + $base;
- }
- }
- }
-}
-
-// --------------------------------------------------------------------------
-// Database routines
-
-/**
- * Load a single task handler by name.
- *
- * Handlers can come from multiple sources; either the database or by normal
- * export method, which is handled by the ctools library, but handlers can
- * also be bundled with task/subtask. We have to check there and perform
- * overrides as appropriate.
- *
- * Handlers bundled with the task are of a higher priority than default
- * handlers provided by normal code, and are of a lower priority than
- * the database, so we have to check the source of handlers when we have
- * multiple to choose from.
- */
-function delegator_load_task_handler($task, $subtask_id, $name) {
- ctools_include('export');
- $result = ctools_export_load_object('delegator_handlers', 'names', array($name));
- $handlers = delegator_get_default_task_handlers($task, $subtask_id);
- return delegator_compare_task_handlers($result, $handlers, $name);
-}
-
-/**
- * Load all task handlers for a given task/subtask.
- */
-function delegator_load_task_handlers($task, $subtask_id = NULL, $default_handlers = NULL) {
- ctools_include('export');
- $conditions = array(
- 'task' => $task['name'],
- );
-
- if (isset($subtask_id)) {
- $conditions['subtask'] = $subtask_id;
- }
-
- $handlers = ctools_export_load_object('delegator_handlers', 'conditions', $conditions);
- $defaults = isset($default_handlers) ? $default_handlers : delegator_get_default_task_handlers($task, $subtask_id);
- foreach ($defaults as $name => $default) {
- $result = delegator_compare_task_handlers($handlers, $defaults, $name);
-
- if ($result) {
- $handlers[$name] = $result;
- // Ensure task and subtask are correct, because it's easy to change task
- // names when editing a default and fail to do it on the associated handlers.
- $result->task = $task['name'];
- $result->subtask = $subtask_id;
- }
- }
-
- // Override weights from the weight table.
- if ($handlers) {
- $names = array();
- $placeholders = array();
- foreach ($handlers as $handler) {
- $names[] = $handler->name;
- $placeholders[] = "'%s'";
- }
-
- $result = db_query("SELECT name, weight FROM {delegator_weights} WHERE name IN (" . implode(', ', $placeholders) . ")", $names);
- while ($weight = db_fetch_object($result)) {
- $handlers[$weight->name]->weight = $weight->weight;
- }
- }
-
- return $handlers;
-}
-
-/**
- * Get the default task handlers from a task, if they exist.
- *
- * Tasks can contain 'default' task handlers which are provided by the
- * default task. Because these can come from either the task or the
- * subtask, the logic is abstracted to reduce code duplication.
- */
-function delegator_get_default_task_handlers($task, $subtask_id) {
- // Load default handlers that are provied by the task/subtask itself.
- $handlers = array();
- if ($subtask_id) {
- $subtask = delegator_get_task_subtask($task, $subtask_id);
- if (isset($subtask['default handlers'])) {
- $handlers = $subtask['default handlers'];
- }
- }
- else if (isset($task['default handlers'])) {
- $handlers = $task['default handlers'];
- }
-
- return $handlers;
-}
-
-/**
- * Compare a single task handler from two lists and provide the correct one.
- *
- * Task handlers can be gotten from multiple sources. As exportable objects,
- * they can be provided by default hooks and the database. But also, because
- * they are tightly bound to tasks, they can also be provided by default
- * tasks. This function reconciles where to pick up a task handler between
- * the exportables list and the defaults provided by the task itself.
- *
- * @param $result
- * A list of handlers provided by export.inc
- * @param $handlers
- * A list of handlers provided by the default task.
- * @param $name
- * Which handler to compare.
- * @return
- * Which handler to use, if any. May be NULL.
- */
-function delegator_compare_task_handlers($result, $handlers, $name) {
- // Compare our special default handler against the actual result, if
- // any, and do the right thing.
- if (!isset($result[$name]) && isset($handlers[$name])) {
- $handlers[$name]->type = t('Default');
- $handlers[$name]->export_type = EXPORT_IN_CODE;
- return $handlers[$name];
- }
- else if (isset($result[$name]) && !isset($handlers[$name])) {
- return $result[$name];
- }
- else if (isset($result[$name]) && isset($handlers[$name])) {
- if ($result[$name]->export_type & EXPORT_IN_DATABASE) {
- $result[$name]->type = t('Overridden');
- $result[$name]->export_type = $result[$name]->export_type | EXPORT_IN_CODE;
- return $result[$name];
- }
- else {
- // In this case, our default is a higher priority than the standard default.
- $handlers[$name]->type = t('Default');
- $handlers[$name]->export_type = EXPORT_IN_CODE;
- return $handlers[$name];
- }
- }
-}
-
-/**
- * Load all task handlers for a given task and subtask and sort them.
- */
-function delegator_load_sorted_handlers($task, $subtask_id = NULL, $enabled = FALSE) {
- $handlers = delegator_load_task_handlers($task, $subtask_id);
- if ($enabled) {
- foreach ($handlers as $id => $handler) {
- if (!empty($handler->disabled)) {
- unset($handlers[$id]);
- }
- }
- }
- uasort($handlers, '_delegator_sort_task_handlers');
- return $handlers;
-}
-
-/**
- * Callback for uasort to sort task handlers.
- *
- * Task handlers are sorted by weight then by name.
- */
-function _delegator_sort_task_handlers($a, $b) {
- if ($a->weight < $b->weight) {
- return -1;
- }
- elseif ($a->weight > $b->weight) {
- return 1;
- }
- elseif ($a->name < $b->name) {
- return -1;
- }
- elseif ($a->name > $b->name) {
- return 1;
- }
-
- return 0;
-}
-
-/**
- * Write a task handler to the database.
- */
-function delegator_save_task_handler(&$handler) {
- $update = (isset($handler->did)) ? array('did') : array();
- // Let the task handler respond to saves:
- if ($function = ctools_plugin_load_function('delegator', 'task_handlers', $handler->handler, 'save')) {
- $function($handler, $update);
- }
-
- drupal_write_record('delegator_handlers', $handler, $update);
- db_query("DELETE FROM {delegator_weights} WHERE name = '%s'", $handler->name);
-
- // If this was previously a default handler, we may have to write task handlers.
- if (!$update) {
- // @todo wtf was I going to do here?
- }
- return $handler;
-}
-
-/**
- * Remove a task handler.
- */
-function delegator_delete_task_handler($handler) {
- // Let the task handler respond to saves:
- if ($function = ctools_plugin_load_function('delegator', 'task_handlers', $handler->handler, 'delete')) {
- $function($handler);
- }
- db_query("DELETE FROM {delegator_handlers} WHERE name = '%s'", $handler->name);
- db_query("DELETE FROM {delegator_weights} WHERE name = '%s'", $handler->name);
-}
-
-/**
- * Export a task handler into code suitable for import or use as a default
- * task handler.
- */
-function delegator_export_task_handler($handler, $indent = '') {
- ctools_include('export');
- ctools_include('plugins');
- $handler = drupal_clone($handler);
-
- $append = '';
- if ($function = ctools_plugin_load_function('delegator', 'task_handlers', $handler->handler, 'export')) {
- $append = $function($handler, $indent);
- }
-
- $output = ctools_export_object('delegator_handlers', $handler, $indent);
- $output .= $append;
-
- return $output;
-}
-
-/**
- * Create a new task handler object.
- *
- * @param $task
- * The task this task handler is for.
- * @param $subtask_id
- * The subtask this task handler is for.
- * @param $plugin
- * The plugin this task handler is created from.
- * @param $weight
- * The weight to give this new task handler.
- * @param $cache
- * The task cache if the task is currently being edited. This must be used
- * if task handlers already exist as it is used to determine a unique name
- * and without this naming collisions could occur.
- */
-function delegator_new_task_handler($task, $subtask_id, $plugin, $weight = 0, $cache = NULL) {
- // Generate a unique name. Unlike most named objects, we don't let people choose
- // names for task handlers because they mostly don't make sense.
- $base = $task['name'];
- if ($subtask_id) {
- $base .= '_' . $subtask_id;
- }
- $base .= '_' . $plugin['name'];
-
- // Once we have a base, check to see if it is used. If it is, start counting up.
- $name = $base;
- $count = 1;
- // If taken
- while (isset($cache->handlers[$name])) {
- $name = $base . '_' . ++$count;
- }
-
- // Create a new, empty handler object.
- $handler = new stdClass;
- $handler->title = '';
- $handler->task = $task['name'];
- $handler->subtask = $subtask_id;
- $handler->name = $name;
- $handler->handler = $plugin['name'];
- $handler->weight = $weight;
- $handler->conf = array();
-
- // These are provided by the core export API provided by ctools and we
- // set defaults here so that we don't cause notices. Perhaps ctools should
- // provide a way to do this for us so we don't have to muck with it.
- $handler->export_type = EXPORT_IN_DATABASE;
- $handler->type = t('Local');
-
- if (isset($plugin['default conf'])) {
- if (is_array($plugin['default conf'])) {
- $handler->conf = $plugin['default conf'];
- }
- else if (function_exists($plugin['default conf'])) {
- $handler->conf = $plugin['default conf']($handler, $task, $subtask_id);
- }
- }
-
- return $handler;
-}
-
-/**
- * Set an overidden weight for a task handler.
- *
- * We do this so that in-code task handlers don't need to get written
- * to the database just because they have their weight changed.
- */
-function delegator_update_task_handler_weight($handler, $weight) {
- db_query("DELETE FROM {delegator_weights} WHERE name = '%s'", $handler->name);
- db_query("INSERT INTO {delegator_weights} (name, weight) VALUES ('%s', %d)", $handler->name, $weight);
-}
-
-/**
- * Shortcut function to get task type plugins.
- */
-function delegator_get_task_types() {
- ctools_include('plugins');
- return ctools_get_plugins('delegator', 'task_types');
-}
-
-/**
- * Shortcut function to get a task type plugin.
- */
-function delegator_get_task_type($id) {
- ctools_include('plugins');
- return ctools_get_plugins('delegator', 'task_types', $id);
-}
-
-/**
- * Shortcut function to get task plugins.
- */
-function delegator_get_tasks() {
- ctools_include('plugins');
- return ctools_get_plugins('delegator', 'tasks');
-}
-
-/**
- * Shortcut function to get a task plugin.
- */
-function delegator_get_task($id) {
- ctools_include('plugins');
- return ctools_get_plugins('delegator', 'tasks', $id);
-}
-
-/**
- * Get all tasks for a given type.
- */
-function delegator_get_tasks_by_type($type) {
- ctools_include('plugins');
- $all_tasks = ctools_get_plugins('delegator', 'tasks');
- $tasks = array();
- foreach ($all_tasks as $id => $task) {
- if (isset($task['task type']) && $task['task type'] == $type) {
- $tasks[$id] = $task;
- }
- }
-
- return $tasks;
-}
-
-/**
- * Fetch all subtasks for a delegator task.
- *
- * @param $task
- * A loaded $task plugin object.
- */
-function delegator_get_task_subtasks($task) {
- if (empty($task['subtasks'])) {
- return array();
- }
-
- if ($function = ctools_plugin_get_function($task, 'subtasks callback')) {
- return $function($task);
- }
-}
-
-/**
- * Fetch all subtasks for a delegator task.
- *
- * @param $task
- * A loaded $task plugin object.
- * @param $subtask_id
- * The subtask ID to load.
- */
-function delegator_get_task_subtask($task, $subtask_id) {
- if (empty($task['subtasks'])) {
- return;
- }
-
- if ($function = ctools_plugin_get_function($task, 'subtask callback')) {
- return $function($task, $subtask_id);
- }
-}
-
-/**
- * Shortcut function to get task handler plugins.
- */
-function delegator_get_task_handlers() {
- ctools_include('plugins');
- return ctools_get_plugins('delegator', 'task_handlers');
-}
-
-/**
- * Shortcut function to get a task handler plugin.
- */
-function delegator_get_task_handler($id) {
- ctools_include('plugins');
- return ctools_get_plugins('delegator', 'task_handlers', $id);
-}
-
-/**
- * Retrieve a list of all applicable task handlers for a given task.
- *
- * This looks at the $task['handler type'] and compares that to $task_handler['handler type'].
- * If the task has no type, the id of the task is used instead.
- */
-function delegator_get_task_handler_plugins($task) {
- $type = isset($task['handler type']) ? $task['handler type'] : $task['name'];
- $name = $task['name'];
-
- $handlers = array();
- $task_handlers = delegator_get_task_handlers();
- foreach ($task_handlers as $id => $handler) {
- $task_type = is_array($handler['handler type']) ? $handler['handler type'] : array($handler['handler type']);
- if (in_array($type, $task_type) || in_array($name, $task_type)) {
- $handlers[$id] = $handler;
- }
- }
-
- return $handlers;
-}
-
-/**
- * Get the title for a given handler.
- *
- * If the plugin has no 'admin title' function, the generic title of the
- * plugin is used instead.
- */
-function delegator_get_handler_title($plugin, $handler, $task, $subtask_id) {
- $function = ctools_plugin_get_function($plugin, 'admin title');
- if ($function) {
- return $function($handler, $task, $subtask_id);
- }
- else {
- return $plugin['title'];
- }
-}
-
-/**
- * Get the admin summary (additional info) for a given handler.
- */
-function delegator_get_handler_summary($plugin, $handler, $task, $subtask_id) {
- if ($function = ctools_plugin_get_function($plugin, 'admin summary')) {
- return $function($handler, $task, $subtask_id);
- }
-}
-
-/**
- * Split a task name into a task id and subtask id, if applicable.
- */
-function delegator_get_task_id($task_name) {
- if (strpos($task_name, '-') !== FALSE) {
- return explode('-', $task_name, 2);
- }
- else {
- return array($task_name, NULL);
- }
-}
-
-/**
- * Turn a task id + subtask_id into a task name.
- */
-function delegator_make_task_name($task_id, $subtask_id) {
- if ($subtask_id) {
- return $task_id . '-' . $subtask_id;
- }
- else {
- return $task_id;
- }
-}
-
-/**
- * Delegator for arg load function because menu system will not load extra
- * files for these; they must be in a .module.
- */
-function dp_arg_load($value, $subtask, $argument) {
- require_once './' . drupal_get_path('module', 'delegator') . '/plugins/tasks/page.inc';
- return _dp_arg_load($value, $subtask, $argument);
-}
-
-/**
- * Possibly provide a default value so that variable items can appear in visible menus.
- */
-function dp_arg_to_arg($bit, $map, $index) {
-/*
- Reference code.
-
- $start = microtime();
- $introspect = debug_backtrace();
- dsm(microtime() - $start);
- foreach ($introspect as $call) {
- if ($call['function'] == '_menu_link_translate') {
- dsm(array($map, $call['args']['0']));
- }
- }
-*/
-}
-
-
-/**
- * Get the render function for a handler.
- */
-function delegator_get_renderer($handler) {
- return ctools_plugin_load_function('delegator', 'task_handlers', $handler->handler, 'render');
-}
-
-/**
- * Callback for access control ajax form on behalf of page.inc task.
- *
- * Returns the cached access config and contexts used.
- */
-function delegator_page_ctools_access_get($argument) {
- ctools_include('context');
- $task = delegator_get_task('page');
-
- require_once './' . drupal_get_path('module', 'delegator') . '/plugins/tasks/page.admin.inc';
- $page = delegator_page_get_page_cache($argument);
-
- $contexts = array();
- // Load contexts based on argument data:
- if ($arguments = _delegator_page_get_arguments($page)) {
- $contexts = ctools_context_get_placeholders_from_argument($arguments);
- }
-
- return array($page->access, $contexts);
-}
-
-/**
- * Callback for access control ajax form on behalf of page.inc task.
- *
- * Writes the changed access to the cache.
- */
-function delegator_page_ctools_access_set($argument, $access) {
- $page = delegator_page_get_page_cache($argument);
- $page->access = $access;
- delegator_page_set_page_cache($page);
-}
-
-/**
- * Callback for access control ajax form on behalf of context task handler.
- *
- * Returns the cached access config and contexts used.
- */
-function delegator_task_handler_ctools_access_get($argument) {
- ctools_include('context');
- require_once './' . drupal_get_path('module', 'delegator') . '/delegator.admin.inc';
-
- list($task_name, $name) = explode('*', $argument);
- list($task_id, $subtask_id) = delegator_get_task_id($task_name);
- $task = delegator_get_task($task_id);
- $cache = delegator_admin_get_task_cache($task, $subtask_id);
-
- $handler = delegator_admin_find_handler($name, $cache, $task_name);
-
- if (!isset($handler->conf['access'])) {
- $handler->conf['access'] = array();
- }
-
- ctools_include('context-task-handler');
-
- $contexts = ctools_context_handler_get_all_contexts($task, $subtask_id, $handler);
-
- return array($handler->conf['access'], $contexts);
-}
-
-/**
- * Callback for access control ajax form on behalf of context task handler.
- *
- * Writes the changed access to the cache.
- */
-function delegator_task_handler_ctools_access_set($argument, $access) {
- list($task_name, $name) = explode('*', $argument);
- list($task_id, $subtask_id) = delegator_get_task_id($task_name);
- $task = delegator_get_task($task_id);
- $cache = delegator_admin_get_task_cache($task, $subtask_id);
-
- $handler = delegator_admin_find_handler($name, $cache, $task_name);
-
- $handler->conf['access'] = $access;
- delegator_admin_set_task_handler_cache($handler, TRUE);
- // Make sure the cache is aware that we're editing this item:
- if (!isset($cache->working) || $cache->working != $handler->name) {
- $cache->working = $handler->name;
- delegator_admin_set_task_cache($task, $subtask_id, $cache);
- }
-}
-
-/**
- * Load a context from an argument for a given page task.
- *
- * This is used as a menu callback to translate arguments and that is why it is
- * here in the .module file.
- *
- * @param $value
- * The incoming argument value.
- * @param $subtask
- * The subtask id.
- * @param $argument
- * The numeric position of the argument in the path, counting from 0.
- *
- * @return
- * A context item if one is configured, the argument if one is not, or
- * FALSE if restricted or invalid.
- */
-function _dp_arg_load($value, $subtask, $argument) {
- $page = delegator_page_load($subtask);
- if (!$page) {
- return FALSE;
- }
-
- $path = explode('/', $page->path);
- if (empty($path[$argument])) {
- return FALSE;
- }
-
- $keyword = substr($path[$argument], 1);
- if (empty($page->arguments[$keyword])) {
- return $value;
- }
-
- $page->arguments[$keyword]['keyword'] = $keyword;
-
- ctools_include('context');
- $context = ctools_context_get_context_from_argument($page->arguments[$keyword], $value);
-
- // convert false equivalents to false.
- return $context ? $context : FALSE;
-}
-
-/**
- * Reset the active menu trail to the trail specified by the task type.
- *
- * The task type can specify an 'admin path'. If it does,
- * 'admin/build/delegator' will be removed from the active trail and replaced
- * with whatever is in the admin path; that way task types can provide
- * their own administration.
- */
-function delegator_set_trail($task, $task_name = NULL) {
- $task_type = delegator_get_task_type($task['task type']);
- if (empty($task_type['admin path'])) {
- return;
- }
-
-
- ctools_include('menu');
- $trail = menu_get_active_trail();
- $remove = ctools_get_menu_trail('admin/build/delegator');
- foreach ($remove as $info) {
- foreach ($trail as $id => $crumb) {
- if ($crumb['href'] == $info['href']) {
- unset($trail[$id]);
- }
- }
- }
-
- if ($task_name) {
- // Clean up broken 'Edit' link that Drupal leaves behind.
- $trail = array_values($trail);
- if (isset($trail[0]) && $trail[0]['title'] == t('Edit')) {
- unset($trail[0]);
- }
-
- array_unshift($trail, menu_get_item('admin/build/delegator/' . $task_name));
- }
-
- $trail = array_merge(ctools_get_menu_trail($task_type['admin path']), $trail);
- menu_set_active_trail($trail);
-}
diff --git a/delegator/images/configure.png b/delegator/images/configure.png
deleted file mode 100644
index e23d67c..0000000
--- a/delegator/images/configure.png
+++ /dev/null
Binary files differ
diff --git a/delegator/images/delete.png b/delegator/images/delete.png
deleted file mode 100644
index 5f0cf69..0000000
--- a/delegator/images/delete.png
+++ /dev/null
Binary files differ
diff --git a/delegator/js/task-handlers.js b/delegator/js/task-handlers.js
deleted file mode 100644
index 809f7ef..0000000
--- a/delegator/js/task-handlers.js
+++ /dev/null
@@ -1,87 +0,0 @@
-// $Id$
-
-Drupal.behaviors.zzGoLastDelegatorTaskList = function(context) {
- var id = 'delegator-task-list-arrange';
-
- /**
- * There's no way from PHP to tell the tabledrag code to turn on the
- * 'table has changed' code. So what we do is look for a particular
- * class and if so, invoke it. We also look to see if we should
- * hilite a particular row as the 'previous' drag.
- */
- if ($('#' + id, context).hasClass('changed') && Drupal.tableDrag[id]) {
- Drupal.tableDrag[id].changed = true;
- $(Drupal.theme('tableDragChangedWarning')).insertAfter(Drupal.tableDrag[id].table);
- if ($('#' + id + ' .delegator-changed', context).size() != 0) {
- var $row = $('#' + id + ' .delegator-changed', context);
- $row.removeClass('delegator-changed').addClass('drag-previous');
- Drupal.tableDrag[id].oldRowElement = $row;
- }
- }
-
- $('.delegator-operations select:not(.delegator-processed)', context).each(function() {
- var $select = $(this);
- var $next = $(this).parent().next('input');
- $next.hide();
- $(this).hide();
-
- $dropdown = $(this).parent().siblings('.ctools-dropdown');
-
- $('.ctools-dropdown-container a', $dropdown).click(function() {
- var val = $(this).attr('href').replace(/^.*[\/\\](\?q=)?/g, '');
-
- // ignore empty
- if (!val) {
- return false;
- }
-
- // force confirm on delete
- if (val == 'delete' && !confirm(Drupal.t('Remove this task?'))) {
- return false;
- }
-
- $select.val(val);
- $next.trigger('click');
- return false;
- });
- });
-}
-
-Drupal.Delegator = {};
-
-Drupal.Delegator.CollapsibleCallback = function($container, handle, content, toggle) {
- var $parent = $container.parents('tr.draggable');
- var id = $parent.attr('id') + '-collapse';
- if (toggle.hasClass('ctools-toggle-collapsed')) {
- // Force any other item to close, like an accordion:
- $('#delegator-task-list-arrange .ctools-toggle:not(.ctools-toggle-collapsed)').trigger('click');
- // Closed, about to be opened.
- var tr = '<tr class="delegator-collapsible" id="' + id + '"><td colspan=4></td></tr>';
- $parent.after(tr);
- $('#' + id + ' td').append(content);
- $('#' + id).addClass($parent.attr('class'));
- }
-};
-
-Drupal.Delegator.CollapsibleCallbackAfterToggle = function($container, handle, content, toggle) {
- var $parent = $container.parents('tr.draggable');
- var id = $parent.attr('id') + '-collapse';
- if (toggle.hasClass('ctools-toggle-collapsed')) {
- // Was just closed.
- content.hide();
- handle.after(content);
- $('#' + id).remove();
- }
-};
-
-$(document).ready(function() {
- if (Drupal.CTools && Drupal.CTools.CollapsibleCallbacks) {
- Drupal.CTools.CollapsibleCallbacks.push(Drupal.Delegator.CollapsibleCallback);
- Drupal.CTools.CollapsibleCallbacksAfterToggle.push(Drupal.Delegator.CollapsibleCallbackAfterToggle);
-
- // Force all our accordions to close when tabledragging to prevent ugliness:
- $('#delegator-task-list-arrange .tabledrag-handle').mousedown(function() {
- $('#delegator-task-list-arrange .ctools-toggle:not(.ctools-toggle-collapsed)').trigger('click');
- });
- }
-}); \ No newline at end of file
diff --git a/delegator/plugins/task_types/page.admin.inc b/delegator/plugins/task_types/page.admin.inc
deleted file mode 100644
index cf3925a..0000000
--- a/delegator/plugins/task_types/page.admin.inc
+++ /dev/null
@@ -1,120 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Administrative functions for the page task type system
- *
- * This system provides a unified administrative interface to handle page
- * tasks of all types, including a generic 'page' task that lets a user
- * create pages as well as specific page tasks for handling built-in pages.
- */
-function delegator_page_type_list() {
- $tasks = delegator_get_tasks_by_type('page');
-
- $tables = array(
- 'singles' => array(
- 'title' => t('System pages'),
- 'description' => t('Pages provided by the system that can be overridden to provide alternate content or layout.'),
- // add extra columns to single tasks.
- 'columns' => array(
- 'admin description' => array(
- 'label' => t('Description'),
- 'class' => 'delegator-page-description',
- ),
- ),
- 'rows' => array(),
- ),
- 'page' => array(), // give this one special weighting,
- );
-
- delegator_page_type_sort_tasks($tasks, $tables, 'singles');
-
- $output = '';
-
- foreach ($tables as $bucket => $info) {
- // The header can vary a little bit based upon what the task said it
- // should have:
- $header = array(
- array('data' => t('Title'), 'class' => 'delegator-page-title'),
- array('data' => t('Path'), 'class' => 'delegator-page-path'),
- );
-
- if (isset($info['columns'])) {
- foreach ($info['columns'] as $id => $column_info) {
- $header[] = array('data' => $column_info['label'], 'class' => $column_info['class']);
- }
- }
-
- $header[] = array('data' => t('Operations'), 'class' => 'delegator-page-operations');
-
- $output .= '<h3>' . check_plain($info['title']) . '</h3>';
- $output .= '<div class="description">' . check_plain($info['description']) . '</div>';
- if (isset($info['operations'])) {
- $info['rows'][] = array('data' => array(array('data' => theme('links', $info['operations']), 'colspan' => count($header))), 'class' => 'delegator-page-operations');
- }
- $output .= theme('table', $header, $info['rows']);
- }
-
- drupal_add_css(drupal_get_path('module', 'delegator') . '/css/page-task.css');
- return $output;
-}
-
-/**
- * Sort tasks into buckets based upon whether or not they have subtasks.
- */
-function delegator_page_type_sort_tasks($tasks, &$tables, $bucket, $task_id = NULL) {
- foreach ($tasks as $id => $task) {
- // If a type has subtasks, add its subtasks in its own table.
- if (!empty($task['subtasks'])) {
- $tables[$id]['title'] = $task['title'];
- $tables[$id]['description'] = $task['description'];
- if (isset($task['operations'])) {
- $tables[$id]['operations'] = $task['operations'];
- }
- if (isset($task['columns'])) {
- $tables[$id]['columns'] = $task['columns'];
- }
-
- delegator_page_type_sort_tasks(delegator_get_task_subtasks($task), $tables, $id, $task['name']);
- continue;
- }
-
- if (isset($task_id)) {
- $task_name = delegator_make_task_name($task_id, $task['name']);
- }
- else {
- $task_name = $task['name'];
- }
-
- $class = 'page-task-' . $id;
- if (isset($task['row class'])) {
- $class .= ' ' . $task['row class'];
- }
-
- $row = array('data' => array(), 'class' => $class);
- $row['data'][] = array('data' => $task['admin title'], 'class' => 'delegator-page-title');
- $row['data'][] = array('data' => $task['admin path'], 'class' => 'delegator-page-path');
- if (isset($tables[$bucket]['columns'])) {
- foreach ($tables[$bucket]['columns'] as $column_id => $column_info) {
- $row['data'][] = array('data' => $task[$column_id], 'class' => $column_info['class']);
- }
- }
-
- if (isset($task['operations'])) {
- $operations = $task['operations'];
- }
- else {
- $operations = array(
- array(
- 'title' => t('Task handlers'),
- 'href' => "admin/build/delegator/$task_name",
- ),
- );
- }
- $row['data'][] = array('data' => theme('ctools_dropdown', t('Operations'), $operations), 'class' => 'delegator-page-operations');
-
- $tables[$bucket]['rows'][] = $row;
- }
-}
-
diff --git a/delegator/plugins/task_types/page.inc b/delegator/plugins/task_types/page.inc
deleted file mode 100644
index 614bff7..0000000
--- a/delegator/plugins/task_types/page.inc
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Groups tasks that provide pages and provides a common administrative UI for them.
- *
- * The page task supports any task that defines itself as type 'page' and it also
- * includes special handling for the 'page' task to provide a nice location to
- * place administer the list of pages, including adding removing and editing
- * the handlers of pages alongside the less generic tasks such as node and
- * user viewing.
- */
-
-/**
- * Specialized implementation of hook_delegator_tasks(). See api-task.html for
- * more information.
- */
-function delegator_page_delegator_task_types() {
- return array(
- 'page' => array(
- 'title' => t('Pages'),
- 'admin path' => 'admin/build/pages',
- 'hook menu' => 'delegator_page_type_menu',
- ),
- );
-}
-
-/**
- * Delegated implementation of hook_menu().
- */
-function delegator_page_type_menu(&$items, $task) {
- // Set up access permissions.
- $access_callback = isset($task['admin access callback']) ? $task['admin access callback'] : 'user_access';
-
- // @todo use 'administer pages' perm instead?
- $access_arguments = isset($task['admin access arguments']) ? $task['admin access arguments'] : array('administer delegator');
-
- $base = array(
- 'access callback' => $access_callback,
- 'access arguments' => $access_arguments,
- 'file' => 'plugins/task_types/page.admin.inc',
- );
-
- $items['admin/build/pages'] = array(
- 'title' => 'Pages',
- 'description' => 'Add, edit and remove overridden system pages and user defined pages from the system.',
- 'page callback' => 'delegator_page_type_list',
- ) + $base;
-
- $items['admin/build/pages/list'] = array(
- 'title' => 'List',
- 'page callback' => 'delegator_page_type_list',
- 'type' => MENU_DEFAULT_LOCAL_TASK,
- 'weight' => -10,
- ) + $base;
-}
-
diff --git a/delegator/plugins/tasks/page.admin.inc b/delegator/plugins/tasks/page.admin.inc
deleted file mode 100644
index f2c5044..0000000
--- a/delegator/plugins/tasks/page.admin.inc
+++ /dev/null
@@ -1,1622 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Administrative functions for the page subtasks.
- *
- * These are attached to the menu system in page.inc via the hook_menu
- * delegation. They are included here so that this code is loaded
- * only when needed.
- */
-
-/**
- * Delegated implementation of hook_menu().
- */
-function delegator_page_menu(&$items, $task) {
- // Set up access permissions.
- $access_callback = isset($task['admin access callback']) ? $task['admin access callback'] : 'user_access';
- $access_arguments = isset($task['admin access arguments']) ? $task['admin access arguments'] : array('administer delegator');
-
- $base = array(
- 'access callback' => $access_callback,
- 'access arguments' => $access_arguments,
- 'file' => 'plugins/tasks/page.admin.inc',
- );
-
- $items['admin/build/pages/add'] = array(
- 'title' => 'Add custom page',
- 'description' => 'Add a delegator page subtask.',
- 'page callback' => 'delegator_page_add_subtask',
- 'page arguments' => array('page'),
- 'type' => MENU_LOCAL_TASK,
- ) + $base;
- $items['admin/build/pages/import'] = array(
- 'title' => 'Import page',
- 'description' => 'Import a delegator page subtask.',
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('delegator_page_import_subtask', 'page'),
- 'type' => MENU_LOCAL_TASK,
- ) + $base;
-
- $form_info = delegator_page_edit_form_info();
- $default_task = FALSE;
- $weight = 0;
- foreach ($form_info['order'] as $step => $form_title) {
- // The first edit form is the default for tabs, so it gets a bit
- // of special treatment here.
- if (!$default_task) {
- $default_task = TRUE;
- // Add the callback for the default tab.
- $items["admin/build/pages/edit/%"] = array(
- 'title' => 'Edit',
- 'page callback' => 'delegator_page_edit_subtask',
- 'page arguments' => array('page', 4, $step),
- 'type' => MENU_CALLBACK,
- ) + $base;
-
- // And make sure it's the default local task.
- $type = MENU_DEFAULT_LOCAL_TASK;
- }
- else {
- // This allows an empty form title to provide a hidden form
- // which is useful for doing more branch-like multi-step
- // functionality.
- $type = $form_title ? MENU_LOCAL_TASK : MENU_CALLBACK;
- }
-
- // Handler to edit delegator task handlers. May exist in its own UI.
- $items["admin/build/pages/edit/%/$step"] = array(
- 'title' => $form_title,
- 'page callback' => 'delegator_page_edit_subtask',
- 'page arguments' => array('page', 4, 5),
- 'type' => $type,
- 'weight' => $weight++,
- ) + $base;
- }
-
- $items["admin/build/pages/delete/%"] = array(
- 'title' => 'Delete',
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('delegator_page_delete_subtask', 4),
- 'type' => MENU_CALLBACK,
- ) + $base;
-
- $items["admin/build/pages/break-lock/%"] = array(
- 'title' => 'Break lock',
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('delegator_page_break_lock_subtask', 4),
- 'type' => MENU_CALLBACK,
- ) + $base;
-
- $items["admin/build/pages/clone/%"] = array(
- 'title' => 'Clone',
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('delegator_page_clone_subtask', 4),
- 'type' => MENU_CALLBACK,
- ) + $base;
-
- $items["admin/build/pages/export/%"] = array(
- 'title' => 'Export',
- 'page callback' => 'delegator_page_export_subtask',
- 'page arguments' => array(4),
- 'type' => MENU_CALLBACK,
- ) + $base;
-
- $items["admin/build/pages/enable/%"] = array(
- 'title' => 'Enable',
- 'page callback' => 'delegator_page_enable_subtask',
- 'page arguments' => array(4),
- 'type' => MENU_CALLBACK,
- ) + $base;
-
- $items["admin/build/pages/disable/%"] = array(
- 'title' => 'Disable',
- 'page callback' => 'delegator_page_disable_subtask',
- 'page arguments' => array(4),
- 'type' => MENU_CALLBACK,
- ) + $base;
-
- // AJAX callbacks for argument modal.
- $items['admin/build/delegator/argument'] = array(
- 'page callback' => 'delegator_page_subtask_argument_ajax',
- 'type' => MENU_CALLBACK,
- ) + $base;
-
- // Add menu entries for each subtask
- foreach (delegator_page_load_all() as $subtask_id => $subtask) {
- if (!empty($subtask->disabled)) {
- continue;
- }
-
- if (!isset($subtask->access['type'])) {
- $subtask->access['type'] = 'none';
- }
- if (!isset($subtask->access['settings'])) {
- $subtask->access['settings'] = NULL;
- }
-
- $path = array();
- $page_arguments = array($subtask_id);
- $access_arguments = array($subtask->access);
- $load_arguments = array($subtask_id, '%index');
-
- // Replace named placeholders with our own placeholder to load contexts.
- $position = 0;
- foreach (explode('/', $subtask->path) as $bit) {
- // Remove things like double slashes completely.
- if (!isset($bit) || $bit === '') {
- continue;
- }
-
- if ($bit[0] == '%' && $bit != '%') {
- // If an argument, swap it out with our argument loader and make sure
- // the argument gets passed through to the page callback.
- $path[] = '%dp_arg';
- $page_arguments[] = $position;
- $access_arguments[] = $position;
- }
- else if ($bit[0] != '!') {
- $path[] = $bit;
- }
-
- // Increment position. We do it like this to skip empty items that
- // could happen from erroneous paths like: this///that
- $position++;
- }
-
- $menu_path = implode('/', $path);
-
- $items[$menu_path] = delegator_page_menu_item($task, $subtask->menu, $access_arguments, $page_arguments, $load_arguments);
-
- // Add a parent menu item if one is configured.
- if (isset($subtask->menu['type']) && $subtask->menu['type'] == 'default tab' && $subtask->menu['parent']['type'] != 'none') {
- array_pop($path);
- $parent_path = implode('/', $path);
- $items[$parent_path] = delegator_page_menu_item($task, $subtask->menu['parent'], $access_arguments, $page_arguments, $load_arguments);
- }
- }
-}
-
-/**
- * Create a menu item for delegator pages.
- *
- * @param $menu
- * The configuration to use. It will contain a type, and depending on the
- * type may also contain weight, title and name. These are presumed to have
- * been configured from the UI.
- * @param $access_arguments
- * Arguments that go with ctools_access_menu; it should be loaded with
- * the access plugin type, settings, and positions of any arguments that
- * may produce contexts.
- * @param $page_arguments
- * This should be seeded with the subtask name for easy loading and like
- * the access arguments above should contain positions of arguments so
- * that the menu system passes contexts through.
- * @param $load_arguments
- * Arguments to send to the arg loader; should be the subtask id and '%index'.
- */
-function delegator_page_menu_item($task, $menu, $access_arguments, $page_arguments, $load_arguments) {
- $item = array(
- 'access callback' => 'ctools_access_menu',
- 'access arguments' => $access_arguments,
- 'page callback' => 'delegator_page_execute',
- 'page arguments' => $page_arguments,
- 'load arguments' => $load_arguments,
- 'file' => 'plugins/tasks/page.inc',
- );
-
- if (isset($menu['title'])) {
- $item['title'] = $menu['title'];
- }
- if (isset($menu['weight'])) {
- $item['weight'] = $menu['weight'];
- }
-
- if (empty($menu['type'])) {
- $menu['type'] = 'none';
- }
-
- switch ($menu['type']) {
- case 'none':
- default:
- $item['type'] = MENU_CALLBACK;
- break;
-
- case 'normal':
- $item['type'] = MENU_NORMAL_ITEM;
- // Insert item into the proper menu
- $item['menu_name'] = $menu['name'];
- break;
-
- case 'tab':
- $item['type'] = MENU_LOCAL_TASK;
- break;
-
- case 'default tab':
- $item['type'] = MENU_DEFAULT_LOCAL_TASK;
- break;
- }
-
- return $item;
-}
-
-/**
- * Get the cached changes to a given task handler.
- */
-function delegator_page_get_page_cache($name) {
- // This ensures the task .inc file is loaded
- $task = delegator_get_task('page');
-
- ctools_include('object-cache');
- $cache = ctools_object_cache_get('delegator_page', $name);
- if (!$cache) {
- $cache = delegator_page_load($name);
- }
-
- if ($name != '::new') {
- $cache->locked = ctools_object_cache_test('delegator_page', $name);
- }
- else {
- $cache->locked = FALSE;
- }
-
- return $cache;
-}
-
-/**
- * Store changes to a task handler in the object cache.
- */
-function delegator_page_set_page_cache($page) {
- // This ensures the task .inc file is loaded
- $task = delegator_get_task('page');
-
- if (!empty($page->locked)) {
- return;
- }
-
- $name = isset($page->new) || !isset($page->name) ? '::new' : $page->name;
- ctools_include('object-cache');
- $page->changed = TRUE;
- $cache = ctools_object_cache_set('delegator_page', $name, $page);
-}
-
-/**
- * Remove an item from the object cache.
- */
-function delegator_page_clear_page_cache($name) {
- // This ensures the task .inc file is loaded
- $task = delegator_get_task('page');
-
- ctools_include('object-cache');
- ctools_object_cache_clear('delegator_page', $name);
-}
-
-/**
- * Draw the "this task is locked from editing" box.
- */
-function theme_delegator_page_lock($lock, $page_name) {
- $account = user_load($lock->uid);
- $name = theme('username', $account);
- $lock_age = format_interval(time() - $lock->updated);
- $break = url('admin/build/pages/break-lock/' . $page_name, array('query' => array('destination' => $_GET['q'], 'cancel' => $_GET['q'])));
-
- ctools_add_css('ctools');
- $output = '<div class="ctools-locked">';
- $output .= t('This page is being edited by user !user, and is therefore locked from editing by others. This lock is !age old. Click here to <a href="!break">break this lock</a>.', array('!user' => $name, '!age' => $lock_age, '!break' => $break));
- $output .= '</div>';
- return $output;
-}
-
-/**
- * Draw the "you have unsaved changes and this task is locked." message.
- */
-function theme_delegator_page_changed() {
- ctools_add_css('ctools');
- $output = '<div class="ctools-owns-lock">';
- $output .= t('You have modified this page but the changes have not yet been permanently saved. It will be locked for editing by others until you update or cancel these changes.');
- $output .= '</div>';
-
- return $output;
-}
-
-/**
- * Page callback to add a subtask.
- */
-function delegator_page_add_subtask($task_name, $step = NULL) {
- // We load the 'page' task to make sure our .inc file is loaded.
- delegator_get_task('page');
-
- $task = delegator_get_task($task_name);
-
- $function = ctools_plugin_get_function($task, 'form info');
- $form_info = $function();
-
- $form_info += array(
- 'path' => $form_info['add path'],
- );
-
- if (!empty($form_info['add order'])) {
- $form_info['order'] = $form_info['add order'];
- }
-
- // If step is unset, we're creating a new one. Wipe out our values and start
- // over.
- if (!isset($step) || !$page = delegator_page_get_page_cache('::new')) {
- $step = current(array_keys($form_info['order']));
- $page = delegator_page_new();
- $page->task = $task_name;
- $page->new = TRUE;
- $page->locked = FALSE;
- delegator_page_set_page_cache($page);
- }
-
- ctools_include('wizard');
- $form_state = array(
- 'cache name' => '::new',
- 'task' => $task,
- 'page' => $page,
- 'type' => 'add',
- );
- $output = ctools_wizard_multistep_form($form_info, $step, $form_state);
-
- if (!$output) {
- // redirect.
- drupal_redirect_form(array(), $form_state['redirect']);
- }
-
- return $output;
-}
-
-/**
- * Page callback to add a subtask.
- */
-function delegator_page_edit_subtask($task_name, $page_name, $step = NULL) {
- if (!$page = delegator_page_get_page_cache($page_name)) {
- return drupal_not_found();
- }
-
- // We load the task to make sure our .inc file is loaded.
- $task = delegator_get_task($task_name);
-
- $function = ctools_plugin_get_function($task, 'form info');
- $form_info = $function();
-
- $path = str_replace('%page_name', $page_name, $form_info['edit path']);
- $form_info = array(
- 'path' => $path,
- 'show trail' => FALSE,
- 'show return' => TRUE,
- 'show cancel' => TRUE,
- 'return path' => 'admin/build/pages',
- ) + $form_info;
-
- if (!empty($form_info['edit order'])) {
- $form_info['order'] = $form_info['edit order'];
- }
-
- // If step is unset, go with the basic step.
- if (!isset($step)) {
- $step = current(array_keys($form_info['order']));
- }
-
- ctools_include('wizard');
- $form_state = array(
- 'task' => $task,
- 'cache name' => $page_name,
- 'page' => $page,
- 'type' => 'edit',
- );
- $output = ctools_wizard_multistep_form($form_info, $step, $form_state);
-
- if (!$output) {
- // redirect.
- drupal_redirect_form(array(), $form_state['redirect']);
- }
-
- if (!empty($page->locked)) {
- $output = theme('delegator_page_lock', $page->locked, $page_name) . $output;
- }
- else if (!empty($page->changed)) {
- $output = theme('delegator_page_changed') . $output;
- }
-
- return $output;
-}
-
-/**
- * Callback generated when the add page process is finished.
- */
-function delegator_page_add_subtask_finish(&$form_state) {
- $page = &$form_state['page'];
- // Ensure $page->arguments contains only real arguments:
- $arguments = delegator_page_get_named_arguments($page->path);
- $args = array();
- foreach ($arguments as $keyword => $position) {
- if (isset($page->arguments[$keyword])) {
- $args[$keyword] = $page->arguments[$keyword];
- }
- else {
- $args[$keyword] = array(
- 'id' => '',
- 'identifier' => '',
- 'argument' => '',
- 'settings' => array(),
- );
- }
- }
- $page->arguments = $args;
- // Create a real object from the cache
- delegator_page_save($page);
-
- // Clear the cache
- delegator_page_clear_page_cache($form_state['cache name']);
-
- // Force a menu rebuild to recognize our new subtask
- menu_rebuild();
-
- if (isset($form_state['create task handler'])) {
- require_once './' . drupal_get_path('module', 'delegator') . '/delegator.admin.inc';
- $task = $form_state['task'];
- $task_name = delegator_make_task_name($task['name'], $page->name);
- $cache = delegator_admin_get_task_cache($task, $page->name, $form_state['task_handlers']);
- $plugin = delegator_get_task_handler($form_state['create task handler']);
-
- // Create a new handler.
- $handler = delegator_new_task_handler($task, $page->name, $plugin, 0, $cache);
- $form_state['redirect'] = delegator_admin_new_task_handler($handler, $task_name, $task, $page->name, $cache, $plugin);
- }
- else if ($form_state['type'] == 'add' && !empty($form_state['task']['uses handlers'])) {
- // Redirect to the new page's task handler editor.
- $form_state['redirect'] = 'admin/build/delegator/page-' . $page->name;
- }
- else {
- $form_state['redirect'] = 'admin/build/pages';
- }
-}
-
-/**
- * Callback generated when the 'next' button is clicked.
- *
- * All we do here is store the cache.
- */
-function delegator_page_add_subtask_next(&$form_state) {
- // Update the cache with changes.
- delegator_page_set_page_cache($form_state['page']);
-}
-
-/**
- * Callback generated when the 'cancel' button is clicked.
- *
- * All we do here is clear the cache.
- */
-function delegator_page_add_subtask_cancel(&$form_state) {
- // Update the cache with changes.
- delegator_page_clear_page_cache($form_state['cache name']);
-}
-
-/**
- * Basic settings form for a delegator page.
- */
-function delegator_page_form_basic(&$form, &$form_state) {
- $page = &$form_state['page'];
-
- $form['name'] = array(
- '#type' => 'textfield',
- '#title' => t('Name'),
- '#description' => t('The machine readable name of this page. It must be unique, and it must contain only alphanumeric characters and underscores. Once created, you will not be able to change this value!'),
- '#default_value' => $page->name,
- );
-
- if (isset($page->pid) || !empty($page->import)) {
- $form['name']['#disabled'] = TRUE;
- }
-
- $form['admin_title'] = array(
- '#type' => 'textfield',
- '#title' => t('Administrative title'),
- '#description' => t('The name of this page. This will appear in the administrative interface to easily identify it.'),
- '#default_value' => $page->admin_title,
- );
-
- // path
- $form['path'] = array(
- '#type' => 'textfield',
- '#title' => t('Path'),
- '#description' => t('The URL path to get to this page. You may create named placeholders for variable parts of the path by using %name for required elements and !name for optional elements. For example: "node/%node/foo", "forum/%forum" or "dashboard/!input". These named placeholders can be turned into contexts on the arguments form.'),
- '#default_value' => $page->path,
- );
-}
-
-function delegator_page_form_basic_validate_filter($value) {
- return $value === -1;
-}
-
-/**
- * Validate the basic form.
- */
-function delegator_page_form_basic_validate(&$form, &$form_state) {
- // Ensure path is unused by other pages.
- $pages = delegator_page_load_all();
- $name = !empty($form_state['values']['name']) ? $form_state['values']['name'] : $form_state['page']->name;
- if (empty($name)) {
- form_error($form['name'], t('Name is required.'));
- }
-
- foreach ($pages as $page) {
- if ($page->name != $name && $page->path == $form_state['values']['path'] && empty($page->disabled)) {
- form_error($form['path'], t('That path is used by another page: @page', array('@page' => $page->admin_title)));
- }
- }
-
- // Ensure path is unused by things NOT pages. We do the double check because
- // we're checking against our page callback.
-
- // Replace named placeholders with our own placeholder to load contexts.
- $path = array();
- if (empty($form_state['values']['path'])) {
- form_error($form['path'], t('Path is required.'));
- // stop processing here if there is no path.
- return;
- }
-
- $found = FALSE;
- $error = FALSE;
- foreach (explode('/', $form_state['values']['path']) as $bit) {
- if (!isset($bit) || $bit === '') {
- continue;
- }
-
- if ($bit[0] == '%') {
- if ($found) {
- form_error($form['path'], t('You cannot have a dynamic path element after an optional path element.'));
- }
- $path[] = '%';
- }
- else if ($bit[0] == '!') {
- $found = TRUE;
- }
- else {
- if ($found) {
- form_error($form['path'], t('You cannot have a static path element after an optional path element.'));
- }
- $path[] = $bit;
- }
- }
-
- $path = implode('/', $path);
- $result = db_query("SELECT * FROM {menu_router} WHERE path = '%s'", $path);
- while ($router = db_fetch_object($result)) {
- if ($router->page_callback != 'delegator_page_execute') {
- form_error($form['path'], t('That path is already in used. This system cannot override existing paths.'));
- }
- }
-
- // Ensure path is properly formed.
- $args = delegator_page_get_named_arguments($form_state['values']['path']);
- if ($invalid_args = array_filter($args, 'delegator_page_form_basic_validate_filter')) {
- foreach ($invalid_args as $arg => $position) {
- form_error($form['path'], t('Duplicated argument %arg', array('%arg' => $arg)));
- }
- }
-
- if (isset($args['%'])) {
- form_error($form['path'], t('Invalid arg <em>%</em>. All arguments must be named with keywords.'));
- }
-
- if (empty($args)) {
- $form_state['clicked_button']['#next'] = 'access';
- }
-
- // Do name checking if it's something that can be changed.
- if (empty($form_state['page']->pid)) {
- // Ensure name fits the rules:
- if (preg_match('/[^a-zA-Z0-9_]/', $form_state['values']['name'])) {
- form_error($form['name'], t('Page name must be alphanumeric or underscores only.'));
- }
-
- // Ensure name is unique.
- if (delegator_page_load($form_state['values']['name'])) {
- form_error($form['name'], t('That name is already used; the name must be unique.'));
- }
- }
-}
-
-/**
- * Store the values from the basic settings form.
- */
-function delegator_page_form_basic_submit(&$form, &$form_state) {
- if ($form_state['page']->locked) {
- drupal_set_message(t('Unable to update page due to lock.'), 'error');
- return;
- }
-
- if (!isset($form_state['page']->pid) && empty($form_state['page']->import)) {
- $form_state['page']->name = $form_state['values']['name'];
- }
-
- $form_state['page']->admin_title = $form_state['values']['admin_title'];
- $form_state['page']->path = $form_state['values']['path'];
-}
-
-/**
- * Form to handle menu item controls.
- */
-function delegator_page_form_menu(&$form, &$form_state) {
- ctools_include('dependent');
- $form['menu'] = array(
- '#prefix' => '<div class="clear-block">',
- '#suffix' => '</div>',
- '#tree' => TRUE,
- );
- $menu = $form_state['page']->menu;
- if (empty($menu)) {
- $menu = array(
- 'type' => 'none',
- 'title' => '',
- 'weight' => 0,
- 'name' => 'navigation',
- 'parent' => array(
- 'type' => 'none',
- 'title' => '',
- 'weight' => 0,
- 'name' => 'navigation',
- ),
- );
- }
-
- $form['menu']['type'] = array(
- '#title' => t('Type'),
- '#type' => 'radios',
- '#options' => array(
- 'none' => t('No menu entry'),
- 'normal' => t('Normal menu entry'),
- 'tab' => t('Menu tab'),
- 'default tab' => t('Default menu tab'),
- ),
- '#default_value' => $menu['type'],
- );
-
- $form['menu']['title'] = array(
- '#title' => t('Title'),
- '#type' => 'textfield',
- '#default_value' => $menu['title'],
- '#description' => t('If set to normal or tab, enter the text to use for the menu item.'),
- '#process' => array('ctools_dependent_process'),
- '#dependency' => array('radio:menu[type]' => array('normal', 'tab', 'default tab')),
- );
-
- list($major, $minor) = explode('.', VERSION, 2);
-
- $form['menu']['name-warning'] = array(
- '#type' => 'markup',
- '#prefix' => '<div class="warning">',
- '#value' => t("Warning: Changing this item's menu will not work reliably in Drupal 6.4 or earlier. Please upgrade your copy of Drupal at !url.", array('!url' => l('drupal.org', 'http://drupal.org/project/Drupal+project'))),
- '#suffix' => '</div>',
- '#process' => array('ctools_dependent_process'),
- '#dependency' => array('radio:menu[type]' => array('normal')),
- '#access' => ($minor < 5),
- );
-
- // Only display the menu selector if menu module is enabled.
- if (module_exists('menu')) {
- $form['menu']['name'] = array(
- '#title' => t('Menu'),
- '#type' => 'select',
- '#options' => menu_get_menus(),
- '#default_value' => $menu['name'],
- '#description' => t('Insert item into an available menu.'),
- '#process' => array('ctools_dependent_process'),
- '#dependency' => array('radio:menu[type]' => array('normal')),
- );
- }
- else {
- $form['menu']['name'] = array(
- '#type' => 'value',
- '#value' => $menu['name'],
- );
- $form['menu']['markup'] = array(
- '#value' => t('Menu selection requires the activation of menu module.'),
- );
- }
- $form['menu']['weight'] = array(
- '#title' => t('Weight'),
- '#type' => 'textfield',
- '#default_value' => isset($menu['weight']) ? $menu['weight'] : 0,
- '#description' => t('The lower the weight the higher/further left it will appear.'),
- '#process' => array('ctools_dependent_process'),
- '#dependency' => array('radio:menu[type]' => array('normal', 'tab', 'default tab')),
- );
-
- $form['menu']['parent']['type'] = array(
- '#prefix' => '<div id="edit-menu-parent-type-wrapper">',
- '#suffix' => '</div>',
- '#title' => t('Parent menu item'),
- '#type' => 'radios',
- '#options' => array('none' => t('Already exists'), 'normal' => t('Normal menu item'), 'tab' => t('Menu tab')),
- '#default_value' => $menu['parent']['type'],
- '#description' => t('When providing a menu item as a default tab, Drupal needs to know what the parent menu item of that tab will be. Sometimes the parent will already exist, but other times you will need to have one created. The path of a parent item will always be the same path with the last part left off. i.e, if the path to this view is <em>foo/bar/baz</em>, the parent path would be <em>foo/bar</em>.'),
- '#process' => array('expand_radios', 'ctools_dependent_process'),
- '#dependency' => array('radio:menu[type]' => array('default tab')),
- );
- $form['menu']['parent']['title'] = array(
- '#title' => t('Parent item title'),
- '#type' => 'textfield',
- '#default_value' => $menu['parent']['title'],
- '#description' => t('If creating a parent menu item, enter the title of the item.'),
- '#process' => array('ctools_dependent_process'),
- '#dependency' => array('radio:menu[type]' => array('default tab'), 'radio:menu[parent][type]' => array('normal', 'tab')),
- '#dependency_count' => 2,
- );
- // Only display the menu selector if menu module is enabled.
- if (module_exists('menu')) {
- $form['menu']['parent']['name'] = array(
- '#title' => t('Parent item menu'),
- '#type' => 'select',
- '#options' => menu_get_menus(),
- '#default_value' => $menu['parent']['name'],
- '#description' => t('Insert item into an available menu.'),
- '#process' => array('ctools_dependent_process'),
- '#dependency' => array('radio:menu[type]' => array('default tab'), 'radio:menu[parent][type]' => array('normal')),
- '#dependency_count' => 2,
- );
- }
- else {
- $form['menu']['parent']['name'] = array(
- '#type' => 'value',
- '#value' => $menu['parent']['name'],
- );
- }
- $form['menu']['parent']['weight'] = array(
- '#title' => t('Tab weight'),
- '#type' => 'textfield',
- '#default_value' => $menu['parent']['weight'],
- '#size' => 5,
- '#description' => t('If the parent menu item is a tab, enter the weight of the tab. The lower the number, the more to the left it will be.'),
- '#process' => array('ctools_dependent_process'),
- '#dependency' => array('radio:menu[type]' => array('default tab'), 'radio:menu[parent][type]' => array('tab')),
- '#dependency_count' => 2,
- );
-}
-
-/**
- * Submit handler for the menu form for add/edit page task.
- */
-function delegator_page_form_menu_submit(&$form, &$form_state) {
- if ($form_state['page']->locked) {
- drupal_set_message(t('Unable to update page due to lock.'), 'error');
- return;
- }
-
- $form_state['page']->menu = $form_state['values']['menu'];
-}
-
-/**
- * Form to handle menu item controls.
- */
-function delegator_page_form_access(&$form, &$form_state) {
- ctools_include('context');
- $form_state['module'] = 'delegator_page';
- $form_state['callback argument'] = $form_state['cache name'];
- $form_state['access'] = $form_state['page']->access;
- $form_state['no buttons'] = TRUE;
- $form_state['contexts'] = array();
-
- // Load contexts based on argument data:
- if ($arguments = _delegator_page_get_arguments($form_state['page'])) {
- $form_state['contexts'] = ctools_context_get_placeholders_from_argument($arguments);
- }
-
- ctools_include('context-access-admin');
- $form = array_merge($form, ctools_access_admin_form($form_state));
-}
-
-/**
- * Submit handler to deal with access control changes.
- */
-function delegator_page_form_access_submit(&$form, &$form_state) {
- if ($form_state['page']->locked) {
- drupal_set_message(t('Unable to update page due to lock.'), 'error');
- return;
- }
-
- $form_state['page']->access['logic'] = $form_state['values']['logic'];
-}
-
-/**
- * Form to handle assigning argument handlers to named arguments.
- */
-function delegator_page_form_argument(&$form, &$form_state) {
- $path = $form_state['page']->path;
- $page = &$form_state['page'];
-
- $arguments = delegator_page_get_named_arguments($path);
-
- $form['table'] = array(
- '#theme' => 'delegator_page_form_argument_table',
- '#delegator-path' => $path,
- 'argument' => array(),
- );
-
- $cache_name = $form_state['cache name'];
- foreach ($arguments as $keyword => $position) {
- $conf = array();
-
- if (isset($page->temporary_arguments[$keyword]) && !empty($form_state['allow temp'])) {
- $conf = $page->temporary_arguments[$keyword];
- }
- else if (isset($page->arguments[$keyword])) {
- $conf = $page->arguments[$keyword];
- }
-
- $context = t('No context assigned');
-
- $plugin = array();
- if ($conf && isset($conf['name'])) {
- ctools_include('context');
- $plugin = ctools_get_argument($conf['name']);
-
- if (isset($plugin['title'])) {
- $context = $plugin['title'];
- }
- }
-
- $form['table']['argument'][$keyword]['#keyword'] = $keyword;
- $form['table']['argument'][$keyword]['#position'] = $position;
- $form['table']['argument'][$keyword]['#context'] = $context;
-
- // The URL for this ajax button
- $form['table']['argument'][$keyword]['change-url'] = array(
- '#attributes' => array('class' => "delegator-context-$keyword-change-url"),
- '#type' => 'hidden',
- '#value' => url("admin/build/delegator/argument/change/$cache_name/$keyword", array('absolute' => TRUE)),
- );
- $form['table']['argument'][$keyword]['change'] = array(
- '#type' => 'submit',
- '#value' => t('Change'),
- '#attributes' => array('class' => 'ctools-use-modal'),
- '#id' => "delegator-context-$keyword-change",
- );
-
- $form['table']['argument'][$keyword]['settings'] = array();
-
- // Only show the button if this has a settings form available:
- if (!empty($plugin)) {
- // The URL for this ajax button
- $form['table']['argument'][$keyword]['settings-url'] = array(
- '#attributes' => array('class' => "delegator-context-$keyword-settings-url"),
- '#type' => 'hidden',
- '#value' => url("admin/build/delegator/argument/settings/$cache_name/$keyword", array('absolute' => TRUE)),
- );
- $form['table']['argument'][$keyword]['settings'] = array(
- '#type' => 'submit',
- '#value' => t('Settings'),
- '#attributes' => array('class' => 'ctools-use-modal'),
- '#id' => "delegator-context-$keyword-settings",
- );
- }
- }
-}
-
-/**
- * Theme the table for this form.
- */
-function theme_delegator_page_form_argument_table($form) {
- $header = array(
- array('data' => t('Argument'), 'class' => 'delegator-argument'),
- array('data' => t('Position in path'), 'class' => 'delegator-position'),
- array('data' => t('Context assigned'), 'class' => 'delegator-context'),
- array('data' => t('Operations'), 'class' => 'delegator-operations'),
- );
-
- $rows = array();
-
- ctools_include('modal');
- ctools_modal_add_js();
- foreach (element_children($form['argument']) as $key) {
- $row = array();
- $row[] = '%' . check_plain($form['argument'][$key]['#keyword']);
- $row[] = check_plain($form['argument'][$key]['#position']);
- $row[] = $form['argument'][$key]['#context'] . ' &nbsp; ' . drupal_render($form['argument'][$key]['change']);;
- $row[] = drupal_render($form['argument'][$key]['settings']) . drupal_render($form['argument'][$key]);
-
- $rows[] = array('data' => $row);
- }
-
- if (!$rows) {
- $rows[] = array(array('data' => t('The path %path has no arguments to configure.', array('%path' => $form['#delegator-path'])), 'colspan' => 4));
- }
-
- $attributes = array(
- 'id' => 'delegator-argument-table',
- );
-
- $output = theme('table', $header, $rows, $attributes);
- return $output;
-}
-
-/**
- * Ajax entry point to edit an item
- */
-function delegator_page_subtask_argument_ajax($step = NULL, $cache_name = NULL, $keyword = NULL) {
- ctools_include('ajax');
- ctools_include('modal');
- ctools_include('context');
- ctools_include('wizard');
-
- if (!$step) {
- return ctools_ajax_render_error();
- }
-
- if (!$page = delegator_page_get_page_cache($cache_name)) {
- return ctools_ajax_render_error(t('Invalid object name.'));
- }
-
- $path = $page->path;
- $arguments = delegator_page_get_named_arguments($path);
-
- // Load stored object from cache.
- if (!isset($arguments[$keyword])) {
- return ctools_ajax_render_error(t('Invalid keyword.'));
- }
-
- // Set up wizard info
- $form_info = array(
- 'id' => 'delegator_page_argument',
- 'path' => "admin/build/delegator/argument/%step/$cache_name/$keyword",
- 'show cancel' => TRUE,
- 'next callback' => 'delegator_page_argument_next',
- 'finish callback' => 'delegator_page_argument_finish',
- 'cancel callback' => 'delegator_page_argument_cancel',
- 'order' => array(
- 'change' => t('Change context type'),
- 'settings' => t('Argument settings'),
- ),
- 'forms' => array(
- 'change' => array(
- 'title' => t('Change argument'),
- 'form id' => 'delegator_page_argument_form_change',
- ),
- 'settings' => array(
- 'title' => t('Argument settings'),
- 'form id' => 'delegator_page_argument_form_settings',
- ),
- ),
- );
-
- $form_state = array(
- 'cache name' => $cache_name,
- 'keyword' => $keyword,
- 'page' => $page,
- 'ajax' => TRUE,
- 'modal' => TRUE,
- 'commands' => array(),
- );
-
- // With 'modal' and 'ajax' true, rendering automatically happens here so
- // we do nothing with the result.
- ctools_wizard_multistep_form($form_info, $step, $form_state);
-}
-
-/**
- * Callback generated when the add page process is finished.
- */
-function delegator_page_argument_finish(&$form_state) {
- // Check to see if there are changes.
- $page = &$form_state['page'];
- $keyword = &$form_state['keyword'];
-
- if (isset($page->temporary_arguments[$keyword])) {
- $page->arguments[$keyword] = $page->temporary_arguments[$keyword];
- }
-
- if (isset($page->temporary_arguments)) {
- unset($page->temporary_arguments);
- }
-
- // Update the cache with changes.
- delegator_page_set_page_cache($form_state['page']);
-
- // Rerender the table so we can ajax it back in.
- // Go directly to the form and retrieve it using a blank form and
- // a clone of our current form state. This is an abbreviated
- // drupal_get_form that is halted prior to render and is never
- // fully processed, but is guaranteed to produce the same form we
- // started with so we don't have to do crazy stuff to rerender
- // just part of it.
-
- // @todo should there be a tool to do this?
-
- $clone_state = $form_state;
- $clone_state['allow temp'] = TRUE;
- $form = array();
- delegator_page_form_argument($form, $clone_state);
- drupal_prepare_form('delegator_page_form_argument', $form, $clone_state);
- $form['#post'] = array();
- $form = form_builder('delegator_page_form_argument', $form, $clone_state);
-
- // Render just the table portion.
- $output = drupal_render($form['table']);
- $form_state['commands'][] = ctools_ajax_command_replace('#delegator-argument-table', $output);
-}
-
-/**
- * Callback generated when the 'next' button is clicked.
- *
- * All we do here is store the cache.
- */
-function delegator_page_argument_next(&$form_state) {
- // Update the cache with changes.
- delegator_page_set_page_cache($form_state['page']);
-}
-
-/**
- * Callback generated when the 'cancel' button is clicked.
- *
- * We might have some temporary data lying around. We must remove it.
- */
-function delegator_page_argument_cancel(&$form_state) {
- if (isset($form_state['page']->temporary_arguments)) {
- unset($form_state['page']->temporary_arguments);
- // Update the cache with changes.
- delegator_page_set_page_cache($form_state['page']);
- }
-}
-
-/**
- * Basic settings form for a delegator page.
- */
-function delegator_page_argument_form_change(&$form, &$form_state) {
- $page = &$form_state['page'];
- $keyword = &$form_state['keyword'];
-
- ctools_include('context');
- $plugins = ctools_get_arguments();
-
- $options = array();
- foreach ($plugins as $id => $plugin) {
- $options[$id] = $plugin['title'];
- }
-
- asort($options);
-
- $options = array('' => t('No context selected')) + $options;
-
- $argument = '';
- if (isset($page->arguments[$keyword]) && isset($page->arguments[$keyword]['name'])) {
- $argument = $page->arguments[$keyword]['name'];
- }
-
- $form['argument'] = array(
- '#type' => 'radios',
- '#options' => $options,
- '#default_value' => $argument,
- );
-}
-
-/**
- * Submit handler to change an argument.
- */
-function delegator_page_argument_form_change_submit(&$form, &$form_state) {
- if ($form_state['page']->locked) {
- drupal_set_message(t('Unable to update page due to lock.'), 'error');
- return;
- }
-
- $page = &$form_state['page'];
- $keyword = &$form_state['keyword'];
- $argument = $form_state['values']['argument'];
-
- // If the argument is not changing, we do not need to do anything.
- if (isset($page->arguments[$keyword]['name']) && $page->arguments[$keyword]['name'] == $argument) {
- // Set the task to cancel since no change means do nothing:
- $form_state['clicked_button']['#wizard type'] = 'cancel';
- return;
- }
-
- ctools_include('context');
-
- // If switching to the no context, just wipe out the old data.
- if (empty($argument)) {
- $form_state['clicked_button']['#wizard type'] = 'finish';
- $page->temporary_arguments[$keyword] = array(
- 'settings' => array(),
- 'identifier' => t('No context'),
- );
- return;
- }
-
- $plugin = ctools_get_argument($argument);
-
- // Acquire defaults.
- $settings = array();
-
- if (isset($plugin['default'])) {
- if (is_array($plugin['default'])) {
- $settings = $plugin['default'];
- }
- else if (function_exists($plugin['default'])) {
- $settings = $plugin['default']();
- }
- }
-
- $id = ctools_context_next_id($page->arguments, $argument);
- $title = isset($plugin['title']) ? $plugin['title'] : t('No context');
-
- // Set the new argument in a temporary location.
- $page->temporary_arguments[$keyword] = array(
- 'id' => $id,
- 'identifier' => $title . ($id > 1 ? ' ' . $id : ''),
- 'name' => $argument,
- 'settings' => $settings,
- );
-}
-
-/**
- * Basic settings form for a delegator page.
- */
-function delegator_page_argument_form_settings(&$form, &$form_state) {
- $page = &$form_state['page'];
- $keyword = &$form_state['keyword'];
-
- if (isset($page->temporary_arguments[$keyword])) {
- $conf = $page->temporary_arguments[$keyword];
- }
- else if (isset($page->arguments[$keyword])) {
- $conf = $page->temporary_arguments[$keyword] = $page->arguments[$keyword];
- }
-
- if (!isset($conf)) {
- // This should be impossible and thus never seen.
- $form['error'] = array('#value' => t('Error: missing argument.'));
- return;
- }
-
- ctools_include('context');
- $plugin = ctools_get_argument($conf['name']);
-
- $form['settings'] = array(
- '#tree' => TRUE,
- );
-
- $form['identifier'] = array(
- '#type' => 'textfield',
- '#title' => t('Context identifier'),
- '#description' => t('This is the title of the context used to identify it later in the administrative process. This will never be shown to a user.'),
- '#default_value' => $conf['identifier'],
- );
-
- if (!$plugin) {
- // This should be impossible and thus never seen.
- $form['error'] = array('#value' => t('Error: missing or invalid argument plugin %argument.', array('%argument', $argument)));
- return;
- }
-
- if ($function = ctools_plugin_get_function($plugin, 'settings form')) {
- $function($form, $form_state, $conf['settings']);
- }
-
- $form_state['plugin'] = $plugin;
-}
-
-/**
- * Validate handler for argument settings.
- */
-function delegator_page_argument_form_settings_validate(&$form, &$form_state) {
- if ($function = ctools_plugin_get_function($form_state['plugin'], 'settings form validate')) {
- $function($form, $form_state);
- }
-}
-
-/**
- * Submit handler for argument settings.
- */
-function delegator_page_argument_form_settings_submit(&$form, &$form_state) {
- if ($form_state['page']->locked) {
- drupal_set_message(t('Unable to update page due to lock.'), 'error');
- return;
- }
-
- if ($function = ctools_plugin_get_function($form_state['plugin'], 'settings form submit')) {
- $function($form, $form_state);
- }
-
- $page = &$form_state['page'];
- $keyword = &$form_state['keyword'];
- // Copy the form to our temporary location which will get moved again when
- // finished. Yes, finished is always next but finish can happen from other
- // locations so we funnel through that path rather than duplicate.
- $page->temporary_arguments[$keyword]['identifier'] = $form_state['values']['identifier'];
- if (isset($form_state['values']['settings'])) {
- $page->temporary_arguments[$keyword]['settings'] = $form_state['values']['settings'];
- }
- else {
- $page->temporary_arguments[$keyword]['settings'] = array();
- }
-}
-
-/**
- * Form to configure a page to have a single task handler or multiple
- * task handlers.
- */
-function delegator_page_argument_form_multiple(&$form, &$form_state) {
- $task = $form_state['task'];
- $task_handlers = delegator_load_task_handlers($task, $form_state['page']->name);
-
- $form['multiple'] = array(
- '#type' => 'radios',
- '#options' => array(
- 1 => t('Allow multiple handlers for this page.'),
- 0 => t('Create a single handler for this page.'),
- ),
- '#default_value' => $form_state['page']->multiple,
- '#description' => t('By allowing multiple handlers, the task handler UI will open up, allowing you to add and remove task handlers and adjust their priority.'),
- );
-
- // If they've selected multiple handlers and actually have more than one
- // don't let them switch back to single.
- if ($form_state['page']->multiple && count($task_handlers) > 1) {
- $form['multiple']['#disabled'] = TRUE;
- $form['multiple']['#description'] .= t('You may not modify this value while multiple task handlers exist. If you wish to change this to single only, you must reduce the number of task handlers attached to the page to zero or one.');
- }
-
- $form_state['task_handlers'] = $task_handlers;
-
- if ($form_state['type'] == 'add' || empty($task_handlers)) {
- // Get a list of possible task handlers for this task.
- $task_handler_plugins = delegator_get_task_handler_plugins($task);
- foreach ($task_handler_plugins as $id => $plugin) {
- $options[$id] = $plugin['title'];
- }
-
- ctools_include('dependent');
- $form['handler'] = array(
- '#title' => t('Select the handler for this page'),
- '#type' => 'select',
- '#options' => $options,
- '#process' => array('ctools_dependent_process'),
- '#dependency' => array('radio:multiple' => array(0)),
- );
- }
-}
-
-/**
- * Submit handler for the multiple form.
- */
-function delegator_page_argument_form_multiple_submit(&$form, &$form_state) {
- if ($form_state['page']->locked) {
- drupal_set_message(t('Unable to update page due to lock.'), 'error');
- return;
- }
-
- $form_state['page']->multiple = $form_state['values']['multiple'];
-
- // The task handler will be created in the _finish hook.
- if (!$form_state['page']->multiple && empty($task_handlers)) {
- $form_state['create task handler'] = $form_state['values']['handler'];
- }
-}
-
-/**
- * Form to break a lock on a delegator page.
- */
-function delegator_page_break_lock_subtask(&$form_state, $name) {
- // This ensures the task .inc file is loaded
- delegator_get_task('page');
- $page = delegator_page_load($name);
-
- $form_state['task'] = delegator_get_task($page->task);
- $form_state['name'] = $name;
-
- ctools_include('object-cache');
- $lock = ctools_object_cache_test('delegator_page', $name);
-
- $form = array();
-
- if (empty($lock)) {
- return array('message' => array('#value' => t('There is no lock on this page to break.')));
- }
-
- $cancel = 'admin/build/pages';
- if (!empty($_REQUEST['cancel'])) {
- $cancel = $_REQUEST['cancel'];
- }
-
- $account = user_load($lock->uid);
- return confirm_form($form,
- t('Are you sure you want to break this lock?'),
- $cancel,
- t('By breaking this lock, any unsaved changes made by !user will be lost!', array('!user' => theme('username', $account))),
- t('Break lock'),
- t('Cancel')
- );
-}
-
-/**
- * Submit handler to break_lock a view.
- */
-function delegator_page_break_lock_subtask_submit(&$form, &$form_state) {
- ctools_object_cache_clear_all('delegator_page', $form_state['name']);
- drupal_set_message(t('The lock has been broken and you may now edit this page.'));
- $form_state['redirect'] = 'admin/build/pages';
-}
-
-/**
- * Import a task handler from cut & paste
- */
-function delegator_page_import_subtask(&$form_state, $task_name) {
- $form_state['task'] = delegator_get_task($task_name);
-
- drupal_set_title(t('Import page'));
- $form['name'] = array(
- '#type' => 'textfield',
- '#title' => t('Page name'),
- '#description' => t('Enter the name to use for this page if it is different from the source page. Leave blank to use the name of the page.'),
- );
-
- $form['object'] = array(
- '#type' => 'textarea',
- '#title' => t('Paste page code here'),
- '#rows' => 15,
- );
-
- $form['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Import'),
- );
- return $form;
-}
-
-/**
- * Ensure we got a valid page.
- */
-function delegator_page_import_subtask_validate($form, &$form_state) {
- ob_start();
- eval($form_state['values']['object']);
- ob_end_clean();
-
- if (!isset($page) || !is_object($page)) {
- form_error($form['object'], t('Unable to interpret page code.'));
- }
-
- $name = !empty($form_state['values']['name']) ? $form_state['values']['name'] : $page->name;
-
- // See if the task is already locked.
- $cache = delegator_page_get_page_cache($name);
- if (!empty($cache->locked)) {
- $account = user_load($cache->locked->uid);
- $username = theme('username', $account);
- $lock_age = format_interval(time() - $cache->locked->updated);
- $break = url('admin/build/pages/break-lock/' . $name, array('query' => array('destination' => $_GET['q'], 'cancel' => $_GET['q'])));
-
- form_error($form['object'], t('Unable to import page because it is being edited by user !user, and is therefore locked from editing by others. This lock is !age old. Click here to <a href="!break">break this lock</a>.', array('!user' => $username, '!age' => $lock_age, '!break' => $break)));
- }
-
- $page->type = t('Normal');
-
- ctools_include('export');
- $page->export_type = EXPORT_IN_DATABASE;
-
- if (isset($cache) && isset($cache->pid)) {
- drupal_set_message(t('Warning: The page you are importing already exists and this operation will overwrite an existing page. If this is not what you intend, you may Cancel this. You should then choose a different page name to ensure you use a unique name.'), 'warning');
- $page->pid = $cache->pid;
- }
-
- if (preg_match('/[^a-zA-Z0-9_]/', $name)) {
- form_error($form['name'], t('Page name must be alphanumeric or underscores only.'));
- }
-
- if ($page->name != $name) {
- $page->old_name = $page->name;
- $page->name = $name;
- }
- $page->import = TRUE; // set a flag so that we can get special import behavior.
- $form_state['page'] = $page;
-}
-
-/**
- * Submit the import page to create the new page and redirect.
- */
-function delegator_page_import_subtask_submit($form, &$form_state) {
- // Use the one from the database or an updated one in cache?
- $page = &$form_state['page'];
-
- // If the name was changed, go through and rename any default handlers so
- // that they'll actually work.
- if (isset($page->old_name) && isset($page->default_handlers)) {
- foreach ($page->default_handlers as $name => $handler) {
- // replace the old name with the new name at the start of the name string.
- $page->default_handlers[$name]->name = preg_replace('/^page_' . $page->old_name . '/', 'page_' . $page->name, $handler->name);
- }
- }
-
- delegator_page_set_page_cache($page);
- $function = ctools_plugin_get_function($form_state['task'], 'form info');
- $form_info = $function();
-
- $form_state['redirect'] = str_replace('%page_name/%step', $page->name, $form_info['edit path']);
-}
-
-/**
- * Entry point to export a page.
- */
-function delegator_page_export_subtask($name, $handlers = FALSE) {
- // This ensures the task .inc file is loaded
- delegator_get_task('page');
- $page = delegator_page_load($name);
- $form_state['task'] = delegator_get_task($page->task);
-
- if (!$page) {
- return drupal_not_found();
- }
-
- $title = $page->admin_title;
- if ($handlers) {
- drupal_set_title(t('Export task page "@title" with handlers', array('@title' => $page->admin_title)));
- $export = delegator_page_export($page, TRUE);
- }
- else {
- drupal_set_title(t('Export task page "@title"', array('@title' => $page->admin_title)));
- $export = delegator_page_export($page);
- }
-
- return drupal_get_form('ctools_export_form', $export, $title);
-}
-
-/**
- * Entry point to clone a page.
- */
-function delegator_page_clone_subtask(&$form_state, $name) {
- // This ensures the task .inc file is loaded
- delegator_get_task('page');
- $page = delegator_page_load($name);
- $form_state['task'] = delegator_get_task($page->task);
-
- if (!$page) {
- drupal_not_found();
- exit;
- }
-
- $form_state['page'] = $page;
- $form = array();
-
- $form['name'] = array(
- '#type' => 'textfield',
- '#title' => t('Page name'),
- '#description' => t('Enter the name to the new page It must be unique and contain only alphanumeric characters and underscores.'),
- );
-
- $form['handlers'] = array(
- '#type' => 'checkbox',
- '#title' => t('Clone handlers'),
- '#description' => t('If checked all task handlers associated with the task will be cloned as well.'),
- );
-
- $form['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Clone'),
- );
-
- return $form;
-}
-
-/**
- * Validate clone page form.
- */
-function delegator_page_clone_subtask_validate(&$form, &$form_state) {
- // Ensure name fits the rules:
- if (preg_match('/[^a-zA-Z0-9_]/', $form_state['values']['name'])) {
- form_error($form['name'], t('Page name must be alphanumeric or underscores only.'));
- }
-
- // Ensure name is unique.
- if (delegator_page_load($form_state['values']['name'])) {
- form_error($form['name'], t('That name is already used; the name must be unique.'));
- }
-}
-
-/**
- * submit clone page form.
- *
- * Load the page, change the name(s) to protect the innocent, and if
- * requested, load all the task handlers so that they get saved properly too.
- */
-function delegator_page_clone_subtask_submit(&$form, &$form_state) {
- $page = $form_state['page'];
- $task = $form_state['task'];
- $name = $form_state['values']['name'];
-
- if (!empty($form_state['values']['handlers'])) {
- $handlers = delegator_load_task_handlers($task, $page->name);
- $page->default_handlers = array();
-
- foreach ($handlers as $handler_name => $handler) {
- // replace the old name with the new name at the start of the name string.
- $handler->name = preg_replace('/^page_' . $page->name . '/', 'page_' . $name, $handler->name);
- $handler->subtask = $name;
- // Pretend these handlers are all default handlers so they get saved as new.
- $handler->type = t('Default');
- $handler->export_type = EXPORT_IN_CODE;
- $page->default_handlers[$handler_name] = $handler;
- }
- }
-
- $page->name = $name;
- unset($page->pid);
-
- delegator_page_set_page_cache($page);
- $function = ctools_plugin_get_function($task, 'form info');
- $form_info = $function();
-
- $form_state['redirect'] = str_replace('%page_name/%step', $page->name, $form_info['edit path']);
-}
-/**
- * Entry point to enable a page.
- */
-function delegator_page_enable_subtask($name) {
- ctools_include('export');
- ctools_export_set_status('delegator_pages', $name, FALSE);
- drupal_goto('admin/build/pages');
-}
-
-/**
- * Entry point to disable a page.
- */
-function delegator_page_disable_subtask($name) {
- ctools_include('export');
- ctools_export_set_status('delegator_pages', $name, TRUE);
- drupal_goto('admin/build/pages');
-}
-
-/**
- * Entry point to export a page.
- */
-function delegator_page_delete_subtask($form_state, $name) {
- $page = delegator_page_get_page_cache($name);
- $task = delegator_get_task($page->task);
-
- if (!$page) {
- return drupal_not_found();
- }
-
- if (!empty($page->locked)) {
- return array('locked' => array('#value' => theme('delegator_page_lock', $page->locked, $name)));
- }
-
- $form_state['page'] = $page;
- $form = array();
-
- $cancel = 'admin/build/pages';
-
- if ($page->type == t('Overridden')) {
- $title = t('Are you sure you want to revert the page %name?', array('%name' => $page->admin_title));
- $desc = t('Reverting the page will delete the page that is in the database, reverting it to the original default page. Any changes you have made will be lost and cannot be recovered.');
- }
- else {
- $title = t('Are you sure you want to delete the page %name?', array('%name' => $page->admin_title));
- $desc = t('Deleting a page cannot be undone.');
- }
-
- return confirm_form($form,
- $title,
- $cancel,
- $desc,
- t('Delete'),
- t('Cancel')
- );
-}
-
-/**
- * Submit handler to delete a view.
- */
-function delegator_page_delete_subtask_submit(&$form, &$form_state) {
- if ($form_state['page']->locked) {
- drupal_set_message(t('Unable to update page due to lock.'), 'error');
- return;
- }
-
- delegator_page_delete($form_state['page']);
- drupal_set_message(t('The page has been deleted.'));
- $form_state['redirect'] = 'admin/build/pages';
-}
-
diff --git a/delegator/plugins/tasks/page.inc b/delegator/plugins/tasks/page.inc
deleted file mode 100644
index 970c4c1..0000000
--- a/delegator/plugins/tasks/page.inc
+++ /dev/null
@@ -1,513 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * Handle the 'page' task, which creates pages with arbitrary tasks and lets
- * handlers decide how they will be rendered.
- *
- * This creates subtasks and stores them in the delegator_pages table. These
- * are exportable objects, too.
- *
- * The render callback for this task type has $handler, $page, $contexts as
- * parameters.
- */
-
-/**
- * Specialized implementation of hook_delegator_tasks(). See api-task.html for
- * more information.
- */
-function delegator_page_delegator_tasks() {
- return array(
- 'page' => array(
- 'title' => t('Custom pages'),
- 'description' => t('Administrator created pages that have a URL path, access control and entries in the Drupal menu system.'),
- 'subtasks' => TRUE,
- 'subtask callback' => 'delegator_page_subtask',
- 'subtasks callback' => 'delegator_page_subtasks',
- 'hook menu' => array(
- 'file' => 'page.admin.inc',
- 'path' => drupal_get_path('module', 'delegator') . '/plugins/tasks',
- 'function' => 'delegator_page_menu',
- ),
- 'hook theme' => 'delegator_page_theme',
- 'form info' => 'delegator_page_edit_form_info',
- // page only items
- 'task type' => 'page',
- 'operations' => array(
- array(
- 'title' => t('Import'),
- 'href' => 'admin/build/pages/import',
- ),
- array(
- 'title' => t('Add custom page'),
- 'href' => 'admin/build/pages/add',
- ),
- ),
- 'columns' => array(
- 'storage' => array(
- 'label' => t('Storage'),
- 'class' => 'delegator-page-storage',
- ),
- ),
- 'page type' => 'custom',
- // What page callback will execute this page. If it has handlers then
- // delegator_page_execute should be the default.
-// 'page callback' => 'delegator_page_execute',
- // context only items
- 'handler type' => 'context',
- 'get arguments' => array(
- 'file' => 'page.admin.inc',
- 'path' => drupal_get_path('module', 'delegator') . '/plugins/tasks',
- 'function' => 'delegator_page_get_arguments',
- ),
- 'get context placeholders' => 'delegator_page_get_contexts',
- 'access restrictions' => 'delegator_page_access_restrictions',
- 'uses handlers' => TRUE,
- ),
- );
-}
-
-/**
- * Return a list of all subtasks.
- */
-function delegator_page_subtasks($task) {
- $pages = delegator_page_load_all($task['name']);
- $return = array();
- foreach ($pages as $name => $page) {
- $return[$name] = delegator_page_build_subtask($task, $page);
- }
-
- return $return;
-}
-
-/**
- * Callback to return a single subtask.
- */
-function delegator_page_subtask($task, $subtask_id) {
- $page = delegator_page_load($subtask_id);
- if ($page) {
- return delegator_page_build_subtask($task, $page);
- }
-}
-
-/**
- * Build a subtask array for a given page.
- */
-function delegator_page_build_subtask($task, $page) {
- $function = ctools_plugin_get_function($task, 'form info');
- $form_info = $function();
-
- $edit_links = array();
- $name = $page->name;
-
- foreach ($form_info['order'] as $form_id => $form_title) {
-
- $edit_links[] = array(
- 'title' => $form_title,
- 'href' => str_replace(array('%page_name', '%step'), array($name, $form_id), $form_info['edit path']),
- );
- }
-
- $operations = array();
- $task_name = delegator_make_task_name($task['name'], $name);
-
- if (empty($page->disabled)) {
- if (!empty($task['uses handlers'])) {
- if ($page->multiple) {
- $operations[] = array(
- 'title' => t('Task handlers'),
- 'href' => "admin/build/delegator/$task_name",
- );
- }
- else {
- $default_handlers = isset($page->default_handlers) ? $page->default_handlers : array();
- $task_handlers = delegator_load_task_handlers($task, $page->name, $default_handlers);
- if ($task_handlers) {
- $handler = array_shift($task_handlers);
- $plugin = delegator_get_task_handler($handler->handler);
- if (!empty($plugin['edit forms'])) {
- $actions = array();
- foreach ($plugin['edit forms'] as $edit_id => $title) {
- if ($title) {
- $actions[] = array(
- 'title' => $title,
- 'href' => "admin/build/delegator/$task_name/$handler->handler/$handler->name/$edit_id",
- );
- }
- }
- $operations[] = array(
- 'title' => '<span class="text">' . t('Edit handler') . '</span>' . theme('links', $actions),
- 'html' => TRUE,
- );
- }
- }
- }
- }
-
- $operations[] = array(
- 'title' => '<span class="text">' . t('Edit page') . '</span>' . theme('links', $edit_links),
- 'html' => TRUE,
- );
- $operations[] = array(
- 'title' => t('Clone'),
- 'href' => "admin/build/pages/clone/$name",
- );
- $operations[] = array(
- 'title' => t('Export'),
- 'href' => "admin/build/pages/export/$name",
- );
- if (!empty($task['has handlers'])) {
- $operations[] = array(
- 'title' => t('Export (with handlers)'),
- 'href' => "admin/build/pages/export/$name/handlers",
- );
- }
- if ($page->export_type == (EXPORT_IN_CODE | EXPORT_IN_DATABASE)) {
- $operations[] = array(
- 'title' => t('Revert'),
- 'href' => "admin/build/pages/delete/$name",
- );
- }
- else if ($page->export_type == EXPORT_IN_CODE) {
- $operations[] = array(
- 'title' => t('Disable'),
- 'href' => "admin/build/pages/disable/$name",
- );
- }
- else {
- $operations[] = array(
- 'title' => t('Delete'),
- 'href' => "admin/build/pages/delete/$name",
- );
- }
- }
- else {
- $operations[] = array(
- 'title' => t('Enable'),
- 'href' => "admin/build/pages/enable/$name",
- );
- }
-
- $subtask = array(
- 'name' => $name,
- 'admin title' => $page->admin_title,
- 'admin path' => $page->path,
- 'subtask' => $page,
- 'operations' => $operations,
- 'single task' => empty($page->multiple),
- 'row class' => empty($page->disabled) ? 'delegator-enabled' : 'delegator-disabled',
- 'storage' => $page->type,
- );
-
- // default handlers may appear from a default subtask.
- if (isset($page->default_handlers)) {
- $subtask['default handlers'] = $page->default_handlers;
- }
- return $subtask;
-}
-
-/**
- * Delegated implementation of hook_theme().
- */
-function delegator_page_theme(&$items, $task) {
- $base = array(
- 'file' => 'page.admin.inc',
- 'path' => drupal_get_path('module', 'delegator') . '/plugins/tasks',
- );
- $items['delegator_page_form_argument_table'] = $base + array(
- 'arguments' => array('form' => NULL),
- );
- $items['delegator_page_lock'] = $base + array(
- 'arguments' => array('lock' => array(), 'task_name' => NULL),
- );
- $items['delegator_page_changed'] = $base + array(
- 'arguments' => array(),
- );
-}
-
-/**
- * Supply information for the multi-step wizard for both edit and add subtask
- */
-function delegator_page_edit_form_info() {
- return array(
- 'id' => 'delegator_page',
- 'show trail' => TRUE,
- 'show back' => TRUE,
- 'show return' => FALSE,
- 'next callback' => 'delegator_page_add_subtask_next',
- 'finish callback' => 'delegator_page_add_subtask_finish',
- 'return callback' => 'delegator_page_add_subtask_finish',
- 'cancel callback' => 'delegator_page_add_subtask_cancel',
- 'order' => array(
- 'basic' => t('Basic settings'),
- 'argument' => t('Argument settings'),
- 'access' => t('Access control'),
- 'menu' => t('Menu settings'),
- 'multiple' => t('Task handlers'),
- ),
- 'forms' => array(
- 'basic' => array(
- 'form id' => 'delegator_page_form_basic',
- ),
- 'access' => array(
- 'form id' => 'delegator_page_form_access',
- ),
- 'menu' => array(
- 'form id' => 'delegator_page_form_menu',
- ),
- 'argument' => array(
- 'form id' => 'delegator_page_form_argument',
- ),
- 'multiple' => array(
- 'form id' => 'delegator_page_argument_form_multiple',
- ),
- ),
- // Items specific to the 'add' routines that will get moved over:
- 'add path' => 'admin/build/pages/add/%step',
- // Items specific to the 'edit' routines that will get moved over:
- 'edit path' => 'admin/build/pages/edit/%page_name/%step',
- );
-}
-
-// --------------------------------------------------------------------------
-// Page execution functions
-
-/**
- * Execute a page task.
- *
- * This is the callback to entries in the Drupal menu system created by the
- * page task.
- *
- * @param $subtask_id
- * The name of the page task used.
- * @param ...
- * A number of context objects as specified by the user when
- * creating named arguments in the path.
- */
-function delegator_page_execute($subtask_id) {
- $page = delegator_page_load($subtask_id);
- $task = delegator_get_task($page->task);
-
- // Turn the contexts into a properly keyed array.
- $contexts = array();
- $args = array();
- foreach (func_get_args() as $count => $arg) {
- if (is_object($arg) && get_class($arg) == 'ctools_context') {
- $contexts[$arg->id] = $arg;
- $args[] = $arg->original_argument;
- }
- else if ($count) {
- $args[] = $arg;
- }
- }
-
- $count = 0;
- $names = delegator_page_get_named_arguments($page->path);
- $bits = explode('/', $page->path);
-
- if ($page->arguments) {
- foreach ($page->arguments as $name => $argument) {
- // Optional arguments must be converted to contexts too, if they exist.
- if ($bits[$names[$name]][0] == '!' && isset($args[$count])) {
- $argument['keyword'] = $name;
- ctools_include('context');
- $context = ctools_context_get_context_from_argument($argument, $args[$count]);
- $contexts[$context->id] = $context;
- }
- $count++;
- }
- }
-
- // Add a fake tab for 'View' so that edit tabs can be added.
- if (user_access('administer delegator') && (!isset($page->menu['type']) || !in_array($page->menu['type'], array('tab', 'default tab')))) {
- ctools_include('menu');
- ctools_menu_add_tab(array(
- 'title' => t('View'),
- 'href' => $_GET['q'],
- 'type' => MENU_DEFAULT_LOCAL_TASK,
- 'weight' => -10,
- ));
- }
-
-
- if ($function = ctools_plugin_get_function($task, 'page callback')) {
- return call_user_func_array($function, array($page, $contexts, $args));
- }
-
- ctools_include('context-task-handler');
- $output = ctools_context_handler_render($task, $subtask_id, $contexts, $args);
- if ($output === FALSE) {
- return drupal_not_found();
- }
-
- return $output;
-}
-
-// --------------------------------------------------------------------------
-// Context type callbacks
-
-/**
- * Return a list of arguments used by this task.
- */
-function delegator_page_get_arguments($task, $subtask_id) {
- $page = delegator_page_get_page_cache($subtask_id);
- return _delegator_page_get_arguments($page);
-}
-
-function _delegator_page_get_arguments($page) {
- $arguments = array();
- if (!empty($page->arguments)) {
- foreach ($page->arguments as $keyword => $argument) {
- if (isset($argument['name'])) {
- $argument['keyword'] = $keyword;
- $arguments[$keyword] = $argument;
- }
- }
- }
- return $arguments;
-}
-
-/**
- * Get a group of context placeholders for the arguments.
- */
-function delegator_page_get_contexts($task, $subtask_id) {
- ctools_include('context');
- return ctools_context_get_placeholders_from_argument(delegator_page_get_arguments($task, $subtask_id));
-}
-
-/**
- * Return a list of arguments used by this task.
- */
-function delegator_page_access_restrictions($task, $subtask_id, $contexts) {
- $page = delegator_page_load($subtask_id);
- return ctools_access_add_restrictions($page->access, $contexts);
-}
-
-// --------------------------------------------------------------------------
-// Page task database info.
-
-/**
- * Create a new page with defaults appropriately set from schema.
- */
-function delegator_page_new() {
- ctools_include('export');
- return ctools_export_new_object('delegator_pages');
-}
-
-/**
- * Load a single page subtask.
- */
-function delegator_page_load($name) {
- ctools_include('export');
- $result = ctools_export_load_object('delegator_pages', 'names', array($name));
- if (isset($result[$name])) {
- return $result[$name];
- }
-}
-
-/**
- * Load all page subtasks.
- */
-function delegator_page_load_all($task = NULL) {
- ctools_include('export');
-
- if (empty($task)) {
- return ctools_export_load_object('delegator_pages');
- }
- else {
- return ctools_export_load_object('delegator_pages', 'conditions', array('task' => $task));
- }
-}
-
-/**
- * Write a page subtask to the database.
- */
-function delegator_page_save(&$page) {
- $update = (isset($page->pid)) ? array('pid') : array();
- $task = delegator_get_task($page->task);
-
- if ($function = ctools_plugin_get_function($task, 'save')) {
- $function($page, $update);
- }
- drupal_write_record('delegator_pages', $page, $update);
-
- // If this was a default page we may need to write default task
- // handlers that we provided as well.
- if (!$update && isset($page->default_handlers)) {
- $handlers = delegator_load_task_handlers(delegator_get_task('page'), $page->name);
- foreach ($page->default_handlers as $name => $handler) {
- if (!isset($handlers[$name]) || !($handlers[$name]->export_type & EXPORT_IN_DATABASE)) {
- // Make sure this is right, as exports can wander a bit.
- $handler->subtask = $page->name;
- delegator_save_task_handler($handler);
- }
- }
- }
- return $page;
-}
-
-/**
- * Remove a page subtask.
- */
-function delegator_page_delete($page) {
- $task = delegator_get_task($page->task);
- if ($function = ctools_plugin_get_function($task, 'delete')) {
- $function($page);
- }
- if (!empty($task['uses handlers'])) {
- $handlers = delegator_load_task_handlers($task, $page->name);
- foreach ($handlers as $handler) {
- delegator_delete_task_handler($handler);
- }
- }
- db_query("DELETE FROM {delegator_pages} WHERE name = '%s'", $page->name);
-}
-
-/**
- * Export a page subtask.
- */
-function delegator_page_export($page, $with_handlers = FALSE, $indent = '') {
- $task = delegator_get_task($page->task);
- if ($function = ctools_plugin_get_function($task, 'export')) {
- $append = $function($page, $indent);
- }
-
- ctools_include('export');
- $output = ctools_export_object('delegator_pages', $page, $indent);
- $output .= $append;
-
- if ($with_handlers) {
- $handlers = delegator_load_task_handlers(delegator_get_task('page'), $page->name);
- $output .= $indent . '$page->default_handlers = array();' . "\n";
- foreach ($handlers as $handler) {
- $output .= delegator_export_task_handler($handler, $indent);
- $output .= $indent . '$page->default_handlers[$handler->name] = $handler;' . "\n";
- }
- }
- return $output;
-}
-
-/**
- * Get a list of named arguments in a delegator page path.
- *
- * @param $path
- * A normal Drupal path.
- *
- * @return
- * An array of % marked variable arguments, keyed by the argument's name.
- * The value will be the position of the argument so that it can easily
- * be found. Items with a position of -1 have multiple positions.
- */
-function delegator_page_get_named_arguments($path) {
- $arguments = array();
- $bits = explode('/', $path);
- foreach ($bits as $position => $bit) {
- if ($bit && ($bit[0] == '%' || $bit[0] == '!')) {
- // special handling for duplicate path items and substr to remove the %
- $arguments[substr($bit, 1)] = isset($arguments[$bit]) ? -1 : $position;
- }
- }
-
- return $arguments;
-}
diff --git a/includes/ajax.inc b/includes/ajax.inc
index d3a9e2d..d009437 100644
--- a/includes/ajax.inc
+++ b/includes/ajax.inc
@@ -111,7 +111,7 @@ function ctools_ajax_image_button($image, $dest, $alt, $class = '') {
* to ctools-use-ajax.
*/
function ctools_ajax_text_button($text, $dest, $alt, $class = '', $type = 'ctools-use-ajax') {
- return l($text, $dest, array('html' => TRUE, 'attributes' => array('class' => "$type $class", 'title' => $alt, 'alt' => $alt)));
+ return l($text, $dest, array('html' => TRUE, 'attributes' => array('class' => "$type $class", 'title' => $alt)));
}
/**
diff --git a/includes/context-task-handler.inc b/includes/context-task-handler.inc
index 16d3cee..8effdc9 100644
--- a/includes/context-task-handler.inc
+++ b/includes/context-task-handler.inc
@@ -22,7 +22,7 @@
*
* @param $task
* The $task object in use.
- * @param $subtask_id
+ * @param $subtask
* The id of the subtask in use.
* @param $contexts
* The context objects in use.
@@ -36,13 +36,13 @@
* Either the output or NULL if there was output, FALSE if no handler
* accepted the task. If $page is FALSE then the $info block is returned instead.
*/
-function ctools_context_handler_render($task, $subtask_id, $contexts, $args, $page = TRUE) {
+function ctools_context_handler_render($task, $subtask, $contexts, $args, $page = TRUE) {
// Load the landlers, choosing only enabled handlers.
- $handlers = delegator_load_sorted_handlers($task, $subtask_id, TRUE);
+ $handlers = page_manager_load_sorted_handlers($task, $subtask ? $subtask['name'] : '', TRUE);
// Try each handler.
foreach ($handlers as $handler) {
- if ($function = delegator_get_renderer($handler)) {
+ if ($function = page_manager_get_renderer($handler)) {
if ($info = $function($handler, $contexts, $args)) {
// If we don't own the page, let the caller deal with rendering.
if (!$page) {
@@ -72,14 +72,14 @@ function ctools_context_handler_render($task, $subtask_id, $contexts, $args, $pa
* Called to execute actions that should happen before a handler is rendered.
*/
function ctools_context_handler_pre_render($handler, $contexts, $args) {
- $plugin = delegator_get_task_handler($handler->handler);
+ $plugin = page_manager_get_task_handler($handler->handler);
- if (user_access('administer delegator') && isset($handler->task)) {
+ if (user_access('administer page manager') && isset($handler->task)) {
// Provide a tab to edit this context:
ctools_include('menu');
ctools_menu_add_tab(array(
'title' => t('Edit @type', array('@type' => $plugin['title'])),
- 'href' => "admin/build/delegator/" . delegator_make_task_name($handler->task, $handler->subtask) . "/$handler->handler/$handler->name",
+ 'href' => "admin/build/pages/edit/" . page_manager_make_task_name($handler->task, $handler->subtask),
));
}
}
@@ -113,19 +113,19 @@ function ctools_context_handler_select($handler, $contexts) {
*
* @param $task
* The loaded task plugin.
- * @param $subtask_id
+ * @param $subtask
* The subtask id.
* @param $handler
* The handler to be checked.
*/
-function ctools_context_handler_summary($task, $subtask_id, $handler) {
+function ctools_context_handler_summary($task, $subtask, $handler) {
if (empty($handler->conf['access']['plugins'])) {
return array();
}
ctools_include('context');
$strings = array();
- $contexts = ctools_context_handler_get_all_contexts($task, $subtask_id, $handler);
+ $contexts = ctools_context_handler_get_all_contexts($task, $subtask, $handler);
foreach ($handler->conf['access']['plugins'] as $test) {
$plugin = ctools_get_access_plugin($test['name']);
@@ -150,10 +150,10 @@ function ctools_context_handler_summary($task, $subtask_id, $handler) {
* This creates the base array of contexts, loaded from arguments, suitable
* for use in rendering.
*/
-function ctools_context_handler_get_task_contexts($task, $subtask_id, $args) {
+function ctools_context_handler_get_task_contexts($task, $subtask, $args) {
$contexts = array();
- $arguments = ctools_context_handler_get_task_arguments($task, $subtask_id);
+ $arguments = ctools_context_handler_get_task_arguments($task, $subtask);
ctools_context_get_context_from_arguments($arguments, $contexts, $args);
return $contexts;
@@ -178,10 +178,10 @@ function ctools_context_handler_get_handler_contexts($contexts, $handler) {
* handler and loads them as a group. Since there is no data, this loads
* the contexts as placeholders.
*/
-function ctools_context_handler_get_all_contexts($task, $subtask_id, $handler) {
- $object = ctools_context_handler_get_task_object($task, $subtask_id, $handler);
+function ctools_context_handler_get_all_contexts($task, $subtask, $handler) {
+ $object = ctools_context_handler_get_task_object($task, $subtask, $handler);
$contexts = ctools_context_load_contexts($object, TRUE);
- ctools_context_handler_set_access_restrictions($task, $subtask_id, $handler, $contexts);
+ ctools_context_handler_set_access_restrictions($task, $subtask, $handler, $contexts);
return $contexts;
}
@@ -203,10 +203,10 @@ function ctools_context_handler_get_handler_object($handler) {
* expects things in a certain, kind of clunky format. This one adds in
* arguments from the task.
*/
-function ctools_context_handler_get_task_object($task, $subtask_id, $handler) {
+function ctools_context_handler_get_task_object($task, $subtask, $handler) {
$object = new stdClass;
$object->name = $handler->name;
- $object->arguments = ctools_context_handler_get_task_arguments($task, $subtask_id);
+ $object->arguments = ctools_context_handler_get_task_arguments($task, $subtask);
$object->contexts = isset($handler->conf['contexts']) ? $handler->conf['contexts'] : array();
$object->relationships = isset($handler->conf['relationships']) ? $handler->conf['relationships'] : array();
@@ -216,9 +216,9 @@ function ctools_context_handler_get_task_object($task, $subtask_id, $handler) {
/**
* Get the arguments from a task that are used to load contexts.
*/
-function ctools_context_handler_get_task_arguments($task, $subtask_id) {
+function ctools_context_handler_get_task_arguments($task, $subtask) {
if ($function = ctools_plugin_get_function($task, 'get arguments')) {
- return $function($task, $subtask_id);
+ return $function($task, $subtask);
}
return array();
@@ -234,10 +234,10 @@ function ctools_context_handler_get_task_arguments($task, $subtask_id) {
* to only 'story' and 'page' types, there is no need for content that
* only applies to the 'poll' type to appear.
*/
-function ctools_context_handler_set_access_restrictions($task, $subtask_id, $handler, &$contexts) {
+function ctools_context_handler_set_access_restrictions($task, $subtask, $handler, &$contexts) {
// First, for the task:
if ($function = ctools_plugin_get_function($task, 'access restrictions')) {
- $function($task, $subtask_id, $contexts);
+ $function($task, $subtask, $contexts);
}
// Then for the handler:
@@ -258,12 +258,12 @@ function ctools_context_handler_edit_criteria(&$form, &$form_state) {
}
ctools_include('context');
- $form_state['module'] = 'delegator_task_handler';
+ $form_state['module'] = 'page_manager_task_handler';
// Encode a bunch of info into the argument so we can get our cache later
$form_state['callback argument'] = $form_state['task_name'] . '*' . $form_state['handler']->name;
$form_state['access'] = $form_state['handler']->conf['access'];
$form_state['no buttons'] = TRUE;
- $form_state['contexts'] = ctools_context_handler_get_all_contexts($form_state['task'], $form_state['subtask_id'], $form_state['handler']);
+ $form_state['contexts'] = ctools_context_handler_get_all_contexts($form_state['task'], $form_state['subtask'], $form_state['handler']);
ctools_include('context-access-admin');
$form = array_merge($form, ctools_access_admin_form($form_state));
@@ -275,7 +275,7 @@ function ctools_context_handler_edit_criteria(&$form, &$form_state) {
function ctools_context_handler_edit_criteria_submit(&$form, &$form_state) {
$form_state['handler']->conf['access']['logic'] = $form_state['values']['logic'];
if (!empty($form_state['handler']->conf['autogenerate_title'])) {
- $strings = ctools_context_handler_summary($form_state['task'], $form_state['subtask_id'], $form_state['handler']);
+ $strings = ctools_context_handler_summary($form_state['task'], $form_state['subtask'], $form_state['handler']);
if (!$strings) {
$form_state['handler']->conf['title'] = t('Panel');
}
@@ -295,11 +295,11 @@ function ctools_context_handler_edit_context(&$form, &$form_state) {
$handler = $form_state['handler'];
// Check our context object cache.
if (!empty($_POST)) {
- $cache = ctools_object_cache_get('context_object:delegator', $handler->name);
+ $cache = ctools_object_cache_get('context_object:page_manager', $handler->name);
}
else {
- $cache = ctools_context_handler_get_task_object($form_state['task'], $form_state['subtask_id'], $form_state['handler']);
- ctools_object_cache_set('context_object:delegator', $handler->name, $cache);
+ $cache = ctools_context_handler_get_task_object($form_state['task'], $form_state['subtask'], $form_state['handler']);
+ ctools_object_cache_set('context_object:page_manager', $handler->name, $cache);
}
$form['right'] = array(
@@ -312,11 +312,11 @@ function ctools_context_handler_edit_context(&$form, &$form_state) {
'#suffix' => '</div></div>',
);
- ctools_context_add_context_form('delegator', $form, $form_state, $form['right']['contexts_table'], $cache);
- ctools_context_add_relationship_form('delegator', $form, $form_state, $form['right']['relationships_table'], $cache);
+ ctools_context_add_context_form('page_manager', $form, $form_state, $form['right']['contexts_table'], $cache);
+ ctools_context_add_relationship_form('page_manager', $form, $form_state, $form['right']['relationships_table'], $cache);
$form['left']['summary'] = array(
- '#prefix' => '<div class="delegator-contexts">',
+ '#prefix' => '<div class="page-manager-contexts">',
'#suffix' => '</div>',
'#value' => theme('ctools_context_list', $cache, t('Summary of contexts')),
);
@@ -333,6 +333,6 @@ function ctools_context_handler_edit_context(&$form, &$form_state) {
function ctools_context_handler_edit_context_submit(&$form, &$form_state) {
$form_state['handler']->conf['contexts'] = $form_state['context_object']->contexts;
$form_state['handler']->conf['relationships'] = $form_state['context_object']->relationships;
- ctools_object_cache_clear('context_object:delegator', $form_state['handler']->name);
+ ctools_object_cache_clear('context_object:page_manager', $form_state['handler']->name);
}
diff --git a/includes/export.inc b/includes/export.inc
index 997ca34..acb6157 100644
--- a/includes/export.inc
+++ b/includes/export.inc
@@ -104,11 +104,17 @@ function ctools_export_load_object($table, $type = 'all', $args = array()) {
$result = db_query($query, $query_args);
+ $status = variable_get($export['status'], array());
// Unpack the results of the query onto objects and cache them.
while ($data = db_fetch_object($result)) {
$object = _ctools_export_unpack_object($schema, $data, $export['object']);
+ $object->table = $table;
$object->type = t('Normal');
$object->export_type = EXPORT_IN_DATABASE;
+ // Determine if default object is enabled or disabled.
+ if (isset($status[$object->name])) {
+ $object->disabled = $status[$object->name];
+ }
$cache[$table][$object->{$export['key']}] = $object;
if ($type == 'conditions') {
@@ -119,7 +125,6 @@ function ctools_export_load_object($table, $type = 'all', $args = array()) {
// @todo Load subrecords.
if ($defaults = _ctools_export_get_defaults($table, $export)) {
- $status = variable_get($export['status'], array());
foreach ($defaults as $object) {
if ($type == 'conditions') {
@@ -152,6 +157,7 @@ function ctools_export_load_object($table, $type = 'all', $args = array()) {
$object->type = t('Default');
$object->export_type = EXPORT_IN_CODE;
$object->in_code_only = TRUE;
+ $object->table = $table;
$cache[$table][$object->name] = $object;
if ($type == 'conditions') {
@@ -425,9 +431,8 @@ function ctools_export_get_schema($table) {
/**
* Set the status of a default $object as a variable.
*
- * The status, in this case, is whether or not it is 'disabled'
- * and is only valid for in-code objects that do not have a database
- * equivalent. This function does not check to make sure $object actually
+ * The status, in this case, is whether or not it is 'disabled'.
+ * This function does not check to make sure $object actually
* exists.
*/
function ctools_export_set_status($table, $name, $new_status = TRUE) {
@@ -439,6 +444,29 @@ function ctools_export_set_status($table, $name, $new_status = TRUE) {
}
/**
+ * Set the status of a default $object as a variable.
+ *
+ * This is more efficient than ctools_export_set_status because it
+ * will actually unset the variable entirely if it's not necessary,
+ * this saving a bit of space.
+ */
+function ctools_export_set_object_status($object, $new_status = TRUE) {
+ $table = $object->table;
+ $schema = ctools_export_get_schema($table);
+ $status = variable_get($schema['export']['status'], array());
+
+ // Compare
+ if (!$new_status && $object->export_type & EXPORT_IN_DATABASE) {
+ unset($status[$object->name]);
+ }
+ else {
+ $status[$object->name] = $new_status;
+ }
+
+ variable_set($schema['export']['status'], $status);
+}
+
+/**
* Provide a form for displaying an export.
*
* This is a simple form that should be invoked like this:
diff --git a/includes/wizard.inc b/includes/wizard.inc
index d573e74..4dc2781 100644
--- a/includes/wizard.inc
+++ b/includes/wizard.inc
@@ -196,94 +196,110 @@ function ctools_wizard_wrapper(&$form, &$form_state) {
);
}
- // Ensure buttons stay on the bottom.
- $form['buttons'] = array(
- '#prefix' => '<div class="clear-block">',
- '#suffix' => '</div>',
- '#weight' => 1000,
- );
-
- if (!empty($form_info['show back']) && isset($form_state['previous'])) {
- $form['buttons']['previous'] = array(
- '#type' => 'submit',
- '#value' => isset($form_info['back text']) ? $form_info['back text'] : t('Back'),
- '#next' => $form_state['previous'],
- '#wizard type' => 'next',
- '#weight' => -2000,
- // hardcode the submit so that it doesn't try to save data.
- '#submit' => array('ctools_wizard_submit'),
+ if (empty($form_info['no buttons'])) {
+ // Ensure buttons stay on the bottom.
+ $form['buttons'] = array(
+ '#prefix' => '<div class="clear-block">',
+ '#suffix' => '</div>',
+ '#weight' => 1000,
);
- }
- // If there is a next form, place the next button.
- if (isset($form_state['next'])) {
- $form['buttons']['next'] = array(
- '#type' => 'submit',
- '#value' => isset($form_info['next text']) ? $form_info['next text'] : t('Continue'),
- '#next' => $form_state['next'],
- '#wizard type' => 'next',
- '#weight' => -1000,
- );
- }
+ $button_attributes = array();
+ if (!empty($form_state['ajax']) && empty($form_state['modal'])) {
+ $button_attributes = array('class' => 'ctools-use-ajax');
+ }
- // There are two ways the return button can appear. If this is not the
- // end of the form list (i.e, there is a next) then it's "update and return"
- // to be clear. If this is the end of the path and there is no next, we
- // call it 'Finish'.
-
- // Even if there is no direct return path (some forms may not want you
- // leaving in the middle) the final button is always a Finish and it does
- // whatever the return action is.
- if (!empty($form_info['show return']) && !empty($form_state['next'])) {
- $form['buttons']['return'] = array(
- '#type' => 'submit',
- '#value' => isset($form_info['return text']) ? $form_info['return text'] : t('Update and return'),
- '#wizard type' => 'return',
- );
- }
- else if (empty($form_state['next'])) {
- $form['buttons']['return'] = array(
- '#type' => 'submit',
- '#value' => isset($form_info['finish text']) ? $form_info['finish text'] : t('Finish'),
- '#wizard type' => 'finish',
- );
- }
+ if (!empty($form_info['show back']) && isset($form_state['previous'])) {
+ $form['buttons']['previous'] = array(
+ '#type' => 'submit',
+ '#value' => isset($form_info['back text']) ? $form_info['back text'] : t('Back'),
+ '#next' => $form_state['previous'],
+ '#wizard type' => 'next',
+ '#weight' => -2000,
+ // hardcode the submit so that it doesn't try to save data.
+ '#submit' => array('ctools_wizard_submit'),
+ '#attributes' => $button_attributes,
+ );
+
+ if (isset($form_info['no back validate']) || isset($info['no back validate'])) {
+ $form['buttons']['previous']['#validate'] = array();
+ }
+ }
- // If we are allowed to cancel, place a cancel button.
- if (isset($form_info['cancel path']) || !empty($form_info['show cancel'])) {
- $form['buttons']['cancel'] = array(
- '#type' => 'submit',
- '#value' => isset($form_info['cancel text']) ? $form_info['cancel text'] : t('Cancel'),
- '#wizard type' => 'cancel',
- // hardcode the submit so that it doesn't try to save data.
- '#submit' => array('ctools_wizard_submit'),
- );
- }
+ // If there is a next form, place the next button.
+ if (isset($form_state['next'])) {
+ $form['buttons']['next'] = array(
+ '#type' => 'submit',
+ '#value' => isset($form_info['next text']) ? $form_info['next text'] : t('Continue'),
+ '#next' => $form_state['next'],
+ '#wizard type' => 'next',
+ '#weight' => -1000,
+ '#attributes' => $button_attributes,
+ );
+ }
- // Set up optional validate handlers.
- $form['#validate'] = array();
- if (function_exists($info['form id'] . '_validate')) {
- $form['#validate'][] = $info['form id'] . '_validate';
- }
- if (isset($info['validate']) && function_exists($info['validate'])) {
- $form['#validate'][] = $info['validate'];
- }
+ // There are two ways the return button can appear. If this is not the
+ // end of the form list (i.e, there is a next) then it's "update and return"
+ // to be clear. If this is the end of the path and there is no next, we
+ // call it 'Finish'.
+
+ // Even if there is no direct return path (some forms may not want you
+ // leaving in the middle) the final button is always a Finish and it does
+ // whatever the return action is.
+ if (!empty($form_info['show return']) && !empty($form_state['next'])) {
+ $form['buttons']['return'] = array(
+ '#type' => 'submit',
+ '#value' => isset($form_info['return text']) ? $form_info['return text'] : t('Update and return'),
+ '#wizard type' => 'return',
+ '#attributes' => $button_attributes,
+ );
+ }
+ else if (empty($form_state['next'])) {
+ $form['buttons']['return'] = array(
+ '#type' => 'submit',
+ '#value' => isset($form_info['finish text']) ? $form_info['finish text'] : t('Finish'),
+ '#wizard type' => 'finish',
+ '#attributes' => $button_attributes,
+ );
+ }
- // Set up our submit handler after theirs. Since putting something here will
- // skip Drupal's autodetect, we autodetect for it.
+ // If we are allowed to cancel, place a cancel button.
+ if (isset($form_info['cancel path']) || !empty($form_info['show cancel'])) {
+ $form['buttons']['cancel'] = array(
+ '#type' => 'submit',
+ '#value' => isset($form_info['cancel text']) ? $form_info['cancel text'] : t('Cancel'),
+ '#wizard type' => 'cancel',
+ // hardcode the submit so that it doesn't try to save data.
+ '#submit' => array('ctools_wizard_submit'),
+ '#attributes' => $button_attributes,
+ );
+ }
- // We make sure ours is after theirs so that they get to change #next if
- // the want to.
- $form['#submit'] = array();
- if (function_exists($info['form id'] . '_submit')) {
- $form['#submit'][] = $info['form id'] . '_submit';
- }
- if (isset($info['submit']) && function_exists($info['submit'])) {
- $form['#submit'][] = $info['submit'];
+ // Set up optional validate handlers.
+ $form['#validate'] = array();
+ if (function_exists($info['form id'] . '_validate')) {
+ $form['#validate'][] = $info['form id'] . '_validate';
+ }
+ if (isset($info['validate']) && function_exists($info['validate'])) {
+ $form['#validate'][] = $info['validate'];
+ }
+
+ // Set up our submit handler after theirs. Since putting something here will
+ // skip Drupal's autodetect, we autodetect for it.
+
+ // We make sure ours is after theirs so that they get to change #next if
+ // the want to.
+ $form['#submit'] = array();
+ if (function_exists($info['form id'] . '_submit')) {
+ $form['#submit'][] = $info['form id'] . '_submit';
+ }
+ if (isset($info['submit']) && function_exists($info['submit'])) {
+ $form['#submit'][] = $info['submit'];
+ }
+ $form['#submit'][] = 'ctools_wizard_submit';
}
- $form['#submit'][] = 'ctools_wizard_submit';
- if (!empty($form_state['modal'])) {
+ if (!empty($form_state['ajax'])) {
$form['#action'] = url(ctools_wizard_get_path($form_state['form_info'], $form_state['step']));
}
@@ -307,7 +323,7 @@ function ctools_wizard_submit(&$form, &$form_state) {
}
else {
if ($type == 'cancel' && isset($form_state['form_info']['cancel path'])) {
- $form_state['redirect'] = $form_state['form_info']['return path'];
+ $form_state['redirect'] = $form_state['form_info']['cancel path'];
}
else if ($type == 'next') {
$form_state['redirect'] = ctools_wizard_get_path($form_state['form_info'], $form_state['clicked_button']['#next']);
diff --git a/js/ajax-responder.js b/js/ajax-responder.js
index 3e2084e..1999bf1 100644
--- a/js/ajax-responder.js
+++ b/js/ajax-responder.js
@@ -37,7 +37,7 @@ Drupal.CTools.AJAX.clickAJAXLink = function() {
var object = $(this);
$(this).addClass('ctools-ajaxing');
try {
- url.replace('/nojs/', '/ajax/');
+ url = url.replace('/nojs/', '/ajax/');
$.ajax({
type: "POST",
url: url,
@@ -79,7 +79,7 @@ Drupal.CTools.AJAX.clickAJAXButton = function() {
var object = $(this);
try {
if (url) {
- url.replace('/nojs/', '/ajax/');
+ url = url.replace('/nojs/', '/ajax/');
$.ajax({
type: "POST",
url: url,
@@ -98,7 +98,7 @@ Drupal.CTools.AJAX.clickAJAXButton = function() {
else {
var form = $(this).parents('form');
url = $(form).attr('action');
- url.replace('/nojs/', '/ajax/');
+ url = url.replace('/nojs/', '/ajax/');
$(form).ajaxSubmit({
type: "POST",
url: url,
diff --git a/page_manager/css/page-manager.css b/page_manager/css/page-manager.css
new file mode 100644
index 0000000..15e0f7a
--- /dev/null
+++ b/page_manager/css/page-manager.css
@@ -0,0 +1,245 @@
+/* $Id$ */
+body form#page-manager-list-pages-form {
+ margin: 0 0 20px 0;
+}
+
+#page-manager-list-pages-form .form-item {
+ padding-right: 1em; /* LTR */
+ float: left; /* LTR */
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+#edit-order-wrapper {
+ clear: left; /* LTR */
+}
+
+#edit-pages-apply,
+#edit-pages-reset {
+ margin-top: 1.65em;
+ float: left; /* LTR */
+}
+
+#page-manager-edit {
+ margin-top: 1em;
+}
+
+#page-manager-edit .page-manager-wrapper {
+ margin: 0;
+ padding: 0 0 0 149px;
+ color: #494949;
+}
+
+#page-manager-edit .page-manager-tabs {
+ border: 1px solid #aaa;
+}
+
+#page-manager-edit .page-manager-edit-operations {
+ float: left;
+ width: 150px;
+ margin-left: -150px;
+ margin-top: -1px;
+ height: 100%;
+ font-size: 90%;
+}
+
+#page-manager-edit .page-manager-edit-operations .inside {
+ border-top: 1px solid #aaa;
+ border-bottom: 1px solid #aaa;
+}
+
+#page-manager-edit .page-manager-edit-operations ul {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+#page-manager-edit .page-manager-edit-operations li {
+ list-style: none;
+ background: #F6F6F6;
+ border-top: 1px solid #aaa;
+ border-left: 1px solid #aaa;
+ border-right: 1px solid #aaa;
+ padding: 0 0 0 0;
+ margin: 0;
+ line-height: 2em;
+}
+
+#page-manager-edit .page-manager-edit-operations li.active,
+#page-manager-edit .page-manager-edit-operations li.active-group .page-manager-group-title {
+ background: #FFFFFF url(../images/arrow-active.png) no-repeat scroll right center;
+}
+
+#page-manager-edit .page-manager-edit-operations li.changed,
+#page-manager-edit .page-manager-edit-operations li.changed-group .page-manager-group-title {
+ background-color: #ffe;
+ font-weight: bold;
+}
+
+/** provide a reset for non active stray paths */
+#page-manager-edit .page-manager-edit-operations li.active-group li.not-active .page-manager-group-title,
+#page-manager-edit .page-manager-edit-operations li.changed-group li.not-changed .page-manager-group-title {
+ background: #F6F6F6;
+}
+
+#page-manager-edit .page-manager-edit-operations li.active {
+ border-right: 1px solid white;
+}
+
+#page-manager-edit .page-manager-edit-operations li.active a,
+#page-manager-edit .page-manager-edit-operations li.active a:hover {
+ background: #FFFFFF url(../images/arrow-active.png) no-repeat scroll right center;
+ color: #000000;
+ font-weight: bold;
+}
+
+#page-manager-edit .page-manager-edit-operations li.operation-first {
+ border-top: none;
+}
+
+#page-manager-edit .page-manager-edit-operations li li.operation-first {
+ border-top: 1px solid #aaa;
+}
+
+#page-manager-edit .page-manager-edit-operations li a {
+ display: block;
+ padding: 0 0 0 .5em;
+ color: #494949;
+}
+
+#page-manager-edit .page-manager-edit-operations li a:hover {
+ background-color: #eee;
+ text-decoration: none;
+}
+
+#page-manager-edit .page-manager-edit-operations li .ctools-collapsible-handle:hover {
+ background-color: #eee;
+}
+
+#page-manager-edit .page-manager-edit-operations li li {
+ border-right: none;
+ border-left: none;
+ margin-left: 1em;
+}
+
+#page-manager-edit .page-manager-edit-operations li ul {
+/* padding-bottom: .2em; */
+}
+
+#page-manager-edit .page-manager-group-title {
+ line-height: 2em;
+ font-weight: bold;
+ padding: 0 0 0 .5em;
+}
+
+/* Change the position of the arrow on the dropdown to look nicer with our defaults. */
+#page-manager-edit .page-manager-edit-operations .ctools-toggle {
+ background-position: 0 9px;
+ width: 10px;
+}
+
+#page-manager-edit .page-manager-ajax-pad {
+ float: left;
+ width: 100%;
+ border-left: none;
+ height: 100%;
+ background: white;
+}
+
+/** A riser to force the ajax pad to a minimum height. **/
+#page-manager-edit .page-manager-ajax-pad .page-manager-riser {
+ width: 1px;
+ float: right;
+ height: 400px;
+}
+
+#page-manager-edit .page-manager-ajax-pad .page-manager-riser span {
+ display: none;
+}
+
+#page-manager-edit .page-manager-ajax-pad .content-title {
+ font-weight: bold;
+ font-size: 120%;
+ background-color: #fafafa;
+ border-bottom: 1px solid #aaa;
+ border-left: 1px solid #aaa;
+ margin-left: -1px;
+ padding: 2px 5px 2px 20px;
+}
+
+#page-manager-edit .actions {
+ padding: 0 0 0 20px;
+ height: 25px;
+}
+
+#page-manager-edit .primary-actions li {
+ border-top: 1px solid #aaa;
+}
+
+#page-manager-edit .secondary-actions {
+ border-bottom: 1px solid #aaa;
+}
+
+#page-manager-edit .actions .page-manager-group-title {
+ float: left;
+ padding-left: 0;
+}
+
+#page-manager-edit .actions ul {
+ float: right;
+ text-align: right;
+ padding: 0;
+ margin: 0;
+ border-right: 1px solid #aaa;
+}
+
+#page-manager-edit .actions li {
+ float: left;
+ background: none;
+ list-style: none;
+ border-left: 1px solid #aaa;
+ margin: 0;
+ padding: 0;
+ line-height: 200%;
+}
+
+#page-manager-edit .actions li a:hover {
+ background-color: #eee;
+ text-decoration: none;
+}
+
+#page-manager-edit .actions li a {
+ display: block;
+ padding: 0 .5em;
+ color:#0062A0;
+ background-color: #F6F6F6;
+}
+
+#page-manager-edit .page-manager-changed {
+ float: right;
+ float: right;
+ font-style: italic;
+ color: #f93;
+ padding-left: 1em;
+ padding-right: 22px;
+ background: url(../images/locked.png) no-repeat scroll right center;
+}
+
+#page-manager-edit .page-manager-ajax-pad .content-content {
+ padding: .5em 20px;
+}
+
+#page-manager-edit .page-manager-ajax-pad textarea {
+ width: 100%;
+}
+
+#page-manager-edit .changed-notification {
+ border: 1px solid #aaa;
+ background-color: #ffe;
+ color: #494949;
+ padding: 1em;
+ margin-top: 1em;
+}
+
+#page-manager-edit .ctools-locked {
+ margin-bottom: 2em;
+}
diff --git a/page_manager/help/api-task-handler.html b/page_manager/help/api-task-handler.html
new file mode 100644
index 0000000..ff45d87
--- /dev/null
+++ b/page_manager/help/api-task-handler.html
@@ -0,0 +1,44 @@
+<!-- $Id$ -->
+task handler definition:
+ title -- visible title of the task handler.
+ description -- description of the task handler.
+ task type -- The type of the task this handler can service.
+ render -- callback of the function to render the handler. The arguments to this callback are specific to the task type.
+ admin title -- callback to render the admin title as this handler is listed.
+ params: $handler, $task, $subtask_id
+ admin summary -- callback to render what's in the collapsible info as the handler is listed. Optional.
+ params: $handler, $task, $subtask_id
+ default conf -- either an array() of default conf data or a callback that returns an array.
+ params: $handler, $task, $subtask_id
+ save -- callback to call just prior to the task handler being saved so it can adjust its data.
+ params: &$handler, $update (as drupal_write_record would receive)
+ export -- callback to call just prior to the task being exported. It should return text to append to the export if necessary.
+ params: &$handler, $indent
+
+ forms => array(
+ 'id' => array(
+ 'form' => form callback (receives $form, $form_state)
+ 'submit' => submit callback
+ 'validate' => validate callback
+ 'include' => an optional file to include to get functionality for this form. Must include full path.
+ 'no return' => hide the 'return' button, meaning that the form requires extra steps if submitted
+ 'alternate next' => an alternate next form. Used for hidden edit forms that don't have tabs.
+ 'no blocks' => if TRUE, use Drupal's mechanism to not render blocks for this form.
+ )
+ )
+ ),
+
+ 'add forms' => array(
+ 'form1', => t('form title'),
+ 'form2', => t('form title'),
+ // ...etc.../
+),
+ 'edit forms' => array(
+ 'id' => t('tab name'),
+ 'id2' => t('tab name'),
+ ),
+
+ If a form name is blank it is a 'hidden' form -- it has no tab but can still be reached.
+
+
+Notes: Because #required validation cannot be skipped when clicking cancel, please don't use it. \ No newline at end of file
diff --git a/page_manager/help/api-task-type.html b/page_manager/help/api-task-type.html
new file mode 100644
index 0000000..144846e
--- /dev/null
+++ b/page_manager/help/api-task-type.html
@@ -0,0 +1,3 @@
+<!-- $Id$ -->
+
+defines a task type, grouping tasks together and providing a common UI for them. \ No newline at end of file
diff --git a/page_manager/help/api-task.html b/page_manager/help/api-task.html
new file mode 100644
index 0000000..0cc3f06
--- /dev/null
+++ b/page_manager/help/api-task.html
@@ -0,0 +1,39 @@
+<!-- $Id$ -->
+task definition:
+ title -- visible title of the task.
+ description -- description of the task.
+ hook menu -- function to delegate from hook_menu. Params: &$items, $task
+ hook menu alter -- function to delegate from hook_menu_alter. Params: &$items, $task
+ hook theme -- function to delegate from hook_theme. Params: &$items, $task
+
+ admin name -- if set an admin menu will appear in the delegator UI
+ admin description -- to describe the admin menu
+
+ admin access callback -- if set, the callback to use to determine administrative
+ access to this task. Defaults to user_access. Note that this is required even
+ if delegator isn't handling administration, since this gets used to on handler
+ edit forms.
+ admin access arguments -- If set, the arguments to use to determine administrative
+ access to this task. Defaults to array('administer delegator');
+
+ type -- The type of the task, used to determine which handlers can service it.
+
+ subtasks -- can be TRUE in which case it supports subtasks with the default
+ configuration or a string (array?) with callbacks to fetch subtask data.
+ subtask callback -- A callback which returns just one subtask. Param: $task, $subtask_id
+ subtasks callback -- A callback which returns an array of all subtasks.
+ This MUST return an array, even if it's empty.Param: $task
+
+ default handlers -- If the task contains any default handlers, they can be included here.
+
+task names must not contain a - as that is used to separate the task name from the subtask ID.
+
+subtasks implement data very similar to their parent task. In particular, they
+implement the following items exactly like their task:
+ hook menu
+ hook menu alter
+ description
+ admin name
+ admin description
+ admin access callback
+ admin access arguments
diff --git a/page_manager/help/page-task-type.html b/page_manager/help/page-task-type.html
new file mode 100644
index 0000000..c382c76
--- /dev/null
+++ b/page_manager/help/page-task-type.html
@@ -0,0 +1,4 @@
+
+Additional 'task' keys support:
+
+operations -- a list of operations suitable for theme('links') \ No newline at end of file
diff --git a/page_manager/images/arrow-active.png b/page_manager/images/arrow-active.png
new file mode 100644
index 0000000..3bbd3c2
--- /dev/null
+++ b/page_manager/images/arrow-active.png
Binary files differ
diff --git a/page_manager/images/locked.png b/page_manager/images/locked.png
new file mode 100644
index 0000000..2116eb1
--- /dev/null
+++ b/page_manager/images/locked.png
Binary files differ
diff --git a/page_manager/page_manager.admin.inc b/page_manager/page_manager.admin.inc
new file mode 100644
index 0000000..59655e2
--- /dev/null
+++ b/page_manager/page_manager.admin.inc
@@ -0,0 +1,1440 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Administrative functions for the page manager.
+ *
+ * This provides the UI to list, create, edit and delete pages, though much
+ * of this is delegated through to individual tasks.
+ */
+
+/**
+ * Output a list of pages that are managed.
+ */
+function page_manager_list_page() {
+ $output = '';
+ $tasks = page_manager_get_tasks_by_type('page');
+ $pages = array();
+
+ page_manager_get_pages($tasks, $pages);
+
+ // Respond to a reset command by clearing session and doing a drupal goto
+ // back to the base URL.
+ if (isset($_GET['op']) && $_GET['op'] == t('Reset')) {
+ unset($_SESSION['page_manager']['#admin']);
+ drupal_goto($_GET['q']);
+ }
+ if (count($_GET) <= 1) {
+ if (isset($_SESSION['page_manager']['#admin']) && is_array($_SESSION['page_manager']['#admin'])) {
+ $_GET += $_SESSION['page_manager']['#admin'];
+ }
+ }
+ else {
+ $_SESSION['page_manager']['#admin'] = $_GET;
+ unset($_SESSION['page_manager']['#admin']['q']);
+ }
+
+ $form_state = array(
+ 'pages' => &$pages,
+ 'input' => $_GET,
+ 'method' => 'get',
+ 'rerender' => TRUE,
+ 'no_redirect' => TRUE,
+ );
+
+ // This form will sort and filter the pages.
+ ctools_include('form');
+ $output .= ctools_build_form('page_manager_list_pages_form', $form_state);
+
+ $header = array(
+ array('data' => t('Type'), 'class' => 'page-manager-page-type'),
+ array('data' => t('Name'), 'class' => 'page-manager-page-name'),
+ array('data' => t('Title'), 'class' => 'page-manager-page-title'),
+ array('data' => t('Path'), 'class' => 'page-manager-page-path'),
+ array('data' => t('Storage'), 'class' => 'page-manager-page-storage'),
+ );
+
+ $header[] = array('data' => t('Operations'), 'class' => 'page-manager-page-operations');
+ $output .= theme('table', $header, $pages['rows']);
+
+ drupal_add_css(drupal_get_path('module', 'page_manager') . '/css/page-manager.css');
+
+ return $output;
+}
+
+/**
+ * Sort tasks into buckets based upon whether or not they have subtasks.
+ */
+function page_manager_get_pages($tasks, &$pages, $task_id = NULL) {
+ foreach ($tasks as $id => $task) {
+ // If a type has subtasks, add its subtasks in its own table.
+ if (!empty($task['subtasks'])) {
+ page_manager_get_pages(page_manager_get_task_subtasks($task), $pages, $task['name']);
+ continue;
+ }
+
+ if (isset($task_id)) {
+ $task_name = page_manager_make_task_name($task_id, $task['name']);
+ }
+ else {
+ $task_name = $task['name'];
+ }
+
+ $class = 'page-task-' . $id;
+ if (isset($task['row class'])) {
+ $class .= ' ' . $task['row class'];
+ }
+
+ $row = array('data' => array(), 'class' => $class);
+
+ $type = isset($task['admin type']) ? $task['admin type'] : t('System');
+ $pages['types'][$type] = $type;
+ $row['data']['type'] = array('data' => $type, 'class' => 'page-manager-page-type');
+
+ $row['data']['name'] = array('data' => $task_name, 'class' => 'page-manager-page-name');
+ $row['data']['title'] = array('data' => $task['admin title'], 'class' => 'page-manager-page-title');
+ $row['data']['path'] = array('data' => '/' . $task['admin path'], 'class' => 'page-manager-page-path');
+
+ $storage = isset($task['storage']) ? $task['storage'] : t('In code');
+ $pages['storages'][$storage] = $storage;
+ $row['data']['storage'] = array('data' => $storage, 'class' => 'page-manager-page-storage');
+
+ $operations = array(
+ array(
+ 'title' => t('Edit'),
+ 'href' => "admin/build/pages/edit/$task_name",
+ ),
+ /*
+ array(
+ 'title' => t('Delete'),
+ 'href' => "admin/build/pages/delete/$task_name",
+ ),
+ array(
+ 'title' => t('Export'),
+ 'href' => "admin/build/pages/export/$task_name",
+ ),
+ */
+ );
+
+ $row['data']['operations'] = array('data' => theme('links', $operations), 'class' => 'page-manager-page-operations');
+
+ $pages['rows'][$task_name] = $row;
+ }
+}
+
+/**
+ * Provide a form for sorting and filtering the list of pages.
+ */
+function page_manager_list_pages_form(&$form_state) {
+ if (!variable_get('clean_url', FALSE)) {
+ $form['q'] = array(
+ '#type' => 'hidden',
+ '#value' => $_GET['q'],
+ );
+ }
+
+ $all = array('all' => t('<All>'));
+
+ $form['type'] = array(
+ '#type' => 'select',
+ '#title' => t('Type'),
+ '#options' => $all + $form_state['pages']['types'],
+ '#default_value' => 'all',
+ );
+
+ $form['storage'] = array(
+ '#type' => 'select',
+ '#title' => t('Storage'),
+ '#options' => $all + $form_state['pages']['storages'],
+ '#default_value' => 'all',
+ );
+
+ $form['order'] = array(
+ '#type' => 'select',
+ '#title' => t('Sort by'),
+ '#options' => array(
+ 'title' => t('Title'),
+ 'name' => t('Name'),
+ 'path' => t('Path'),
+ 'type' => t('Type'),
+ 'storage' => t('Storage'),
+ ),
+ '#default_value' => 'title',
+ );
+
+ $form['sort'] = array(
+ '#type' => 'select',
+ '#title' => t('Order'),
+ '#options' => array(
+ 'asc' => t('Up'),
+ 'desc' => t('Down'),
+ ),
+ '#default_value' => 'asc',
+ );
+
+ $form['submit'] = array(
+ '#name' => '', // so it won't in the $_GET args
+ '#type' => 'submit',
+ '#id' => 'edit-pages-apply',
+ '#value' => t('Apply'),
+ );
+
+ if (!empty($_SESSION['page_manager']['#admin'])) {
+ $form['reset'] = array(
+ '#type' => 'submit',
+ '#id' => 'edit-pages-reset',
+ '#value' => t('Reset'),
+ );
+ }
+
+ $form['#theme'] = array('page_manager_list_pages_form');
+ return $form;
+}
+
+/**
+ * Accept submission from the page manager sort/filter form and apply it
+ * to the list of pages.
+ */
+function page_manager_list_pages_form_submit(&$form, &$form_state) {
+ // Filter and re-sort the pages.
+
+ // This is a copy.
+ $rows = $form_state['pages']['rows'];
+
+ $sorts = array();
+ foreach ($rows as $name => $data) {
+ // Filter
+ if ($form_state['values']['type'] != 'all' && $form_state['values']['type'] != $data['data']['type']['data']) {
+ continue;
+ }
+
+ if ($form_state['values']['storage'] != 'all' && $form_state['values']['storage'] != $data['data']['storage']['data']) {
+ continue;
+ }
+
+ // Set up sorting
+ switch ($form_state['values']['order']) {
+ case 'title':
+ $sorts[$name] = $data['data']['title']['data'];
+ break;
+ case 'name':
+ $sorts[$name] = $data['data']['name']['data'];
+ break;
+ case 'path':
+ $sorts[$name] = $data['data']['path']['data'];
+ break;
+ case 'type':
+ $sorts[$name] = $data['data']['type']['data'];
+ break;
+ case 'storage':
+ $sorts[$name] = $data['data']['storage']['data'];
+ break;
+ }
+ }
+
+ // Now actually sort
+ if ($form_state['values']['sort'] == 'desc') {
+ arsort($sorts);
+ }
+ else {
+ asort($sorts);
+ }
+
+ // Nuke the original.
+ $form_state['pages']['rows'] = array();
+ // And restore.
+ foreach ($sorts as $name => $title) {
+ $form_state['pages']['rows'][$name] = $rows[$name];
+ }
+
+}
+
+/**
+ * Render the edit page for a a page, custom or system.
+ */
+function page_manager_edit_page($page) {
+ ctools_include('form');
+ drupal_set_title($page->subtask['admin title']);
+ // Provide and process the save page form before anything else.
+ $form_state = array('page' => &$page);
+ $form = ctools_build_form('page_manager_save_page_form', $form_state);
+
+ $operations = page_manager_get_operations($page);
+ $args = array('summary');
+ $rendered_operations = page_manager_render_operations($page, $operations, $args, array('class' => 'operations-main'), 'nav');
+ $content = page_manager_get_operation_content(FALSE, $page, $args, $operations);
+
+ $output = theme('page_manager_edit_page', $page, $form, $rendered_operations, $content);
+ return $output;
+}
+
+/**
+ * Entry point to edit a single operation for a page.
+ *
+ * @param $js
+ * Whether or not the page was called via javascript.
+ * @param $page
+ * The cached page that is being edited.
+ * @param ...
+ * A number of items used to drill down into the actual operation called.
+ */
+function page_manager_edit_page_operation() {
+ $args = func_get_args();
+ $js = array_shift($args);
+ $page = array_shift($args);
+
+ $operations = page_manager_get_operations($page);
+ $content = page_manager_get_operation_content($js, $page, $args, $operations);
+
+ // If the operation requested we go somewhere else afterward, oblige it.
+ if (isset($content['new trail'])) {
+ $args = $content['new trail'];
+ // Get operations again, for the operation may have changed their availability.
+ $operations = page_manager_get_operations($page);
+ $content = page_manager_get_operation_content($js, $page, $args, $operations);
+ }
+
+ // Rendering the content may have been a form submission that changed the
+ // operations, such as renaming or adding a handler. Thus we get a new set
+ // of operations.
+ $operations = page_manager_get_operations($page);
+ $rendered_operations = page_manager_render_operations($page, $operations, $args, array('class' => 'operations-main'), 'nav');
+
+ // Since this form should never be submitted to this page, process it late so
+ // that we can be sure it notices changes.
+ ctools_include('form');
+ $form_state = array('page' => &$page);
+ $form = ctools_build_form('page_manager_save_page_form', $form_state);
+
+ $output = theme('page_manager_edit_page', $page, $form, $rendered_operations, $content);
+
+ if ($js) {
+ $commands = array();
+ if (isset($content['js settings'])) {
+ $commands[] = ctools_ajax_command_settings($content['js settings']);
+ }
+ $commands[] = ctools_ajax_command_replace('#page-manager-edit', $output);
+
+ ctools_ajax_render($commands);
+ }
+
+ drupal_set_title($page->subtask['admin title']);
+ return $output;
+}
+
+/**
+ * Take the operations array from a task and expand it.
+ *
+ * This allows some of the operations to be dynamic, based upon settings
+ * on the task or the task's handlers. Each operation should have a type. In
+ * addition to all the types allowed in page_manager_render_operations, these
+ * types will be dynamically replaced with something else:
+ * - 'handlers': An automatically created group that contains all the task's
+ * handlers and appropriate links.
+ * - 'function': A callback (which will be placed in the 'function' parameter
+ * that should return an array of operations. This can be used to provide
+ * additional, dynamic links if needed.
+ */
+function page_manager_get_operations($page, $operations = NULL) {
+ if (!isset($operations)) {
+ // All tasks have at least these 2 ops:
+ $operations = array(
+ 'summary' => array(
+ 'title' => t('Summary'),
+ 'description' => t('Get a summary of the information about this page.'),
+ 'path' => 'admin/build/pages/edit',
+ 'ajax' => FALSE,
+ 'no operations' => TRUE,
+ 'form info' => array(
+ 'no buttons' => TRUE,
+ ),
+ 'form' => 'page_manager_page_summary',
+ ),
+ 'actions' => array(
+ 'type' => 'group',
+ 'title' => '',
+ 'class' => 'operations-actions',
+ 'location' => 'primary',
+ 'children' => array(),
+ ),
+ );
+
+ if (isset($page->subtask['operations'])) {
+ $operations += $page->subtask['operations'];
+ // add actions separately.
+ if (!empty($page->subtask['operations']['actions'])) {
+ $operations['actions']['children'] += $page->subtask['operations']['actions']['children'];
+ }
+ }
+ $operations['handlers'] = array('type' => 'handlers');
+ }
+
+ $result = array();
+ foreach ($operations as $id => $operation) {
+ if (empty($operation['type'])) {
+ $operation['type'] = 'operation';
+ }
+ switch ($operation['type']) {
+ case 'handlers':
+ $result[$id] = page_manager_get_handler_operations($page);
+ break;
+ case 'function':
+ if (function_exists($operation['function'])) {
+ $retval = $function($page, $operation);
+ if (is_array($retval)) {
+ $result[$id] = $retval;
+ }
+ }
+ break;
+ default:
+ $result[$id] = $operation;
+ }
+ }
+
+ $result['actions']['children']['add'] = array(
+ 'title' => t('Add variant'),
+ 'description' => t('Add a new variant to this page.'),
+ 'form' => 'page_manager_handler_add',
+ 'ajax' => FALSE,
+ 'silent' => TRUE, // prevents a message about updating and prevents this item from showing as changed.
+ 'form info' => array(
+ 'finish text' => t('Add variant'),
+ ),
+ );
+
+ $result['actions']['children']['import'] = array(
+ 'title' => t('Import variant'),
+ 'description' => t('Add a new variant to this page from code exported from another page.'),
+ 'form' => 'page_manager_handler_import',
+ );
+
+ if (count($page->handlers) > 1) {
+ $result['actions']['children']['rearrange'] = array(
+ 'title' => t('Reorder variants'),
+ 'ajax' => FALSE,
+ 'description' => t('Change the priority of the variants to ensure that the right one gets selected.'),
+ 'form' => 'page_manager_handler_rearrange',
+ );
+ }
+
+ // This is a special operation used to configure a new task handler before
+ // it is added.
+ if (isset($page->new_handler)) {
+ $plugin = page_manager_get_task_handler($page->new_handler->handler);
+ $result['actions']['children']['configure'] = array(
+ 'title' => t('Configure'),
+ 'description' => t('Configure a newly created variant prior to actually adding it to the page.'),
+ 'ajax' => FALSE,
+ 'form info' => array(
+ // We use our own cancel and finish callback to handle the fun stuff.
+ 'finish callback' => 'page_manager_handler_add_finish',
+ 'cancel callback' => 'page_manager_handler_add_cancel',
+ 'show trail' => TRUE,
+ 'show back' => TRUE,
+ 'finish text' => t('Add variant'),
+ ),
+ 'form' => array(
+ 'forms' => $plugin['forms'],
+ ),
+ );
+
+ foreach ($page->forms as $id) {
+ if (isset($plugin['add features'][$id])) {
+ $result['actions']['children']['configure']['form']['order'][$id] = $plugin['add features'][$id];
+ }
+ else if (isset($plugin['required forms'][$id])) {
+ $result['actions']['children']['configure']['form']['order'][$id] = $plugin['required forms'][$id];
+ }
+ }
+ }
+
+ if ($page->locked) {
+ $result['actions']['children']['break-lock'] = array(
+ 'title' => t('Break lock'),
+ 'description' => t('Break the lock on this page so that you can edit it.'),
+ 'form' => 'page_manager_break_lock',
+ 'ajax' => FALSE,
+ 'form info' => array(
+ 'finish text' => t('Break lock'),
+ ),
+ 'even locked' => TRUE, // show button even if locked
+ 'silent' => TRUE,
+ );
+ }
+
+ return $result;
+}
+
+/**
+ * Collect all the operations related to task handlers (variants) and
+ * build a menu.
+ */
+function page_manager_get_handler_operations(&$page) {
+ ctools_include('export');
+ $group = array(
+ 'type' => 'group',
+ 'class' => 'operations-handlers',
+ 'title' => t('Variants'),
+ );
+
+ $operations = array();
+
+ foreach ($page->handler_info as $id => $info) {
+ if ($info['changed'] & PAGE_MANAGER_CHANGED_DELETED) {
+ continue;
+ }
+ $handler = $page->handlers[$id];
+ $plugin = page_manager_get_task_handler($handler->handler);
+
+ $operations[$id] = array(
+ 'type' => 'group',
+ 'class' => 'operations-handlers-' . $id,
+ 'title' => page_manager_get_handler_title($plugin, $handler, $page->task, $page->subtask_id),
+ 'collapsible' => TRUE,
+ 'children' => array(),
+ );
+
+ $operations[$id]['children']['actions'] = array(
+ 'type' => 'group',
+ 'class' => 'operations-handlers-actions-' . $id,
+ 'title' => t('Variant operations'),
+ 'children' => array(),
+ 'location' => $id,
+ );
+
+ // There needs to be a 'summary' item here for variants.
+ $operations[$id]['children']['summary'] = array(
+ 'title' => t('Summary'),
+ 'description' => t('Get a summary of the information about this variant.'),
+ 'form info' => array(
+ 'no buttons' => TRUE,
+ ),
+ 'form' => 'page_manager_handler_summary',
+ );
+
+ if ($plugin && isset($plugin['operations'])) {
+ $operations[$id]['children'] += $plugin['operations'];
+ }
+
+ $actions = &$operations[$id]['children']['actions']['children'];
+
+ $actions['clone'] = array(
+ 'title' => t('Clone'),
+ 'description' => t('Make an exact copy of this variant.'),
+ 'form' => 'page_manager_handler_clone',
+ );
+ $actions['export'] = array(
+ 'title' => t('Export'),
+ 'description' => t('Export this variant into code to import into another page.'),
+ 'form' => 'page_manager_handler_export',
+ );
+ if ($handler->export_type == (EXPORT_IN_CODE | EXPORT_IN_DATABASE)) {
+ $actions['delete'] = array(
+ 'title' => t('Revert'),
+ 'description' => t('Remove all changes to this variant and revert to the version in code.'),
+ 'form' => 'page_manager_handler_delete',
+ 'form info' => array(
+ 'finish text' => t('Revert'),
+ ),
+ );
+ }
+ else if ($handler->export_type != EXPORT_IN_CODE) {
+ $actions['delete'] = array(
+ 'title' => t('Delete'),
+ 'description' => t('Remove this variant from the page completely.'),
+ 'form' => 'page_manager_handler_delete',
+ 'form info' => array(
+ 'finish text' => t('Delete'),
+ ),
+ );
+ }
+ if (!empty($handler->disabled)) {
+ $actions['enable'] = array(
+ 'title' => t('Enable'),
+ 'description' => t('Activate this variant so that it will be in use in your system.'),
+ 'form' => 'page_manager_handler_enable',
+ 'silent' => TRUE,
+ 'form info' => array(
+ 'finish text' => t('Enable'),
+ ),
+ );
+ }
+ else {
+ $actions['disable'] = array(
+ 'title' => t('Disable'),
+ 'description' => t('De-activate this variant. The data will remain but the variant will not be in use on your system.'),
+ 'form' => 'page_manager_handler_disable',
+ 'silent' => TRUE,
+ 'form info' => array(
+ 'finish text' => t('Disable'),
+ ),
+ );
+ }
+ }
+ if (empty($operations)) {
+ $operations['empty'] = array(
+ 'type' => 'text',
+ 'title' => t('No variants'),
+ );
+ }
+
+ $group['children'] = $operations;
+ return $group;
+}
+
+/**
+ * Get an operation from a trail.
+ *
+ * @return array($operation, $active, $args)
+ */
+function page_manager_get_operation($operations, $trail) {
+ $args = $trail;
+ $stop = FALSE;
+ $active = array();
+ $titles = array();
+ // Drill down into operations array:
+ while (!$stop) {
+ $check = reset($args);
+ $stop = TRUE;
+ if (is_array($operations)) {
+ if (isset($operations[$check])) {
+ $active[] = $check;
+ $operation = array_shift($args);
+ // check to see if this operation has children. If so, we continue.
+ if (!isset($operations[$check]['children'])) {
+ $operations = $operations[$check];
+ }
+ else {
+ $titles[] = $operations[$check]['title'];
+ $operations = $operations[$check]['children'];
+ // continue only if the operation hs children.
+ $stop = FALSE;
+ }
+ }
+ }
+ }
+
+ return array($operations, $active, $args, $titles);
+}
+
+/**
+ * Fetch the content for an operation.
+ *
+ * First, this drills down through the arguments to find the operation, and
+ * turns whatever it finds into the active trail which is then used to
+ * hilite where we are when rendering the operation list.
+ *
+ * The arguments are discovered from the URL, and are an exact match for where
+ * the operation is in the hierarchy. For example, handlers/foo/settings will
+ * be the operation to edit the settings for the handler named foo. This comes
+ * in as an array ('handlers', 'foo', 'settings') and is used to find where the
+ * data for that operation is in the array.
+ */
+function page_manager_get_operation_content($js, &$page, $trail, $operations) {
+ list($operation, $active, $args, $titles) = page_manager_get_operation($operations, $trail);
+ // Once we've found the operation, send it off to render.
+ if ($operation) {
+ $content = _page_manager_get_operation_content($js, $page, $active, $operation, $titles, $args);
+ }
+
+ if (empty($content)) {
+ $content = _page_manager_get_operation_content($js, $page, array('summary'), $operations['summary']);
+ }
+
+ return $content;
+}
+
+/**
+ * Fetch the content for an operation, after it's been discovered from arguments.
+ *
+ * This system runs through the CTools form wizard. Each operation specifies a form
+ * or set of forms that it may use. Operations may also specify wrappers and can
+ * set their own next/finish handlers so that they can make additional things happen
+ * at the end.
+ */
+function _page_manager_get_operation_content($js, &$page, $active, $operation, $titles = array(), $args = array()) {
+ if (isset($operation['form'])) {
+ $form_info = array(
+ 'id' => 'page_manager_page',
+ 'finish text' => t('Update'),
+ 'show trail' => FALSE,
+ 'show back' => FALSE,
+ 'show return' => FALSE,
+ 'show cancel' => FALSE,
+ 'next callback' => 'page_manager_edit_page_next',
+ 'finish callback' => 'page_manager_edit_page_finish',
+ // Items specific to the 'edit' routines that will get moved over:
+ 'path' => "admin/build/pages/nojs/operation/$page->task_name/" . implode('/', $active) . "/%step",
+ );
+
+ // If $operation['form'] is simply a string, then it is the function
+ // name of the form.
+ if (!is_array($operation['form'])) {
+ $form_info['order'] = array(
+ 'form' => $operation['title'],
+ );
+ $form_info['forms'] = array(
+ 'form' => array('form id' => $operation['form']),
+ );
+ if (isset($operation['wrapper'])) {
+ $form_info['forms']['form']['wrapper'] = $operation['wrapper'];
+ }
+ }
+ // Otherwise it's the order and forms arrays directly.
+ else {
+ $form_info['order'] = $operation['form']['order'];
+ $form_info['forms'] = $operation['form']['forms'];
+ }
+
+ // Allow the operation to override any form info settings:
+ if (isset($operation['form info'])) {
+ foreach ($operation['form info'] as $key => $setting) {
+ $form_info[$key] = $setting;
+ }
+ }
+
+ if (!empty($page->subtask['operations include'])) {
+ // Quickly load any files necessary to display the forms.
+ $page->subtask['operations include']['function'] = 'nop';
+ ctools_plugin_get_function($page->subtask, 'operations include');
+ }
+
+ $step = array_shift($args);
+ // If step is unset, go with the basic step.
+ if (!isset($step)) {
+ $step = current(array_keys($form_info['order']));
+ }
+
+ // If it is locked, hide the buttonzzz!
+ if ($page->locked && empty($operation['even locked'])) {
+ $form_info['no buttons'] = TRUE;
+ }
+
+ ctools_include('wizard');
+ $form_state = array(
+ 'page' => $page,
+ 'type' => 'edit',
+ 'ajax' => $js && (!isset($operation['ajax']) || !empty($operation['ajax'])),
+ 'rerender' => TRUE,
+ 'no_redirect' => $js,
+ 'trail' => $active,
+ 'task_name' => $page->task_name,
+ 'task_id' => $page->task_id,
+ 'task' => $page->task,
+ 'subtask_id' => $page->subtask_id,
+ 'subtask' => $page->subtask,
+ 'operation' => $operation,
+ );
+
+ if ($active && $active[0] == 'handlers' && isset($form_state['page']->handlers[$form_state['trail'][1]])) {
+ $form_state['handler_id'] = $form_state['trail'][1];
+ $form_state['handler'] = &$form_state['page']->handlers[$form_state['handler_id']];
+ }
+
+ if ($active && $active[0] == 'handlers' && $active[1] == 'configure' && isset($form_state['page']->new_handler)) {
+ $form_state['handler_id'] = $form_state['page']->new_handler->name;
+ $form_state['handler'] = &$form_state['page']->new_handler;
+ }
+
+ $output = ctools_wizard_multistep_form($form_info, $step, $form_state);
+ $title = empty($form_state['title']) ? $operation['title'] : $form_state['title'];
+
+ $titles[] = $title;
+ $title = implode(' &raquo ', array_filter($titles));
+ if (isset($form_state['js settings'])) {
+ $js_settings = $form_state['js settings'];
+ }
+
+ // If there are messages for the form, render them.
+ if ($messages = theme('status_messages')) {
+ $output = $messages . $output;
+ }
+
+ $return = array(
+ 'title' => $title,
+ 'content' => $output
+ );
+
+ // If the form wanted us to go somewhere else next, pass that along.
+ if (isset($form_state['new trail'])) {
+ $return['new trail'] = $form_state['new trail'];
+ }
+ }
+ else {
+ $output = '<pre>' . check_plain(var_export($active, 1)) . '</pre>';
+ $output .= '<pre>' . check_plain(var_export($operation, 1)) . '</pre>';
+ $return = array(
+ 'title' => t('Test data'),
+ 'content' => t('If this were finished, you would see useful output here. It is not, so you do not.') . $output
+ );
+ }
+
+ if (isset($js_settings)) {
+ $return['js settings'] = $js_settings;
+ }
+
+ $return['active'] = $active;
+ return $return;
+}
+
+/**
+ * Callback generated when the add page process is finished.
+ */
+function page_manager_edit_page_finish(&$form_state) {
+ if (empty($form_state['operation']['silent'])) {
+ drupal_set_message(t('The page has been updated. Changes will not be permanent until you save.'));
+ $path = array();
+ foreach ($form_state['trail'] as $operation) {
+ $path[] = $operation;
+ $form_state['page']->changes[implode('/', $path)] = TRUE;
+ }
+ }
+
+ // If a handler was modified, set it to changed so we know to overwrite it.
+ if (isset($form_state['handler_id'])) {
+ $form_state['page']->handler_info[$form_state['handler_id']]['changed'] |= PAGE_MANAGER_CHANGED_CACHED;
+ }
+
+ // While we make buttons go away on locked pages, it is still possible to
+ // have a lock a appear while you were editing, and have your changes
+ // disappear. This at least warns the user that this has happened.
+ if (!empty($page->locked)) {
+ drupal_set_message(t('Unable to update changes due to lock.'));
+ }
+
+ if (empty($form_state['do not cache'])) {
+ page_manager_set_page_cache($form_state['page']);
+ }
+
+ if (isset($form_state['new trail']) && empty($form_state['ajax'])) {
+ $form_state['redirect'] = 'admin/build/pages/nojs/operation/' . $form_state['page']->task_name . '/' . implode('/', $form_state['new trail']);
+ }
+}
+
+/**
+ * Callback generated when the 'next' button is clicked.
+ *
+ * All we do here is store the cache.
+ */
+function page_manager_edit_page_next(&$form_state) {
+ page_manager_set_page_cache($form_state['page']);
+}
+
+/**
+ * Callback generated when the 'cancel' button is clicked.
+ *
+ * All we do here is clear the cache.
+ */
+function page_manager_edit_page_cancel(&$form_state) {
+ return;
+}
+
+/**
+ * Render an operations array.
+ *
+ * This renders an array of operations into a series of nested UL statements,
+ * with ajax automatically on unless specified otherwise. Operations will
+ * automatically have the URLs generated nested.
+ *
+ * Each operation should have a 'type', which tells the renderer how to deal
+ * with it:
+ * - 'operation': An AJAX link to render. This is the default and is
+ * assumed if a type is not specified. Other fields for the operation:
+ * - - 'title': The text to display. Can be an image. Must be pre-sanitized.
+ * - - 'description': Text to place in the hover box over the link using the
+ * title attribute.
+ * - - 'arguments': Anything optional to put at the end of the URL.
+ * - - 'path': If set, overrides the default path.
+ * - - 'no operations': If set, the path will not have operations appended.
+ * - - 'no task': If set, the path will not have the task id.
+ * - - 'no link': If set, this item will just be text, not a link.
+ * - - 'ajax': If set to TRUE, ajax will be used. The default is TRUE.
+ * - - 'class': An optional class to specify for the link.
+ * - - 'form': The form to display for this operation, if using a single form.
+ * - - 'forms': An array of forms that must be paired with 'order' of this
+ * operation uses multiple forms. See wizard tool for details.
+ * - - 'order': The form order to use for multiple forms. See wizard tool for
+ * details.
+ * - - 'form info': Form info overrides for the wizard. See the wizard tool
+ * for available settings
+ * - 'group':
+ * - - 'title': The title of the link. May be HTML.
+ * - - 'title class': A class to apply to the title.
+ * - - 'children': An array of more operations that this group represents.
+ * All operations within this group will have this group's ID as part
+ * of the AJAX url to make it easier to find.
+ * - - 'class': A class to apply to the UL of the children.
+ * - - 'collapsible': If TRUE the collapsible tool will be used.
+ */
+function page_manager_render_operations(&$page, $operations, $active_trail, $attributes, $location, $parents = array()) {
+ if (!isset($output[$location])) {
+ $output[$location] = '';
+ }
+
+ $keys = array_keys($operations);
+ $first = array_shift($keys);
+ $last = array_pop($keys);
+
+ // Make sure the 'first' and 'last' operations are part of THIS nav tree:
+ while ($keys && isset($operations[$first]['location']) && $operations[$first]['location'] != $location) {
+ $first = array_shift($keys);
+ }
+ while ($keys && isset($operations[$last]['location']) && $operations[$last]['location'] != $location) {
+ $last = array_pop($keys);
+ }
+
+ $active = reset($active_trail);
+ foreach ($operations as $id => $operation) {
+ $current_path = '';
+ if ($parents) {
+ $current_path .= implode('/', $parents) . '/';
+ }
+ $current_path .= $id;
+
+ if (empty($operation['type'])) {
+ $operation['type'] = 'operation';
+ }
+
+ // We only render an li for things in the same nav tree.
+ if (empty($operation['location']) || $operation['location'] == $location) {
+ $class = $attributes['class'];
+ if ($id == $first) {
+ $class .= ' operation-first';
+ }
+ else if ($id == $last) {
+ $class .= ' operation-last';
+ }
+
+ if (empty($operation['silent']) && !empty($page->changes[$current_path])) {
+ $class .= $operation['type'] == 'group' ? ' changed-group' : ' changed';
+ }
+ else {
+ $class .= ' not-changed';
+ }
+
+ if ($active == $id) {
+ $class .= $operation['type'] == 'group' ? ' active-group' : ' active';
+ }
+ else {
+ $class .= ' not-active';
+ }
+
+ $output[$location] .= '<li class="' . $class . '">';
+ }
+
+ switch ($operation['type']) {
+ case 'text':
+ $output[$location] .= $operation['title'];
+ break;
+ case 'operation':
+ $path = isset($operation['path']) ? $operation['path'] : 'admin/build/pages/nojs/operation';
+ if (!isset($operation['no task'])) {
+ $path .= '/' . $page->task_name;
+ }
+
+ if (!isset($operation['no operations'])) {
+ $path .= '/' . $current_path;
+ if (isset($operation['arguments'])) {
+ $path .= '/' . $arguments;
+ }
+ }
+
+ $class = 'page-manager-operation';
+ if (!isset($operation['ajax']) || !empty($operation['ajax'])) {
+ $class .= ' ctools-use-ajax';
+ }
+ if (!empty($operation['class'])) {
+ $class .= ' ' . $operation['class'];
+ }
+
+ $description = isset($operation['description']) ? $operation['description'] : '';
+ if (empty($operation['silent']) && !empty($page->changes[$current_path])) {
+ $description .= ' ' . t('This setting contains unsaved changes.');
+ }
+
+ $output[$location] .= l($operation['title'], $path, array('attributes' => array('id' => 'page-manager-operation-' . $id, 'class' => $class, 'title' => $description), 'html' => TRUE));
+ break;
+ case 'group':
+ if ($active == $id) {
+ $trail = $active_trail;
+ array_shift($trail);
+ }
+ else {
+ $trail = array();
+ }
+ $group_location = isset($operation['location']) ? $operation['location'] : $location;
+ $temp = page_manager_render_operations($page, $operation['children'], $trail, $operation, $group_location, array_merge($parents, array($id)));
+ if ($temp) {
+ foreach ($temp as $id => $text) {
+ if (empty($output[$id])) {
+ $output[$id] = '';
+ }
+ $output[$id] .= $text;
+ }
+ }
+ break;
+ }
+
+ if (empty($operation['location']) || $operation['location'] == $location) {
+ $output[$location] .= '</li>';
+ }
+ }
+
+ if ($output[$location]) {
+ $output[$location] = '<ul class="page-manager-operations ' . $attributes['class'] . '">' . $output[$location] . '</ul>';
+
+ if (!empty($attributes['title'])) {
+ $class = '';
+ if (isset($attributes['title class'])) {
+ $class = $attributes['title class'];
+ }
+ $title = '<div class="page-manager-group-title' . $class . '">' . $attributes['title'] . '</div>';
+
+ if (!empty($attributes['collapsible'])) {
+ $output[$location] = theme('ctools_collapsible', $title, $output[$location], empty($active_trail));
+ }
+ else {
+ $output[$location] = $title . $output[$location];
+ }
+ }
+ return $output;
+ }
+}
+
+/**
+ * Provide a simple form for saving the page manager info out of the cache.
+ */
+function page_manager_save_page_form(&$form_state) {
+ if (!empty($form_state['page']->changed)) {
+ $form['markup'] = array(
+ '#value' => '<div class="changed-notification">' . t('You have unsaved changes to this page. You must select Save to write them to the database, or Cancel to discard these changes.') . '</div>',
+ );
+
+ // Always make sure we submit back to the proper age.
+ $form['#action'] = url('admin/build/pages/edit/' . $form_state['page']->task_name);
+ $form['save'] = array(
+ '#type' => 'submit',
+ '#value' => t('Save'),
+ '#submit' => array('page_manager_save_page_form_submit'),
+ );
+
+ $form['cancel'] = array(
+ '#type' => 'submit',
+ '#value' => t('Cancel'),
+ '#submit' => array('page_manager_save_page_form_cancel'),
+ );
+ return $form;
+ }
+}
+
+/**
+ * Save the page.
+ */
+function page_manager_save_page_form_submit(&$form, &$form_state) {
+ page_manager_save_page_cache($form_state['page']);
+}
+
+/**
+ * Discard changes to the page.
+ */
+function page_manager_save_page_form_cancel(&$form, &$form_state) {
+ drupal_set_message(t('All pending changes have been discarded, and the page is now unlocked.'));
+ page_manager_clear_page_cache($form_state['page']->task_name);
+}
+
+// --------------------------------------------------------------------------
+// Handler (variant) related forms.
+
+/**
+ * Add a new task handler.
+ */
+function page_manager_handler_add(&$form, &$form_state) {
+ // Get a list of possible task handlers for this task.
+ page_manager_handler_add_form($form, $form_state);
+}
+
+/**
+ * Handler related forms.
+ */
+function page_manager_handler_add_submit(&$form, &$form_state) {
+ $cache = $form_state['page'];
+ $plugin = page_manager_get_task_handler($form_state['values']['handler']);
+
+ // Create a new handler.
+ $handler = page_manager_new_task_handler($plugin);
+ if (!empty($form_state['values']['title'])) {
+ $handler->conf['title'] = $form_state['values']['title'];
+ }
+ else {
+ $handler->conf['title'] = $plugin['title'];
+ }
+ $cache->new_handler = $handler;
+
+ // Figure out which forms to present them with
+ $cache->forms = array();
+
+ $features = $form_state['values']['features'];
+ if (isset($features[$form_state['values']['handler']])) {
+ $cache->forms = array_merge($cache->forms, array_keys(array_filter($features[$form_state['values']['handler']])));
+ }
+
+ if (isset($plugin['required forms'])) {
+ $cache->forms = array_merge($cache->forms, array_keys($plugin['required forms']));
+ }
+
+ $form_state['no_rerender'] = TRUE;
+ if (!empty($cache->forms)) {
+ // Tell the form to go to the config page.
+ drupal_set_message(t('Before this variant can be added, it must be configured. When you are finished, click "Add variant" to add this to your page.'));
+ $form_state['new trail'] = array('handlers', 'configure');
+ }
+ else {
+ // It has no forms at all. Add the variant and go to its first operation.
+ $keys = array_keys($plugin['operations']);
+ $form_state['new trail'] = array('handlers', $name, reset($keys));
+ }
+}
+
+/**
+ * Finish the add process and make the new handler official.
+ */
+function page_manager_handler_add_finish(&$form_state) {
+ $page = &$form_state['page'];
+ $handler = $page->new_handler;
+ page_manager_handler_add_to_page($page, $handler);
+
+ // Remove the temporary page.
+ unset($page->new_handler);
+ unset($page->forms);
+
+ // Set the new destination
+ $plugin = page_manager_get_task_handler($handler->handler);
+ $keys = array_keys($plugin['operations']);
+ $form_state['new trail'] = array('handlers', $name, reset($keys));
+
+ // Pass through.
+ page_manager_edit_page_finish($form_state);
+}
+
+/**
+ * Throw away a new handler and return to the add form
+ */
+function page_manager_handler_add_cancel(&$form_state) {
+ $form_state['new trail'] = array('handlers', 'add');
+
+ // Remove the temporary page.
+ unset($page->new_handler);
+ unset($page->forms);
+}
+
+/**
+ * Provide a consistent UI for adding handlers.
+ */
+function page_manager_handler_add_form(&$form, $form_state, $features = array()) {
+ $task = $form_state['task'];
+ $task_handler_plugins = page_manager_get_task_handler_plugins($task);
+ foreach ($task_handler_plugins as $id => $plugin) {
+ $options[$id] = $plugin['title'];
+ if (isset($plugin['add features'])) {
+ $features[$id] = $plugin['add features'];
+ }
+ }
+
+ $form['handler'] = array(
+ '#title' => t('Variant type'),
+ '#type' => 'select',
+ '#options' => $options,
+ );
+
+ // This set of checkboxes is not dangerous at all.
+ $form['features'] = array(
+ '#type' => 'checkboxes',
+ '#validated' => TRUE,
+ '#title' => t('Features'),
+ '#options' => array(),
+ '#description' => t('Check any optional features you need to be presented with forms for configuring them. If you do not check them here you will still be able to utilize these features once the new page is created. If you are not sure, leave these unchecked.'),
+ '#tree' => TRUE,
+ );
+
+ ctools_include('dependent');
+ foreach ($features as $plugin => $feature_list) {
+ foreach ($feature_list as $feature_id => $feature) {
+ $form['features'][$plugin][$feature_id] = array(
+ '#type' => 'checkbox',
+ '#title' => $feature,
+ );
+
+ if ($plugin != 'default') {
+ $form['features'][$plugin][$feature_id] += array(
+ '#process' => array('ctools_dependent_process'),
+ '#dependency' => array('edit-handler' => array($plugin)),
+ );
+ }
+ }
+ }
+}
+
+/**
+ * Rearrange the order of variants.
+ */
+function page_manager_handler_import(&$form, &$form_state) {
+ $form['title'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Variant name'),
+ '#description' => t('Enter the name of the new variant.'),
+ );
+
+ $form['object'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Paste variant code here'),
+ '#rows' => 15,
+ );
+}
+
+/**
+ * Make sure that an import actually provides a handler.
+ */
+function page_manager_handler_import_validate($form, &$form_state) {
+ $export = page_manager_export_task_handler($form_state['values']['object']);
+ ob_start();
+ eval($export);
+ ob_end_clean();
+
+ if (empty($handler)) {
+ $errors = ob_get_contents();
+ if (empty($errors)) {
+ $errors = t('No variant found.');
+ }
+
+ form_error($form['object'], t('Unable to get a variant from the import. Errors reported: @errors', array('@errors' => $errors)));
+ }
+
+ $form_state['handler'] = $handler;
+}
+
+/**
+ * Clone an existing task handler into a new handler.
+ */
+function page_manager_handler_import_submit($form, &$form_state) {
+ $export = page_manager_export_task_handler($form_state['handler']);
+
+ page_manager_handler_add_to_page($form_state['page'], $handler, $form_state['values']['title']);
+
+ $plugin = page_manager_get_task_handler($handler->handler);
+ // It has no forms at all. Add the variant and go to its first operation.
+ $keys = array_keys($plugin['operations']);
+ $form_state['new trail'] = array('handlers', $name, reset($keys));
+}
+
+/**
+ * Rearrange the order of variants.
+ */
+function page_manager_handler_rearrange(&$form, &$form_state) {
+ $page = $form_state['page'];
+
+ $form['handlers'] = array('#tree' => TRUE);
+
+ foreach ($page->handler_info as $id => $info) {
+ if ($info['changed'] & PAGE_MANAGER_CHANGED_DELETED) {
+ continue;
+ }
+ $handler = $page->handlers[$id];
+ $plugin = page_manager_get_task_handler($handler->handler);
+
+ $form['handlers'][$id]['title'] = array(
+ '#value' => page_manager_get_handler_title($plugin, $handler, $page->task, $page->subtask_id),
+ );
+
+ $form['handlers'][$id]['weight'] = array(
+ '#type' => 'weight',
+ '#default_value' => $info['weight'],
+ );
+ }
+}
+
+function page_manager_handler_rearrange_submit(&$form, &$form_state) {
+ $handler_info = &$form_state['page']->handler_info;
+
+ foreach ($form_state['values']['handlers'] as $id => $info) {
+ if ($handler_info[$id]['weight'] = $info['weight']) {
+ $handler_info[$id]['weight'] = $info['weight'];
+ $handler_info[$id]['changed'] |= PAGE_MANAGER_CHANGED_MOVED;
+ }
+ }
+
+ // Sort the new cache.
+ uasort($handler_info, '_page_manager_handler_sort');
+
+}
+
+/**
+ * Used as a callback to uasort to sort the task cache by weight.
+ *
+ * The 'name' field is used as a backup when weights are the same, which
+ * can happen when multiple modules put items out there at the same
+ * weight.
+ */
+function _page_manager_handler_sort($a, $b) {
+ if ($a['weight'] < $b['weight']) {
+ return -1;
+ }
+ elseif ($a['weight'] > $b['weight']) {
+ return 1;
+ }
+ elseif ($a['name'] < $b['name']) {
+ return -1;
+ }
+ elseif ($a['name'] > $b['name']) {
+ return 1;
+ }
+}
+
+/**
+ * Rearrange the order of variants.
+ */
+function page_manager_handler_delete(&$form, &$form_state) {
+ if ($form_state['handler']->type == t('Overridden')) {
+ $text = t('Reverting the variant will delete the variant that is in the database, reverting it to the original default variant. This deletion will not be made permanent until you click Save.');
+ }
+ else {
+ $text = t('Are you sure you want to delete this variant? This deletion will not be made permanent until you click Save.');
+ }
+ $form['markup'] = array(
+ '#value' => '<p>' . $text . '</p>',
+ );
+
+}
+
+/**
+ * Submit handler to delete a view.
+ */
+function page_manager_handler_delete_submit(&$form, &$form_state) {
+ $form_state['page']->handler_info[$form_state['handler_id']]['changed'] |= PAGE_MANAGER_CHANGED_DELETED;
+ $form_state['new trail'] = array('summary');
+}
+
+/**
+ * Entry point to export a page.
+ */
+function page_manager_handler_export(&$form, &$form_state) {
+ $export = page_manager_export_task_handler($form_state['handler']);
+
+ $lines = substr_count($export, "\n");
+ $form['code'] = array(
+ '#type' => 'textarea',
+ '#default_value' => $export,
+ '#rows' => $lines,
+ );
+
+ unset($form['buttons']);
+}
+
+/**
+ * Rearrange the order of variants.
+ */
+function page_manager_handler_clone(&$form, &$form_state) {
+ // This provides its own button because it does something totally different.
+ $form['title'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Variant name'),
+ '#description' => t('Enter the name of the new variant.'),
+ );
+}
+
+/**
+ * Clone an existing task handler into a new handler.
+ */
+function page_manager_handler_clone_submit($form, &$form_state) {
+ $handler = $form_state['handler'];
+ page_manager_handler_add_to_page($form_state['page'], $handler, $form_state['values']['title']);
+
+ $plugin = page_manager_get_task_handler($handler->handler);
+ // It has no forms at all. Add the variant and go to its first operation.
+ $keys = array_keys($plugin['operations']);
+ $form_state['new trail'] = array('handlers', $name, reset($keys));
+}
+
+/**
+ * Form to enable a handler.
+ */
+function page_manager_handler_enable(&$form, &$form_state) {
+ $form['markup'] = array(
+ '#value' => t('This variant is currently disabled. Enabling it will make it available in your system. This will not take effect until you save this page.'),
+ );
+}
+
+/**
+ * Enable the page after it has been confirmed.
+ */
+function page_manager_handler_enable_submit(&$form, &$form_state) {
+ $form_state['handler']->disabled = FALSE;
+ $form_state['page']->handler_info[$form_state['handler_id']]['disabled'] = FALSE;
+ $form_state['page']->handler_info[$form_state['handler_id']]['changed'] |= PAGE_MANAGER_CHANGED_STATUS;
+ $form_state['new trail'] = array('handlers', $form_state['handler_id'], 'disable');
+}
+
+/**
+ * Form to disable a page.
+ */
+function page_manager_handler_disable(&$form, &$form_state) {
+ $form['markup'] = array(
+ '#value' => t('This variant is currently enabled. Disabling it will make it unavailable in your system, and it will not be used. This will not take effect until you save this page.'),
+ );
+}
+
+/**
+ * Form to disable a page.
+ */
+function page_manager_handler_summary(&$form, &$form_state) {
+ $handler = $form_state['handler'];
+ $page = $form_state['page'];
+ $plugin = page_manager_get_task_handler($handler->handler);
+
+ $form['markup'] = array(
+ '#value' => page_manager_get_handler_summary($plugin, $handler, $page->task, $page->subtask_id),
+ );
+}
+
+/**
+ * Disable the page after it has been confirmed.
+ */
+function page_manager_handler_disable_submit(&$form, &$form_state) {
+ $form_state['handler']->disabled = TRUE;
+ $form_state['page']->handler_info[$form_state['handler_id']]['disabled'] = TRUE;
+ $form_state['page']->handler_info[$form_state['handler_id']]['changed'] |= PAGE_MANAGER_CHANGED_STATUS;
+ $form_state['new trail'] = array('handlers', $form_state['handler_id'], 'enable');
+}
+
+/**
+ * Break the lock on a page so that it can be edited.
+ */
+function page_manager_break_lock(&$form, &$form_state) {
+ $form['markup'] = array(
+ '#value' => t('Breaking the lock on this page will <strong>discard</strong> any pending changes made by the locking user. Are you REALLY sure you want to do this?')
+ );
+}
+
+/**
+ * Submit to break the lock on a page.
+ */
+function page_manager_break_lock_submit(&$form, &$form_state) {
+ $page = &$form_state['page'];
+ $form_state['page']->locked = FALSE;
+ ctools_object_cache_clear_all('page_manager_page', $page->task_name);
+ $form_state['do not cache'] = TRUE;
+ $form_state['new trail'] = array('summary');
+ drupal_set_message(t('The lock has been cleared and all changes discarded. You may now make changes to this page.'));
+}
+
+/**
+ * Print the summary information for a page.
+ */
+function page_manager_page_summary(&$form, &$form_state) {
+ $page = $form_state['page'];
+ if (isset($form_state['subtask']['description'])) {
+ $form['description'] = array(
+ '#value' => filter_xss_admin($form_state['subtask']['description']),
+ );
+ }
+} \ No newline at end of file
diff --git a/page_manager/page_manager.info b/page_manager/page_manager.info
new file mode 100644
index 0000000..b0a573e
--- /dev/null
+++ b/page_manager/page_manager.info
@@ -0,0 +1,5 @@
+; $Id$
+name = Page manager
+description = Provides a UI and API to manage pages within the site.
+core = 6.x
+dependencies[] = ctools
diff --git a/page_manager/page_manager.install b/page_manager/page_manager.install
new file mode 100644
index 0000000..81dd65f
--- /dev/null
+++ b/page_manager/page_manager.install
@@ -0,0 +1,217 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Installation routines for page manager module.
+ */
+
+/**
+ * Implementation of hook_schema().
+ */
+function page_manager_schema() {
+ // This should always point to our 'current' schema. This makes it relatively easy
+ // to keep a record of schema as we make changes to it.
+ return page_manager_schema_1();
+}
+
+/**
+ * Schema version 1 for Panels in D6.
+ */
+function page_manager_schema_1() {
+ $schema = array();
+
+ $schema['page_manager_handlers'] = array(
+ 'export' => array(
+ 'identifier' => 'handler',
+ 'api' => array(
+ 'owner' => 'page_manager',
+ 'api' => 'pages_default',
+ 'minimum_version' => 1,
+ 'current_version' => 1,
+ ),
+ ),
+ 'fields' => array(
+ 'did' => array(
+ 'type' => 'serial',
+ 'not null' => TRUE,
+ 'description' => 'Primary ID field for the table. Not used for anything except internal lookups.',
+ 'no export' => TRUE,
+ ),
+ 'name' => array(
+ 'type' => 'varchar',
+ 'length' => '255',
+ 'description' => 'Unique ID for this task handler. Used to identify it programmatically.',
+ ),
+ 'task' => array(
+ 'type' => 'varchar',
+ 'length' => '64',
+ 'description' => 'ID of the task this handler is for.',
+ ),
+ 'subtask' => array(
+ 'type' => 'varchar',
+ 'length' => '64',
+ 'description' => 'ID of the subtask this handler is for.',
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'handler' => array(
+ 'type' => 'varchar',
+ 'length' => '64',
+ 'description' => 'ID of the task handler being used.',
+ ),
+ 'weight' => array(
+ 'type' => 'int',
+ 'description' => 'The order in which this handler appears. Lower numbers go first.',
+ ),
+ 'conf' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'description' => 'Serialized configuration of the handler, if needed.',
+ 'not null' => TRUE,
+ 'serialize' => TRUE,
+ 'object default' => array(),
+ ),
+ ),
+ 'primary key' => array('did'),
+ 'unique keys' => array(
+ 'name' => array('name'),
+ ),
+ 'indexes' => array('fulltask' => array('task', 'subtask', 'weight')),
+ );
+
+ $schema['page_manager_weights'] = array(
+ 'description' => 'Contains override weights for page_manager handlers that are in code.',
+ 'fields' => array(
+ 'name' => array(
+ 'type' => 'varchar',
+ 'length' => '255',
+ 'description' => 'Unique ID for this task handler. Used to identify it programmatically.',
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'weight' => array(
+ 'type' => 'int',
+ 'description' => 'The order in which this handler appears. Lower numbers go first.',
+ ),
+ ),
+ 'primary key' => array('name'),
+ 'indexes' => array(
+ 'weights' => array('name', 'weight'),
+ ),
+ );
+
+ $schema['page_manager_pages'] = array(
+ 'description' => 'Contains page subtasks for implementing pages with arbitrary tasks.',
+ 'export' => array(
+ 'identifier' => 'page',
+ 'api' => array(
+ 'owner' => 'page_manager',
+ 'api' => 'pages_default',
+ 'minimum_version' => 1,
+ 'current_version' => 1,
+ ),
+ ),
+ 'fields' => array(
+ 'pid' => array(
+ 'type' => 'serial',
+ 'not null' => TRUE,
+ 'description' => 'Primary ID field for the table. Not used for anything except internal lookups.',
+ 'no export' => TRUE,
+ ),
+ 'name' => array(
+ 'type' => 'varchar',
+ 'length' => '255',
+ 'description' => 'Unique ID for this subtask. Used to identify it programmatically.',
+ ),
+ 'task' => array(
+ 'type' => 'varchar',
+ 'length' => '64',
+ 'description' => 'What type of page this is, so that we can use the same mechanism for creating tighter UIs for targeted pages.',
+ 'default' => 'page',
+ ),
+ 'admin_title' => array(
+ 'type' => 'varchar',
+ 'length' => '255',
+ 'description' => 'Human readable title for this page subtask.',
+ ),
+ 'path' => array(
+ 'type' => 'varchar',
+ 'length' => '255',
+ 'description' => 'The menu path that will invoke this task.',
+ ),
+ 'access' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'description' => 'Access configuration for this path.',
+ 'not null' => TRUE,
+ 'serialize' => TRUE,
+ 'object default' => array(),
+ ),
+ 'menu' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'description' => 'Serialized configuration of Drupal menu visibility settings for this item.',
+ 'not null' => TRUE,
+ 'serialize' => TRUE,
+ 'object default' => array(),
+ ),
+ 'arguments' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'description' => 'Configuration of arguments for this menu item.',
+ 'not null' => TRUE,
+ 'serialize' => TRUE,
+ 'object default' => array(),
+ ),
+ 'conf' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'description' => 'Serialized configuration of the page, if needed.',
+ 'not null' => TRUE,
+ 'serialize' => TRUE,
+ 'object default' => array(),
+ ),
+ ),
+ 'primary key' => array('pid'),
+ 'unique keys' => array(
+ 'name' => array('name'),
+ ),
+ 'indexes' => array('task' => array('task')),
+ );
+
+ return $schema;
+}
+
+/**
+ * Implementation of hook_install().
+ */
+function page_manager_install() {
+ drupal_install_schema('page_manager');
+
+ // If we're swapping over from delegator module, take away its tables.
+ // Take THAT, delegator!
+ if (db_table_exists('{delegator_pages}')) {
+ db_query("INSERT INTO {page_manager_pages} (SELECT pid, name, task, admin_title, path, access, menu, arguments, conf FROM {delegator_pages})");
+ db_query("INSERT INTO {page_manager_handlers} (SELECT * FROM {delegator_handlers})");
+ db_query("INSERT INTO {page_manager_weights} (SELECT * FROM {delegator_weights})");
+
+ db_drop_table('{delegator_pages}');
+ db_drop_table('{delegator_handlers}');
+ db_drop_table('{delegator_weights}');
+ }
+
+ db_query("UPDATE {system} SET weight = 99 WHERE name = 'page_manager'");
+}
+
+/**
+ * Implementation of hook_uninstall().
+ */
+function page_manager_uninstall() {
+ drupal_uninstall_schema('page_manager');
+}
+
+function page_manager_update_6101() {
+ $ret = array();
+ return $ret;
+}
diff --git a/page_manager/page_manager.module b/page_manager/page_manager.module
new file mode 100644
index 0000000..0925705
--- /dev/null
+++ b/page_manager/page_manager.module
@@ -0,0 +1,884 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * The page manager module provides a UI and API to manage pages.
+ *
+ * It defines pages, both for system pages, overrides of system pages, and
+ * custom pages using Drupal's normal menu system. It allows complex
+ * manipulations of these pages, their content, and their hierarchy within
+ * the site. These pages can be exported to code for superior revision
+ * control.
+ */
+
+/**
+ * Bit flag on the 'changed' value to tell us if an item was moved.
+ */
+define('PAGE_MANAGER_CHANGED_MOVED', 0x01);
+
+/**
+ * Bit flag on the 'changed' value to tell us if an item edited or added.
+ */
+define('PAGE_MANAGER_CHANGED_CACHED', 0x02);
+
+/**
+ * Bit flag on the 'changed' value to tell us if an item deleted.
+ */
+define('PAGE_MANAGER_CHANGED_DELETED', 0x04);
+
+/**
+ * Bit flag on the 'changed' value to tell us if an item has had its disabled status changed.
+ */
+define('PAGE_MANAGER_CHANGED_STATUS', 0x08);
+
+// --------------------------------------------------------------------------
+// Drupal hooks
+
+/**
+ * Implementation of hook_perm().
+ */
+function page_manager_perm() {
+ return array('use page manager', 'administer page manager');
+}
+
+/**
+ * Implementation of hook_ctools_plugin_dierctory() to let the system know
+ * we implement task and task_handler plugins.
+ */
+function page_manager_ctools_plugin_directory($module, $plugin) {
+ if ($module == 'page_manager') {
+ return 'plugins/' . $plugin;
+ }
+}
+
+/**
+ * Delegated implementation of hook_menu().
+ */
+function page_manager_menu() {
+ $items = array();
+ $base = array(
+ 'access arguments' => array('use page manager'),
+ 'file' => 'page_manager.admin.inc',
+ );
+
+ $items['admin/build/pages'] = array(
+ 'title' => 'Pages',
+ 'description' => 'Add, edit and remove overridden system pages and user defined pages from the system.',
+ 'page callback' => 'page_manager_list_page',
+ ) + $base;
+
+ $items['admin/build/pages/list'] = array(
+ 'title' => 'List',
+ 'page callback' => 'page_manager_list_page',
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ 'weight' => -10,
+ ) + $base;
+
+ $items['admin/build/pages/edit/%page_manager_cache'] = array(
+ 'title' => 'Edit',
+ 'page callback' => 'page_manager_edit_page',
+ 'page arguments' => array(4),
+ 'type' => MENU_CALLBACK,
+ ) + $base;
+
+ $items['admin/build/pages/%ctools_js/operation/%page_manager_cache'] = array(
+ 'page callback' => 'page_manager_edit_page_operation',
+ 'page arguments' => array(3, 5),
+ 'type' => MENU_CALLBACK,
+ ) + $base;
+
+ $tasks = page_manager_get_tasks();
+
+ // Provide menu items for each task.
+ foreach ($tasks as $task_id => $task) {
+ $handlers = page_manager_get_task_handler_plugins($task);
+ // Allow the task to add its own menu items.
+ if ($function = ctools_plugin_get_function($task, 'hook menu')) {
+ $function($items, $task);
+ }
+
+ // And for those that provide subtasks, provide menu items for them, as well.
+ foreach (page_manager_get_task_subtasks($task) as $subtask_id => $subtask) {
+ // Allow the task to add its own menu items.
+ if ($function = ctools_plugin_get_function($task, 'hook menu')) {
+ $function($items, $subtask);
+ }
+ }
+ }
+
+ return $items;
+}
+
+/**
+ * Implementation of hook_menu_alter.
+ *
+ * Get a list of all tasks and delegate to them.
+ */
+function page_manager_menu_alter(&$items) {
+ $tasks = page_manager_get_tasks();
+
+ foreach ($tasks as $task) {
+ if ($function = ctools_plugin_get_function($task, 'hook menu alter')) {
+ $function($items, $task);
+ }
+ // let the subtasks alter the menu items too.
+ foreach (page_manager_get_task_subtasks($task) as $subtask_id => $subtask) {
+ if ($function = ctools_plugin_get_function($subtask, 'hook menu alter')) {
+ $function($items, $subtask);
+ }
+ }
+ }
+
+ return $items;
+}
+
+/*
+ * Implementation of hook_theme()
+ */
+function page_manager_theme() {
+ $base = array(
+ 'path' => drupal_get_path('module', 'page_manager') . '/theme',
+ 'file' => 'page_manager.theme.inc',
+ );
+
+ $items = array(
+ 'page_manager_list_pages_form' => array(
+ 'arguments' => array('form' => NULL),
+ ) + $base,
+ 'page_manager_handler_rearrange' => array(
+ 'arguments' => array('form' => NULL),
+ ) + $base,
+ 'page_manager_edit_page' => array(
+ 'template' => 'page-manager-edit-page',
+ 'arguments' => array('page' => NULL, 'save' => NULL, 'operations' => array(), 'content' => array()),
+ ) + $base,
+ 'page_manager_lock' => array(
+ 'arguments' => array('page' => array()),
+ ) + $base,
+ 'page_manager_changed' => array(
+ 'arguments' => array('text' => NULL, 'description' => NULL),
+ ) + $base,
+ );
+
+ // Allow task plugins to have theme registrations by passing through:
+ $tasks = page_manager_get_tasks();
+
+ // Provide menu items for each task.
+ foreach ($tasks as $task_id => $task) {
+ if ($function = ctools_plugin_get_function($task, 'hook theme')) {
+ $function($items, $task);
+ }
+ }
+
+ return $items;
+}
+
+// --------------------------------------------------------------------------
+// Page caching
+//
+// The page cache is used to store a page temporarily, using the ctools object
+// cache. When loading from the page cache, it will either load the cached
+// version, or if there is not one, load the real thing and create a cache
+// object which can then be easily stored.
+
+/**
+ * Get the cached changes to a given task handler.
+ */
+function page_manager_get_page_cache($task_name) {
+ ctools_include('object-cache');
+ $cache = ctools_object_cache_get('page_manager_page', $task_name);
+ if (!$cache) {
+ $cache = new stdClass();
+ $cache->task_name = $task_name;
+ list($cache->task_id, $cache->subtask_id) = page_manager_get_task_id($cache->task_name);
+
+ $cache->task = page_manager_get_task($cache->task_id);
+ if (empty($cache->task)) {
+ return FALSE;
+ }
+
+ if ($cache->subtask_id) {
+ $cache->subtask = page_manager_get_task_subtask($cache->task, $cache->subtask_id);
+ if (empty($cache->subtask)) {
+ return FALSE;
+ }
+ }
+ else {
+ $cache->subtask = $cache->task;
+ }
+
+ $cache->handlers = page_manager_load_sorted_handlers($cache->task, $cache->subtask_id);
+ $cache->handler_info = array();
+ foreach ($cache->handlers as $id => $handler) {
+ $cache->handler_info[$id] = array(
+ 'weight' => $handler->weight,
+ 'changed' => FALSE,
+ 'name' => $id,
+ );
+ }
+ }
+ else {
+ // ensure the task is loaded.
+ page_manager_get_task($cache->task_id);
+ }
+
+ if ($task_name != '::new') {
+ $cache->locked = ctools_object_cache_test('page_manager_page', $task_name);
+ }
+ else {
+ $cache->locked = FALSE;
+ }
+
+ return $cache;
+}
+
+/**
+ * Store changes to a task handler in the object cache.
+ */
+function page_manager_set_page_cache($page) {
+ if (!empty($page->locked)) {
+ return;
+ }
+
+ if (empty($page->task_name)) {
+ return;
+ }
+
+ ctools_include('object-cache');
+ $page->changed = TRUE;
+ $cache = ctools_object_cache_set('page_manager_page', $page->task_name, $page);
+}
+
+/**
+ * Remove an item from the object cache.
+ */
+function page_manager_clear_page_cache($name) {
+ ctools_include('object-cache');
+ ctools_object_cache_clear('page_manager_page', $name);
+}
+
+/**
+ * Write all changes from the page cache and clear it out.
+ */
+function page_manager_save_page_cache($cache) {
+ // Save the subtask:
+ if ($function = ctools_plugin_get_function($cache->task, 'save subtask callback')) {
+ $function($cache->subtask);
+ }
+
+ // Iterate through handlers and save/delete/update as necessary.
+ // Go through each of the task handlers, check to see if it needs updating,
+ // and update it if so.
+ foreach ($cache->handler_info as $id => $info) {
+ $handler = &$cache->handlers[$id];
+ // If it has been marked for deletion, delete it.
+
+ if ($info['changed'] & PAGE_MANAGER_CHANGED_DELETED) {
+ page_manager_delete_task_handler($handler);
+ }
+ // If it has been somehow edited (or added), write the cached version
+ elseif ($info['changed'] & PAGE_MANAGER_CHANGED_CACHED) {
+ // Make sure we get updated weight from the form for this.
+ $handler->weight = $info['weight'];
+ page_manager_save_task_handler($handler);
+ }
+ // Otherwise, check to see if it has moved and, if so, update the weight.
+ elseif ($info['weight'] != $handler->weight) {
+ // Theoretically we could only do this for in code objects, but since our
+ // load mechanism checks for all, this is less database work.
+ page_manager_update_task_handler_weight($handler, $info['weight']);
+ }
+
+ // Set enable/disabled status.
+ if ($info['changed'] & PAGE_MANAGER_CHANGED_STATUS) {
+ ctools_include('export');
+ ctools_export_set_object_status($cache->handlers[$id], $info['disabled']);
+ }
+ }
+
+ page_manager_clear_page_cache($cache->task_name);
+
+ if (!empty($cache->path_changed) || !empty($cache->new)) {
+ // Force a menu rebuild to make sure the menu entries are set.
+ menu_rebuild();
+ }
+}
+
+/**
+ * Menu callback to load a page manager cache object for menu callbacks.
+ */
+function page_manager_cache_load($task_name) {
+ return page_manager_get_page_cache($task_name);
+}
+
+/**
+ * Generate a unique name for a task handler.
+ *
+ * Task handlers need to be named but they aren't allowed to set their own
+ * names. Instead, they are named based upon their parent task and type.
+ */
+function page_manager_handler_get_name($task_name, $handlers, $handler) {
+ $base = str_replace('-', '_', $task_name);
+ // Generate a unique name. Unlike most named objects, we don't let people choose
+ // names for task handlers because they mostly don't make sense.
+ $base .= '_' . $handler->handler;
+
+ // Once we have a base, check to see if it is used. If it is, start counting up.
+ $name = $base;
+ $count = 1;
+ // If taken
+ while (isset($handlers[$name])) {
+ $name = $base . '_' . ++$count;
+ }
+
+ return $name;
+}
+
+/**
+ * Import a handler into a page.
+ *
+ * This is used by both import and clone, since clone just exports the
+ * handler and immediately imports it.
+ */
+function page_manager_handler_add_to_page(&$page, $handler, $title = NULL) {
+ $last = end($page->handler_info);
+ $handler->weight = $last ? $last['weight'] + 1 : 0;
+ $handler->task = $page->task_id;
+ $handler->subtask = $page->subtask_id;
+ $handler->export_type = EXPORT_IN_DATABASE;
+ $handler->type = t('Normal');
+
+ if ($title) {
+ $handler->conf['title'] = $title;
+ }
+
+ $name = page_manager_handler_get_name($page->task_name, $page->handlers, $handler);
+
+ $handler->name = $name;
+
+ $page->handlers[$name] = $handler;
+ $page->handler_info[$name] = array(
+ 'weight' => $handler->weight,
+ 'name' => $handler->name,
+ 'changed' => PAGE_MANAGER_CHANGED_CACHED,
+ );
+}
+
+// --------------------------------------------------------------------------
+// Database routines
+//
+// This includes fetching plugins and plugin info as well as specialized
+// fetch methods to get groups of task handlers per task.
+
+/**
+ * Load a single task handler by name.
+ *
+ * Handlers can come from multiple sources; either the database or by normal
+ * export method, which is handled by the ctools library, but handlers can
+ * also be bundled with task/subtask. We have to check there and perform
+ * overrides as appropriate.
+ *
+ * Handlers bundled with the task are of a higher priority than default
+ * handlers provided by normal code, and are of a lower priority than
+ * the database, so we have to check the source of handlers when we have
+ * multiple to choose from.
+ */
+function page_manager_load_task_handler($task, $subtask_id, $name) {
+ ctools_include('export');
+ $result = ctools_export_load_object('delegator_handlers', 'names', array($name));
+ $handlers = page_manager_get_default_task_handlers($task, $subtask_id);
+ return page_manager_compare_task_handlers($result, $handlers, $name);
+}
+
+/**
+ * Load all task handlers for a given task/subtask.
+ */
+function page_manager_load_task_handlers($task, $subtask_id = NULL, $default_handlers = NULL) {
+ ctools_include('export');
+ $conditions = array(
+ 'task' => $task['name'],
+ );
+
+ if (isset($subtask_id)) {
+ $conditions['subtask'] = $subtask_id;
+ }
+
+ $handlers = ctools_export_load_object('delegator_handlers', 'conditions', $conditions);
+ $defaults = isset($default_handlers) ? $default_handlers : page_manager_get_default_task_handlers($task, $subtask_id);
+ foreach ($defaults as $name => $default) {
+ $result = page_manager_compare_task_handlers($handlers, $defaults, $name);
+
+ if ($result) {
+ $handlers[$name] = $result;
+ // Ensure task and subtask are correct, because it's easy to change task
+ // names when editing a default and fail to do it on the associated handlers.
+ $result->task = $task['name'];
+ $result->subtask = $subtask_id;
+ }
+ }
+
+ // Override weights from the weight table.
+ if ($handlers) {
+ $names = array();
+ $placeholders = array();
+ foreach ($handlers as $handler) {
+ $names[] = $handler->name;
+ $placeholders[] = "'%s'";
+ }
+
+ $result = db_query("SELECT name, weight FROM {delegator_weights} WHERE name IN (" . implode(', ', $placeholders) . ")", $names);
+ while ($weight = db_fetch_object($result)) {
+ $handlers[$weight->name]->weight = $weight->weight;
+ }
+ }
+
+ return $handlers;
+}
+
+/**
+ * Get the default task handlers from a task, if they exist.
+ *
+ * Tasks can contain 'default' task handlers which are provided by the
+ * default task. Because these can come from either the task or the
+ * subtask, the logic is abstracted to reduce code duplication.
+ */
+function page_manager_get_default_task_handlers($task, $subtask_id) {
+ // Load default handlers that are provied by the task/subtask itself.
+ $handlers = array();
+ if ($subtask_id) {
+ $subtask = page_manager_get_task_subtask($task, $subtask_id);
+ if (isset($subtask['default handlers'])) {
+ $handlers = $subtask['default handlers'];
+ }
+ }
+ else if (isset($task['default handlers'])) {
+ $handlers = $task['default handlers'];
+ }
+
+ return $handlers;
+}
+
+/**
+ * Compare a single task handler from two lists and provide the correct one.
+ *
+ * Task handlers can be gotten from multiple sources. As exportable objects,
+ * they can be provided by default hooks and the database. But also, because
+ * they are tightly bound to tasks, they can also be provided by default
+ * tasks. This function reconciles where to pick up a task handler between
+ * the exportables list and the defaults provided by the task itself.
+ *
+ * @param $result
+ * A list of handlers provided by export.inc
+ * @param $handlers
+ * A list of handlers provided by the default task.
+ * @param $name
+ * Which handler to compare.
+ * @return
+ * Which handler to use, if any. May be NULL.
+ */
+function page_manager_compare_task_handlers($result, $handlers, $name) {
+ // Compare our special default handler against the actual result, if
+ // any, and do the right thing.
+ if (!isset($result[$name]) && isset($handlers[$name])) {
+ $handlers[$name]->type = t('Default');
+ $handlers[$name]->export_type = EXPORT_IN_CODE;
+ return $handlers[$name];
+ }
+ else if (isset($result[$name]) && !isset($handlers[$name])) {
+ return $result[$name];
+ }
+ else if (isset($result[$name]) && isset($handlers[$name])) {
+ if ($result[$name]->export_type & EXPORT_IN_DATABASE) {
+ $result[$name]->type = t('Overridden');
+ $result[$name]->export_type = $result[$name]->export_type | EXPORT_IN_CODE;
+ return $result[$name];
+ }
+ else {
+ // In this case, our default is a higher priority than the standard default.
+ $handlers[$name]->type = t('Default');
+ $handlers[$name]->export_type = EXPORT_IN_CODE;
+ return $handlers[$name];
+ }
+ }
+}
+
+/**
+ * Load all task handlers for a given task and subtask and sort them.
+ */
+function page_manager_load_sorted_handlers($task, $subtask_id = NULL, $enabled = FALSE) {
+ $handlers = page_manager_load_task_handlers($task, $subtask_id);
+ if ($enabled) {
+ foreach ($handlers as $id => $handler) {
+ if (!empty($handler->disabled)) {
+ unset($handlers[$id]);
+ }
+ }
+ }
+ uasort($handlers, 'page_manager_sort_task_handlers');
+ return $handlers;
+}
+
+/**
+ * Callback for uasort to sort task handlers.
+ *
+ * Task handlers are sorted by weight then by name.
+ */
+function page_manager_sort_task_handlers($a, $b) {
+ if ($a->weight < $b->weight) {
+ return -1;
+ }
+ elseif ($a->weight > $b->weight) {
+ return 1;
+ }
+ elseif ($a->name < $b->name) {
+ return -1;
+ }
+ elseif ($a->name > $b->name) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Write a task handler to the database.
+ */
+function page_manager_save_task_handler(&$handler) {
+ $update = (isset($handler->did)) ? array('did') : array();
+ // Let the task handler respond to saves:
+ if ($function = ctools_plugin_load_function('page_manager', 'task_handlers', $handler->handler, 'save')) {
+ $function($handler, $update);
+ }
+
+ drupal_write_record('delegator_handlers', $handler, $update);
+ db_query("DELETE FROM {delegator_weights} WHERE name = '%s'", $handler->name);
+
+ // If this was previously a default handler, we may have to write task handlers.
+ if (!$update) {
+ // @todo wtf was I going to do here?
+ }
+ return $handler;
+}
+
+/**
+ * Remove a task handler.
+ */
+function page_manager_delete_task_handler($handler) {
+ // Let the task handler respond to saves:
+ if ($function = ctools_plugin_load_function('page_manager', 'task_handlers', $handler->handler, 'delete')) {
+ $function($handler);
+ }
+ db_query("DELETE FROM {delegator_handlers} WHERE name = '%s'", $handler->name);
+ db_query("DELETE FROM {delegator_weights} WHERE name = '%s'", $handler->name);
+}
+
+/**
+ * Export a task handler into code suitable for import or use as a default
+ * task handler.
+ */
+function page_manager_export_task_handler($handler, $indent = '') {
+ ctools_include('export');
+ ctools_include('plugins');
+ $handler = drupal_clone($handler);
+
+ $append = '';
+ if ($function = ctools_plugin_load_function('page_manager', 'task_handlers', $handler->handler, 'export')) {
+ $append = $function($handler, $indent);
+ }
+
+ $output = ctools_export_object('delegator_handlers', $handler, $indent);
+ $output .= $append;
+
+ return $output;
+}
+
+/**
+ * Create a new task handler object.
+ *
+ * @param $plugin
+ * The plugin this task handler is created from.
+ */
+function page_manager_new_task_handler($plugin) {
+ // Generate a unique name. Unlike most named objects, we don't let people choose
+ // names for task handlers because they mostly don't make sense.
+
+ // Create a new, empty handler object.
+ $handler = new stdClass;
+ $handler->title = $plugin['title'];
+ $handler->task = NULL;
+ $handler->subtask = NULL;
+ $handler->name = NULL;
+ $handler->handler = $plugin['name'];
+ $handler->weight = 0;
+ $handler->conf = array();
+
+ // These are provided by the core export API provided by ctools and we
+ // set defaults here so that we don't cause notices. Perhaps ctools should
+ // provide a way to do this for us so we don't have to muck with it.
+ $handler->export_type = EXPORT_IN_DATABASE;
+ $handler->type = t('Local');
+
+ if (isset($plugin['default conf'])) {
+ if (is_array($plugin['default conf'])) {
+ $handler->conf = $plugin['default conf'];
+ }
+ else if (function_exists($plugin['default conf'])) {
+ $handler->conf = $plugin['default conf']($handler);
+ }
+ }
+
+ return $handler;
+}
+
+/**
+ * Set an overidden weight for a task handler.
+ *
+ * We do this so that in-code task handlers don't need to get written
+ * to the database just because they have their weight changed.
+ */
+function page_manager_update_task_handler_weight($handler, $weight) {
+ db_query("DELETE FROM {delegator_weights} WHERE name = '%s'", $handler->name);
+ db_query("INSERT INTO {delegator_weights} (name, weight) VALUES ('%s', %d)", $handler->name, $weight);
+}
+
+
+/**
+ * Shortcut function to get task plugins.
+ */
+function page_manager_get_tasks() {
+ ctools_include('plugins');
+ return ctools_get_plugins('page_manager', 'tasks');
+}
+
+/**
+ * Shortcut function to get a task plugin.
+ */
+function page_manager_get_task($id) {
+ ctools_include('plugins');
+ return ctools_get_plugins('page_manager', 'tasks', $id);
+}
+
+/**
+ * Get all tasks for a given type.
+ */
+function page_manager_get_tasks_by_type($type) {
+ ctools_include('plugins');
+ $all_tasks = ctools_get_plugins('page_manager', 'tasks');
+ $tasks = array();
+ foreach ($all_tasks as $id => $task) {
+ if (isset($task['task type']) && $task['task type'] == $type) {
+ $tasks[$id] = $task;
+ }
+ }
+
+ return $tasks;
+}
+
+/**
+ * Fetch all subtasks for a page managertask.
+ *
+ * @param $task
+ * A loaded $task plugin object.
+ */
+function page_manager_get_task_subtasks($task) {
+ if (empty($task['subtasks'])) {
+ return array();
+ }
+
+ if ($function = ctools_plugin_get_function($task, 'subtasks callback')) {
+ return $function($task);
+ }
+}
+
+/**
+ * Fetch all subtasks for a page managertask.
+ *
+ * @param $task
+ * A loaded $task plugin object.
+ * @param $subtask_id
+ * The subtask ID to load.
+ */
+function page_manager_get_task_subtask($task, $subtask_id) {
+ if (empty($task['subtasks'])) {
+ return;
+ }
+
+ if ($function = ctools_plugin_get_function($task, 'subtask callback')) {
+ return $function($task, $subtask_id);
+ }
+}
+
+/**
+ * Shortcut function to get task handler plugins.
+ */
+function page_manager_get_task_handlers() {
+ ctools_include('plugins');
+ return ctools_get_plugins('page_manager', 'task_handlers');
+}
+
+/**
+ * Shortcut function to get a task handler plugin.
+ */
+function page_manager_get_task_handler($id) {
+ ctools_include('plugins');
+ return ctools_get_plugins('page_manager', 'task_handlers', $id);
+}
+
+/**
+ * Retrieve a list of all applicable task handlers for a given task.
+ *
+ * This looks at the $task['handler type'] and compares that to $task_handler['handler type'].
+ * If the task has no type, the id of the task is used instead.
+ */
+function page_manager_get_task_handler_plugins($task, $all = FALSE) {
+ $type = isset($task['handler type']) ? $task['handler type'] : $task['name'];
+ $name = $task['name'];
+
+ $handlers = array();
+ $task_handlers = page_manager_get_task_handlers();
+ foreach ($task_handlers as $id => $handler) {
+ $task_type = is_array($handler['handler type']) ? $handler['handler type'] : array($handler['handler type']);
+ if (in_array($type, $task_type) || in_array($name, $task_type)) {
+ if ($all || !empty($handler['visible'])) {
+ $handlers[$id] = $handler;
+ }
+ }
+ }
+
+ return $handlers;
+}
+
+/**
+ * Get the title for a given handler.
+ *
+ * If the plugin has no 'admin title' function, the generic title of the
+ * plugin is used instead.
+ */
+function page_manager_get_handler_title($plugin, $handler, $task, $subtask_id) {
+ $function = ctools_plugin_get_function($plugin, 'admin title');
+ if ($function) {
+ return $function($handler, $task, $subtask_id);
+ }
+ else {
+ return $plugin['title'];
+ }
+}
+
+/**
+ * Get the admin summary (additional info) for a given handler.
+ */
+function page_manager_get_handler_summary($plugin, $handler, $task, $subtask_id) {
+ if ($function = ctools_plugin_get_function($plugin, 'admin summary')) {
+ return $function($handler, $task, $subtask_id);
+ }
+}
+
+/**
+ * Split a task name into a task id and subtask id, if applicable.
+ */
+function page_manager_get_task_id($task_name) {
+ if (strpos($task_name, '-') !== FALSE) {
+ return explode('-', $task_name, 2);
+ }
+ else {
+ return array($task_name, NULL);
+ }
+}
+
+/**
+ * Turn a task id + subtask_id into a task name.
+ */
+function page_manager_make_task_name($task_id, $subtask_id) {
+ if ($subtask_id) {
+ return $task_id . '-' . $subtask_id;
+ }
+ else {
+ return $task_id;
+ }
+}
+
+/**
+ * Get the render function for a handler.
+ */
+function page_manager_get_renderer($handler) {
+ return ctools_plugin_load_function('page_manager', 'task_handlers', $handler->handler, 'render');
+}
+
+// --------------------------------------------------------------------------
+// Functions existing on behalf of tasks and task handlers
+
+
+/**
+ * Page managerfor arg load function because menu system will not load extra
+ * files for these; they must be in a .module.
+ */
+function pm_arg_load($value, $subtask, $argument) {
+ page_manager_get_task('page');
+ return _pm_arg_load($value, $subtask, $argument);
+}
+
+/**
+ * Callback for access control ajax form on behalf of page.inc task.
+ *
+ * Returns the cached access config and contexts used.
+ */
+function page_manager_page_ctools_access_get($argument) {
+ $page = page_manager_get_page_cache($argument);
+
+ $contexts = array();
+
+ // Load contexts based on argument data:
+ if ($arguments = _page_manager_page_get_arguments($page->subtask['subtask'])) {
+ $contexts = ctools_context_get_placeholders_from_argument($arguments);
+ }
+
+ return array($page->subtask['subtask']->access, $contexts);
+}
+
+/**
+ * Callback for access control ajax form on behalf of page.inc task.
+ *
+ * Writes the changed access to the cache.
+ */
+function page_manager_page_ctools_access_set($argument, $access) {
+ $page = page_manager_get_page_cache($argument);
+ $page->subtask['subtask']->access = $access;
+ page_manager_set_page_cache($page);
+}
+
+/**
+ * Callback for access control ajax form on behalf of context task handler.
+ *
+ * Returns the cached access config and contexts used.
+ */
+function page_manager_task_handler_ctools_access_get($argument) {
+ list($task_name, $name) = explode('*', $argument);
+ $page = page_manager_get_page_cache($task_name);
+ $handler = &$page->handlers[$name];
+
+ if (!isset($handler->conf['access'])) {
+ $handler->conf['access'] = array();
+ }
+
+ ctools_include('context-task-handler');
+
+ $contexts = ctools_context_handler_get_all_contexts($page->task, $page->subtask, $handler);
+
+ return array($handler->conf['access'], $contexts);
+}
+
+/**
+ * Callback for access control ajax form on behalf of context task handler.
+ *
+ * Writes the changed access to the cache.
+ */
+function page_manager_task_handler_ctools_access_set($argument, $access) {
+ list($task_name, $name) = explode('*', $argument);
+ $page = page_manager_get_page_cache($task_name);
+ $handler = &$page->handlers[$name];
+
+ $handler->conf['access'] = $access;
+ page_manager_set_page_cache($page);
+}
+
diff --git a/delegator/plugins/tasks/node_edit.inc b/page_manager/plugins/tasks/node_edit.inc
index cb4e0fc..29526cd 100644
--- a/delegator/plugins/tasks/node_edit.inc
+++ b/page_manager/plugins/tasks/node_edit.inc
@@ -2,10 +2,10 @@
// $Id$
/**
- * Specialized implementation of hook_delegator_tasks(). See api-task.html for
+ * Specialized implementation of hook_page_manager_task_tasks(). See api-task.html for
* more information.
*/
-function delegator_node_edit_delegator_tasks() {
+function page_manager_node_edit_page_manager_tasks() {
return array(
// This is a 'page' task and will fall under the page admin UI
'task type' => 'page',
@@ -16,31 +16,31 @@ function delegator_node_edit_delegator_tasks() {
'admin description' => 'Overrides for the built in node edit handler at <em>node/%node/edit</em>.',
'admin path' => 'node/%node/edit',
- // Callback to add items to the delegator task administration form:
- 'task admin' => 'delegator_node_edit_task_admin',
+ // Callback to add items to the page managertask administration form:
+ 'task admin' => 'page_manager_node_edit_task_admin',
// Menu hooks so that we can alter the node/%node menu entry to point to us.
- 'hook menu' => 'delegator_node_edit_menu',
- 'hook menu alter' => 'delegator_node_edit_menu_alter',
+ 'hook menu' => 'page_manager_node_edit_menu',
+ 'hook menu alter' => 'page_manager_node_edit_menu_alter',
// This is task uses 'context' handlers and must implement these to give the
// handler data it needs.
'handler type' => 'context',
- 'get arguments' => 'delegator_node_edit_get_arguments',
- 'get context placeholders' => 'delegator_node_edit_get_contexts',
+ 'get arguments' => 'page_manager_node_edit_get_arguments',
+ 'get context placeholders' => 'page_manager_node_edit_get_contexts',
);
}
/**
- * Callback defined by delegator_node_edit_delegator_tasks().
+ * Callback defined by page_manager_node_edit_page_manager_tasks().
*
* Alter the node edit input so that node edit comes to us rather than the
* normal node edit process.
*/
-function delegator_node_edit_menu_alter(&$items, $task) {
+function page_manager_node_edit_menu_alter(&$items, $task) {
// Override the node edit handler for our purpose.
- if ($items['node/%node/edit']['page callback'] == 'node_page_edit' || variable_get('delegator_override_anyway', FALSE)) {
- $items['node/%node/edit']['page callback'] = 'delegator_node_edit';
+ if ($items['node/%node/edit']['page callback'] == 'node_page_edit' || variable_get('page_manager_override_anyway', FALSE)) {
+ $items['node/%node/edit']['page callback'] = 'page_manager_node_edit';
$items['node/%node/edit']['file path'] = $task['path'];
$items['node/%node/edit']['file'] = $task['file'];
}
@@ -52,7 +52,7 @@ function delegator_node_edit_menu_alter(&$items, $task) {
continue;
}
- $items[$path]['page callback'] = 'delegator_node_add';
+ $items[$path]['page callback'] = 'page_manager_node_add';
$items[$path]['file path'] = $task['path'];
$items[$path]['file'] = $task['file'];
// Why str_replace things back?
@@ -63,10 +63,10 @@ function delegator_node_edit_menu_alter(&$items, $task) {
/**
* Warn if we are unable to override the taxonomy term page.
*/
-function delegator_node_edit_task_admin(&$form, &$form_state) {
+function page_manager_node_edit_task_admin(&$form, &$form_state) {
$callback = db_result(db_query("SELECT page_callback FROM {menu_router} WHERE path = 'node/%/edit'"));
- if ($callback != 'delegator_node_edit') {
- drupal_set_message(t('Delegator module is unable to override node/%node/edit because some other module already has overridden with %callback. Delegator will not be able to handle this page.', array('%callback' => $callback)), 'warning');
+ if ($callback != 'page_manager_node_edit') {
+ drupal_set_message(t('Page managermodule is unable to override node/%node/edit because some other module already has overridden with %callback. Page managerwill not be able to handle this page.', array('%callback' => $callback)), 'warning');
}
}
@@ -77,9 +77,9 @@ function delegator_node_edit_task_admin(&$form, &$form_state) {
* to run with it. If no one does, it passes through to Drupal core's
* node edit, which is node_page_edit().
*/
-function delegator_node_edit($node) {
+function page_manager_node_edit($node) {
// Load my task plugin
- $task = delegator_get_task('node_edit');
+ $task = page_manager_get_task('node_edit');
// Load the node into a context.
ctools_include('context');
@@ -102,13 +102,13 @@ function delegator_node_edit($node) {
/**
* Callback to handle the process of adding a node.
*
- * This creates a basic $node and passes that off to delegator_node_edit().
+ * This creates a basic $node and passes that off to page_manager_node_edit().
* It is modeled after Drupal's node_add() function.
*
* Unlike node_add() we do not need to check node_access because that was
* already checked by the menu system.
*/
-function delegator_node_add($type) {
+function page_manager_node_add($type) {
global $user;
$types = node_get_types();
@@ -122,7 +122,7 @@ function delegator_node_add($type) {
);
drupal_set_title(t('Create @name', array('@name' => $types[$type]->name)));
- return delegator_node_edit($node);
+ return page_manager_node_edit($node);
}
/**
@@ -131,7 +131,7 @@ function delegator_node_add($type) {
* Since this is the node edit and there is no UI on the arguments, we
* create dummy arguments that contain the needed data.
*/
-function delegator_node_edit_get_arguments($task, $subtask_id) {
+function page_manager_node_edit_get_arguments($task, $subtask_id) {
return array(
array(
'keyword' => 'node',
@@ -146,7 +146,7 @@ function delegator_node_edit_get_arguments($task, $subtask_id) {
/**
* Callback to get context placeholders provided by this handler.
*/
-function delegator_node_edit_get_contexts($task, $subtask_id) {
- return ctools_context_get_placeholders_from_argument(delegator_node_edit_get_arguments($task, $subtask_id));
+function page_manager_node_edit_get_contexts($task, $subtask_id) {
+ return ctools_context_get_placeholders_from_argument(page_manager_node_edit_get_arguments($task, $subtask_id));
}
diff --git a/delegator/plugins/tasks/node_view.inc b/page_manager/plugins/tasks/node_view.inc
index 679e228..975af54 100644
--- a/delegator/plugins/tasks/node_view.inc
+++ b/page_manager/plugins/tasks/node_view.inc
@@ -5,51 +5,51 @@
* @file
* Handle the 'node view' override task.
*
- * This plugin overrides node/%node and reroutes it to the delegator, where
+ * This plugin overrides node/%node and reroutes it to the page manager, where
* a list of tasks can be used to service this request based upon criteria
* supplied by access plugins.
*/
/**
- * Specialized implementation of hook_delegator_tasks(). See api-task.html for
+ * Specialized implementation of hook_page_manager_task_tasks(). See api-task.html for
* more information.
*/
-function delegator_node_view_delegator_tasks() {
+function page_manager_node_view_page_manager_tasks() {
return array(
// This is a 'page' task and will fall under the page admin UI
'task type' => 'page',
- 'title' => t('Node view'),
+ 'title' => t('Node template'),
'description' => t('The node view task allows you to control what handler will handle the job of rendering a node view at the path <em>node/%node</em>. If no handler is set or matches the criteria, the default Drupal node renderer will be used.'),
'admin title' => 'Node view', // translated by menu system
'admin description' => 'Overrides for the built in node view handler at <em>node/%node</em>.',
'admin path' => 'node/%node',
- // Callback to add items to the delegator task administration form:
- 'task admin' => 'delegator_node_view_task_admin',
+ // Callback to add items to the page managertask administration form:
+ 'task admin' => 'page_manager_node_view_task_admin',
// Menu hooks so that we can alter the node/%node menu entry to point to us.
- 'hook menu' => 'delegator_node_view_menu',
- 'hook menu alter' => 'delegator_node_view_menu_alter',
+ 'hook menu' => 'page_manager_node_view_menu',
+ 'hook menu alter' => 'page_manager_node_view_menu_alter',
// This is task uses 'context' handlers and must implement these to give the
// handler data it needs.
'handler type' => 'context',
- 'get arguments' => 'delegator_node_view_get_arguments',
- 'get context placeholders' => 'delegator_node_view_get_contexts',
+ 'get arguments' => 'page_manager_node_view_get_arguments',
+ 'get context placeholders' => 'page_manager_node_view_get_contexts',
);
}
/**
- * Callback defined by delegator_node_view_delegator_tasks().
+ * Callback defined by page_manager_node_view_page_manager_tasks().
*
* Alter the node view input so that node view comes to us rather than the
* normal node view process.
*/
-function delegator_node_view_menu_alter(&$items, $task) {
+function page_manager_node_view_menu_alter(&$items, $task) {
// Override the node view handler for our purpose.
- if ($items['node/%node']['page callback'] == 'node_page_view' || variable_get('delegator_override_anyway', FALSE)) {
- $items['node/%node']['page callback'] = 'delegator_node_view';
+ if ($items['node/%node']['page callback'] == 'node_page_view' || variable_get('page_manager_override_anyway', FALSE)) {
+ $items['node/%node']['page callback'] = 'page_manager_node_view';
$items['node/%node']['file path'] = $task['path'];
$items['node/%node']['file'] = $task['file'];
}
@@ -59,10 +59,10 @@ function delegator_node_view_menu_alter(&$items, $task) {
/**
* Warn if we are unable to override the taxonomy term page.
*/
-function delegator_node_view_task_admin(&$form, &$form_state) {
+function page_manager_node_view_task_admin(&$form, &$form_state) {
$callback = db_result(db_query("SELECT page_callback FROM {menu_router} WHERE path = 'node/%'"));
- if ($callback != 'delegator_node_view') {
- drupal_set_message(t('Delegator module is unable to override node/%node because some other module already has overridden with %callback. Delegator will not be able to handle this page.', array('%callback' => $callback)), 'warning');
+ if ($callback != 'page_manager_node_view') {
+ drupal_set_message(t('Page managermodule is unable to override node/%node because some other module already has overridden with %callback. Page managerwill not be able to handle this page.', array('%callback' => $callback)), 'warning');
}
}
@@ -73,9 +73,9 @@ function delegator_node_view_task_admin(&$form, &$form_state) {
* to run with it. If no one does, it passes through to Drupal core's
* node view, which is node_page_view().
*/
-function delegator_node_view($node) {
+function page_manager_node_view($node) {
// Load my task plugin
- $task = delegator_get_task('node_view');
+ $task = page_manager_get_task('node_view');
// Load the node into a context.
ctools_include('context');
@@ -89,8 +89,8 @@ function delegator_node_view($node) {
}
$function = 'node_page_view';
- foreach (module_implements('delegator_override') as $module) {
- $call = $module . '_delegator_override';
+ foreach (module_implements('page_manager_override') as $module) {
+ $call = $module . '_page_manager_override';
if (($rc = $call('node_view')) && function_exists($rc)) {
$function = $rc;
break;
@@ -107,7 +107,7 @@ function delegator_node_view($node) {
* Since this is the node view and there is no UI on the arguments, we
* create dummy arguments that contain the needed data.
*/
-function delegator_node_view_get_arguments($task, $subtask_id) {
+function page_manager_node_view_get_arguments($task, $subtask_id) {
return array(
array(
'keyword' => 'node',
@@ -122,7 +122,7 @@ function delegator_node_view_get_arguments($task, $subtask_id) {
/**
* Callback to get context placeholders provided by this handler.
*/
-function delegator_node_view_get_contexts($task, $subtask_id) {
- return ctools_context_get_placeholders_from_argument(delegator_node_view_get_arguments($task, $subtask_id));
+function page_manager_node_view_get_contexts($task, $subtask_id) {
+ return ctools_context_get_placeholders_from_argument(page_manager_node_view_get_arguments($task, $subtask_id));
}
diff --git a/page_manager/plugins/tasks/page.admin.inc b/page_manager/plugins/tasks/page.admin.inc
new file mode 100644
index 0000000..7b65531
--- /dev/null
+++ b/page_manager/plugins/tasks/page.admin.inc
@@ -0,0 +1,1439 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Administrative functions for the page subtasks.
+ *
+ * These are attached to the menu system in page.inc via the hook_menu
+ * delegation. They are included here so that this code is loaded
+ * only when needed.
+ */
+
+/**
+ * Delegated implementation of hook_menu().
+ */
+function page_manager_page_menu(&$items, $task) {
+ // Set up access permissions.
+ $access_callback = isset($task['admin access callback']) ? $task['admin access callback'] : 'user_access';
+ $access_arguments = isset($task['admin access arguments']) ? $task['admin access arguments'] : array('administer page manager');
+
+ $base = array(
+ 'access callback' => $access_callback,
+ 'access arguments' => $access_arguments,
+ 'file' => 'plugins/tasks/page.admin.inc',
+ );
+
+ $items['admin/build/pages/add'] = array(
+ 'title' => 'Add custom page',
+ 'page callback' => 'page_manager_page_add_subtask',
+ 'page arguments' => array(),
+ 'type' => MENU_LOCAL_TASK,
+ ) + $base;
+
+ $items['admin/build/pages/import'] = array(
+ 'title' => 'Import page',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('page_manager_page_import_subtask', 'page'),
+ 'type' => MENU_LOCAL_TASK,
+ ) + $base;
+
+ // AJAX callbacks for argument modal.
+ $items['admin/build/pages/argument'] = array(
+ 'page callback' => 'page_manager_page_subtask_argument_ajax',
+ 'type' => MENU_CALLBACK,
+ ) + $base;
+
+ // Add menu entries for each subtask
+ foreach (page_manager_page_load_all() as $subtask_id => $subtask) {
+ if (!empty($subtask->disabled)) {
+ continue;
+ }
+
+ if (!isset($subtask->access['type'])) {
+ $subtask->access['type'] = 'none';
+ }
+ if (!isset($subtask->access['settings'])) {
+ $subtask->access['settings'] = NULL;
+ }
+
+ $path = array();
+ $page_arguments = array($subtask_id);
+ $access_arguments = array($subtask->access);
+ $load_arguments = array($subtask_id, '%index');
+
+ // Replace named placeholders with our own placeholder to load contexts.
+ $position = 0;
+ foreach (explode('/', $subtask->path) as $bit) {
+ // Remove things like double slashes completely.
+ if (!isset($bit) || $bit === '') {
+ continue;
+ }
+
+ if ($bit[0] == '%' && $bit != '%') {
+ // If an argument, swap it out with our argument loader and make sure
+ // the argument gets passed through to the page callback.
+ $path[] = '%pm_arg';
+ $page_arguments[] = $position;
+ $access_arguments[] = $position;
+ }
+ else if ($bit[0] != '!') {
+ $path[] = $bit;
+ }
+
+ // Increment position. We do it like this to skip empty items that
+ // could happen from erroneous paths like: this///that
+ $position++;
+ }
+
+ $menu_path = implode('/', $path);
+
+ $items[$menu_path] = page_manager_page_menu_item($task, $subtask->menu, $access_arguments, $page_arguments, $load_arguments);
+
+ // Add a parent menu item if one is configured.
+ if (isset($subtask->menu['type']) && $subtask->menu['type'] == 'default tab' && $subtask->menu['parent']['type'] != 'none') {
+ array_pop($path);
+ $parent_path = implode('/', $path);
+ $items[$parent_path] = page_manager_page_menu_item($task, $subtask->menu['parent'], $access_arguments, $page_arguments, $load_arguments);
+ }
+ }
+}
+
+/**
+ * Create a menu item for page manager pages.
+ *
+ * @param $menu
+ * The configuration to use. It will contain a type, and depending on the
+ * type may also contain weight, title and name. These are presumed to have
+ * been configured from the UI.
+ * @param $access_arguments
+ * Arguments that go with ctools_access_menu; it should be loaded with
+ * the access plugin type, settings, and positions of any arguments that
+ * may produce contexts.
+ * @param $page_arguments
+ * This should be seeded with the subtask name for easy loading and like
+ * the access arguments above should contain positions of arguments so
+ * that the menu system passes contexts through.
+ * @param $load_arguments
+ * Arguments to send to the arg loader; should be the subtask id and '%index'.
+ */
+function page_manager_page_menu_item($task, $menu, $access_arguments, $page_arguments, $load_arguments) {
+ $item = array(
+ 'access callback' => 'ctools_access_menu',
+ 'access arguments' => $access_arguments,
+ 'page callback' => 'page_manager_page_execute',
+ 'page arguments' => $page_arguments,
+ 'load arguments' => $load_arguments,
+ 'file' => 'plugins/tasks/page.inc',
+ );
+
+ if (isset($menu['title'])) {
+ $item['title'] = $menu['title'];
+ }
+ if (isset($menu['weight'])) {
+ $item['weight'] = $menu['weight'];
+ }
+
+ if (empty($menu['type'])) {
+ $menu['type'] = 'none';
+ }
+
+ switch ($menu['type']) {
+ case 'none':
+ default:
+ $item['type'] = MENU_CALLBACK;
+ break;
+
+ case 'normal':
+ $item['type'] = MENU_NORMAL_ITEM;
+ // Insert item into the proper menu
+ $item['menu_name'] = $menu['name'];
+ break;
+
+ case 'tab':
+ $item['type'] = MENU_LOCAL_TASK;
+ break;
+
+ case 'default tab':
+ $item['type'] = MENU_DEFAULT_LOCAL_TASK;
+ break;
+ }
+
+ return $item;
+}
+
+/**
+ * Page callback to add a subtask.
+ */
+function page_manager_page_add_subtask($task_name = NULL, $step = NULL) {
+ $task = page_manager_get_task('page');
+
+ $form_info = array(
+ 'id' => 'page_manager_add_page',
+ 'show trail' => TRUE,
+ 'show back' => TRUE,
+ 'show return' => FALSE,
+ 'next callback' => 'page_manager_page_add_subtask_next',
+ 'finish callback' => 'page_manager_page_add_subtask_finish',
+ 'return callback' => 'page_manager_page_add_subtask_finish',
+ 'cancel callback' => 'page_manager_page_add_subtask_cancel',
+ 'add order' => array(
+ 'basic' => t('Basic settings'),
+ 'argument' => t('Argument settings'),
+ 'access' => t('Access control'),
+ 'menu' => t('Menu settings'),
+ 'multiple' => t('Task handlers'),
+ ),
+ 'forms' => array(
+ 'basic' => array(
+ 'form id' => 'page_manager_page_form_basic',
+ ),
+ 'access' => array(
+ 'form id' => 'page_manager_page_form_access',
+ ),
+ 'menu' => array(
+ 'form id' => 'page_manager_page_form_menu',
+ ),
+ 'argument' => array(
+ 'form id' => 'page_manager_page_form_argument',
+ ),
+ 'multiple' => array(
+ 'form id' => 'page_manager_page_argument_form_multiple',
+ ),
+ ),
+ );
+
+ if ($task_name) {
+ $page = page_manager_get_page_cache($task_name);
+ if (empty($page)) {
+ return drupal_not_found();
+ }
+
+ $form_info['path'] = "admin/build/pages/add/$task_name/%step";
+
+ $handler_plugin = page_manager_get_task_handler($page->handler);
+
+ $form_info['forms'] += $handler_plugin['forms'];
+
+ if (isset($page->forms)) {
+ foreach ($page->forms as $id) {
+ if (isset($form_info['add order'][$id])) {
+ $form_info['order'][$id] = $form_info['add order'][$id];
+ }
+ else if (isset($handler_plugin['add features'][$id])) {
+ $form_info['order'][$id] = $handler_plugin['add features'][$id];
+ }
+ else if (isset($handler_plugin['required forms'][$id])) {
+ $form_info['order'][$id] = $handler_plugin['required forms'][$id];
+ }
+ }
+ }
+ else {
+ $form_info['order'] = $form_info['add order'];
+ }
+
+ // This means we just submitted our form from the default list
+ // of steps, which we've traded in for a newly generated list of
+ // steps above. We need to translate this 'next' step into what
+ // our questions determined would be next.
+ if ($step == 'next') {
+ $keys = array_keys($form_info['order']);
+ // get rid of 'basic' from the list of forms.
+ array_shift($keys);
+ $step = array_shift($keys);
+
+ // If $step == 'basic' at this point, we were not presented with any
+ // additional forms at all. Let's just save and go!
+ if ($step == 'basic') {
+ page_manager_save_page_cache($page);
+ // Redirect to the new page's task handler editor.
+ drupal_goto('admin/build/pages/edit/' . $page->task_name);
+ }
+ }
+ }
+ else {
+ $new_page = page_manager_page_new();
+ $new_page->name = NULL;
+
+ $page = new stdClass();
+ page_manager_page_new_page_cache($new_page, $page);
+ $form_info['path'] = 'admin/build/pages/add/%task_name/%step';
+ $form_info['show trail'] = FALSE;
+ $form_info['order'] = array(
+ 'basic' => t('Basic settings'),
+ 'next' => t('A meaningless second page'),
+ );
+ }
+
+ ctools_include('wizard');
+ $form_state = array(
+ 'task' => $task,
+ 'subtask' => $page->subtask,
+ 'page' => &$page,
+ 'type' => 'add',
+ 'task_id' => 'page',
+ 'task_name' => $page->task_name,
+ 'creating' => TRUE,
+ );
+
+ if (!empty($page->handlers)) {
+ $keys = array_keys($page->handlers);
+ $key = array_shift($keys);
+ $form_state['handler'] = &$page->handlers[$key];
+ }
+
+ $output = ctools_wizard_multistep_form($form_info, $step, $form_state);
+
+ if (!$output) {
+ // redirect.
+ drupal_redirect_form(array(), $form_state['redirect']);
+ }
+
+ return $output;
+}
+
+/**
+ * Callback generated when the add page process is finished.
+ */
+function page_manager_page_add_subtask_finish(&$form_state) {
+ $page = &$form_state['page'];
+ // Redirect to the new page's task handler editor.
+ $form_state['redirect'] = 'admin/build/pages/edit/' . $page->task_name;
+ return;
+}
+
+/**
+ * Callback generated when the 'next' button is clicked.
+ *
+ * All we do here is store the cache.
+ */
+function page_manager_page_add_subtask_next(&$form_state) {
+ if (empty($form_state['task_name']) || $form_state['task_name'] == 'page') {
+ // We may not have known the path to go next, because we didn't yet know the
+ // task name. This fixes that.
+ $form_state['form_info']['path'] = str_replace('%task_name', $form_state['page']->task_name, $form_state['form_info']['path']);
+
+ $form_state['redirect'] = ctools_wizard_get_path($form_state['form_info'], $form_state['clicked_button']['#next']);
+ }
+
+ // Update the cache with changes.
+ page_manager_set_page_cache($form_state['page']);
+}
+
+/**
+ * Callback generated when the 'cancel' button is clicked.
+ *
+ * All we do here is clear the cache.
+ */
+function page_manager_page_add_subtask_cancel(&$form_state) {
+ // Wipe all our stored changes.
+ if (isset($form_state['page']->task_name)) {
+ page_manager_clear_page_cache($form_state['page']->task_name);
+ }
+}
+
+/**
+ * Basic settings form for a page manager page.
+ */
+function page_manager_page_form_basic(&$form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+ $task = $form_state['task'];
+
+ $form['name'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Name'),
+ '#description' => t('The machine readable name of this page. It must be unique, and it must contain only alphanumeric characters and underscores. Once created, you will not be able to change this value!'),
+ '#default_value' => $page->name,
+ );
+
+ if (isset($page->pid) || empty($form_state['creating'])) {
+ $form['name']['#disabled'] = TRUE;
+ $form['name']['#value'] = $page->name;
+ }
+
+ $form['admin_title'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Administrative title'),
+ '#description' => t('The name of this page. This will appear in the administrative interface to easily identify it.'),
+ '#default_value' => $page->admin_title,
+ );
+
+ // path
+ $form['path'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Path'),
+ '#description' => t('The URL path to get to this page. You may create named placeholders for variable parts of the path by using %name for required elements and !name for optional elements. For example: "node/%node/foo", "forum/%forum" or "dashboard/!input". These named placeholders can be turned into contexts on the arguments form.'),
+ '#default_value' => $page->path,
+ );
+
+ if (!isset($page->pid) && !empty($form_state['creating'])) {
+ $features['default'] = array(
+ 'access' => t('Access control'),
+ 'menu' => t('Visible menu item'),
+ );
+
+ module_load_include('inc', 'page_manager', 'page_manager.admin');
+ page_manager_handler_add_form($form, $form_state, $features);
+ }
+
+}
+
+function page_manager_page_form_basic_validate_filter($value) {
+ return $value === -1;
+}
+
+/**
+ * Validate the basic form.
+ */
+function page_manager_page_form_basic_validate(&$form, &$form_state) {
+ // Ensure path is unused by other pages.
+ $page = $form_state['page']->subtask['subtask'];
+ $name = !empty($form_state['values']['name']) ? $form_state['values']['name'] : $page->name;
+ if (empty($name)) {
+ form_error($form['name'], t('Name is required.'));
+ }
+
+ // If this is new, make sure the name is unique:
+ if (empty($page->name)) {
+ $test = page_manager_page_load($name);
+ if ($test) {
+ form_error($form['name'], t('That name is used by another page: @page', array('@page' => $test->admin_title)));
+ }
+
+ // Ensure name fits the rules:
+ if (preg_match('/[^a-zA-Z0-9_]/', $form_state['values']['name'])) {
+ form_error($form['name'], t('Page name must be alphanumeric or underscores only.'));
+ }
+ }
+
+ $pages = page_manager_page_load_all();
+ foreach ($pages as $test) {
+ if ($test->name != $name && $test->path == $form_state['values']['path'] && empty($test->disabled)) {
+ form_error($form['path'], t('That path is used by another page: @page', array('@page' => $test->admin_title)));
+ }
+ }
+
+ // Ensure path is unused by things NOT pages. We do the double check because
+ // we're checking against our page callback.
+ $path = array();
+ if (empty($form_state['values']['path'])) {
+ form_error($form['path'], t('Path is required.'));
+ // stop processing here if there is no path.
+ return;
+ }
+
+ $found = FALSE;
+ $error = FALSE;
+ foreach (explode('/', $form_state['values']['path']) as $bit) {
+ if (!isset($bit) || $bit === '') {
+ continue;
+ }
+
+ if ($bit[0] == '%') {
+ if ($found) {
+ form_error($form['path'], t('You cannot have a dynamic path element after an optional path element.'));
+ }
+ $path[] = '%';
+ }
+ else if ($bit[0] == '!') {
+ $found = TRUE;
+ }
+ else {
+ if ($found) {
+ form_error($form['path'], t('You cannot have a static path element after an optional path element.'));
+ }
+ $path[] = $bit;
+ }
+ }
+
+ $path = implode('/', $path);
+ $result = db_query("SELECT * FROM {menu_router} WHERE path = '%s'", $path);
+ while ($router = db_fetch_object($result)) {
+ if ($router->page_callback != 'page_manager_page_execute') {
+ form_error($form['path'], t('That path is already in used. This system cannot override existing paths.'));
+ }
+ }
+
+ // Ensure the path is not already an alias
+ // @todo
+
+ // Ensure path is properly formed.
+ $args = page_manager_page_get_named_arguments($form_state['values']['path']);
+ if ($invalid_args = array_filter($args, 'page_manager_page_form_basic_validate_filter')) {
+ foreach ($invalid_args as $arg => $position) {
+ form_error($form['path'], t('Duplicated argument %arg', array('%arg' => $arg)));
+ }
+ }
+
+ if (isset($args['%'])) {
+ form_error($form['path'], t('Invalid arg <em>%</em>. All arguments must be named with keywords.'));
+ }
+
+ $form_state['arguments'] = $args;
+}
+
+/**
+ * Store the values from the basic settings form.
+ */
+function page_manager_page_form_basic_submit(&$form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+ $cache = &$form_state['page'];
+
+ // If this is a new thing, then we have to do a bunch of setup to create
+ // the cache record with the right ID and some basic data that we could
+ // not know until we asked the user some questions.
+ if (!isset($page->pid) && !empty($form_state['creating'])) {
+ // Update the data with our new name.
+ $page->name = $form_state['values']['name'];
+ $form_state['page']->task_name = page_manager_make_task_name($form_state['task_id'], $page->name);
+ $cache->handler = $form_state['values']['handler'];
+ $cache->subtask_id = $page->name;
+ $plugin = page_manager_get_task_handler($cache->handler);
+
+ // Create a new handler.
+ $handler = page_manager_new_task_handler($plugin);
+ $title = !empty($form_state['values']['title']) ? $form_state['values']['title'] : $plugin['title'];
+ page_manager_handler_add_to_page($cache, $handler, $title);
+
+ // Figure out which forms to present them with
+ $cache->forms = array();
+ $cache->forms[] = 'basic'; // This one is always there.
+ if (!empty($form_state['arguments'])) {
+ $cache->forms[] = 'argument';
+ }
+
+ $features = $form_state['values']['features'];
+ $cache->forms = array_merge($cache->forms, array_keys(array_filter($features['default'])));
+ if (isset($features[$form_state['values']['handler']])) {
+ $cache->forms = array_merge($cache->forms, array_keys(array_filter($features[$form_state['values']['handler']])));
+ }
+
+ if (isset($plugin['required forms'])) {
+ $cache->forms = array_merge($cache->forms, array_keys($plugin['required forms']));
+ }
+ }
+
+ $page->admin_title = $form_state['values']['admin_title'];
+ if ($page->path != $form_state['values']['path']) {
+ $page->path = $form_state['values']['path'];
+ $cache->path_changed = TRUE;
+ }
+}
+
+/**
+ * Form to handle menu item controls.
+ */
+function page_manager_page_form_menu(&$form, &$form_state) {
+ ctools_include('dependent');
+ $form['menu'] = array(
+ '#prefix' => '<div class="clear-block">',
+ '#suffix' => '</div>',
+ '#tree' => TRUE,
+ );
+ $menu = $form_state['page']->subtask['subtask']->menu;
+ if (empty($menu)) {
+ $menu = array(
+ 'type' => 'none',
+ 'title' => '',
+ 'weight' => 0,
+ 'name' => 'navigation',
+ 'parent' => array(
+ 'type' => 'none',
+ 'title' => '',
+ 'weight' => 0,
+ 'name' => 'navigation',
+ ),
+ );
+ }
+
+ $form['menu']['type'] = array(
+ '#title' => t('Type'),
+ '#type' => 'radios',
+ '#options' => array(
+ 'none' => t('No menu entry'),
+ 'normal' => t('Normal menu entry'),
+ 'tab' => t('Menu tab'),
+ 'default tab' => t('Default menu tab'),
+ ),
+ '#default_value' => $menu['type'],
+ );
+
+ $form['menu']['title'] = array(
+ '#title' => t('Title'),
+ '#type' => 'textfield',
+ '#default_value' => $menu['title'],
+ '#description' => t('If set to normal or tab, enter the text to use for the menu item.'),
+ '#process' => array('ctools_dependent_process'),
+ '#dependency' => array('radio:menu[type]' => array('normal', 'tab', 'default tab')),
+ );
+
+ list($major, $minor) = explode('.', VERSION, 2);
+
+ $form['menu']['name-warning'] = array(
+ '#type' => 'markup',
+ '#prefix' => '<div class="warning">',
+ '#value' => t("Warning: Changing this item's menu will not work reliably in Drupal 6.4 or earlier. Please upgrade your copy of Drupal at !url.", array('!url' => l('drupal.org', 'http://drupal.org/project/Drupal+project'))),
+ '#suffix' => '</div>',
+ '#process' => array('ctools_dependent_process'),
+ '#dependency' => array('radio:menu[type]' => array('normal')),
+ '#access' => ($minor < 5),
+ );
+
+ // Only display the menu selector if menu module is enabled.
+ if (module_exists('menu')) {
+ $form['menu']['name'] = array(
+ '#title' => t('Menu'),
+ '#type' => 'select',
+ '#options' => menu_get_menus(),
+ '#default_value' => $menu['name'],
+ '#description' => t('Insert item into an available menu.'),
+ '#process' => array('ctools_dependent_process'),
+ '#dependency' => array('radio:menu[type]' => array('normal')),
+ );
+ }
+ else {
+ $form['menu']['name'] = array(
+ '#type' => 'value',
+ '#value' => $menu['name'],
+ );
+ $form['menu']['markup'] = array(
+ '#value' => t('Menu selection requires the activation of menu module.'),
+ );
+ }
+ $form['menu']['weight'] = array(
+ '#title' => t('Weight'),
+ '#type' => 'textfield',
+ '#default_value' => isset($menu['weight']) ? $menu['weight'] : 0,
+ '#description' => t('The lower the weight the higher/further left it will appear.'),
+ '#process' => array('ctools_dependent_process'),
+ '#dependency' => array('radio:menu[type]' => array('normal', 'tab', 'default tab')),
+ );
+
+ $form['menu']['parent']['type'] = array(
+ '#prefix' => '<div id="edit-menu-parent-type-wrapper">',
+ '#suffix' => '</div>',
+ '#title' => t('Parent menu item'),
+ '#type' => 'radios',
+ '#options' => array('none' => t('Already exists'), 'normal' => t('Normal menu item'), 'tab' => t('Menu tab')),
+ '#default_value' => $menu['parent']['type'],
+ '#description' => t('When providing a menu item as a default tab, Drupal needs to know what the parent menu item of that tab will be. Sometimes the parent will already exist, but other times you will need to have one created. The path of a parent item will always be the same path with the last part left off. i.e, if the path to this view is <em>foo/bar/baz</em>, the parent path would be <em>foo/bar</em>.'),
+ '#process' => array('expand_radios', 'ctools_dependent_process'),
+ '#dependency' => array('radio:menu[type]' => array('default tab')),
+ );
+ $form['menu']['parent']['title'] = array(
+ '#title' => t('Parent item title'),
+ '#type' => 'textfield',
+ '#default_value' => $menu['parent']['title'],
+ '#description' => t('If creating a parent menu item, enter the title of the item.'),
+ '#process' => array('ctools_dependent_process'),
+ '#dependency' => array('radio:menu[type]' => array('default tab'), 'radio:menu[parent][type]' => array('normal', 'tab')),
+ '#dependency_count' => 2,
+ );
+ // Only display the menu selector if menu module is enabled.
+ if (module_exists('menu')) {
+ $form['menu']['parent']['name'] = array(
+ '#title' => t('Parent item menu'),
+ '#type' => 'select',
+ '#options' => menu_get_menus(),
+ '#default_value' => $menu['parent']['name'],
+ '#description' => t('Insert item into an available menu.'),
+ '#process' => array('ctools_dependent_process'),
+ '#dependency' => array('radio:menu[type]' => array('default tab'), 'radio:menu[parent][type]' => array('normal')),
+ '#dependency_count' => 2,
+ );
+ }
+ else {
+ $form['menu']['parent']['name'] = array(
+ '#type' => 'value',
+ '#value' => $menu['parent']['name'],
+ );
+ }
+ $form['menu']['parent']['weight'] = array(
+ '#title' => t('Tab weight'),
+ '#type' => 'textfield',
+ '#default_value' => $menu['parent']['weight'],
+ '#size' => 5,
+ '#description' => t('If the parent menu item is a tab, enter the weight of the tab. The lower the number, the more to the left it will be.'),
+ '#process' => array('ctools_dependent_process'),
+ '#dependency' => array('radio:menu[type]' => array('default tab'), 'radio:menu[parent][type]' => array('tab')),
+ '#dependency_count' => 2,
+ );
+}
+
+/**
+ * Submit handler for the menu form for add/edit page task.
+ */
+function page_manager_page_form_menu_submit(&$form, &$form_state) {
+ $form_state['page']->subtask['subtask']->menu = $form_state['values']['menu'];
+ $form_state['page']->path_changed = TRUE;
+}
+
+/**
+ * Form to handle menu item controls.
+ */
+function page_manager_page_form_access(&$form, &$form_state) {
+ ctools_include('context');
+ $form_state['module'] = 'page_manager_page';
+ $form_state['callback argument'] = $form_state['page']->task_name;
+ $form_state['access'] = $form_state['page']->subtask['subtask']->access;
+ $form_state['no buttons'] = TRUE;
+ $form_state['contexts'] = array();
+
+ // Load contexts based on argument data:
+ if ($arguments = _page_manager_page_get_arguments($form_state['page']->subtask['subtask'])) {
+ $form_state['contexts'] = ctools_context_get_placeholders_from_argument($arguments);
+ }
+
+ ctools_include('context-access-admin');
+ $form = array_merge($form, ctools_access_admin_form($form_state));
+}
+
+/**
+ * Submit handler to deal with access control changes.
+ */
+function page_manager_page_form_access_submit(&$form, &$form_state) {
+ $form_state['page']->subtask['subtask']->access['logic'] = $form_state['values']['logic'];
+ $form_state['page']->path_changed = TRUE;
+}
+
+/**
+ * Form to handle assigning argument handlers to named arguments.
+ */
+function page_manager_page_form_argument(&$form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+ $path = $page->path;
+
+ $arguments = page_manager_page_get_named_arguments($path);
+
+ $form['table'] = array(
+ '#theme' => 'page_manager_page_form_argument_table',
+ '#page-manager-path' => $path,
+ 'argument' => array(),
+ );
+
+ $task_name = $form_state['page']->task_name;
+ foreach ($arguments as $keyword => $position) {
+ $conf = array();
+
+ if (isset($page->temporary_arguments[$keyword]) && !empty($form_state['allow temp'])) {
+ $conf = $page->temporary_arguments[$keyword];
+ }
+ else if (isset($page->arguments[$keyword])) {
+ $conf = $page->arguments[$keyword];
+ }
+
+ $context = t('No context assigned');
+
+ $plugin = array();
+ if ($conf && isset($conf['name'])) {
+ ctools_include('context');
+ $plugin = ctools_get_argument($conf['name']);
+
+ if (isset($plugin['title'])) {
+ $context = $plugin['title'];
+ }
+ }
+
+ $form['table']['argument'][$keyword]['#keyword'] = $keyword;
+ $form['table']['argument'][$keyword]['#position'] = $position;
+ $form['table']['argument'][$keyword]['#context'] = $context;
+
+ // The URL for this ajax button
+ $form['table']['argument'][$keyword]['change-url'] = array(
+ '#attributes' => array('class' => "page-manager-context-$keyword-change-url"),
+ '#type' => 'hidden',
+ '#value' => url("admin/build/pages/argument/change/$task_name/$keyword", array('absolute' => TRUE)),
+ );
+ $form['table']['argument'][$keyword]['change'] = array(
+ '#type' => 'submit',
+ '#value' => t('Change'),
+ '#attributes' => array('class' => 'ctools-use-modal'),
+ '#id' => "page-manager-context-$keyword-change",
+ );
+
+ $form['table']['argument'][$keyword]['settings'] = array();
+
+ // Only show the button if this has a settings form available:
+ if (!empty($plugin)) {
+ // The URL for this ajax button
+ $form['table']['argument'][$keyword]['settings-url'] = array(
+ '#attributes' => array('class' => "page-manager-context-$keyword-settings-url"),
+ '#type' => 'hidden',
+ '#value' => url("admin/build/pages/argument/settings/$task_name/$keyword", array('absolute' => TRUE)),
+ );
+ $form['table']['argument'][$keyword]['settings'] = array(
+ '#type' => 'submit',
+ '#value' => t('Settings'),
+ '#attributes' => array('class' => 'ctools-use-modal'),
+ '#id' => "page-manager-context-$keyword-settings",
+ );
+ }
+ }
+}
+
+/**
+ * Theme the table for this form.
+ */
+function theme_page_manager_page_form_argument_table($form) {
+ $header = array(
+ array('data' => t('Argument'), 'class' => 'page-manager-argument'),
+ array('data' => t('Position in path'), 'class' => 'page-manager-position'),
+ array('data' => t('Context assigned'), 'class' => 'page-manager-context'),
+ array('data' => t('Operations'), 'class' => 'page-manager-operations'),
+ );
+
+ $rows = array();
+
+ ctools_include('modal');
+ ctools_modal_add_js();
+ foreach (element_children($form['argument']) as $key) {
+ $row = array();
+ $row[] = '%' . check_plain($form['argument'][$key]['#keyword']);
+ $row[] = check_plain($form['argument'][$key]['#position']);
+ $row[] = $form['argument'][$key]['#context'] . ' &nbsp; ' . drupal_render($form['argument'][$key]['change']);;
+ $row[] = drupal_render($form['argument'][$key]['settings']) . drupal_render($form['argument'][$key]);
+
+ $rows[] = array('data' => $row);
+ }
+
+ if (!$rows) {
+ $rows[] = array(array('data' => t('The path %path has no arguments to configure.', array('%path' => $form['#page-manager-path'])), 'colspan' => 4));
+ }
+
+ $attributes = array(
+ 'id' => 'page-manager-argument-table',
+ );
+
+ $output = theme('table', $header, $rows, $attributes);
+ return $output;
+}
+
+/**
+ * Ajax entry point to edit an item
+ */
+function page_manager_page_subtask_argument_ajax($step = NULL, $task_name = NULL, $keyword = NULL) {
+ ctools_include('ajax');
+ ctools_include('modal');
+ ctools_include('context');
+ ctools_include('wizard');
+
+ if (!$step) {
+ return ctools_ajax_render_error();
+ }
+
+ if (!$cache = page_manager_get_page_cache($task_name)) {
+ return ctools_ajax_render_error(t('Invalid object name.'));
+ }
+
+ $page = &$cache->subtask['subtask'];
+ $path = $page->path;
+ $arguments = page_manager_page_get_named_arguments($path);
+
+ // Load stored object from cache.
+ if (!isset($arguments[$keyword])) {
+ return ctools_ajax_render_error(t('Invalid keyword.'));
+ }
+
+ // Set up wizard info
+ $form_info = array(
+ 'id' => 'page_manager_page_argument',
+ 'path' => "admin/build/pages/argument/%step/$task_name/$keyword",
+ 'show cancel' => TRUE,
+ 'next callback' => 'page_manager_page_argument_next',
+ 'finish callback' => 'page_manager_page_argument_finish',
+ 'cancel callback' => 'page_manager_page_argument_cancel',
+ 'order' => array(
+ 'change' => t('Change context type'),
+ 'settings' => t('Argument settings'),
+ ),
+ 'forms' => array(
+ 'change' => array(
+ 'title' => t('Change argument'),
+ 'form id' => 'page_manager_page_argument_form_change',
+ ),
+ 'settings' => array(
+ 'title' => t('Argument settings'),
+ 'form id' => 'page_manager_page_argument_form_settings',
+ ),
+ ),
+ );
+
+ $form_state = array(
+ 'page' => $cache,
+ 'keyword' => $keyword,
+ 'ajax' => TRUE,
+ 'modal' => TRUE,
+ 'commands' => array(),
+ );
+
+ // With 'modal' and 'ajax' true, rendering automatically happens here so
+ // we do nothing with the result.
+ ctools_wizard_multistep_form($form_info, $step, $form_state);
+}
+
+/**
+ * Callback generated when the add page process is finished.
+ */
+function page_manager_page_argument_finish(&$form_state) {
+ // Check to see if there are changes.
+ $page = &$form_state['page']->subtask['subtask'];
+ $keyword = &$form_state['keyword'];
+
+ if (isset($page->temporary_arguments[$keyword])) {
+ $page->arguments[$keyword] = $page->temporary_arguments[$keyword];
+ }
+
+ if (isset($page->temporary_arguments)) {
+ unset($page->temporary_arguments);
+ }
+
+ // Update the cache with changes.
+ page_manager_set_page_cache($form_state['page']);
+
+ // Rerender the table so we can ajax it back in.
+ // Go directly to the form and retrieve it using a blank form and
+ // a clone of our current form state. This is an abbreviated
+ // drupal_get_form that is halted prior to render and is never
+ // fully processed, but is guaranteed to produce the same form we
+ // started with so we don't have to do crazy stuff to rerender
+ // just part of it.
+
+ // @todo should there be a tool to do this?
+
+ $clone_state = $form_state;
+ $clone_state['allow temp'] = TRUE;
+ $form = array();
+ page_manager_page_form_argument($form, $clone_state);
+ drupal_prepare_form('page_manager_page_form_argument', $form, $clone_state);
+ $form['#post'] = array();
+ $form = form_builder('page_manager_page_form_argument', $form, $clone_state);
+
+ // Render just the table portion.
+ $output = drupal_render($form['table']);
+ $form_state['commands'][] = ctools_ajax_command_replace('#page-manager-argument-table', $output);
+}
+
+/**
+ * Callback generated when the 'next' button is clicked.
+ *
+ * All we do here is store the cache.
+ */
+function page_manager_page_argument_next(&$form_state) {
+ // Update the cache with changes.
+ page_manager_set_page_cache($form_state['page']);
+}
+
+/**
+ * Callback generated when the 'cancel' button is clicked.
+ *
+ * We might have some temporary data lying around. We must remove it.
+ */
+function page_manager_page_argument_cancel(&$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+ if (isset($page->temporary_arguments)) {
+ unset($page->temporary_arguments);
+ // Update the cache with changes.
+ page_manager_set_page_cache($page);
+ }
+}
+
+/**
+ * Basic settings form for a page manager page.
+ */
+function page_manager_page_argument_form_change(&$form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+ $keyword = &$form_state['keyword'];
+
+ ctools_include('context');
+ $plugins = ctools_get_arguments();
+
+ $options = array();
+ foreach ($plugins as $id => $plugin) {
+ $options[$id] = $plugin['title'];
+ }
+
+ asort($options);
+
+ $options = array('' => t('No context selected')) + $options;
+
+ $argument = '';
+ if (isset($page->arguments[$keyword]) && isset($page->arguments[$keyword]['name'])) {
+ $argument = $page->arguments[$keyword]['name'];
+ }
+
+ $form['argument'] = array(
+ '#type' => 'radios',
+ '#options' => $options,
+ '#default_value' => $argument,
+ );
+}
+
+/**
+ * Submit handler to change an argument.
+ */
+function page_manager_page_argument_form_change_submit(&$form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+ $keyword = &$form_state['keyword'];
+ $argument = $form_state['values']['argument'];
+
+ // If the argument is not changing, we do not need to do anything.
+ if (isset($page->arguments[$keyword]['name']) && $page->arguments[$keyword]['name'] == $argument) {
+ // Set the task to cancel since no change means do nothing:
+ $form_state['clicked_button']['#wizard type'] = 'cancel';
+ return;
+ }
+
+ ctools_include('context');
+
+ // If switching to the no context, just wipe out the old data.
+ if (empty($argument)) {
+ $form_state['clicked_button']['#wizard type'] = 'finish';
+ $page->temporary_arguments[$keyword] = array(
+ 'settings' => array(),
+ 'identifier' => t('No context'),
+ );
+ return;
+ }
+
+ $plugin = ctools_get_argument($argument);
+
+ // Acquire defaults.
+ $settings = array();
+
+ if (isset($plugin['default'])) {
+ if (is_array($plugin['default'])) {
+ $settings = $plugin['default'];
+ }
+ else if (function_exists($plugin['default'])) {
+ $settings = $plugin['default']();
+ }
+ }
+
+ $id = ctools_context_next_id($page->arguments, $argument);
+ $title = isset($plugin['title']) ? $plugin['title'] : t('No context');
+
+ // Set the new argument in a temporary location.
+ $page->temporary_arguments[$keyword] = array(
+ 'id' => $id,
+ 'identifier' => $title . ($id > 1 ? ' ' . $id : ''),
+ 'name' => $argument,
+ 'settings' => $settings,
+ );
+}
+
+/**
+ * Basic settings form for a page manager page.
+ */
+function page_manager_page_argument_form_settings(&$form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+ $keyword = &$form_state['keyword'];
+
+ if (isset($page->temporary_arguments[$keyword])) {
+ $conf = $page->temporary_arguments[$keyword];
+ }
+ else if (isset($page->arguments[$keyword])) {
+ $conf = $page->temporary_arguments[$keyword] = $page->arguments[$keyword];
+ }
+
+ if (!isset($conf)) {
+ // This should be impossible and thus never seen.
+ $form['error'] = array('#value' => t('Error: missing argument.'));
+ return;
+ }
+
+ ctools_include('context');
+ $plugin = ctools_get_argument($conf['name']);
+
+ $form['settings'] = array(
+ '#tree' => TRUE,
+ );
+
+ $form['identifier'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Context identifier'),
+ '#description' => t('This is the title of the context used to identify it later in the administrative process. This will never be shown to a user.'),
+ '#default_value' => $conf['identifier'],
+ );
+
+ if (!$plugin) {
+ // This should be impossible and thus never seen.
+ $form['error'] = array('#value' => t('Error: missing or invalid argument plugin %argument.', array('%argument', $argument)));
+ return;
+ }
+
+ if ($function = ctools_plugin_get_function($plugin, 'settings form')) {
+ $function($form, $form_state, $conf['settings']);
+ }
+
+ $form_state['plugin'] = $plugin;
+}
+
+/**
+ * Validate handler for argument settings.
+ */
+function page_manager_page_argument_form_settings_validate(&$form, &$form_state) {
+ if ($function = ctools_plugin_get_function($form_state['plugin'], 'settings form validate')) {
+ $function($form, $form_state);
+ }
+}
+
+/**
+ * Submit handler for argument settings.
+ */
+function page_manager_page_argument_form_settings_submit(&$form, &$form_state) {
+ if ($function = ctools_plugin_get_function($form_state['plugin'], 'settings form submit')) {
+ $function($form, $form_state);
+ }
+
+ $page = &$form_state['page']->subtask['subtask'];
+ $keyword = &$form_state['keyword'];
+ // Copy the form to our temporary location which will get moved again when
+ // finished. Yes, finished is always next but finish can happen from other
+ // locations so we funnel through that path rather than duplicate.
+ $page->temporary_arguments[$keyword]['identifier'] = $form_state['values']['identifier'];
+ if (isset($form_state['values']['settings'])) {
+ $page->temporary_arguments[$keyword]['settings'] = $form_state['values']['settings'];
+ }
+ else {
+ $page->temporary_arguments[$keyword]['settings'] = array();
+ }
+}
+
+/**
+ * Import a task handler from cut & paste
+ */
+function page_manager_page_import_subtask(&$form_state, $task_name) {
+ $form_state['task'] = page_manager_get_task($task_name);
+
+ drupal_set_title(t('Import page'));
+ $form['name'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Page name'),
+ '#description' => t('Enter the name to use for this page if it is different from the source page. Leave blank to use the original name of the page.'),
+ );
+
+ $form['path'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Path'),
+ '#description' => t('Enter the path to use for this page if it is different from the source page. Leave blank to use the original path of the page.'),
+ );
+
+ $form['overwrite'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Allow overwrite of an existing page'),
+ '#description' => t('If the name you selected already exists in the database, this page will be allowed to overwrite the existing page.'),
+ );
+
+ $form['object'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Paste page code here'),
+ '#rows' => 15,
+ );
+
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Import'),
+ );
+ return $form;
+}
+
+/**
+ * Ensure we got a valid page.
+ */
+function page_manager_page_import_subtask_validate(&$form, &$form_state) {
+ ob_start();
+ eval($form_state['values']['object']);
+ ob_end_clean();
+
+ if (!isset($page) || !is_object($page)) {
+ $errors = ob_get_contents();
+ if (empty($errors)) {
+ $errors = t('No handler found.');
+ }
+ form_error($form['object'], t('Unable to get a page from the import. Errors reported: @errors', array('@errors' => $errors)));
+ }
+
+ if (empty($form_state['values']['name'])) {
+ $form_state['values']['name'] = $page->name;
+ }
+
+ $task_name = page_manager_make_task_name('page', $form_state['values']['name']);
+ $form_state['cache'] = page_manager_get_page_cache($task_name);
+
+ if ($form_state['cache']->locked) {
+ form_error($form['name'], t('That page name is in use and locked by another user. You must <a href="!break">break the lock</a> on that page before proceeding, or choose a different name.', array('!break' => url("admin/build/pages/nojs/operation/$task_name/actions/break-lock"))));
+ return;
+ }
+
+ if (empty($form_state['values']['path'])) {
+ $form_state['values']['path'] = $page->path;
+ }
+
+ if (empty($form_state['values']['overwrite'])) {
+ $page->name = NULL;
+ }
+
+ //
+ $form_state['page'] = new stdClass();
+ $form_state['page']->subtask['subtask'] = $page;
+ page_manager_page_form_basic_validate($form, $form_state);
+}
+
+/**
+ * Submit the import page to create the new page and redirect.
+ */
+function page_manager_page_import_subtask_submit($form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+ $page->name = $form_state['values']['name'];
+ $page->path = $form_state['values']['path'];
+
+ $task_name = page_manager_make_task_name('page', $page->name);
+ $cache = page_manager_get_page_cache($task_name);
+ if (!$cache) {
+ $cache = new stdClass();
+ }
+
+ page_manager_page_new_page_cache($page, $cache);
+ page_manager_set_page_cache($cache);
+
+ $form_state['redirect'] = 'admin/build/pages/edit/' . $task_name;
+}
+
+/**
+ * Entry point to export a page.
+ */
+function page_manager_page_form_export(&$form, &$form_state) {
+ $page = $form_state['page']->subtask['subtask'];
+
+ $export = page_manager_page_export($page, $form_state['page']->handlers);
+
+ $lines = substr_count($export, "\n");
+ $form['code'] = array(
+ '#type' => 'textarea',
+ '#default_value' => $export,
+ '#rows' => $lines,
+ );
+
+ unset($form['buttons']);
+}
+
+/**
+ * Entry point to clone a page.
+ */
+function page_manager_page_form_clone(&$form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+
+ // This provides its own button because it does something totally different.
+ unset($form['buttons']);
+ $form['name'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Page name'),
+ '#description' => t('Enter the name to the new page It must be unique and contain only alphanumeric characters and underscores.'),
+ );
+
+ $form['admin_title'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Administrative title'),
+ '#description' => t('The name of this page. This will appear in the administrative interface to easily identify it.'),
+ '#default_value' => $page->admin_title,
+ );
+
+ // path
+ $form['path'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Path'),
+ '#description' => t('The URL path to get to this page. You may create named placeholders for variable parts of the path by using %name for required elements and !name for optional elements. For example: "node/%node/foo", "forum/%forum" or "dashboard/!input". These named placeholders can be turned into contexts on the arguments form. You cannot use the same path as the original page.'),
+ '#default_value' => $page->path,
+ );
+
+ $form['handlers'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Clone variants'),
+ '#description' => t('If checked all variants associated with the page will be cloned as well. If not checked the page will be cloned without variants.'),
+ '#default_value' => TRUE,
+ );
+
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Clone'),
+ );
+}
+
+/**
+ * Validate clone page form.
+ */
+function page_manager_page_form_clone_validate(&$form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+
+ $page->old_name = $page->name;
+ $page->name = NULL;
+ page_manager_page_form_basic_validate($form, $form_state);
+}
+
+/**
+ * submit clone page form.
+ *
+ * Load the page, change the name(s) to protect the innocent, and if
+ * requested, load all the task handlers so that they get saved properly too.
+ */
+function page_manager_page_form_clone_submit(&$form, &$form_state) {
+ $original = $form_state['page']->subtask['subtask'];
+
+ $original->name = $form_state['values']['name'];
+ $original->admin_title = $form_state['values']['admin_title'];
+ $original->path = $form_state['values']['path'];
+
+ $handlers = !empty($form_state['values']['handlers']) ? $form_state['page']->handlers : FALSE;
+ // Export the handler, which is a fantastic way to clean database IDs out of it.
+ $export = page_manager_page_export($original, $handlers);
+ ob_start();
+ eval($export);
+ ob_end_clean();
+
+ $task_name = page_manager_make_task_name('page', $page->name);
+ $cache = new stdClass();
+
+ page_manager_page_new_page_cache($page, $cache);
+ page_manager_set_page_cache($cache);
+
+ $form_state['redirect'] = 'admin/build/pages/edit/' . $task_name;
+}
+
+/**
+ * When adding or cloning a new page, this creates a new page cache
+ * and adds our page to it.
+ *
+ * This does not check to see if the existing cache is already locked.
+ * This must be done beforehand.
+ *
+ * @param &$page
+ * The page to create.
+ * @param &$cache
+ * The cache to use. If the cache has any existing task handlers,
+ * they will be marked for deletion. This may be a blank object.
+ */
+function page_manager_page_new_page_cache(&$page, &$cache) {
+ // Does a page already exist? If so, we are overwriting it so
+ // take its pid.
+ if (!empty($cache->subtask) && !empty($cache->subtask['subtask'])) {
+ $page->pid = $cache->subtask['subtask']->pid;
+ }
+ else {
+ $cache->new = TRUE;
+ }
+
+ $cache->task_name = page_manager_make_task_name('page', $page->name);
+ $cache->task_id = 'page';
+ $cache->task = page_manager_get_task('page');
+ $cache->subtask_id = $page->name;
+ $page->export_type = EXPORT_IN_DATABASE;
+ $page->type = t('Normal');
+ $cache->subtask = page_manager_page_build_subtask($cache->task, $page);
+
+ if (isset($cache->handlers)) {
+ foreach($cache->handlers as $id => $handler) {
+ $cache->handler_info[$id]['changed'] = PAGE_MANAGER_CHANGED_DELETED;
+ }
+ }
+ else {
+ $cache->handlers = array();
+ $cache->handler_info = array();
+ }
+
+ if (!empty($page->default_handlers)) {
+ foreach ($page->default_handlers as $id => $handler) {
+ page_manager_handler_add_to_page($cache, $handler);
+ }
+ }
+
+ $cache->locked = FALSE;
+ $cache->changed = TRUE;
+}
+
+/**
+ * Form to enable a page.
+ */
+function page_manager_page_form_enable(&$form, &$form_state) {
+ $form['markup'] = array(
+ '#value' => t('Enabling this page will immediately make it available in your system (there is no need to wait for a save.)'),
+ );
+}
+
+/**
+ * Enable the page after it has been confirmed.
+ */
+function page_manager_page_form_enable_submit(&$form, &$form_state) {
+ $page = $form_state['page']->subtask['subtask'];
+ ctools_include('export');
+ ctools_export_set_object_status($page, FALSE);
+ menu_rebuild();
+ $form_state['new trail'] = array('actions', 'disable');
+
+ $page->disabled = FALSE;
+ // We don't want to cause this to cache if it wasn't already. If it was
+ // cached, however, we want to update the enabled state.
+ if (empty($form_state['page']->changed)) {
+ $form_state['do not cache'] = TRUE;
+ }
+}
+
+/**
+ * Form to disable a page.
+ */
+function page_manager_page_form_disable(&$form, &$form_state) {
+ $form['markup'] = array(
+ '#value' => t('Disabling this page will immediately make it unavailable in your system (there is no need to wait for a save.)'),
+ );
+}
+
+/**
+ * Disable the page after it has been confirmed.
+ */
+function page_manager_page_form_disable_submit(&$form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+ ctools_include('export');
+ ctools_export_set_object_status($page, TRUE);
+ menu_rebuild();
+ $form_state['new trail'] = array('actions', 'enable');
+
+ $page->disabled = TRUE;
+ // We don't want to cause this to cache if it wasn't already. If it was
+ // cached, however, we want to update the enabled state.
+ if (empty($form_state['page']->changed)) {
+ $form_state['do not cache'] = TRUE;
+ }
+}
+
+/**
+ * Entry point to export a page.
+ */
+function page_manager_page_form_delete(&$form, &$form_state) {
+ $page = &$form_state['page']->subtask['subtask'];
+
+ if ($page->type == t('Overridden')) {
+ $text = t('Reverting the page will delete the page that is in the database, reverting it to the original default page. Any changes you have made will be lost and cannot be recovered.');
+ }
+ else {
+ $text = t('Are you sure you want to delete this page? Deleting a page cannot be undone.');
+ }
+ $form['markup'] = array(
+ '#value' => '<p>' . $text . '</p>',
+ );
+
+ if (empty($form_state['page']->locked)) {
+ unset($form['buttons']);
+ $form['delete'] = array(
+ '#type' => 'submit',
+ '#value' => t('Delete'),
+ );
+ }
+}
+
+/**
+ * Submit handler to delete a view.
+ */
+function page_manager_page_form_delete_submit(&$form, &$form_state) {
+ page_manager_page_delete($form_state['page']->subtask['subtask']);
+ drupal_set_message(t('The page has been deleted.'));
+ $form_state['redirect'] = 'admin/build/pages';
+}
diff --git a/page_manager/plugins/tasks/page.inc b/page_manager/plugins/tasks/page.inc
new file mode 100644
index 0000000..62d1f80
--- /dev/null
+++ b/page_manager/plugins/tasks/page.inc
@@ -0,0 +1,540 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Handle the 'page' task, which creates pages with arbitrary tasks and lets
+ * handlers decide how they will be rendered.
+ *
+ * This creates subtasks and stores them in the page_manager_pages table. These
+ * are exportable objects, too.
+ *
+ * The render callback for this task type has $handler, $page, $contexts as
+ * parameters.
+ */
+
+/**
+ * Specialized implementation of hook_page_manager_task_tasks(). See api-task.html for
+ * more information.
+ */
+function page_manager_page_page_manager_tasks() {
+ return array(
+ 'title' => t('Custom pages'),
+ 'description' => t('Administrator created pages that have a URL path, access control and entries in the Drupal menu system.'),
+ 'subtasks' => TRUE,
+ 'subtask callback' => 'page_manager_page_subtask',
+ 'subtasks callback' => 'page_manager_page_subtasks',
+ 'save subtask callback' => 'page_manager_page_save_subtask',
+ 'hook menu' => array(
+ 'file' => 'page.admin.inc',
+ 'path' => drupal_get_path('module', 'page_manager') . '/plugins/tasks',
+ 'function' => 'page_manager_page_menu',
+ ),
+ 'hook theme' => 'page_manager_page_theme',
+ // page only items
+ 'task type' => 'page',
+ 'operations' => array(
+ array(
+ 'title' => t('Import'),
+ 'href' => 'admin/build/pages/import',
+ ),
+ array(
+ 'title' => t('Add custom page'),
+ 'href' => 'admin/build/pages/add',
+ ),
+ ),
+ 'columns' => array(
+ 'storage' => array(
+ 'label' => t('Storage'),
+ 'class' => 'page-manager-page-storage',
+ ),
+ ),
+ 'page type' => 'custom',
+
+ // context only items
+ 'handler type' => 'context',
+ 'get arguments' => array(
+ 'file' => 'page.admin.inc',
+ 'path' => drupal_get_path('module', 'page_manager') . '/plugins/tasks',
+ 'function' => 'page_manager_page_get_arguments',
+ ),
+ 'get context placeholders' => 'page_manager_page_get_contexts',
+ 'access restrictions' => 'page_manager_page_access_restrictions',
+ 'uses handlers' => TRUE,
+ );
+}
+
+/**
+ * Task callback to get all subtasks.
+ *
+ * Return a list of all subtasks.
+ */
+function page_manager_page_subtasks($task) {
+ $pages = page_manager_page_load_all($task['name']);
+ $return = array();
+ foreach ($pages as $name => $page) {
+ $return[$name] = page_manager_page_build_subtask($task, $page);
+ }
+
+ return $return;
+}
+
+/**
+ * Callback to return a single subtask.
+ */
+function page_manager_page_subtask($task, $subtask_id) {
+ $page = page_manager_page_load($subtask_id);
+ if ($page) {
+ return page_manager_page_build_subtask($task, $page);
+ }
+}
+
+/**
+ * Call back from the administrative system to save a page.
+ *
+ * We get the $subtask as created by page_manager_page_build_subtask.
+ */
+function page_manager_page_save_subtask($subtask) {
+ $page = &$subtask['subtask'];
+
+ // Ensure $page->arguments contains only real arguments:
+ $arguments = page_manager_page_get_named_arguments($page->path);
+ $args = array();
+ foreach ($arguments as $keyword => $position) {
+ if (isset($page->arguments[$keyword])) {
+ $args[$keyword] = $page->arguments[$keyword];
+ }
+ else {
+ $args[$keyword] = array(
+ 'id' => '',
+ 'identifier' => '',
+ 'argument' => '',
+ 'settings' => array(),
+ );
+ }
+ }
+ $page->arguments = $args;
+ // Create a real object from the cache
+ page_manager_page_save($page);
+}
+
+/**
+ * Build a subtask array for a given page.
+ */
+function page_manager_page_build_subtask($task, $page) {
+ $operations = array();
+ $operations['settings'] = array(
+ 'type' => 'group',
+ 'class' => 'operations-settings',
+ 'title' => t('Settings'),
+ 'children' => array(),
+ );
+
+ $settings = &$operations['settings']['children'];
+
+ $settings['basic'] = array(
+ 'title' => t('Basic'),
+ 'description' => t('Edit name, path and other basic settings for the page.'),
+ 'form' => 'page_manager_page_form_basic',
+ );
+
+ if (!page_manager_page_get_named_arguments($page->path)) {
+ $settings['argument'] = array(
+ 'title' => t('Argument contexts'),
+ 'description' => t('Set up contexts for the arguments on this page.'),
+ 'form' => 'page_manager_page_form_argument',
+ );
+ }
+
+ $settings['access'] = array(
+ 'title' => t('Access'),
+ 'description' => t('Control what users can access this page.'),
+ 'form' => 'page_manager_page_form_access',
+ );
+
+ $settings['menu'] = array(
+ 'title' => t('Menu'),
+ 'description' => t('Provide this page a visible menu or a menu tab.'),
+ 'form' => 'page_manager_page_form_menu',
+ );
+
+ $operations['actions']['children']['clone'] = array(
+ 'title' => t('Clone'),
+ 'description' => t('Make a copy of this page'),
+ 'form' => 'page_manager_page_form_clone',
+ );
+ $operations['actions']['children']['export'] = array(
+ 'title' => t('Export'),
+ 'description' => t('Export this page as code that can be imported or embedded into a module.'),
+ 'form' => 'page_manager_page_form_export',
+ );
+ if ($page->export_type == (EXPORT_IN_CODE | EXPORT_IN_DATABASE)) {
+ $operations['actions']['children']['delete'] = array(
+ 'title' => t('Revert'),
+ 'description' => t('Remove all changes to this page and revert to the version in code.'),
+ 'form' => 'page_manager_page_form_delete',
+ );
+ }
+ else if ($page->export_type != EXPORT_IN_CODE) {
+ $operations['actions']['children']['delete'] = array(
+ 'title' => t('Delete'),
+ 'description' => t('Remove this page from your system completely.'),
+ 'form' => 'page_manager_page_form_delete',
+ );
+ }
+
+ if (!empty($page->disabled)) {
+ $operations['actions']['children']['enable'] = array(
+ 'title' => t('Enable'),
+ 'description' => t('Activate this page so that it will be in use in your system.'),
+ 'form' => 'page_manager_page_form_enable',
+ 'ajax' => FALSE,
+ 'form info' => array(
+ 'finish text' => t('Enable'),
+ ),
+ );
+ }
+ else {
+ $operations['actions']['children']['disable'] = array(
+ 'title' => t('Disable'),
+ 'description' => t('De-activate this page. The data will remain but the page will not be in use on your system.'),
+ 'form' => 'page_manager_page_form_disable',
+ 'ajax' => FALSE,
+ 'form info' => array(
+ 'finish text' => t('Disable'),
+ ),
+ );
+ }
+
+ $subtask = array(
+ 'name' => $page->name,
+ 'admin title' => $page->admin_title,
+ 'admin path' => $page->path,
+ 'admin type' => t('Custom'),
+ 'subtask' => $page,
+ 'operations' => $operations,
+ 'operations include' => array(
+ 'file' => 'page.admin.inc',
+ 'path' => drupal_get_path('module', 'page_manager') . '/plugins/tasks',
+ ),
+ 'single task' => empty($page->multiple),
+ 'row class' => empty($page->disabled) ? 'page-manager-enabled' : 'page-manager-disabled',
+ 'storage' => $page->type,
+ );
+
+ // default handlers may appear from a default subtask.
+ if (isset($page->default_handlers)) {
+ $subtask['default handlers'] = $page->default_handlers;
+ }
+ return $subtask;
+}
+
+/**
+ * Delegated implementation of hook_theme().
+ */
+function page_manager_page_theme(&$items, $task) {
+ $base = array(
+ 'file' => 'page.admin.inc',
+ 'path' => drupal_get_path('module', 'page_manager') . '/plugins/tasks',
+ );
+ $items['page_manager_page_form_argument_table'] = $base + array(
+ 'arguments' => array('form' => NULL),
+ );
+ $items['page_manager_page_lock'] = $base + array(
+ 'arguments' => array('lock' => array(), 'task_name' => NULL),
+ );
+ $items['page_manager_page_changed'] = $base + array(
+ 'arguments' => array(),
+ );
+}
+
+// --------------------------------------------------------------------------
+// Page execution functions
+
+/**
+ * Execute a page task.
+ *
+ * This is the callback to entries in the Drupal menu system created by the
+ * page task.
+ *
+ * @param $subtask_id
+ * The name of the page task used.
+ * @param ...
+ * A number of context objects as specified by the user when
+ * creating named arguments in the path.
+ */
+function page_manager_page_execute($subtask_id) {
+ $page = page_manager_page_load($subtask_id);
+ $task = page_manager_get_task($page->task);
+ $subtask = page_manager_get_task_subtask($task, $subtask_id);
+
+ // Turn the contexts into a properly keyed array.
+ $contexts = array();
+ $args = array();
+ foreach (func_get_args() as $count => $arg) {
+ if (is_object($arg) && get_class($arg) == 'ctools_context') {
+ $contexts[$arg->id] = $arg;
+ $args[] = $arg->original_argument;
+ }
+ else if ($count) {
+ $args[] = $arg;
+ }
+ }
+
+ $count = 0;
+ $names = page_manager_page_get_named_arguments($page->path);
+ $bits = explode('/', $page->path);
+
+ if ($page->arguments) {
+ foreach ($page->arguments as $name => $argument) {
+ // Optional arguments must be converted to contexts too, if they exist.
+ if ($bits[$names[$name]][0] == '!' && isset($args[$count])) {
+ $argument['keyword'] = $name;
+ ctools_include('context');
+ $context = ctools_context_get_context_from_argument($argument, $args[$count]);
+ $contexts[$context->id] = $context;
+ }
+ $count++;
+ }
+ }
+
+ // Add a fake tab for 'View' so that edit tabs can be added.
+ if (user_access('administer page manager') && (!isset($page->menu['type']) || !in_array($page->menu['type'], array('tab', 'default tab')))) {
+ ctools_include('menu');
+ ctools_menu_add_tab(array(
+ 'title' => t('View'),
+ 'href' => $_GET['q'],
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ 'weight' => -10,
+ ));
+ }
+
+
+ if ($function = ctools_plugin_get_function($task, 'page callback')) {
+ return call_user_func_array($function, array($page, $contexts, $args));
+ }
+
+ ctools_include('context-task-handler');
+ $output = ctools_context_handler_render($task, $subtask, $contexts, $args);
+ if ($output === FALSE) {
+ return drupal_not_found();
+ }
+
+ return $output;
+}
+
+// --------------------------------------------------------------------------
+// Context type callbacks
+
+/**
+ * Return a list of arguments used by this task.
+ */
+function page_manager_page_get_arguments($task, $subtask) {
+ return _page_manager_page_get_arguments($subtask['subtask']);
+}
+
+function _page_manager_page_get_arguments($page) {
+ $arguments = array();
+ if (!empty($page->arguments)) {
+ foreach ($page->arguments as $keyword => $argument) {
+ if (isset($argument['name'])) {
+ $argument['keyword'] = $keyword;
+ $arguments[$keyword] = $argument;
+ }
+ }
+ }
+ return $arguments;
+}
+
+/**
+ * Get a group of context placeholders for the arguments.
+ */
+function page_manager_page_get_contexts($task, $subtask) {
+ ctools_include('context');
+ return ctools_context_get_placeholders_from_argument(page_manager_page_get_arguments($task, $subtask));
+}
+
+/**
+ * Return a list of arguments used by this task.
+ */
+function page_manager_page_access_restrictions($task, $subtask, $contexts) {
+ $page = $subtask['subtask'];
+ return ctools_access_add_restrictions($page->access, $contexts);
+}
+
+// --------------------------------------------------------------------------
+// Page task database info.
+
+/**
+ * Create a new page with defaults appropriately set from schema.
+ */
+function page_manager_page_new() {
+ ctools_include('export');
+ return ctools_export_new_object('page_manager_pages');
+}
+
+/**
+ * Load a single page subtask.
+ */
+function page_manager_page_load($name) {
+ ctools_include('export');
+ $result = ctools_export_load_object('page_manager_pages', 'names', array($name));
+ if (isset($result[$name])) {
+ return $result[$name];
+ }
+}
+
+/**
+ * Load all page subtasks.
+ */
+function page_manager_page_load_all($task = NULL) {
+ ctools_include('export');
+
+ if (empty($task)) {
+ return ctools_export_load_object('page_manager_pages');
+ }
+ else {
+ return ctools_export_load_object('page_manager_pages', 'conditions', array('task' => $task));
+ }
+}
+
+/**
+ * Write a page subtask to the database.
+ */
+function page_manager_page_save(&$page) {
+ $update = (isset($page->pid)) ? array('pid') : array();
+ $task = page_manager_get_task($page->task);
+
+ if ($function = ctools_plugin_get_function($task, 'save')) {
+ $function($page, $update);
+ }
+ drupal_write_record('page_manager_pages', $page, $update);
+
+ // If this was a default page we may need to write default task
+ // handlers that we provided as well.
+ if (!$update && isset($page->default_handlers)) {
+ $handlers = page_manager_load_task_handlers(page_manager_get_task('page'), $page->name);
+ foreach ($page->default_handlers as $name => $handler) {
+ if (!isset($handlers[$name]) || !($handlers[$name]->export_type & EXPORT_IN_DATABASE)) {
+ // Make sure this is right, as exports can wander a bit.
+ $handler->subtask = $page->name;
+ page_manager_save_task_handler($handler);
+ }
+ }
+ }
+ return $page;
+}
+
+/**
+ * Remove a page subtask.
+ */
+function page_manager_page_delete($page) {
+ $task = page_manager_get_task($page->task);
+ if ($function = ctools_plugin_get_function($task, 'delete')) {
+ $function($page);
+ }
+ if (!empty($task['uses handlers'])) {
+ $handlers = page_manager_load_task_handlers($task, $page->name);
+ foreach ($handlers as $handler) {
+ page_manager_delete_task_handler($handler);
+ }
+ }
+ db_query("DELETE FROM {page_manager_pages} WHERE name = '%s'", $page->name);
+}
+
+/**
+ * Export a page subtask.
+ */
+function page_manager_page_export($page, $with_handlers = FALSE, $indent = '') {
+ $task = page_manager_get_task($page->task);
+ $append = '';
+
+ if ($function = ctools_plugin_get_function($task, 'export')) {
+ $append = $function($page, $indent);
+ }
+
+ ctools_include('export');
+ $output = ctools_export_object('page_manager_pages', $page, $indent);
+ $output .= $append;
+
+ if ($with_handlers) {
+ if (is_array($with_handlers)) {
+ $handlers = $with_handlers;
+ }
+ else {
+ $handlers = page_manager_load_task_handlers(page_manager_get_task('page'), $page->name);
+ }
+ $output .= $indent . '$page->default_handlers = array();' . "\n";
+ foreach ($handlers as $handler) {
+ $output .= page_manager_export_task_handler($handler, $indent);
+ $output .= $indent . '$page->default_handlers[$handler->name] = $handler;' . "\n";
+ }
+ }
+ return $output;
+}
+
+/**
+ * Get a list of named arguments in a page managerpage path.
+ *
+ * @param $path
+ * A normal Drupal path.
+ *
+ * @return
+ * An array of % marked variable arguments, keyed by the argument's name.
+ * The value will be the position of the argument so that it can easily
+ * be found. Items with a position of -1 have multiple positions.
+ */
+function page_manager_page_get_named_arguments($path) {
+ $arguments = array();
+ $bits = explode('/', $path);
+ foreach ($bits as $position => $bit) {
+ if ($bit && ($bit[0] == '%' || $bit[0] == '!')) {
+ // special handling for duplicate path items and substr to remove the %
+ $arguments[substr($bit, 1)] = isset($arguments[$bit]) ? -1 : $position;
+ }
+ }
+
+ return $arguments;
+}
+
+/**
+ * Load a context from an argument for a given page task.
+ *
+ * This is used as a menu callback to translate arguments and that is why it is
+ * here in the .module file.
+ *
+ * @param $value
+ * The incoming argument value.
+ * @param $subtask
+ * The subtask id.
+ * @param $argument
+ * The numeric position of the argument in the path, counting from 0.
+ *
+ * @return
+ * A context item if one is configured, the argument if one is not, or
+ * FALSE if restricted or invalid.
+ */
+function _pm_arg_load($value, $subtask, $argument) {
+ $page = page_manager_page_load($subtask);
+ if (!$page) {
+ return FALSE;
+ }
+
+ $path = explode('/', $page->path);
+ if (empty($path[$argument])) {
+ return FALSE;
+ }
+
+ $keyword = substr($path[$argument], 1);
+ if (empty($page->arguments[$keyword])) {
+ return $value;
+ }
+
+ $page->arguments[$keyword]['keyword'] = $keyword;
+
+ ctools_include('context');
+ $context = ctools_context_get_context_from_argument($page->arguments[$keyword], $value);
+
+ // convert false equivalents to false.
+ return $context ? $context : FALSE;
+}
diff --git a/delegator/plugins/tasks/term_view.inc b/page_manager/plugins/tasks/term_view.inc
index a78120c..4cc101f 100644
--- a/delegator/plugins/tasks/term_view.inc
+++ b/page_manager/plugins/tasks/term_view.inc
@@ -5,16 +5,16 @@
* @file
* Handle the 'term view' override task.
*
- * This plugin overrides term/%term and reroutes it to the delegator, where
+ * This plugin overrides term/%term and reroutes it to the page manager, where
* a list of tasks can be used to service this request based upon criteria
* supplied by access plugins.
*/
/**
- * Specialized implementation of hook_delegator_tasks(). See api-task.html for
+ * Specialized implementation of hook_page_manager_task_tasks(). See api-task.html for
* more information.
*/
-function delegator_term_view_delegator_tasks() {
+function page_manager_term_view_page_manager_tasks() {
if (module_exists('taxonomy')) {
return array(
// This is a 'page' task and will fall under the page admin UI
@@ -27,61 +27,45 @@ function delegator_term_view_delegator_tasks() {
'admin path' => 'taxonomy/term/%term',
// Menu hooks so that we can alter the term/%term menu entry to point to us.
- 'hook menu' => 'delegator_term_view_menu',
- 'hook menu alter' => 'delegator_term_view_menu_alter',
+ 'hook menu' => 'page_manager_term_view_menu',
+ 'hook menu alter' => 'page_manager_term_view_menu_alter',
// Provide a setting to the primary settings UI for Panels
- 'admin settings' => 'delegator_term_view_admin_settings',
+ 'admin settings' => 'page_manager_term_view_admin_settings',
- // Callback to add items to the delegator task administration form:
- 'task admin' => 'delegator_term_view_task_admin',
+ // Callback to add items to the page managertask administration form:
+ 'task admin' => 'page_manager_term_view_task_admin',
// This is task uses 'context' handlers and must implement these to give the
// handler data it needs.
'handler type' => 'context',
- 'get arguments' => 'delegator_term_view_get_arguments',
- 'get context placeholders' => 'delegator_term_view_get_contexts',
+ 'get arguments' => 'page_manager_term_view_get_arguments',
+ 'get context placeholders' => 'page_manager_term_view_get_contexts',
// Allow additional operations
'operations' => array(
- array(
- 'title' => t('Task handlers'),
- 'href' => "admin/build/delegator/term_view",
- ),
- array(
+ 'settings' => array(
'title' => t('Settings'),
- 'href' => "admin/build/delegator/term_view/settings",
+ 'description' => t('Update settings specific to the taxonomy term view.'),
),
+ // This lets it automatically add relevant information for task handlers.
+ 'handlers' => array('type' => 'handlers'),
),
);
}
}
/**
- * Create the term view settings page menu item.
- */
-function delegator_term_view_menu(&$items, $task) {
- $items['admin/build/delegator/term_view/settings'] = array(
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('delegator_term_view_settings'),
- 'file path' => $task['path'],
- 'file' => $task['file'],
- 'access arguments' => array('administer delegator'),
- 'type' => MENU_CALLBACK,
- );
-}
-
-/**
- * Callback defined by delegator_term_view_delegator_tasks().
+ * Callback defined by page_manager_term_view_page_manager_tasks().
*
* Alter the term view input so that term view comes to us rather than the
* normal term view process.
*/
-function delegator_term_view_menu_alter(&$items, $task) {
+function page_manager_term_view_menu_alter(&$items, $task) {
// Override the term view handler for our purpose, but only if someone else
// has not already done so.
- if ($items['taxonomy/term/%']['page callback'] == 'taxonomy_term_page' || variable_get('delegator_override_anyway', FALSE)) {
- $items['taxonomy/term/%']['page callback'] = 'delegator_term_view';
+ if ($items['taxonomy/term/%']['page callback'] == 'taxonomy_term_page' || variable_get('page_manager_override_anyway', FALSE)) {
+ $items['taxonomy/term/%']['page callback'] = 'page_manager_term_view';
$items['taxonomy/term/%']['file path'] = $task['path'];
$items['taxonomy/term/%']['file'] = $task['file'];
}
@@ -90,10 +74,10 @@ function delegator_term_view_menu_alter(&$items, $task) {
/**
* Warn if we are unable to override the taxonomy term page.
*/
-function delegator_term_view_task_admin(&$form, &$form_state) {
+function page_manager_term_view_task_admin(&$form, &$form_state) {
$callback = db_result(db_query("SELECT page_callback FROM {menu_router} WHERE path = 'taxonomy/term/%'"));
- if ($callback != 'delegator_term_view') {
- drupal_set_message(t('Delegator module is unable to override taxonomy/term/% because some other module already has overridden with %callback. Delegator will not be able to handle this page.', array('%callback' => $callback)), 'warning');
+ if ($callback != 'page_manager_term_view') {
+ drupal_set_message(t('Page managermodule is unable to override taxonomy/term/% because some other module already has overridden with %callback. Page managerwill not be able to handle this page.', array('%callback' => $callback)), 'warning');
}
}
@@ -104,12 +88,12 @@ function delegator_term_view_task_admin(&$form, &$form_state) {
* to run with it. If no one does, it passes through to Drupal core's
* term view, which is term_page_view().
*/
-function delegator_term_view($terms, $depth = 0, $op = 'page') {
+function page_manager_term_view($terms, $depth = 0, $op = 'page') {
// While we ordinarily should never actually get feeds through here,
// just in case
if ($op != 'feed') {
// Load my task plugin
- $task = delegator_get_task('term_view');
+ $task = page_manager_get_task('term_view');
// Load the term into a context.
ctools_include('context');
@@ -120,7 +104,7 @@ function delegator_term_view($terms, $depth = 0, $op = 'page') {
}
// Add a fake tab for 'View' so that edit tabs can be added.
- if (user_access('administer delegator')) {
+ if (user_access('administer page manager')) {
ctools_include('menu');
ctools_menu_add_tab(array(
'title' => t('View'),
@@ -159,13 +143,13 @@ function delegator_term_view($terms, $depth = 0, $op = 'page') {
* Since this is the term view and there is no UI on the arguments, we
* create dummy arguments that contain the needed data.
*/
-function delegator_term_view_get_arguments($task, $subtask_id) {
+function page_manager_term_view_get_arguments($task, $subtask_id) {
return array(
array(
'keyword' => 'term',
- 'identifier' => variable_get('delegator_term_view_type', 'multiple') == 'multiple' ? t('Term(s) being viewed') : t('Term being viewed'),
+ 'identifier' => variable_get('page_manager_term_view_type', 'multiple') == 'multiple' ? t('Term(s) being viewed') : t('Term being viewed'),
'id' => 1,
- 'name' => variable_get('delegator_term_view_type', 'multiple') == 'multiple' ? 'terms' : 'term',
+ 'name' => variable_get('page_manager_term_view_type', 'multiple') == 'multiple' ? 'terms' : 'term',
'settings' => array('input_form' => 'tid'),
'default' => '404',
),
@@ -182,27 +166,26 @@ function delegator_term_view_get_arguments($task, $subtask_id) {
/**
* Callback to get context placeholders provided by this handler.
*/
-function delegator_term_view_get_contexts($task, $subtask_id) {
- return ctools_context_get_placeholders_from_argument(delegator_term_view_get_arguments($task, $subtask_id));
+function page_manager_term_view_get_contexts($task, $subtask_id) {
+ return ctools_context_get_placeholders_from_argument(page_manager_term_view_get_arguments($task, $subtask_id));
}
/**
* Settings page for this item.
*/
-function delegator_term_view_settings() {
- $task = delegator_get_task('term_view');
- delegator_set_trail($task);
+function page_manager_term_view_settings() {
+ $task = page_manager_get_task('term_view');
$form = array();
// This passes thru because the setting can also appear on the main Panels
// settings form.
- delegator_term_view_admin_settings($form);
- $form['delegator_term_view_type'] = array(
+ page_manager_term_view_admin_settings($form);
+ $form['page_manager_term_view_type'] = array(
'#type' => 'radios',
'#title' => t('Allow multiple terms'),
'#options' => array('single' => t('Single term'), 'multiple' => t('Multiple terms')),
'#description' => t('By default, Drupal allows multiple terms as an argument by separating them with commas or plus signs. If you set this to single, that feature will be disabled.'),
- '#default_value' => variable_get('delegator_term_view_type', 'multiple'),
+ '#default_value' => variable_get('page_manager_term_view_type', 'multiple'),
);
return system_settings_form($form);
@@ -211,13 +194,13 @@ function delegator_term_view_settings() {
/**
* Provide a setting to the Panels administrative form.
*/
-function delegator_term_view_admin_settings(&$form) {
- $form['delegator_term_view_type'] = array(
+function page_manager_term_view_admin_settings(&$form) {
+ $form['page_manager_term_view_type'] = array(
'#type' => 'radios',
'#title' => t('Allow multiple terms on taxonomy/term/%term'),
'#options' => array('single' => t('Single term'), 'multiple' => t('Multiple terms')),
'#description' => t('By default, Drupal allows multiple terms as an argument by separating them with commas or plus signs. If you set this to single, that feature will be disabled.'),
- '#default_value' => variable_get('delegator_term_view_type', 'multiple'),
+ '#default_value' => variable_get('page_manager_term_view_type', 'multiple'),
);
}
diff --git a/delegator/plugins/tasks/user_view.inc b/page_manager/plugins/tasks/user_view.inc
index 83f9045..3236ac7 100644
--- a/delegator/plugins/tasks/user_view.inc
+++ b/page_manager/plugins/tasks/user_view.inc
@@ -2,43 +2,43 @@
// $Id$
/**
- * Specialized implementation of hook_delegator_tasks(). See api-task.html for
+ * Specialized implementation of hook_page_manager_task_tasks(). See api-task.html for
* more information.
*/
-function delegator_user_view_delegator_tasks() {
+function page_manager_user_view_page_manager_tasks() {
return array(
// This is a 'page' task and will fall under the page admin UI
'task type' => 'page',
- 'title' => t('User view'),
+ 'title' => t('User profile template'),
'description' => t('The user view task allows you to control which modules serve requests made to user/%. By default, the core user module will show the user account page. The first task that matches the user will be used to display the user. If no task handlers exist, or if none of the existing task handlers are configured to handle the currently requested user, then the request falls back to the default Drupal user view mechanism.'),
'admin title' => 'User view', // translated by menu system
'admin description' => 'Overrides for the built in user handler, allowing customized user output.',
'admin path' => 'user/%user',
- // Callback to add items to the delegator task administration form:
- 'task admin' => 'delegator_term_view_task_admin',
+ // Callback to add items to the page managertask administration form:
+ 'task admin' => 'page_manager_term_view_task_admin',
- 'hook menu' => 'delegator_user_view_menu',
- 'hook menu alter' => 'delegator_user_view_menu_alter',
+ 'hook menu' => 'page_manager_user_view_menu',
+ 'hook menu alter' => 'page_manager_user_view_menu_alter',
// This is task uses 'context' handlers and must implement these to give the
// handler data it needs.
'handler type' => 'context', // handler type -- misnamed
- 'get arguments' => 'delegator_user_view_get_arguments',
- 'get context placeholders' => 'delegator_user_view_get_contexts',
+ 'get arguments' => 'page_manager_user_view_get_arguments',
+ 'get context placeholders' => 'page_manager_user_view_get_contexts',
);
}
/**
- * Callback defined by delegator_user_view_delegator_tasks().
+ * Callback defined by page_manager_user_view_page_manager_tasks().
*
* Alter the user view input so that user view comes to us rather than the
* normal user view process.
*/
-function delegator_user_view_menu_alter(&$items, $task) {
+function page_manager_user_view_menu_alter(&$items, $task) {
// Override the user view handler for our purpose.
- if ($items['user/%user_uid_optional']['page callback'] == 'user_view' || variable_get('delegator_override_anyway', FALSE)) {
- $items['user/%user_uid_optional']['page callback'] = 'delegator_user_view';
+ if ($items['user/%user_uid_optional']['page callback'] == 'user_view' || variable_get('page_manager_override_anyway', FALSE)) {
+ $items['user/%user_uid_optional']['page callback'] = 'page_manager_user_view';
$items['user/%user_uid_optional']['file path'] = $task['path'];
$items['user/%user_uid_optional']['file'] = $task['file'];
}
@@ -47,10 +47,10 @@ function delegator_user_view_menu_alter(&$items, $task) {
/**
* Warn if we are unable to override the taxonomy term page.
*/
-function delegator_user_view_task_admin(&$form, &$form_state) {
+function page_manager_user_view_task_admin(&$form, &$form_state) {
$callback = db_result(db_query("SELECT page_callback FROM {menu_router} WHERE path = 'user/%'"));
- if ($callback != 'delegator_user_view') {
- drupal_set_message(t('Delegator module is unable to override user/%user because some other module already has overridden with %callback. Delegator will not be able to handle this page.', array('%callback' => $callback)), 'warning');
+ if ($callback != 'page_manager_user_view') {
+ drupal_set_message(t('Page managermodule is unable to override user/%user because some other module already has overridden with %callback. Page managerwill not be able to handle this page.', array('%callback' => $callback)), 'warning');
}
}
@@ -61,9 +61,9 @@ function delegator_user_view_task_admin(&$form, &$form_state) {
* to run with it. If no one does, it passes through to Drupal core's
* user view, which is user_page_view().
*/
-function delegator_user_view($account) {
+function page_manager_user_view($account) {
// Load my task plugin:
- $task = delegator_get_task('user_view');
+ $task = page_manager_get_task('user_view');
// Load the account into a context.
ctools_include('context');
@@ -86,7 +86,7 @@ function delegator_user_view($account) {
* Since this is the node view and there is no UI on the arguments, we
* create dummy arguments that contain the needed data.
*/
-function delegator_user_view_get_arguments($task, $subtask_id) {
+function page_manager_user_view_get_arguments($task, $subtask_id) {
return array(
array(
'keyword' => 'user',
@@ -101,7 +101,7 @@ function delegator_user_view_get_arguments($task, $subtask_id) {
/**
* Callback to get context placeholders provided by this handler.
*/
-function delegator_user_view_get_contexts($task, $subtask_id) {
- return ctools_context_get_placeholders_from_argument(delegator_user_view_get_arguments($task, $subtask_id));
+function page_manager_user_view_get_contexts($task, $subtask_id) {
+ return ctools_context_get_placeholders_from_argument(page_manager_user_view_get_arguments($task, $subtask_id));
}
diff --git a/page_manager/theme/page-manager-edit-page.tpl.php b/page_manager/theme/page-manager-edit-page.tpl.php
new file mode 100644
index 0000000..65b0667
--- /dev/null
+++ b/page_manager/theme/page-manager-edit-page.tpl.php
@@ -0,0 +1,49 @@
+<?php
+// $Id$
+/**
+ * @file
+ * Template for the page manager page editor.
+ *
+ * Variables available:
+ * -
+ *
+ * For javascript purposes the id must not change.
+ */
+?>
+<div id="page-manager-edit">
+ <?php print $locked; ?>
+ <div class="page-manager-wrapper">
+ <?php if (isset($operations['primary'])): ?>
+ <div class="primary-actions clear-block actions">
+ <?php print $operations['primary']; ?>
+ </div>
+ <?php endif; ?>
+ <div class="page-manager-tabs clear-block">
+ <div class="page-manager-edit-operations">
+ <div class="inside">
+ <?php print $operations['nav']; ?>
+ </div>
+ </div>
+ <div class="page-manager-ajax-pad">
+ <div class="inside">
+ <div class="content-header">
+ <div class="content-title">
+ <?php print $changed; ?>
+ <?php print $content['title']; ?>
+ </div>
+ <?php if (isset($operations['secondary'])): ?>
+ <div class="secondary-actions clear-block actions">
+ <?php print $operations['secondary']; ?>
+ </div>
+ <?php endif; ?>
+ </div>
+
+ <div class="content-content">
+ <?php print $content['content']; ?>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <?php print $save; ?>
+</div> \ No newline at end of file
diff --git a/page_manager/theme/page_manager.theme.inc b/page_manager/theme/page_manager.theme.inc
new file mode 100644
index 0000000..5768564
--- /dev/null
+++ b/page_manager/theme/page_manager.theme.inc
@@ -0,0 +1,137 @@
+<?php
+// $Id
+
+/**
+ * @file
+ * Preprocess functions for page manager editing templates.
+ */
+
+/**
+ * Preprocess the page manager edit page.
+ */
+function template_preprocess_page_manager_edit_page(&$vars) {
+ ctools_include('ajax');
+ ctools_include('modal');
+ ctools_modal_add_js();
+ ctools_add_js('dependent');
+
+ drupal_add_css(drupal_get_path('module', 'page_manager') . '/css/page-manager.css');
+ ctools_add_css('wizard');
+
+ $task = $vars['page']->task;
+ $task_handler_plugins = page_manager_get_task_handler_plugins($task);
+ foreach ($task_handler_plugins as $id => $plugin) {
+ if (isset($plugin['admin css'])) {
+ foreach ($plugin['admin css'] as $file) {
+ drupal_add_css($file);
+ }
+ }
+ if (isset($plugin['admin js'])) {
+ foreach ($plugin['admin js'] as $file) {
+ drupal_add_js($file);
+ }
+ }
+ }
+
+ $page = &$vars['page'];
+
+ $vars['locked'] = '';
+ $vars['changed'] = '';
+ if (!empty($page->locked)) {
+ $vars['locked'] = theme('page_manager_lock', $page);
+ $vars['changed'] = theme('page_manager_changed', t('Locked'), t('This page is being edited by another user and you cannot make changes to it.'));
+ }
+ else if (!empty($page->new)) {
+ $vars['changed'] = theme('page_manager_changed', t('New'), t('This page is newly created and has not yet been saved to the database. It will not be available until you save it.'));
+ }
+ else if (!empty($page->changed)) {
+ $vars['changed'] = theme('page_manager_changed', t('Changed'), t('This page has been modified, but these modifications are not yet live. While modifying this page, it is locked from modification by other users.'));
+ }
+
+ $form_state = array(
+ 'page' => &$vars['page'],
+ );
+
+ $active = $vars['content']['active'];
+ if ($active[0] == 'handlers' && isset($vars['operations'][$active[1]])) {
+ $vars['operations']['secondary'] = $vars['operations'][$active[1]];
+ }
+}
+
+/**
+ * Remove some items from the form so they don't submit.
+ */
+function theme_page_manager_list_pages_form($form) {
+ // Don't render these:
+ unset($form['form_id']);
+ unset($form['form_build_id']);
+ unset($form['form_token']);
+ return drupal_render($form);
+}
+
+/**
+ * Turn the rearrange form into a table with tablesorting on.
+ */
+function theme_page_manager_handler_rearrange($form) {
+ // Assemble the data for a table from everything in $form['handlers']
+ foreach (element_children($form['handlers']) as $id) {
+ // provide a reference shortcut.
+ $element = &$form['handlers'][$id];
+ if (isset($element['title'])) {
+ $row = array();
+
+ $row[] = array(
+ 'data' => drupal_render($element['title']),
+ 'class' => 'page-manager-handler',
+ );
+
+ $element['weight']['#attributes']['class'] = 'weight';
+ $row[] = drupal_render($element['weight']);
+
+ $rows[] = array('data' => $row, 'class' => 'draggable', 'id' => 'page-manager-row-' . $id);
+ }
+ }
+
+ if (empty($rows)) {
+ $rows[] = array(array('data' => t('No task handlers are defined for this task.'), 'colspan' => '5'));
+ }
+
+ $header = array(
+ array('data' => t('Variant'), 'class' => 'page-manager-handler'),
+ t('Weight'),
+ );
+
+ drupal_add_tabledrag('page-manager-arrange-handlers', 'order', 'sibling', 'weight');
+
+ $output = theme('table', $header, $rows, array('id' => 'page-manager-arrange-handlers'));
+ $output .= drupal_render($form);
+ return $output;
+}
+
+/**
+ * Draw the "this task is locked from editing" box.
+ */
+function theme_page_manager_lock($page) {
+ $account = user_load($page->locked->uid);
+ $name = theme('username', $account);
+ $lock_age = format_interval(time() - $page->locked->updated);
+ $break = url('admin/build/pages/nojs/operation/' . $page->task_name . '/actions/break-lock/');
+
+ ctools_add_css('ctools');
+ $output = '<div class="ctools-locked">';
+ $output .= t('This page is being edited by user !user, and is therefore locked from editing by others. This lock is !age old. Click here to <a href="!break">break this lock</a>.', array('!user' => $name, '!age' => $lock_age, '!break' => $break));
+ $output .= '</div>';
+ return $output;
+}
+
+/**
+ * Draw the "you have unsaved changes and this task is locked." message.
+ */
+function theme_page_manager_changed($text, $description) {
+ ctools_add_css('ctools');
+ $output = '<div class="page-manager-changed" title="' . $description . '">';
+ $output .= $text;
+ $output .= '</div>';
+
+ return $output;
+}