Skip to content
Snippets Groups Projects
Commit bb0ea4bb authored by Earl Miles's avatar Earl Miles
Browse files

Checkpoint checkin: Can now create arbitrary pages! Yay. Task handler...

Checkpoint checkin: Can now create arbitrary pages! Yay. Task handler manipulation on arbitrary pages works, though there are some clear disconnects in the UI that will be addressed next. Not all page admin functions exist yet (no clone, enable, disable, revert, delete). Not all arguments have criteria yet.
parent 28d25c7b
No related branches found
No related tags found
No related merge requests found
Showing
with 804 additions and 141 deletions
...@@ -106,3 +106,76 @@ function ctools_get_roles() { ...@@ -106,3 +106,76 @@ function ctools_get_roles() {
return $roles; return $roles;
} }
/**
* Determine if the current user has access via a plugin.
*
* This function is meant to be embedded in the Drupal menu system, and
* therefore is in the .module file since sub files can't be loaded, and
* takes arguments a little bit more haphazardly than ctools_access().
*
* @param $plugin_name
* The access plugin to use. If empty or 'none' then no access control
* is being used and this function returns TRUE. If the plugin can't be
* found otherwise, this function automatically returns FALSE.
* @param $settings
* An array of settings theoretically set by the user.
* @param ...
* zero or more context arguments generated from argument plugins. These
* contexts must have an 'id' attached to them so that they can be
* properly associated. The argument plugin system should set this, but
* if the context is coming from elsewhere it will need to be set manually.
*
* @return
* TRUE if access is granted, false if otherwise.
*/
function ctools_access_menu($plugin_name, $settings) {
$contexts = array();
foreach (func_get_args() as $arg) {
if (is_object($arg) && get_class($arg) == 'ctools_context') {
$contexts[$arg->id] = $arg;
}
}
global $user;
return ctools_access($plugin_name, $settings, $contexts, $user);
}
/**
* Determine if the current user has access via plugin.
*
* @param $plugin_name
* The access plugin to use. If empty or 'none' then no access control
* is being used and this function returns TRUE. If the plugin can't be
* found otherwise, this function automatically returns FALSE.
* @param $settings
* An array of settings theoretically set by the user.
* @param $contexts
* An array of zero or more contexts that may be used to determine if
* the user has access.
* @param $account
* The account to test against. If NULL the logged in user will be tested.
*
* @return
* TRUE if access is granted, false if otherwise.
*/
function ctools_access($plugin_name, $settings, $contexts = array(), $account = NULL) {
if (!$account) {
global $user;
$account = $user;
}
if (empty($plugin_name) || $plugin_name == 'none') {
return TRUE;
}
ctools_include('context');
$plugin = ctools_get_access_plugin($plugin_name);
if (!$plugin) {
return FALSE;
}
if ($function = ctools_plugin_get_function($plugin, 'callback')) {
return $function($settings, $contexts, $account);
}
}
/* $Id$ */
.delegator-page-operations {
text-align: right;
}
td.delegator-page-operations {
width: 175px;
}
...@@ -539,3 +539,12 @@ function delegator_make_task_name($task_id, $subtask_id) { ...@@ -539,3 +539,12 @@ function delegator_make_task_name($task_id, $subtask_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);
}
<!-- $Id$ -->
defines a task type, grouping tasks together and providing a common UI for them.
\ No newline at end of file
Additional 'task' keys support:
operations -- a list of operations suitable for theme('links')
\ No newline at end of file
<?php
// $Id$
/**
* @file
* Administrative functions for the page system
*/
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.'),
'rows' => array(),
),
'page' => array(), // give this one special weighting,
);
delegator_page_type_sort_tasks($tasks, $tables, 'singles');
$output = '';
$header = array(
array('data' => t('Title'), 'class' => 'delegator-page-title'),
array('data' => t('Path'), 'class' => 'delegator-page-path'),
array('data' => t('Operations'), 'class' => 'delegator-page-operations'),
);
foreach ($tables as $bucket => $info) {
if (!empty($info['rows'])) {
$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' => 3)), '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'];
}
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'];
}
$row = array('data' => array(), 'class' => 'page-task-' . $id);
$row['data'][] = array('data' => $task['admin title'], 'class' => 'delegator-page-title');
$row['data'][] = array('data' => $task['admin path'], 'class' => 'delegator-page-path');
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;
}
}
<?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,
);
}
...@@ -53,6 +53,10 @@ function delegator_node_view($node) { ...@@ -53,6 +53,10 @@ function delegator_node_view($node) {
if ($function = ctools_plugin_load_function('delegator', 'task_handlers', $handler->handler, 'render')) { if ($function = ctools_plugin_load_function('delegator', 'task_handlers', $handler->handler, 'render')) {
$output = $function($handler, $node); $output = $function($handler, $node);
if ($output) { if ($output) {
// Since we're not using node_show() we need to emulate what it used to do.
// Update the history table, stating that this user viewed this node.
node_tag_new($node->nid);
// TRUE is a special return used to let us know that it handled the // TRUE is a special return used to let us know that it handled the
// task but does not wish us to render anything, as it already did. // task but does not wish us to render anything, as it already did.
// This is needed for the 'no blocks' functionality. // This is needed for the 'no blocks' functionality.
......
...@@ -9,6 +9,161 @@ ...@@ -9,6 +9,161 @@
* only when needed. * 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 page',
'description' => 'Add a delegator page subtask.',
'page callback' => 'delegator_page_add_subtask',
'type' => MENU_LOCAL_TASK,
) + $base;
$form_info = delegator_page_edit_form_info();
$default_task = FALSE;
$weight = 0;
foreach ($form_info['order'] as $form_id => $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' => t('Edit'),
'page callback' => 'delegator_page_edit_subtask',
'page arguments' => array(4, $form_id),
) + $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/%/$form_id"] = array(
'title' => $form_title,
'page callback' => 'delegator_page_edit_subtask',
'page arguments' => array(4, 5),
'type' => $type,
'weight' => $weight++,
) + $base;
}
// AJAX callbacks for argument modal.
$items['admin/build/delegator/argument'] = array(
'page callback' => 'delegator_page_subtask_argument_ajax',
) + $base;
// Add menu entries for each subtask
foreach (delegator_page_load_all() as $subtask_id => $subtask) {
if (!isset($subtask->access['settings'])) {
$subtask->access['settings'] = NULL;
}
$path = array();
$page_arguments = array($subtask_id);
$access_arguments = array($subtask->access['type'], $subtask->access['settings']);
$load_arguments = array($subtask_id, '%index');
// Replace named placeholders with our own placeholder to load contexts.
foreach (explode('/', $subtask->path) as $position => $bit) {
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 {
$path[] = $bit;
}
}
$menu_path = implode('/', $path);
$items[$menu_path] = delegator_page_menu_item($subtask->menu, $access_arguments, $page_arguments, $load_arguments);
// Add a parent menu item if one is configured.
if ($subtask->menu['type'] == 'default tab' && $subtask->menu['parent']['type'] != 'none') {
array_pop($path);
$parent_path = implode('/', $path);
$items[$parent_path] = delegator_page_menu_item($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($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.admin.inc',
);
if (isset($menu['title'])) {
$item['title'] = $menu['title'];
}
if (isset($menu['weight'])) {
$item['weight'] = $menu['weight'];
}
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. * Get the cached changes to a given task handler.
*/ */
...@@ -147,7 +302,7 @@ function delegator_page_edit_subtask($page_name, $step = NULL) { ...@@ -147,7 +302,7 @@ function delegator_page_edit_subtask($page_name, $step = NULL) {
function delegator_page_add_subtask_finish(&$form_state) { function delegator_page_add_subtask_finish(&$form_state) {
$page = &$form_state['page']; $page = &$form_state['page'];
// Ensure $page->arguments contains only real arguments: // Ensure $page->arguments contains only real arguments:
$arguments = delegator_page_get_arguments($page->path); $arguments = delegator_page_get_named_arguments($page->path);
$args = array(); $args = array();
foreach ($arguments as $keyword => $position) { foreach ($arguments as $keyword => $position) {
if (isset($page->arguments[$keyword])) { if (isset($page->arguments[$keyword])) {
...@@ -240,7 +395,7 @@ function delegator_page_form_basic_validate_filter($value) { ...@@ -240,7 +395,7 @@ function delegator_page_form_basic_validate_filter($value) {
*/ */
function delegator_page_form_basic_validate(&$form, &$form_state) { function delegator_page_form_basic_validate(&$form, &$form_state) {
// Ensure name is properly formed. // Ensure name is properly formed.
$args = delegator_page_get_arguments($form_state['values']['path']); $args = delegator_page_get_named_arguments($form_state['values']['path']);
if ($invalid_args = array_filter($args, 'delegator_page_form_basic_validate_filter')) { if ($invalid_args = array_filter($args, 'delegator_page_form_basic_validate_filter')) {
foreach ($invalid_args as $arg => $position) { foreach ($invalid_args as $arg => $position) {
form_error($form['path'], t('Duplicated argument %arg', array('%arg' => $arg))); form_error($form['path'], t('Duplicated argument %arg', array('%arg' => $arg)));
...@@ -428,14 +583,7 @@ function delegator_page_form_access(&$form, &$form_state) { ...@@ -428,14 +583,7 @@ function delegator_page_form_access(&$form, &$form_state) {
$contexts = array(); $contexts = array();
// Load contexts based on argument data: // Load contexts based on argument data:
if ($page->arguments) { if ($arguments = delegator_page_get_arguments($page)) {
$arguments = array();
foreach ($page->arguments as $keyword => $argument) {
if (isset($argument['name'])) {
$argument['keyword'] = $keyword;
$arguments[$argument['id']] = $argument;
}
}
$contexts = ctools_context_get_placeholders_from_argument($arguments); $contexts = ctools_context_get_placeholders_from_argument($arguments);
} }
...@@ -549,7 +697,7 @@ function delegator_page_form_argument(&$form, &$form_state) { ...@@ -549,7 +697,7 @@ function delegator_page_form_argument(&$form, &$form_state) {
$path = $form_state['page']->path; $path = $form_state['page']->path;
$page = &$form_state['page']; $page = &$form_state['page'];
$arguments = delegator_page_get_arguments($path); $arguments = delegator_page_get_named_arguments($path);
$form['table'] = array( $form['table'] = array(
'#theme' => 'delegator_page_form_argument_table', '#theme' => 'delegator_page_form_argument_table',
...@@ -671,7 +819,7 @@ function delegator_page_subtask_argument_ajax($step = NULL, $cache_name = NULL, ...@@ -671,7 +819,7 @@ function delegator_page_subtask_argument_ajax($step = NULL, $cache_name = NULL,
} }
$path = $page->path; $path = $page->path;
$arguments = delegator_page_get_arguments($path); $arguments = delegator_page_get_named_arguments($path);
// Load stored object from cache. // Load stored object from cache.
if (!isset($arguments[$keyword])) { if (!isset($arguments[$keyword])) {
......
...@@ -7,6 +7,9 @@ ...@@ -7,6 +7,9 @@
* *
* This creates subtasks and stores them in the delegator_pages table. These * This creates subtasks and stores them in the delegator_pages table. These
* are exportable objects, too. * are exportable objects, too.
*
* The render callback for this task type has $handler, $page, $contexts as
* parameters.
*/ */
/** /**
...@@ -19,8 +22,13 @@ function delegator_page_delegator_tasks() { ...@@ -19,8 +22,13 @@ function delegator_page_delegator_tasks() {
'title' => t('User pages'), 'title' => t('User pages'),
'description' => t('Administrator created pages that have a URL path, access control and entries in the Drupal menu system.'), 'description' => t('Administrator created pages that have a URL path, access control and entries in the Drupal menu system.'),
'subtasks' => TRUE, 'subtasks' => TRUE,
'subtask callback' => 'delegator_page_subtask',
'subtasks callback' => 'delegator_page_subtasks', 'subtasks callback' => 'delegator_page_subtasks',
'hook menu' => 'delegator_page_menu', 'hook menu' => array(
'file' => 'page.admin.inc',
'path' => drupal_get_path('module', 'delegator') . '/plugins/tasks',
'function' => 'delegator_page_menu',
),
'hook theme' => 'delegator_page_theme', 'hook theme' => 'delegator_page_theme',
// page only items // page only items
...@@ -35,6 +43,11 @@ function delegator_page_delegator_tasks() { ...@@ -35,6 +43,11 @@ function delegator_page_delegator_tasks() {
'href' => 'admin/build/pages/add', 'href' => 'admin/build/pages/add',
), ),
), ),
// context only items
'type' => 'context',
'get arguments' => 'delegator_page_get_argument_callback',
'get context placeholders' => 'delegator_page_get_contexts',
), ),
); );
} }
...@@ -43,131 +56,92 @@ function delegator_page_delegator_tasks() { ...@@ -43,131 +56,92 @@ function delegator_page_delegator_tasks() {
* Return a list of all subtasks. * Return a list of all subtasks.
*/ */
function delegator_page_subtasks($task) { function delegator_page_subtasks($task) {
$subtasks = delegator_page_load_all(); $pages = delegator_page_load_all();
$return = array(); $return = array();
foreach ($subtasks as $name => $subtask) { foreach ($pages as $name => $page) {
$form_info = delegator_page_edit_form_info(); $return[$name] = delegator_page_build_subtask($task, $page);
$edit_links = array();
foreach ($form_info['order'] as $form_id => $form_title) {
$edit_links[] = array(
'title' => $form_title,
'href' => "admin/build/pages/edit/$name/$form_id",
);
}
$operations = array();
if (empty($subtask->disabled)) {
$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 ($subtask->export_type == (EXPORT_IN_CODE | EXPORT_IN_DATABASE)) {
$operations[] = array(
'title' => t('Revert'),
'href' => "admin/build/pages/delete/$name",
);
}
else if ($subtask->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",
);
}
$return[$name] = array(
'name' => $name,
'admin title' => $subtask->admin_title,
'admin description' => t('TODO'),
'admin path' => $subtask->path,
'subtask' => $subtask,
'operations' => $operations,
);
} }
return $return; return $return;
} }
/** /**
* Delegated implementation of hook_menu(). * Callback to return a single subtask.
*/ */
function delegator_page_menu(&$items, $task) { function delegator_page_subtask($task, $subtask_id) {
// Set up access permissions. $page = delegator_page_load($subtask_id);
$access_callback = isset($task['admin access callback']) ? $task['admin access callback'] : 'user_access'; if ($page) {
$access_arguments = isset($task['admin access arguments']) ? $task['admin access arguments'] : array('administer delegator'); return delegator_page_build_subtask($task, $page);
}
$base = array( }
'access callback' => $access_callback,
'access arguments' => $access_arguments,
'file' => 'plugins/tasks/page.admin.inc',
);
$items['admin/build/pages/add'] = array(
'title' => 'Add page',
'description' => 'Add a delegator page subtask.',
'page callback' => 'delegator_page_add_subtask',
'type' => MENU_LOCAL_TASK,
) + $base;
/**
* Build a subtask array for a given page.
*/
function delegator_page_build_subtask($task, $page) {
$form_info = delegator_page_edit_form_info(); $form_info = delegator_page_edit_form_info();
$default_task = FALSE; $edit_links = array();
$weight = 0; $name = $page->name;
foreach ($form_info['order'] as $form_id => $form_title) { foreach ($form_info['order'] as $form_id => $form_title) {
// The first edit form is the default for tabs, so it gets a bit $edit_links[] = array(
// of special treatment here. 'title' => $form_title,
if (!$default_task) { 'href' => "admin/build/pages/edit/$name/$form_id",
$default_task = TRUE; );
// Add the callback for the default tab. }
$items["admin/build/pages/edit/%"] = array(
'title' => t('Edit'), $operations = array();
'page callback' => 'delegator_page_edit_subtask',
'page arguments' => array(4, $form_id), if (empty($page->disabled)) {
) + $base; $operations[] = array(
'title' => t('Task handlers'),
// And make sure it's the default local task. 'href' => "admin/build/delegator/" . delegator_make_task_name($task['name'], $name),
$type = MENU_DEFAULT_LOCAL_TASK; );
$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 ($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 { else {
// This allows an empty form title to provide a hidden form $operations[] = array(
// which is useful for doing more branch-like multi-step 'title' => t('Delete'),
// functionality. 'href' => "admin/build/pages/delete/$name",
$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/%/$form_id"] = array(
'title' => $form_title,
'page callback' => 'delegator_page_edit_subtask',
'page arguments' => array(4, 5),
'type' => $type,
'weight' => $weight++,
) + $base;
} }
else {
// AJAX callbacks for argument modal. $operations[] = array(
$items['admin/build/delegator/argument'] = array( 'title' => t('Enable'),
'page callback' => 'delegator_page_subtask_argument_ajax', 'href' => "admin/build/pages/enable/$name",
) + $base; );
}
return array(
'name' => $name,
'admin title' => $page->admin_title,
'admin description' => t('TODO'),
'admin path' => $page->path,
'subtask' => $page,
'operations' => $operations,
);
} }
/** /**
...@@ -221,6 +195,139 @@ function delegator_page_edit_form_info() { ...@@ -221,6 +195,139 @@ function delegator_page_edit_form_info() {
); );
} }
// --------------------------------------------------------------------------
// Page execution functions
/**
* Load a context from an argument for a given page task.
*
* @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;
}
/**
* 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) {
// Turn the contexts into a properly keyed array.
$contexts = array();
foreach (func_get_args() as $arg) {
if (is_object($arg) && get_class($arg) == 'ctools_context') {
$contexts[$arg->id] = $arg;
}
}
$task = delegator_get_task('page');
$handlers = delegator_load_sorted_handlers($task, $subtask_id);
$page = delegator_page_load($subtask_id);
// Try each handler.
foreach ($handlers as $handler) {
if ($function = ctools_plugin_load_function('delegator', 'task_handlers', $handler->handler, 'render')) {
$output = $function($handler, $page, $contexts);
if ($output) {
// TRUE is a special return used to let us know that it handled the
// task but does not wish us to render anything, as it already did.
// This is needed for the 'no blocks' functionality.
if ($output === TRUE) {
return;
}
return $output;
}
}
}
return drupal_access_denied();
}
// --------------------------------------------------------------------------
// Context type callbacks
/**
* Return a list of arguments used by this task.
*/
function delegator_page_get_argument_callback($task, $subtask_id) {
$page = delegator_page_load($subtask_id);
if ($page) {
return delegator_page_get_arguments($page);
}
}
/**
* Get a group of context placeholders for the arguments.
*/
function delegator_page_get_contexts($task, $subtask_id) {
$page = delegator_page_load($subtask_id);
if ($page && $arguments = delegator_page_get_arguments($page)) {
ctools_include('context');
return ctools_context_get_placeholders_from_argument($arguments);
}
}
/**
* Return a list of arguments used by this page.
*
* This provides a list of arguments suitable for using in the context
* system, which is slightly more data than we store in the database.
* This also filters out arguments that have no contexts.
*/
function delegator_page_get_arguments($page) {
if ($page->arguments) {
$arguments = array();
foreach ($page->arguments as $keyword => $argument) {
if (isset($argument['name'])) {
$argument['keyword'] = $keyword;
$arguments[$keyword] = $argument;
}
}
return $arguments;
}
}
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// Page task database info. // Page task database info.
......
...@@ -13,3 +13,8 @@ string as though it came from a URL element. ...@@ -13,3 +13,8 @@ string as though it came from a URL element.
'settings form' => params: $form, $form_state, $conf -- gets the whole form. Should put anything it wants to keep automatically in $form['settings'] '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 validate' => params: $form, $form_state
'settings form submit' => params: $form, $form_state 'settings form submit' => params: $form, $form_state
'criteria form' => params: $form, &$form_state, $conf, $argument, $id -- gets the whole argument. It should only put form widgets in $form[$id]. $conf may not be properly initialized so always guard against this due to arguments being changed and handlers not being updated to match.
+ submit + validate
'criteria select' => returns true if the selected criteria matches the context. params: $context, $conf
...@@ -480,6 +480,8 @@ function ctools_context_get_context_from_argument($argument, $arg, $empty = FALS ...@@ -480,6 +480,8 @@ function ctools_context_get_context_from_argument($argument, $arg, $empty = FALS
$context->identifier = $argument['identifier']; $context->identifier = $argument['identifier'];
$context->page_title = isset($argument['title']) ? $argument['title'] : ''; $context->page_title = isset($argument['title']) ? $argument['title'] : '';
$context->keyword = $argument['keyword']; $context->keyword = $argument['keyword'];
$context->id = ctools_context_id($argument, 'argument');
$context->original_argument = $arg;
} }
return $context; return $context;
} }
......
...@@ -3,7 +3,34 @@ ...@@ -3,7 +3,34 @@
/** /**
* @file * @file
* Theme registry for dropdown-div tool. * Provide a javascript based dropdown menu.
*
* The dropdown menu will show up as a clickable link; when clicked,
* a small menu will slide down beneath it, showing the list of links.
*
* The dropdown will stay open until either the user has moved the mouse
* away from the box for > .5 seconds, or can be immediately closed by
* clicking the link again. The code is smart enough that if the mouse
* moves away and then back within the .5 second window, it will not
* re-close.
*
* Multiple dropdowns can be placed per page.
*
* If the user does not have javascript enabled, the link will not appear,
* and instead by default the list of links will appear as a normal inline
* list.
*
* The menu is heavily styled by default, and to make it look different
* will require a little bit of CSS. You can apply your own class to the
* dropdown to help ensure that your CSS can override the dropdown's CSS.
*
* In particular, the text, link, background and border colors may need to
* be changed. Please see dropdown.css for information about the existing
* styling.
*/
/**
* Delegated implementation of hook_theme()
*/ */
function ctools_dropdown_theme(&$items) { function ctools_dropdown_theme(&$items) {
$items['ctools_dropdown'] = array( $items['ctools_dropdown'] = array(
...@@ -14,6 +41,16 @@ function ctools_dropdown_theme(&$items) { ...@@ -14,6 +41,16 @@ function ctools_dropdown_theme(&$items) {
/** /**
* Create a dropdown menu. * Create a dropdown menu.
*
* @param $title
* The text to place in the clickable area to activate the dropdown.
* @param $links
* A list of links to provide within the dropdown, suitable for use
* in via Drupal's theme('links').
* @param $class
* An optional class to add to the dropdown's container div to allow you
* to style a single dropdown however you like without interfering with
* other dropdowns.
*/ */
function theme_ctools_dropdown($title, $links, $class = '') { function theme_ctools_dropdown($title, $links, $class = '') {
// Provide a unique identifier for every dropdown on the page. // Provide a unique identifier for every dropdown on the page.
......
...@@ -258,7 +258,6 @@ function ctools_plugin_get_function($plugin, $function_name) { ...@@ -258,7 +258,6 @@ function ctools_plugin_get_function($plugin, $function_name) {
// If cached the .inc file may not have been loaded. require_once is quite safe // If cached the .inc file may not have been loaded. require_once is quite safe
// and fast so it's okay to keep calling it. // and fast so it's okay to keep calling it.
if (isset($plugin['file'])) { if (isset($plugin['file'])) {
if (!is_array($plugin)) { vpr_trace(); }
require_once './' . $plugin['path'] . '/' . $plugin['file']; require_once './' . $plugin['path'] . '/' . $plugin['file'];
} }
......
// $Id$ // $Id$
// All CTools tools begin with this:
if (!Drupal.CTools) {
Drupal.CTools = {};
}
/** /**
* @file
* Javascript required for a simple collapsible div.
*
* Creating a collapsible div with this doesn't take too much. There are * Creating a collapsible div with this doesn't take too much. There are
* three classes necessary: * three classes necessary:
* *
...@@ -21,6 +18,12 @@ if (!Drupal.CTools) { ...@@ -21,6 +18,12 @@ if (!Drupal.CTools) {
* a class, which will cause the container to draw collapsed. * a class, which will cause the container to draw collapsed.
*/ */
// All CTools tools begin with this if they need to use the CTools namespace.
if (!Drupal.CTools) {
Drupal.CTools = {};
}
// Set up an array for callbacks. // Set up an array for callbacks.
Drupal.CTools.CollapsibleCallbacks = []; Drupal.CTools.CollapsibleCallbacks = [];
Drupal.CTools.CollapsibleCallbacksAfterToggle = []; Drupal.CTools.CollapsibleCallbacksAfterToggle = [];
......
// $Id$ // $Id$
/** /**
* @file dependent.js * @file
* *
* Written by dmitrig01 (Dmitri Gaskin) for CTools; this provides dependent * Written by dmitrig01 (Dmitri Gaskin) for CTools; this provides dependent
* visibility for form items in CTools' ajax forms. * visibility for form items in CTools' ajax forms.
......
// $Id$ // $Id$
/**
* @file
* Implement a simple, clickable dropdown menu.
*
* See dropdown.theme.inc for primary documentation.
*
* The javascript relies on four classes:
* - The dropdown must be fully contained in a div with the class
* ctools-dropdown. It must also contain the class ctools-dropdown-no-js
* which will be immediately removed by the javascript; this allows for
* graceful degradation.
* - The trigger that opens the dropdown must be an a tag wit hthe class
* ctools-dropdown-link. The href should just be '#' as this will never
* be allowed to complete.
* - The part of the dropdown that will appear when the link is clicked must
* be a div with class ctools-dropdown-container.
* - Finally, ctools-dropdown-hover will be placed on any link that is being
* hovered over, so that the browser can restyle the links.
*
* This tool isn't meant to replace click-tips or anything, it is specifically
* meant to work well presenting menus.
*/
Drupal.behaviors.CToolsDropdown = function() { Drupal.behaviors.CToolsDropdown = function() {
$('div.ctools-dropdown:not(.ctools-dropdown-processed)') $('div.ctools-dropdown:not(.ctools-dropdown-processed)')
.removeClass('ctools-dropdown-no-js') .removeClass('ctools-dropdown-no-js')
......
...@@ -15,7 +15,9 @@ function ctools_nid_ctools_arguments() { ...@@ -15,7 +15,9 @@ function ctools_nid_ctools_arguments() {
'title' => t("Node ID"), 'title' => t("Node ID"),
'keyword' => 'node', 'keyword' => 'node',
'description' => t('Creates a node context from a node ID argument.'), 'description' => t('Creates a node context from a node ID argument.'),
'context' => 'ctools_nid_context', 'context' => 'ctools_argument_nid_context',
'criteria form' => 'ctools_argument_nid_criteria_form',
'criteria select' => 'ctools_argument_nid_criteria_select',
); );
return $args; return $args;
} }
...@@ -23,19 +25,65 @@ function ctools_nid_ctools_arguments() { ...@@ -23,19 +25,65 @@ function ctools_nid_ctools_arguments() {
/** /**
* Discover if this argument gives us the node we crave. * Discover if this argument gives us the node we crave.
*/ */
function ctools_nid_context($node = NULL, $conf = NULL, $empty = FALSE) { function ctools_argument_nid_context($arg = NULL, $conf = NULL, $empty = FALSE) {
// If unset it wants a generic, unfilled context. // If unset it wants a generic, unfilled context.
if ($empty) { if ($empty) {
return ctools_context_create_empty('node'); return ctools_context_create_empty('node');
} }
if (!is_object($node)) { if (!is_numeric($arg)) {
return NULL; return FALSE;
} }
if (array_filter($conf['types']) && empty($conf['types'][$node->type])) { $node = node_load($arg);
return NULL; if (!$node) {
return FALSE;
} }
return ctools_context_create('node', $node); return ctools_context_create('node', $node);
} }
/**
* Provide a criteria form for selecting a node.
*/
function ctools_argument_nid_criteria_form(&$form, &$form_state, $conf, $argument, $id) {
// Ensure $conf has valid defaults:
if (!is_array($conf)) {
$conf = array();
}
$conf += array(
'type' => array(),
);
$types = node_get_types();
foreach ($types as $type => $info) {
$options[$type] = check_plain($info->name);
}
$form[$id]['type'] = array(
'#title' => t('Select types for %identifier', array('%identifier' => $argument['identifier'])),
'#type' => 'checkboxes',
'#options' => $options,
'#description' => t('This item will only be selected for nodes having the selected node types. If no node types are selected, it will be selected for all node types.'),
'#default_value' => $conf['type'],
);
}
/**
* Provide a criteria form for selecting a node.
*/
function ctools_argument_nid_criteria_select($conf, $context) {
// As far as I know there should always be a context at this point, but this
// is safe.
if (empty($context) || empty($context->data) || empty($context->data->type)) {
return FALSE;
}
if (array_filter($conf['type']) && empty($conf['type'][$context->data->type])) {
return FALSE;
}
return TRUE;
}
...@@ -16,7 +16,9 @@ function ctools_uid_ctools_arguments() { ...@@ -16,7 +16,9 @@ function ctools_uid_ctools_arguments() {
// keyword to use for %substitution // keyword to use for %substitution
'keyword' => 'user', 'keyword' => 'user',
'description' => t('Creates a user context from a user ID argument.'), 'description' => t('Creates a user context from a user ID argument.'),
'context' => 'ctools_uid_context', 'context' => 'ctools_argument_uid_context',
'criteria form' => 'ctools_argument_uid_criteria_form',
'criteria select' => 'ctools_argument_uid_criteria_select',
); );
return $args; return $args;
} }
...@@ -24,7 +26,7 @@ function ctools_uid_ctools_arguments() { ...@@ -24,7 +26,7 @@ function ctools_uid_ctools_arguments() {
/** /**
* Discover if this argument gives us the user we crave. * Discover if this argument gives us the user we crave.
*/ */
function ctools_uid_context($arg = NULL, $conf = NULL, $empty = FALSE) { function ctools_argument_uid_context($arg = NULL, $conf = NULL, $empty = FALSE) {
// If unset it wants a generic, unfilled context. // If unset it wants a generic, unfilled context.
if ($empty) { if ($empty) {
return ctools_context_create_empty('user'); return ctools_context_create_empty('user');
...@@ -41,3 +43,47 @@ function ctools_uid_context($arg = NULL, $conf = NULL, $empty = FALSE) { ...@@ -41,3 +43,47 @@ function ctools_uid_context($arg = NULL, $conf = NULL, $empty = FALSE) {
return ctools_context_create('user', $account); return ctools_context_create('user', $account);
} }
/**
* Provide a criteria form for selecting a node.
*/
function ctools_argument_uid_criteria_form(&$form, &$form_state, $conf, $argument, $id) {
// Ensure $conf has valid defaults:
if (!is_array($conf)) {
$conf = array();
}
$conf += array(
'rids' => array(),
);
$form[$id]['rids'] = array(
'#type' => 'checkboxes',
'#title' => t('Select roles for %identifier', array('%identifier' => $argument['identifier'])),
'#default_value' => $conf['rids'],
'#options' => ctools_get_roles(),
'#description' => t('This item will only be selected for users having the selected roles. If no roles are selected, it will be selected for all users.'),
);
}
/**
* Provide a criteria form for selecting a node.
*/
function ctools_argument_uid_criteria_select($conf, $context) {
// As far as I know there should always be a context at this point, but this
// is safe.
if (empty($context) || empty($context->data) || !isset($context->data->roles)) {
return FALSE;
}
$rids = array_filter($conf['rids']);
if (!$rids) {
return TRUE;
}
$roles = array_keys($context->data->roles);
$roles[] = $context->data->uid ? DRUPAL_AUTHENTICATED_RID : DRUPAL_ANONYMOUS_RID;
return array_intersect($rids, $roles);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment