From fe1482419dd6f082645bbb368f5aefd56b8ce5c9 Mon Sep 17 00:00:00 2001 From: Earl Miles <merlin@logrus.com> Date: Wed, 17 Dec 2008 08:29:21 +0000 Subject: [PATCH] Checkpoint checkin. Partial implementation of page task with generic subtasks, plus revamp of arguments, new access plugins (stolen from Views), updates to the wizard to make it fully compatible with the modal and some minor improvements to make it more flexible. --- ctools.module | 16 + delegator/delegator.admin.inc | 4 +- delegator/delegator.install | 8 - delegator/delegator.module | 14 +- delegator/help/api-task.html | 1 + delegator/js/task-handlers.js | 14 +- delegator/plugins/tasks/page.admin.inc | 899 +++++++++++++++++++++++++ delegator/plugins/tasks/page.inc | 138 ++++ help/context-access.html | 15 + help/context-arguments.html | 5 +- help/wizard.html | 16 +- includes/context.inc | 33 +- includes/dependent.inc | 6 +- includes/wizard.inc | 85 ++- js/modal.js | 14 +- plugins/access/node.inc | 0 plugins/access/perm.inc | 53 ++ plugins/access/role.inc | 54 ++ plugins/arguments/nid.inc | 50 +- plugins/arguments/node_add.inc | 41 +- plugins/arguments/node_edit.inc | 41 +- plugins/arguments/term.inc | 49 +- plugins/arguments/uid.inc | 2 +- plugins/arguments/vid.inc | 2 +- 24 files changed, 1333 insertions(+), 227 deletions(-) create mode 100644 delegator/plugins/tasks/page.admin.inc create mode 100644 delegator/plugins/tasks/page.inc create mode 100644 help/context-access.html create mode 100644 plugins/access/node.inc create mode 100644 plugins/access/perm.inc create mode 100644 plugins/access/role.inc diff --git a/ctools.module b/ctools.module index f0ce9a31..dc461734 100644 --- a/ctools.module +++ b/ctools.module @@ -90,3 +90,19 @@ function ctools_ctools_plugin_directory($module, $plugin) { return 'plugins/' . $plugin; } } + +/** + * Get a list of roles in the system. + */ +function ctools_get_roles() { + static $roles = NULL; + if (!isset($roles)) { + $roles = array(); + $result = db_query("SELECT r.rid, r.name FROM {role} r ORDER BY r.name"); + while ($obj = db_fetch_object($result)) { + $roles[$obj->rid] = $obj->name; + } + } + + return $roles; +} diff --git a/delegator/delegator.admin.inc b/delegator/delegator.admin.inc index 37546c71..0a0c82da 100644 --- a/delegator/delegator.admin.inc +++ b/delegator/delegator.admin.inc @@ -255,7 +255,7 @@ function delegator_admin_list_form(&$form_state) { // Skip deleted items. $handler = delegator_admin_find_handler($id, $form_state['cache'], $task_handlers); - if ($info['changed'] & DGA_CHANGED_DELETED && !($handler->export_type & EXPORT_IN_CODE)) { + if (!$handler || ($info['changed'] & DGA_CHANGED_DELETED && !($handler->export_type & EXPORT_IN_CODE))) { $form['#changed'] = TRUE; continue; } @@ -970,7 +970,7 @@ function delegator_administer_task_handler_add($task_name, $name, $form_id) { } $task = delegator_get_task($task_id); - $subtask = delegator_get_subtask($task, $subtask_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 diff --git a/delegator/delegator.install b/delegator/delegator.install index 9d031b9e..0b66211d 100644 --- a/delegator/delegator.install +++ b/delegator/delegator.install @@ -128,14 +128,6 @@ function delegator_schema_1() { 'default' => '', 'serialize' => TRUE, ), - 'parent_menu' => array( - 'type' => 'text', - 'size' => 'big', - 'description' => t('Serialized configuration of Drupal parent menu visibility settings for this item.'), - 'not null' => TRUE, - 'default' => '', - 'serialize' => TRUE, - ), 'arguments' => array( 'type' => 'text', 'size' => 'big', diff --git a/delegator/delegator.module b/delegator/delegator.module index 942c7b6c..8f881315 100644 --- a/delegator/delegator.module +++ b/delegator/delegator.module @@ -29,7 +29,7 @@ function delegator_perm() { * Implementation of hook_theme(). */ function delegator_theme() { - return array( + $items = array( 'delegator_admin_list_form' => array( 'arguments' => array('form' => NULL), 'file' => 'delegator.admin.inc', @@ -39,6 +39,18 @@ function delegator_theme() { '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; } /** diff --git a/delegator/help/api-task.html b/delegator/help/api-task.html index 759a4f8e..9eb7ed40 100644 --- a/delegator/help/api-task.html +++ b/delegator/help/api-task.html @@ -4,6 +4,7 @@ task definition: 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 diff --git a/delegator/js/task-handlers.js b/delegator/js/task-handlers.js index 4b666a32..99e3b2fd 100644 --- a/delegator/js/task-handlers.js +++ b/delegator/js/task-handlers.js @@ -68,11 +68,13 @@ Drupal.Delegator.CollapsibleCallbackAfterToggle = function($container, handle, c }; $(document).ready(function() { - Drupal.CTools.CollapsibleCallbacks.push(Drupal.Delegator.CollapsibleCallback); - Drupal.CTools.CollapsibleCallbacksAfterToggle.push(Drupal.Delegator.CollapsibleCallbackAfterToggle); + 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'); - }); + // 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/tasks/page.admin.inc b/delegator/plugins/tasks/page.admin.inc new file mode 100644 index 00000000..1f03414d --- /dev/null +++ b/delegator/plugins/tasks/page.admin.inc @@ -0,0 +1,899 @@ +<?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. + */ + +/** + * Get the cached changes to a given task handler. + */ +function delegator_page_get_page_cache($name) { + ctools_include('object-cache'); + return ctools_object_cache_get('delegator_page', $name); +} + +/** + * Store changes to a task handler in the object cache. + */ +function delegator_page_set_page_cache($page) { + $name = isset($page->new) || !isset($page->name) ? '::new' : $page->name; + ctools_include('object-cache'); + $cache = ctools_object_cache_set('delegator_page', $name, $page); +} + +/** + * Remove an item from the object cache. + */ +function delegator_page_clear_page_cache($name) { + ctools_include('object-cache'); + ctools_object_cache_clear('delegator_page', $name); +} + +/** + * 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_arguments($path) { + $arguments = array(); + $bits = explode('/', $path); + foreach ($bits as $position => $bit) { + if ($bit && $bit[0] == '%') { + // special handling for duplicate path items and substr to remove the % + $arguments[substr($bit, 1)] = isset($arguments[$bit]) ? -1 : $position; + } + } + + return $arguments; +} + +/** + * Page callback to add a subtask. + */ +function delegator_page_add_subtask($step = NULL) { + $form_info = array( + 'id' => 'delegator_page', + 'path' => 'admin/build/delegator/page/add/%step', + 'return path' => 'admin/build/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', + 'cancel callback' => 'delegator_page_add_subtask_cancel', + 'order' => array( + 'basic' => t('Basic settings'), + 'argument' => t('Argument settings'), + 'access' => t('Access type'), + 'access-settings' => t('Access settings'), + 'menu' => t('Menu settings'), + ), + 'forms' => array( + 'basic' => array( + 'form id' => 'delegator_page_form_basic' + ), + 'access' => array( + 'form id' => 'delegator_page_form_access' + ), + 'access-settings' => array( + 'form id' => 'delegator_page_form_access_settings' + ), + 'menu' => array( + 'form id' => 'delegator_page_form_menu' + ), + 'argument' => array( + 'form id' => 'delegator_page_form_argument' + ), + ), + ); + + // We load the task to make sure our .inc file is loaded. + $task = delegator_get_task('page'); + + // 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 = 'basic'; + $page = delegator_page_new(); + $page->new = TRUE; + delegator_page_set_page_cache($page); + } + + ctools_include('wizard'); + $form_state = array( + 'cache name' => '::new', + 'page' => $page, + ); + $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 delegator_page_add_subtask_finish(&$form_state) { + $page = &$form_state['page']; + // Ensure $page->arguments contains only real arguments: + $arguments = delegator_page_get_arguments($page->path); + $args = array(); + foreach ($arguments as $keyword => $position) { + if (isset($page->arguments[$keyword])) { + $args[$keyword] = $page->arguments[$keyword]; + } + else { + $args[$keyword] = array('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(); + + // Redirect to the new page's task handler editor. + $form_state['redirect'] = 'admin/build/delegator/page-' . $page->name; +} + +/** + * 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']; + + // @todo do not let the user change this name during the edit process. + $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.'), + '#default_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 placeholders in the path by using %, or named placeholders by using %name. For example: "node/%node/foo", "forum/%forum" or "dashboard/%". Named placeholders can have more information attached to them 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 name is properly formed. + $args = delegator_page_get_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'; + } + + // Ensure name is unique. + // @todo + + // Ensure path is unused. + // @todo +} + +/** + * Store the values from the basic settings form. + */ +function delegator_page_form_basic_submit(&$form, &$form_state) { + $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')), + ); + + $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' => (VERSION < 6.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) { + $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'); + $page = &$form_state['page']; + if (!isset($page->access['type'])) { + $page->access['type'] = ''; + } + + $contexts = array(); + // Load contexts based on argument data: + if ($page->arguments) { + $contexts = ctools_context_get_placeholders_from_argument($page->arguments); + } + + $plugins = ctools_get_access_plugins(); + $options = array(); + foreach ($plugins as $id => $plugin) { + if (!empty($plugin['required context']) && !ctools_context_filter($contexts, $plugin['required context'])) { + continue; + } + $options[$id] = $plugin['title']; + } + + asort($options); + $options = array('' => t('No access control')) + $options; + + $form['type'] = array( + '#type' => 'radios', + '#options' => $options, + '#default_value' => $page->access['type'], + ); +} + +/** + * Submit handler to deal with access control changes. + */ +function delegator_page_form_access_submit(&$form, &$form_state) { + // First, see if the new mechanism is different from the existing mechanism. + $access = &$form_state['page']->access; + $type = $form_state['values']['type']; + + ctools_include('context'); + $plugin = $type ? ctools_get_access_plugin($type) : array(); + if (!isset($access['type']) || $access['type'] != $type) { + if (isset($plugin['default'])) { + if (is_array($plugin['default'])) { + $access['settings'] = $plugin['default']; + } + else if (function_exists($plugin['default'])) { + $access['settings'] = $plugin['default'](); + } + else { + $access['settings'] = array(); + } + } + } + + $access['type'] = $type; + + // If there's no settings form, skip the settings form. + if (!ctools_plugin_get_function($plugin, 'settings form')) { + $form_state['clicked_button']['#next'] = 'menu'; + } +} + +/** + * Form to change access settings. + */ +function delegator_page_form_access_settings(&$form, &$form_state) { + $access = &$form_state['page']->access; + + if (!isset($access['type'])) { + // This should be impossible and thus never seen. + $form['error'] = array('#value' => t('Error: missing argument.')); + return; + } + + ctools_include('context'); + $plugin = ctools_get_access_plugin($access['type']); + + $form['settings'] = array( + '#tree' => TRUE, + ); + + 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, $access['settings']); + } + + $form_state['plugin'] = $plugin; +} + +/** + * Validate handler for argument settings. + */ +function delegator_page_form_access_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_form_access_settings_submit(&$form, &$form_state) { + if ($function = ctools_plugin_get_function($form_state['plugin'], 'settings form submit')) { + $function($form, $form_state); + } + + $form_state['page']->access['settings'] = $form_state['values']['settings']; +} + +/** + * 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_arguments($path); + + $form['table'] = array( + '#theme' => 'delegator_page_form_argument_table', + '#delegator-path' => $path, + ); + + $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'); + + if ($conf) { + 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 (isset($plugin) && ctools_plugin_get_function($plugin, 'settings form')) { + // 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'] . ' ' . 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_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) { + $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'); + $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'](); + } + } + + // Set the new argument in a temporary location. + $page->temporary_arguments[$keyword] = array( + 'name' => $argument, + 'settings' => $settings, + ); + + // Does the new type actually have settings? If not, we are actually + // finished here. + if (!ctools_plugin_get_function($plugin, 'settings form')) { + $form_state['clicked_button']['#wizard type'] = 'finish'; + } +} + +/** + * 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.'), + '#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 ($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]['settings'] = $form_state['values']['settings']; +} diff --git a/delegator/plugins/tasks/page.inc b/delegator/plugins/tasks/page.inc new file mode 100644 index 00000000..e8ca19e4 --- /dev/null +++ b/delegator/plugins/tasks/page.inc @@ -0,0 +1,138 @@ +<?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. + */ + +/** + * Specialized implementation of hook_delegator_tasks(). See api-task.html for + * more information. + */ +function delegator_page_delegator_tasks() { + return array( + 'page' => array( + 'title' => t('Page'), + 'type' => 'node_view', + 'subtasks' => TRUE, + 'subtasks callback' => 'delegator_page_subtasks', + 'hook menu' => 'delegator_page_menu', + 'hook theme' => 'delegator_page_theme', + ), + ); +} + +/** + * Return a list of all subtasks. + */ +function delegator_page_subtasks($task) { + $subtasks = delegator_page_load_all(); + $return = array(); + foreach ($subtasks as $name => $subtask) { + $return[$name] = array( + 'admin title' => $subtask->admin_title, + 'admin description' => t('TODO'), + 'subtask' => $subtask, + ); + } + + return $return; +} + +/** + * 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/delegator/page/add'] = array( + 'title' => 'Add page', + 'description' => 'Add a delegator page subtask.', + 'page callback' => 'delegator_page_add_subtask', + ) + $base; + + + // AJAX callbacks for argument love. + $items['admin/build/delegator/argument'] = array( + 'page callback' => 'delegator_page_subtask_argument_ajax', + ) + $base; +} + +/** + * Delegated implementation of hook_theme(). + */ +function delegator_page_theme(&$items, $task) { + $items['delegator_page_form_argument_table'] = array( + 'arguments' => array('form' => NULL), + 'file' => 'page.admin.inc', + 'path' => drupal_get_path('module', 'delegator') . '/plugins/tasks', + ); +} + +// -------------------------------------------------------------------------- +// 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() { + ctools_include('export'); + return ctools_export_load_object('delegator_pages'); +} + +/** + * Write a page subtask to the database. + */ +function delegator_page_save(&$subtask) { + $update = (isset($subtask->did)) ? array('pid') : array(); + drupal_write_record('delegator_pages', $subtask, $update); + return $subtask; +} + +/** + * Remove a page subtask. + */ +function delegator_page_delete($subtask) { + db_query("DELETE FROM {delegator_pages} WHERE name = '%s'", $subtask->name); +} + +/** + * Export a page subtask. + */ +function delegator_page_export($subtask, $indent = '') { + ctools_include('export'); + $output = ctools_export_object('delegator_pages', $subtask, 'page', $indent); + return $output; +} + diff --git a/help/context-access.html b/help/context-access.html new file mode 100644 index 00000000..a0ae874f --- /dev/null +++ b/help/context-access.html @@ -0,0 +1,15 @@ +<!-- $Id$ --> + +access plugins allow context based access control to pages. + + + 'title' => Title of the plugin + 'description' => Description of the plugin + 'callback' => callback to see if there is access is available. params: $conf, $contexts, $account + 'required context' => zero or more required contexts for this access plugin + 'default' => an array of defaults or a callback giving defaults + 'settings form' => settings form. params: &$form, &$form_state, $conf + settings form validate + settings form submit + +warning: Your settings array will be stored IN THE MENU SYSTEM to reduce loads, so be TRIM. \ No newline at end of file diff --git a/help/context-arguments.html b/help/context-arguments.html index a5ff5ebd..fca3a017 100644 --- a/help/context-arguments.html +++ b/help/context-arguments.html @@ -7,6 +7,9 @@ string as though it came from a URL element. 'description' => Description 'keyword' => Default keyword for the context 'context' => Callback to create the context. Params: $arg = NULL, $conf = NULL, $empty = FALSE - 'settings form' => params: $conf + + 'default' => either an array of default settings or a string which is a callback or null to not use. + + 'settings form' => params: $form, $form_state, $conf -- gets the whole form. Should put anything it wants to keep automatically in $form['settings'] 'settings form validate' => params: $form, $form_state 'settings form submit' => params: $form, $form_state diff --git a/help/wizard.html b/help/wizard.html index ebe7a709..02a503ce 100644 --- a/help/wizard.html +++ b/help/wizard.html @@ -3,11 +3,24 @@ form_info => array( 'id' => An id for this multistep. Will be used for things like trail theming. 'path' => /path/to/form/%step, - 'return path' => Where to go when exiting the wizard. Required [maybe] + 'return path' => Where to go when exiting the wizard. Required if 'return' is shown. 'show trail' => (default false), 'show back' => (default false) -- show a back button 'show return' => (default false) show a return button + 'show cancel' => (default false) show a cancel button (only valid if AJAX on, otherwise use cancel path) + + // text overrides + 'back text' => defaults to t('Back'), + 'next text' => defaults to t('Continue'), + 'return text' => defaults to t('Update and return'), + 'finish text' => defaults to t('Finish'), + 'cancel text' => defaults to t('Cancel'), + + // ajax tools + 'ajax' => TRUE, // turn on ajax capabilities + 'modal' => TRUE, // put the wizard in the modal tool. The modal must already be open and called from an ajax button for this to work. + 'ajax render' => A callback to display the rendered form via ajax. Params: &$form_state, $output // callbacks 'finish callback', @@ -23,6 +36,7 @@ form_info => array( 'form_id' => array( 'include' => .., 'form id' => .., + 'title' => .. optional page title to set on form. Required on modals unless $form_state['title'] will be set. ), ), diff --git a/includes/context.inc b/includes/context.inc index 1e5c34a1..3a7152fb 100644 --- a/includes/context.inc +++ b/includes/context.inc @@ -446,11 +446,11 @@ function ctools_get_arguments() { function ctools_context_get_context_from_argument($argument, $arg, $empty = FALSE) { ctools_include('plugins'); if ($function = ctools_plugin_load_function('ctools', 'arguments', $argument['name'], 'context')) { - if (!isset($argument['argument_settings'])) { - $argument['argument_settings'] = array(); + if (!isset($argument['settings'])) { + $argument['settings'] = array(); } - $context = $function($arg, $argument['argument_settings'], $empty); + $context = $function($arg, $argument['settings'], $empty); if (is_object($context)) { $context->identifier = $argument['identifier']; @@ -826,3 +826,30 @@ function ctools_context_get_form($contexts) { } } +// --------------------------------------------------------------------------- +// Functions related to loading access control plugins + +/** + * Fetch metadata on a specific access control plugin. + * + * @param $name + * Name of a plugin. + * + * @return + * An array with information about the requested access control plugin. + */ +function ctools_get_access_plugin($name) { + ctools_include('plugins'); + return ctools_get_plugins('ctools', 'access', $name); +} + +/** + * Fetch metadata for all access control plugins. + * + * @return + * An array of arrays with information about all available access control plugins. + */ +function ctools_get_access_plugins() { + ctools_include('plugins'); + return ctools_get_plugins('ctools', 'access'); +} diff --git a/includes/dependent.inc b/includes/dependent.inc index 2a78b149..39c3f10c 100644 --- a/includes/dependent.inc +++ b/includes/dependent.inc @@ -13,8 +13,10 @@ * any of the listed values are selected, the item will be visible. Otherwise, * the item will be visible. * - * If dependent upon multiple items, all of the items must contain one of the - * acceptable values. + * If dependent upon multiple items, use #dependency_count = X to set the + * number of items that must be set in order to make this item visible. This + * defaults to 1. If set to 2, then at least 2 form items in the list must + * have their items set for the item to become visible. * * Checkboxes don't have their own id, so you need to add one in a div * around the checkboxes via #prefix and #suffix. You actually need to add TWO diff --git a/includes/wizard.inc b/includes/wizard.inc index 6a1f6eb4..1a506534 100644 --- a/includes/wizard.inc +++ b/includes/wizard.inc @@ -79,36 +79,66 @@ function ctools_wizard_multistep_form($form_info, $step, &$form_state) { ctools_include('form'); $output = ctools_build_form($info['form id'], $form_state); - if (!empty($form_state['ajax render']) && empty($form_state['executed'])) { - // Any include files should already be included by this point: - return $form_state['ajax render']($form_state, $output); - } + if (empty($form_state['executed'])) { + if (!empty($form_state['ajax render'])) { + // Any include files should already be included by this point: + return $form_state['ajax render']($form_state, $output); + } - if (!empty($form_state['executed'])) { + // Automatically use the modal tool if set to true. + if (!empty($form_state['modal'])) { + ctools_include('modal'); + $title = isset($form_state['title']) ? $form_state['title'] : $info['title']; + $form_state['commands'][] = ctools_modal_command_display($title, $output); + } + } + else { // We use the plugins get_function format because it's powerful and // not limited to just functions. ctools_include('plugins'); if (isset($form_state['clicked_button']['#wizard type'])) { $type = $form_state['clicked_button']['#wizard type']; - // If the finish button was clicked, call the finish callback. + // If we have a callback depending upon the type of button that was + // clicked, call it. if ($function = ctools_plugin_get_function($form_info, "$type callback")) { $function($form_state); } + + // If the modal is in use, some special code for it: + if (!empty($form_state['modal'])) { + if ($type != 'next') { + // Automatically dismiss the modal if we're not going to another form. + ctools_include('modal'); + $form_state['commands'][] = ctools_modal_command_dismiss(); + } + } } if (empty($form_state['ajax'])) { // redirect, if one is set. return drupal_redirect_form(array(), $form_state['redirect']); } - else if (isset($form_state['commands'])) { - // If the callbacks wanted to do something besides go to the next form, - // it needs to have set $form_state['commands'] with something that can - // be rendered. - return ctools_ajax_render($form_state['commands']); + else if (isset($form_state['ajax next'])) { + // Clear a few items off the form state so we don't double post: + $next = $form_state['ajax next']; + unset($form_state['ajax next']); + unset($form_state['executed']); + unset($form_state['post']); + unset($form_state['next']); + return ctools_wizard_multistep_form($form_info, $next, $form_state); } + // If the callbacks wanted to do something besides go to the next form, + // it needs to have set $form_state['commands'] with something that can + // be rendered. } + // Render ajax commands if we have any. + if (isset($form_state['ajax']) && isset($form_state['commands'])) { + return ctools_ajax_render($form_state['commands']); + } + + // Otherwise, return the output. return $output; } @@ -167,7 +197,7 @@ function ctools_wizard_wrapper(&$form, &$form_state) { if (!empty($form_info['show back']) && isset($form_state['previous'])) { $form['buttons']['previous'] = array( '#type' => 'submit', - '#value' => t('Back'), + '#value' => isset($form_info['back text']) ? $form_info['back text'] : t('Back'), '#next' => $form_state['previous'], '#wizard type' => 'next', '#weight' => -2000, @@ -180,7 +210,7 @@ function ctools_wizard_wrapper(&$form, &$form_state) { if (isset($form_state['next'])) { $form['buttons']['next'] = array( '#type' => 'submit', - '#value' => t('Continue'), + '#value' => isset($form_info['next text']) ? $form_info['next text'] : t('Continue'), '#next' => $form_state['next'], '#wizard type' => 'next', '#weight' => -1000, @@ -198,23 +228,23 @@ function ctools_wizard_wrapper(&$form, &$form_state) { if (!empty($form_info['show return']) && !empty($form_state['next'])) { $form['buttons']['return'] = array( '#type' => 'submit', - '#value' => t('Update and return'), + '#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' => t('Finish'), + '#value' => isset($form_info['finish text']) ? $form_info['finish text'] : t('Finish'), '#wizard type' => 'finish', ); } // If we are allowed to cancel, place a cancel button. - if (isset($form_info['cancel path'])) { + if (isset($form_info['cancel path']) || !empty($form_info['show cancel'])) { $form['buttons']['cancel'] = array( '#type' => 'submit', - '#value' => t('Cancel'), + '#value' => isset($form_info['cancel text']) ? $form_info['cancel text'] : t('Cancel'), '#submit' => array('ctools_wizard_cancel'), '#wizard type' => 'cancel', ); @@ -230,6 +260,10 @@ function ctools_wizard_wrapper(&$form, &$form_state) { $form['#submit'][] = $info['form id'] . '_submit'; } $form['#submit'][] = 'ctools_wizard_submit'; + + if (!empty($form_state['modal'])) { + $form['#action'] = url(ctools_wizard_get_path($form_state['form_info'], $form_state['step'])); + } } /** @@ -238,17 +272,18 @@ function ctools_wizard_wrapper(&$form, &$form_state) { function ctools_wizard_submit(&$form, &$form_state) { if (isset($form_state['clicked_button']['#wizard type'])) { $type = $form_state['clicked_button']['#wizard type']; - if ($type == 'return' || $type == 'finish') { - if (empty($form_state['ajax']) && isset($form_state['form_info']['return path'])) { - // Do we need to do something here or just let it go? - $form_state['redirect'] = $form_state['form_info']['return path']; + + // if AJAX enabled, we proceed slightly differently here. + if (!empty($form_state['ajax'])) { + if ($type == 'next') { + $form_state['ajax next'] = $form_state['clicked_button']['#next']; } - // Calling functions using AJAX need to to provide commands - // for the 'finished' and 'cancel' operations. } else { - if (!empty($form_state['ajax'])) { - $form_state['ajax next'] = $form_state['clicked_button']['#next']; + if ($type == 'return' || $type == 'finish') { + if (isset($form_state['form_info']['return path'])) { + $form_state['redirect'] = $form_state['form_info']['return path']; + } } else { $form_state['redirect'] = ctools_wizard_get_path($form_state['form_info'], $form_state['clicked_button']['#next']); diff --git a/js/modal.js b/js/modal.js index a9ad9b50..ae6f7569 100644 --- a/js/modal.js +++ b/js/modal.js @@ -127,11 +127,19 @@ Drupal.behaviors.CToolsModal = function(context) { .click(Drupal.CTools.Modal.clickAjaxButton); if ($(context).attr('id') == 'modal-content') { - console.log($('form', context)); // Bind submit links in the modal form. - $('form', context) + $('form:not(.ctools-use-modal-processed)', context) .addClass('ctools-use-modal-processed') - .submit(Drupal.CTools.Modal.submitAjaxForm); + .submit(Drupal.CTools.Modal.submitAjaxForm) + .append('<input type="hidden" name="op" value=""'); + // add click handlers so that we can tell which button was clicked, + // because the AJAX submit does not set the values properly. + $('input[type="submit"]:not(.ctools-use-modal-processed)', context) + .addClass('ctools-use-modal-processed') + .click(function() { + var name = $(this).attr('name'); + $('input[name="' + name + '"]', context).val($(this).val()); + }); } }; diff --git a/plugins/access/node.inc b/plugins/access/node.inc new file mode 100644 index 00000000..e69de29b diff --git a/plugins/access/perm.inc b/plugins/access/perm.inc new file mode 100644 index 00000000..8949add8 --- /dev/null +++ b/plugins/access/perm.inc @@ -0,0 +1,53 @@ +<?php +// $Id$ + +/** + * @file + * + * Plugin to provide an argument handler for a node id + */ + +/** + * Implementation of specially named hook_ctools_arguments(). + */ +function ctools_perm_ctools_access() { + $args['perm'] = array( + 'title' => t("By permission string"), + 'description' => t('Control access by permission string.'), + 'callback' => 'ctools_perm_ctools_access_check', + 'default' => array('perm' => 'access content'), + 'settings form' => 'ctools_perm_ctools_access_settings', + ); + + return $args; +} + +/** + * Settings form for the 'by perm' access plugin + */ +function ctools_perm_ctools_access_settings(&$form, &$form_state, $conf) { + $perms = array(); + // Get list of permissions + foreach (module_list(FALSE, FALSE, TRUE) as $module) { + // By keeping them keyed by module we can use optgroups with the + // 'select' type. + if ($permissions = module_invoke($module, 'perm')) { + $perms[$module] = drupal_map_assoc($permissions); + } + } + + $form['settings']['perm'] = array( + '#type' => 'select', + '#options' => $perms, + '#title' => t('Permission'), + '#default_value' => $conf['perm'], + '#description' => t('Only users with the selected permission flag will be able to access this.'), + ); +} + +/** + * Check for access. + */ +function ctools_perm_ctools_access_check($conf, $contexts, $account) { + return user_access($conf['perm'], $account); +} \ No newline at end of file diff --git a/plugins/access/role.inc b/plugins/access/role.inc new file mode 100644 index 00000000..e980ed58 --- /dev/null +++ b/plugins/access/role.inc @@ -0,0 +1,54 @@ +<?php +// $Id$ + +/** + * @file + * + * Plugin to provide an argument handler for a node id + */ + +/** + * Implementation of specially named hook_ctools_arguments(). + */ +function ctools_role_ctools_access() { + $args['role'] = array( + 'title' => t("By role"), + 'description' => t('Control access by role.'), + 'callback' => 'ctools_role_ctools_access_check', + 'default' => array('rids' => array()), + 'settings form' => 'ctools_role_ctools_access_settings', + 'settings form submit' => 'ctools_role_ctools_access_settings_submit', + + ); + + return $args; +} + +/** + * Settings form for the 'by role' access plugin + */ +function ctools_role_ctools_access_settings(&$form, &$form_state, $conf) { + $form['settings']['rids'] = array( + '#type' => 'checkboxes', + '#title' => t('Role'), + '#default_value' => $conf['rids'], + '#options' => ctools_get_roles(), + '#description' => t('Only the checked roles will be granted access.'), + ); +} + +/** + * Compress the roles allowed to the minimum. + */ +function ctools_role_ctools_access_settings_submit(&$form, &$form_state) { + $form_state['values']['settings']['rids'] = array_keys(array_filter($form_state['values']['settings']['rids'])); +} + +/** + * Check for access. + */ +function ctools_role_ctools_access_check($conf, $contexts, $account) { + $roles = array_keys($account->roles); + $roles[] = $account->uid ? DRUPAL_AUTHENTICATED_RID : DRUPAL_ANONYMOUS_RID; + return array_intersect($conf['rids'], $roles); +} \ No newline at end of file diff --git a/plugins/arguments/nid.inc b/plugins/arguments/nid.inc index 1036390c..2dc9009b 100644 --- a/plugins/arguments/nid.inc +++ b/plugins/arguments/nid.inc @@ -14,10 +14,8 @@ function ctools_nid_ctools_arguments() { $args['nid'] = array( 'title' => t("Node ID"), 'keyword' => 'node', - 'description' => t('Restricts the argument to a node id.'), + 'description' => t('Creates a node context from a node ID argument.'), 'context' => 'ctools_nid_context', - 'settings form' => 'ctools_nid_settings_form', - 'settings form submit' => 'ctools_nid_settings_form_submit', ); return $args; } @@ -41,49 +39,3 @@ function ctools_nid_context($node = NULL, $conf = NULL, $empty = FALSE) { return ctools_context_create('node', $node); } - -/** - * Settings form for the argument - */ -function ctools_nid_settings_form($conf) { - if (empty($conf)) { - $conf = array( - 'types' => array(), - 'own_default' => FALSE, - 'displays' => array(), - ); - } - - $options = array(); - foreach (node_get_types() as $type => $info) { - $options[$type] = $info->name; - } - - $form['types'] = array( - '#title' => t('Node types'), - '#type' => 'checkboxes', - '#options' => $options, - '#description' => t('You can restrict this argument to use the checked node types. Arguments from non-conforming node types will be ignored, and ctools will behave as if no argument were given. Leave all unchecked to impose no restriction.'), - '#default_value' => $conf['types'], - '#prefix' => '<div class="clear-block">', - '#suffix' => '</div>', - ); - - return $form; -} - -/** - * There appears to be a bit of a bug with the way we're handling forms; it causes - * 'checkboxes' to get invalid values added to them when empty. This takes care - * of that. - */ -function ctools_nid_settings_form_submit(&$values) { - $types = node_get_types(); - if (!empty($values['types'])) { - foreach ($values['types'] as $type => $value) { - if (empty($types[$type])) { - unset($values['types'][$type]); - } - } - } -} diff --git a/plugins/arguments/node_add.inc b/plugins/arguments/node_add.inc index 0e04aece..c2384fe9 100644 --- a/plugins/arguments/node_add.inc +++ b/plugins/arguments/node_add.inc @@ -15,10 +15,8 @@ function ctools_node_add_ctools_arguments() { 'title' => t("Node add form"), // keyword to use for %substitution 'keyword' => 'node_type', - 'description' => t('Displays the node add form for a content type.'), + 'description' => t('Creates a node add form context from a node type argument.'), 'context' => 'ctools_node_add_context', - 'settings form' => 'ctools_node_add_settings_form', - 'settings form submit' => 'ctools_node_add_settings_form_submit', ); return $args; } @@ -38,40 +36,3 @@ function ctools_node_add_context($arg = NULL, $conf = NULL, $empty = FALSE) { return ctools_context_create('node_add_form', $arg); } - -/** - * Settings form for the argument - */ -function ctools_node_add_settings_form($conf) { - $options = array(); - foreach (node_get_types() as $type => $info) { - $options[$type] = $info->name; - } - $form['types'] = array( - '#title' => t('Node types'), - '#type' => 'checkboxes', - '#options' => $options, - '#description' => t('You can restrict this argument to use the checked node types. Arguments from non-conforming node types will be ignored, and ctools will behave as if no argument were given. Leave all unchecked to impose no restriction.'), - '#default_value' => $conf['types'], - '#prefix' => '<div class="clear-block">', - '#suffix' => '</div>', - ); - - return $form; -} - -/** - * There appears to be a bit of a bug with the way we're handling forms; it causes - * 'checkboxes' to get invalid values added to them when empty. This takes care - * of that. - */ -function ctools_node_add_settings_form_submit(&$values) { - $types = node_get_types(); - if (!empty($values['types'])) { - foreach ($values['types'] as $type => $value) { - if (empty($types[$type])) { - unset($values['types'][$type]); - } - } - } -} diff --git a/plugins/arguments/node_edit.inc b/plugins/arguments/node_edit.inc index 87562e73..dff82b32 100644 --- a/plugins/arguments/node_edit.inc +++ b/plugins/arguments/node_edit.inc @@ -15,10 +15,8 @@ function ctools_node_edit_ctools_arguments() { 'title' => t("Node edit form"), // keyword to use for %substitution 'keyword' => 'node', - 'description' => t('Displays the node edit form for a node.'), + 'description' => t('Creates a node edit form context from a node ID argument.'), 'context' => 'ctools_node_edit_context', - 'settings form' => 'ctools_node_edit_settings_form', - 'settings form submit' => 'ctools_node_edit_settings_form_submit', ); return $args; } @@ -48,40 +46,3 @@ function ctools_node_edit_context($arg = NULL, $conf = NULL, $empty = FALSE) { // This will perform a node_access check, so we don't have to. return ctools_context_create('node_edit_form', $node); } - -/** - * Settings form for the argument - */ -function ctools_node_edit_settings_form($conf) { - $options = array(); - foreach (node_get_types() as $type => $info) { - $options[$type] = $info->name; - } - $form['types'] = array( - '#title' => t('Node types'), - '#type' => 'checkboxes', - '#options' => $options, - '#description' => t('You can restrict this argument to use the checked node types. Arguments from non-conforming node types will be ignored, and ctools will behave as if no argument were given. Leave all unchecked to impose no restriction.'), - '#default_value' => $conf['types'], - '#prefix' => '<div class="clear-block">', - '#suffix' => '</div>', - ); - - return $form; -} - -/** - * There appears to be a bit of a bug with the way we're handling forms; it causes - * 'checkboxes' to get invalid values added to them when empty. This takes care - * of that. - */ -function ctools_node_edit_settings_form_submit(&$values) { - $types = node_get_types(); - if (!empty($values['types'])) { - foreach ($values['types'] as $type => $value) { - if (empty($types[$type])) { - unset($values['types'][$type]); - } - } - } -} diff --git a/plugins/arguments/term.inc b/plugins/arguments/term.inc index d4d633a7..e5d81544 100644 --- a/plugins/arguments/term.inc +++ b/plugins/arguments/term.inc @@ -15,10 +15,10 @@ function ctools_term_ctools_arguments() { 'title' => t("Taxonomy term"), // keyword to use for %substitution 'keyword' => 'term', - 'description' => t('Restricts the argument to a taxonomy term.'), + 'description' => t('Creates a single taxonomy term from a taxonomy ID or taxonomy term name.'), 'context' => 'ctools_term_context', + 'default' => array('input_form' => 'tid'), 'settings form' => 'ctools_term_settings_form', - 'settings form submit' => 'ctools_term_settings_form_submit', ); return $args; } @@ -74,19 +74,9 @@ function ctools_term_context($arg = NULL, $conf = NULL, $empty = FALSE) { /** * Settings form for the argument */ -function ctools_term_settings_form($conf) { - if (empty($conf)) { - $conf = array( - 'input_form' => 'tid', - ); - } - - $options = array(); - foreach (taxonomy_get_vocabularies() as $vid => $vocabulary) { - $options[$vid] = $vocabulary->name; - } - - $form['input_form'] = array( +function ctools_term_settings_form(&$form, &$form_state, $conf) { + // @todo allow synonym use like Views does. + $form['settings']['input_form'] = array( '#title' => t('Argument type'), '#type' => 'radios', '#options' => array('tid' => t('Term ID'), 'term' => t('Term name')), @@ -94,33 +84,4 @@ function ctools_term_settings_form($conf) { '#prefix' => '<div class="clear-block">', '#suffix' => '</div>', ); - - - $form['vids'] = array( - '#title' => t('Vocabularies'), - '#type' => 'checkboxes', - '#options' => $options, - '#description' => t('You can restrict this argument to use the checked vocabularies. Arguments from non-conforming vocabularies will be ignored, and ctools will behave as if no argument were given. Leave all unchecked to impose no restriction.'), - '#default_value' => $conf['vids'], - '#prefix' => '<div class="clear-block">', - '#suffix' => '</div>', - ); - - return $form; -} - -/** - * There appears to be a bit of a bug with the way we're handling forms; it causes - * 'checkboxes' to get invalid values added to them when empty. This takes care - * of that. - */ -function ctools_term_settings_form_submit(&$values) { - $vocs = taxonomy_get_vocabularies(); - if (!empty($values['vids'])) { - foreach ($values['vids'] as $vid => $value) { - if (empty($vocs[$vid])) { - unset($values['vids'][$vid]); - } - } - } } diff --git a/plugins/arguments/uid.inc b/plugins/arguments/uid.inc index dcd7b890..8e76d75e 100644 --- a/plugins/arguments/uid.inc +++ b/plugins/arguments/uid.inc @@ -15,7 +15,7 @@ function ctools_uid_ctools_arguments() { 'title' => t("User ID"), // keyword to use for %substitution 'keyword' => 'user', - 'description' => t('Creates a user object from the argument.'), + 'description' => t('Creates a user context from a user ID argument.'), 'context' => 'ctools_uid_context', ); return $args; diff --git a/plugins/arguments/vid.inc b/plugins/arguments/vid.inc index 0a96daf0..a0ad990b 100644 --- a/plugins/arguments/vid.inc +++ b/plugins/arguments/vid.inc @@ -15,7 +15,7 @@ function ctools_vid_ctools_arguments() { 'title' => t("Vocabulary ID"), // keyword to use for %substitution 'keyword' => 'vocabulary', - 'description' => t('Loads a vocabulary object from the argument.'), + 'description' => t('Creates a vocabulary context from a vocabulary ID argument.'), 'context' => 'ctools_vid_context', ); return $args; -- GitLab