'page_manager', 'topic' => 'getting-started', 'type' => t('See the getting started guide for more information.'), ))); } $tasks = page_manager_get_tasks_by_type('page'); $pages = array('operations' => array(), 'tasks' => array()); page_manager_get_pages($tasks, $pages); // Add lock icon to all locked tasks. global $user; ctools_include('object-cache'); $locks = ctools_object_cache_test_objects('page_manager_page', $pages['tasks']); foreach ($locks as $task_name => $lock) { if ($lock->uid == $user->uid) { $pages['rows'][$task_name]['class'][] = ' page-manager-locked'; $pages['rows'][$task_name]['title'] = t('This page is currently locked for editing by you. Nobody else may edit this page until these changes are saved or canceled.'); } else { $pages['rows'][$task_name]['class'][] = ' page-manager-locked-other'; $pages['rows'][$task_name]['title'] = t('This page is currently locked for editing by another user. You may not edit this page without breaking the lock.'); } } $input = $_POST; // Respond to a reset command by clearing session and doing a drupal goto // back to the base URL. if (isset($input['op']) && $input['op'] == t('Reset')) { unset($_SESSION['page_manager']['#admin']); if (!$js) { drupal_goto($_GET['q']); } // clear everything but form id, form build id and form token: $keys = array_keys($input); foreach ($keys as $id) { if ($id != 'form_id' && $id != 'form_build_id' && $id != 'form_token') { unset($input[$id]); } } $replace_form = TRUE; } if (count($input) <= 1) { if (isset($_SESSION['page_manager']['#admin']) && is_array($_SESSION['page_manager']['#admin'])) { $input = $_SESSION['page_manager']['#admin']; } } else { $_SESSION['page_manager']['#admin'] = $input; unset($_SESSION['page_manager']['#admin']['q']); } $form_state = array( 'pages' => &$pages, 'input' => $input, 'rerender' => TRUE, 'no_redirect' => TRUE, ); // This form will sort and filter the pages. $form = drupal_build_form('page_manager_list_pages_form', $form_state); $header = array( array('data' => t('Type'), 'class' => array('page-manager-page-type')), array('data' => t('Name'), 'class' => array('page-manager-page-name')), array('data' => t('Title'), 'class' => array('page-manager-page-title')), array('data' => t('Path'), 'class' => array('page-manager-page-path')), array('data' => t('Storage'), 'class' => array('page-manager-page-storage')), ); $header[] = array('data' => t('Operations'), 'class' => array('page-manager-page-operations')); $table = theme('table', array('header' => $header, 'rows' => $pages['rows'], 'attributes' => array('id' => 'page-manager-list-pages'))); $operations = ''; drupal_add_css(drupal_get_path('module', 'page_manager') . '/css/page-manager.css'); if (!$js) { return array('#markup' => drupal_render($form) . $table . $operations); } ctools_include('ajax'); $commands = array(); $commands[] = ajax_command_replace('#page-manager-list-pages', $table); if (!empty($replace_form)) { $commands[] = ajax_command_replace('#page-manager-list-pages-form', drupal_render($form)); } print ajax_render($commands); ajax_footer(); } /** * Sort tasks into buckets based upon whether or not they have subtasks. */ function page_manager_get_pages($tasks, &$pages, $task_id = NULL) { foreach ($tasks as $id => $task) { if (empty($task_id) && !empty($task['page operations'])) { $pages['operations'] = array_merge($pages['operations'], $task['page operations']); } // If a type has subtasks, add its subtasks in its own table. if (!empty($task['subtasks'])) { page_manager_get_pages(page_manager_get_task_subtasks($task), $pages, $task['name']); continue; } if (isset($task_id)) { $task_name = page_manager_make_task_name($task_id, $task['name']); } else { $task_name = $task['name']; } $class = array('page-task-' . $id); if (isset($task['row class'])) { $class[] = $task['row class']; } if (!empty($task['disabled'])) { $class[] = 'page-manager-disabled'; } $path = array(); $visible_path = ''; if (!empty($task['admin path'])) { foreach (explode('/', $task['admin path']) as $bit) { if (isset($bit[0]) && $bit[0] != '!') { $path[] = $bit; } } $path = implode('/', $path); if (empty($task['disabled']) && strpos($path, '%') === FALSE) { $visible_path = l('/' . $task['admin path'], $path); } else { $visible_path = '/' . $task['admin path']; } } $row = array('data' => array(), 'class' => $class, 'title' => strip_tags($task['admin description'])); $type = isset($task['admin type']) ? $task['admin type'] : t('System'); $pages['types'][$type] = $type; $row['data']['type'] = array('data' => $type, 'class' => array('page-manager-page-type')); $row['data']['name'] = array('data' => $task_name, 'class' => array('page-manager-page-name')); $row['data']['title'] = array('data' => $task['admin title'], 'class' => array('page-manager-page-title')); $row['data']['path'] = array('data' => $visible_path, 'class' => array('page-manager-page-path')); $storage = isset($task['storage']) ? $task['storage'] : t('In code'); $pages['storages'][$storage] = $storage; $row['data']['storage'] = array('data' => $storage, 'class' => array('page-manager-page-storage')); /* if (empty($task['disabled'])) { $item = menu_get_item($path); if (empty($item)) { dsm($path); } else { dsm($item); } } */ $operations = array( array( 'title' => t('Edit'), 'href' => page_manager_edit_url($task_name), ), ); if (!empty($task['enable callback'])) { if (!empty($task['disabled'])) { array_unshift($operations, array( 'title' => t('Enable'), 'href' => 'admin/structure/pages/nojs/enable/' . $task_name, 'query' => array('token' => drupal_get_token($task_name)), )); } else { $operations[] = array( 'title' => t('Disable'), 'href' => 'admin/structure/pages/nojs/disable/' . $task_name, 'query' => array('token' => drupal_get_token($task_name)), ); } } $ops = theme('links__ctools_dropbutton', array('links' => $operations, 'attributes' => array('class' => array('links', 'inline')))); $row['data']['operations'] = array('data' => $ops, 'class' => array('page-manager-page-operations')); $pages['disabled'][$task_name] = !empty($task['disabled']); $pages['tasks'][] = $task_name; $pages['rows'][$task_name] = $row; } } /** * Provide a form for sorting and filtering the list of pages. */ function page_manager_list_pages_form($form, &$form_state) { // This forces the form to *always* treat as submitted which is // necessary to make it work. if (empty($_POST)) { $form["#programmed"] = TRUE; } $form['#action'] = url('admin/structure/pages/nojs/', array('absolute' => TRUE)); if (!variable_get('clean_url', FALSE)) { $form['q'] = array( '#type' => 'hidden', '#value' => $_GET['q'], ); } $all = array('all' => t('')); $form['type'] = array( '#type' => 'select', '#title' => t('Type'), '#options' => $all + $form_state['pages']['types'], '#default_value' => 'all', ); $form['storage'] = array( '#type' => 'select', '#title' => t('Storage'), '#options' => $all + $form_state['pages']['storages'], '#default_value' => 'all', ); $form['disabled'] = array( '#type' => 'select', '#title' => t('Enabled'), '#options' => $all + array('0' => t('Enabled'), '1' => t('Disabled')), '#default_value' => 'all', ); $form['search'] = array( '#type' => 'textfield', '#title' => t('Search'), ); $form['order'] = array( '#type' => 'select', '#title' => t('Sort by'), '#options' => array( 'disabled' => t('Enabled, title'), 'title' => t('Title'), 'name' => t('Name'), 'path' => t('Path'), 'type' => t('Type'), 'storage' => t('Storage'), ), '#default_value' => 'disabled', ); $form['sort'] = array( '#type' => 'select', '#title' => t('Order'), '#options' => array( 'asc' => t('Up'), 'desc' => t('Down'), ), '#default_value' => 'asc', ); $form['submit'] = array( '#name' => '', // so it won't in the $_GET args '#type' => 'submit', '#id' => 'edit-pages-apply', '#value' => t('Apply'), '#attributes' => array('class' => array('use-ajax-submit ctools-auto-submit-click')), ); $form['reset'] = array( '#type' => 'submit', '#id' => 'edit-pages-reset', '#value' => t('Reset'), '#attributes' => array('class' => array('use-ajax-submit')), ); $form['#attached']['js'] = array(ctools_attach_js('auto-submit'), ctools_attach_js('page-list', 'page_manager')); $form['#attached']['library'][] = array('system', 'drupal.ajax'); $form['#attached']['library'][] = array('system', 'jquery.form'); $form['#prefix'] = '
'; $form['#suffix'] = '
'; $form['#attributes'] = array('class' => array('ctools-auto-submit-full-form')); return $form; } /** * Accept submission from the page manager sort/filter form and apply it * to the list of pages. */ function page_manager_list_pages_form_submit(&$form, &$form_state) { // Filter and re-sort the pages. // This is a copy. $rows = $form_state['pages']['rows']; $sorts = array(); foreach ($rows as $name => $data) { // Filter if ($form_state['values']['type'] != 'all' && $form_state['values']['type'] != $data['data']['type']['data']) { continue; } if ($form_state['values']['storage'] != 'all' && $form_state['values']['storage'] != $data['data']['storage']['data']) { continue; } if ($form_state['values']['disabled'] != 'all' && $form_state['values']['disabled'] != $form_state['pages']['disabled'][$name]) { continue; } if ($form_state['values']['search'] && strpos($data['data']['name']['data'], $form_state['values']['search']) === FALSE && strpos($data['data']['path']['data'], $form_state['values']['search']) === FALSE && strpos($data['data']['title']['data'], $form_state['values']['search']) === FALSE) { continue; } // Set up sorting switch ($form_state['values']['order']) { case 'disabled': $sorts[$name] = !$form_state['pages']['disabled'][$name] . $data['data']['title']['data']; break; case 'title': $sorts[$name] = $data['data']['title']['data']; break; case 'name': $sorts[$name] = $data['data']['name']['data']; break; case 'path': $sorts[$name] = $data['data']['path']['data']; break; case 'type': $sorts[$name] = $data['data']['type']['data']; break; case 'storage': $sorts[$name] = $data['data']['storage']['data']; break; } } // Now actually sort if ($form_state['values']['sort'] == 'desc') { arsort($sorts); } else { asort($sorts); } // Nuke the original. $form_state['pages']['rows'] = array(); // And restore. foreach ($sorts as $name => $title) { $form_state['pages']['rows'][$name] = $rows[$name]; } } /** * Render the edit page for a a page, custom or system. */ function page_manager_edit_page($page) { drupal_set_title($page->subtask['admin title']); // Provide and process the save page form before anything else. $form_state = array('page' => &$page); $built_form = drupal_build_form('page_manager_save_page_form', $form_state); $form = drupal_render($built_form); $operations = page_manager_get_operations($page); $args = array('summary'); $rendered_operations = page_manager_render_operations($page, $operations, $args, array('class' => array('operations-main')), 'nav'); $content = page_manager_get_operation_content(FALSE, $page, $args, $operations); $output = theme('page_manager_edit_page', array('page' => $page, 'save' => $form, 'operations' => $rendered_operations, 'content' => $content)); return array('#markup' => $output); } /** * Entry point to edit a single operation for a page. * * @param $js * Whether or not the page was called via javascript. * @param $page * The cached page that is being edited. * @param ... * A number of items used to drill down into the actual operation called. */ function page_manager_edit_page_operation() { $args = func_get_args(); $js = array_shift($args); $page = array_shift($args); $operations = page_manager_get_operations($page); $content = page_manager_get_operation_content($js, $page, $args, $operations); // If the operation requested we go somewhere else afterward, oblige it. if (isset($content['new trail'])) { $args = $content['new trail']; // Get operations again, for the operation may have changed their availability. $operations = page_manager_get_operations($page); $content = page_manager_get_operation_content($js, $page, $args, $operations); } // Rendering the content may have been a form submission that changed the // operations, such as renaming or adding a handler. Thus we get a new set // of operations. $operations = page_manager_get_operations($page); $rendered_operations = page_manager_render_operations($page, $operations, $args, array('class' => array('operations-main')), 'nav'); // Since this form should never be submitted to this page, process it late so // that we can be sure it notices changes. $form_state = array('page' => &$page); $built_form = drupal_build_form('page_manager_save_page_form', $form_state); $form = drupal_render($built_form); $output = theme('page_manager_edit_page', array('page' => $page, 'save' => $form, 'operations' => $rendered_operations, 'content' => $content)); if ($js) { $commands = array(); $commands[] = ajax_command_replace('#page-manager-edit', $output); print ajax_render($commands); ajax_footer(); return; } drupal_set_title($page->subtask['admin title']); return $output; } /** * Take the operations array from a task and expand it. * * This allows some of the operations to be dynamic, based upon settings * on the task or the task's handlers. Each operation should have a type. In * addition to all the types allowed in page_manager_render_operations, these * types will be dynamically replaced with something else: * - 'handlers': An automatically created group that contains all the task's * handlers and appropriate links. * - 'function': A callback (which will be placed in the 'function' parameter * that should return an array of operations. This can be used to provide * additional, dynamic links if needed. */ function page_manager_get_operations($page, $operations = NULL) { if (!isset($operations)) { // All tasks have at least these 2 ops: $operations = array( 'summary' => array( 'title' => t('Summary'), 'description' => t('Get a summary of the information about this page.'), 'path' => 'admin/structure/pages/edit', 'ajax' => FALSE, 'no operations' => TRUE, 'form info' => array( 'no buttons' => TRUE, ), 'form' => 'page_manager_page_summary', ), 'actions' => array( 'type' => 'group', 'title' => '', 'class' => array('operations-actions'), 'location' => 'primary', 'children' => array(), ), ); if (isset($page->subtask['operations'])) { $operations += $page->subtask['operations']; // add actions separately. if (!empty($page->subtask['operations']['actions'])) { $operations['actions']['children'] += $page->subtask['operations']['actions']['children']; } } $operations['handlers'] = array('type' => 'handlers'); } $result = array(); foreach ($operations as $id => $operation) { if (empty($operation['type'])) { $operation['type'] = 'operation'; } switch ($operation['type']) { case 'handlers': $result[$id] = page_manager_get_handler_operations($page); break; case 'function': if (function_exists($operation['function'])) { $retval = $function($page, $operation); if (is_array($retval)) { $result[$id] = $retval; } } break; default: $result[$id] = $operation; } } if (!empty($page->subtask['enable callback']) && !empty($page->subtask['disabled']) && empty($result['actions']['children']['enable'])) { $result['actions']['children']['enable'] = array( 'title' => t('Enable'), 'description' => t('Activate this page so that it will be in use in your system.'), 'form' => 'page_manager_enable_form', 'ajax' => FALSE, 'silent' => TRUE, 'no update and save' => TRUE, 'form info' => array( 'finish text' => t('Enable'), ), ); } if (!empty($page->subtask['enable callback']) && empty($page->subtask['disabled']) && empty($result['actions']['children']['disable'])) { $result['actions']['children']['disable'] = array( 'title' => t('Disable'), 'description' => t('De-activate this page. The data will remain but the page will not be in use on your system.'), 'form' => 'page_manager_disable_form', 'ajax' => FALSE, 'silent' => TRUE, 'no update and save' => TRUE, 'form info' => array( 'finish text' => t('Disable'), ), ); } $result['actions']['children']['add'] = array( 'title' => t('Add variant'), 'description' => t('Add a new variant to this page.'), 'form' => 'page_manager_handler_add', 'ajax' => FALSE, 'silent' => TRUE, // prevents a message about updating and prevents this item from showing as changed. 'no update and save' => TRUE, // get rid of update and save button which is bad here. 'form info' => array( 'finish text' => t('Create variant'), ), ); // Restrict variant import to users who can already execute arbitrary PHP if (user_access('use PHP for settings')) { $result['actions']['children']['import'] = array( 'title' => t('Import variant'), 'description' => t('Add a new variant to this page from code exported from another page.'), 'form' => 'page_manager_handler_import', ); } if (count($page->handlers) > 1) { $result['actions']['children']['rearrange'] = array( 'title' => t('Reorder variants'), 'ajax' => FALSE, 'description' => t('Change the priority of the variants to ensure that the right one gets selected.'), 'form' => 'page_manager_handler_rearrange', ); } // This is a special operation used to configure a new task handler before // it is added. if (isset($page->new_handler)) { $plugin = page_manager_get_task_handler($page->new_handler->handler); $result['actions']['children']['configure'] = array( 'title' => t('Configure'), 'description' => t('Configure a newly created variant prior to actually adding it to the page.'), 'ajax' => FALSE, 'no update and save' => TRUE, // get rid of update and save button which is bad here. 'form info' => array( // We use our own cancel and finish callback to handle the fun stuff. 'finish callback' => 'page_manager_handler_add_finish', 'cancel callback' => 'page_manager_handler_add_cancel', 'show trail' => TRUE, 'show back' => TRUE, 'finish text' => t('Create variant'), ), 'form' => array( 'forms' => $plugin['forms'], ), ); foreach ($page->forms as $id) { if (isset($plugin['add features'][$id])) { $result['actions']['children']['configure']['form']['order'][$id] = $plugin['add features'][$id]; } else if (isset($plugin['required forms'][$id])) { $result['actions']['children']['configure']['form']['order'][$id] = $plugin['required forms'][$id]; } } } if ($page->locked) { $result['actions']['children']['break-lock'] = array( 'title' => t('Break lock'), 'description' => t('Break the lock on this page so that you can edit it.'), 'form' => 'page_manager_break_lock', 'ajax' => FALSE, 'no update and save' => TRUE, // get rid of update and save button which is bad here. 'form info' => array( 'finish text' => t('Break lock'), ), 'even locked' => TRUE, // show button even if locked 'silent' => TRUE, ); } drupal_alter('page_manager_operations', $result, $page); return $result; } /** * Collect all the operations related to task handlers (variants) and * build a menu. */ function page_manager_get_handler_operations(&$page) { ctools_include('export'); $group = array( 'type' => 'group', 'class' => array('operations-handlers'), 'title' => t('Variants'), ); $operations = array(); // If there is only one variant, let's not have it collapsible. $collapsible = count($page->handler_info) != 1; foreach ($page->handler_info as $id => $info) { if ($info['changed'] & PAGE_MANAGER_CHANGED_DELETED) { continue; } $handler = $page->handlers[$id]; $plugin = page_manager_get_task_handler($handler->handler); $operations[$id] = array( 'type' => 'group', 'class' => array('operations-handlers-' . $id), 'title' => page_manager_get_handler_title($plugin, $handler, $page->task, $page->subtask_id), 'collapsible' => $collapsible, 'children' => array(), ); $operations[$id]['children']['actions'] = array( 'type' => 'group', 'class' => array('operations-handlers-actions-' . $id), 'title' => t('Variant operations'), 'children' => array(), 'location' => $id, ); // There needs to be a 'summary' item here for variants. $operations[$id]['children']['summary'] = array( 'title' => t('Summary'), 'description' => t('Get a summary of the information about this variant.'), 'form info' => array( 'no buttons' => TRUE, ), 'form' => 'page_manager_handler_summary', ); if ($plugin && isset($plugin['operations'])) { $operations[$id]['children'] += $plugin['operations']; } $actions = &$operations[$id]['children']['actions']['children']; $actions['clone'] = array( 'title' => t('Clone'), 'description' => t('Make an exact copy of this variant.'), 'form' => 'page_manager_handler_clone', ); $actions['export'] = array( 'title' => t('Export'), 'description' => t('Export this variant into code to import into another page.'), 'form' => 'page_manager_handler_export', ); if ($handler->export_type == (EXPORT_IN_CODE | EXPORT_IN_DATABASE)) { $actions['delete'] = array( 'title' => t('Revert'), 'description' => t('Remove all changes to this variant and revert to the version in code.'), 'form' => 'page_manager_handler_delete', 'no update and save' => TRUE, 'form info' => array( 'finish text' => t('Revert'), ), ); } else if ($handler->export_type != EXPORT_IN_CODE) { $actions['delete'] = array( 'title' => t('Delete'), 'description' => t('Remove this variant from the page completely.'), 'form' => 'page_manager_handler_delete', 'form info' => array( 'finish text' => t('Delete'), 'save text' => t('Delete and save'), ), ); } if (!empty($handler->disabled)) { $actions['enable'] = array( 'title' => t('Enable'), 'description' => t('Activate this variant so that it will be in use in your system.'), 'form' => 'page_manager_handler_enable', 'silent' => TRUE, 'form info' => array( 'finish text' => t('Enable'), 'save text' => t('Enable and save'), ), ); } else { $actions['disable'] = array( 'title' => t('Disable'), 'description' => t('De-activate this variant. The data will remain but the variant will not be in use on your system.'), 'form' => 'page_manager_handler_disable', 'silent' => TRUE, 'form info' => array( 'finish text' => t('Disable'), 'save text' => t('Disable and save'), ), ); } drupal_alter('page_manager_variant_operations', $operations[$id], $handler); } if (empty($operations)) { $operations['empty'] = array( 'type' => 'text', 'title' => t('No variants'), ); } $group['children'] = $operations; return $group; } /** * Get an operation from a trail. * * @return array($operation, $active, $args) */ function page_manager_get_operation($operations, $trail) { $args = $trail; $stop = FALSE; $active = array(); $titles = array(); // Drill down into operations array: while (!$stop) { $check = reset($args); $stop = TRUE; if (is_array($operations)) { if (isset($operations[$check])) { $active[] = $check; $operation = array_shift($args); // check to see if this operation has children. If so, we continue. if (!isset($operations[$check]['children'])) { $operations = $operations[$check]; } else { $titles[] = $operations[$check]['title']; $operations = $operations[$check]['children']; // continue only if the operation hs children. $stop = FALSE; } } } } return array($operations, $active, $args, $titles); } /** * Fetch the content for an operation. * * First, this drills down through the arguments to find the operation, and * turns whatever it finds into the active trail which is then used to * hilite where we are when rendering the operation list. * * The arguments are discovered from the URL, and are an exact match for where * the operation is in the hierarchy. For example, handlers/foo/settings will * be the operation to edit the settings for the handler named foo. This comes * in as an array ('handlers', 'foo', 'settings') and is used to find where the * data for that operation is in the array. */ function page_manager_get_operation_content($js, &$page, $trail, $operations) { list($operation, $active, $args, $titles) = page_manager_get_operation($operations, $trail); // Once we've found the operation, send it off to render. if ($operation) { $content = _page_manager_get_operation_content($js, $page, $active, $operation, $titles, $args); } if (empty($content)) { $content = _page_manager_get_operation_content($js, $page, array('summary'), $operations['summary']); } return $content; } /** * Fetch the content for an operation, after it's been discovered from arguments. * * This system runs through the CTools form wizard. Each operation specifies a form * or set of forms that it may use. Operations may also specify wrappers and can * set their own next/finish handlers so that they can make additional things happen * at the end. */ function _page_manager_get_operation_content($js, &$page, $active, $operation, $titles = array(), $args = array()) { if (isset($operation['form'])) { $form_info = array( 'id' => 'page_manager_page', 'finish text' => t('Update'), 'show trail' => FALSE, 'show back' => FALSE, 'show return' => FALSE, 'show cancel' => FALSE, 'next callback' => 'page_manager_edit_page_next', 'finish callback' => 'page_manager_edit_page_finish', // Items specific to the 'edit' routines that will get moved over: 'path' => page_manager_edit_url($page->task_name, $active) . "/%step", // wrapper function to add an extra finish button. 'wrapper' => 'page_manager_operation_wrapper', ); // If $operation['form'] is simply a string, then it is the function // name of the form. if (!is_array($operation['form'])) { $form_info['order'] = array( 'form' => $operation['title'], ); $form_info['forms'] = array( 'form' => array('form id' => $operation['form']), ); if (isset($operation['wrapper'])) { $form_info['forms']['form']['wrapper'] = $operation['wrapper']; } } // Otherwise it's the order and forms arrays directly. else { $form_info['order'] = $operation['form']['order']; $form_info['forms'] = $operation['form']['forms']; } // Allow the operation to override any form info settings: if (isset($operation['form info'])) { foreach ($operation['form info'] as $key => $setting) { $form_info[$key] = $setting; } } if (!empty($page->subtask['operations include'])) { // Quickly load any files necessary to display the forms. $page->subtask['operations include']['function'] = 'nop'; ctools_plugin_get_function($page->subtask, 'operations include'); } $step = array_shift($args); // If step is unset, go with the basic step. if (!isset($step)) { $step = current(array_keys($form_info['order'])); } // If it is locked, hide the buttonzzz! if ($page->locked && empty($operation['even locked'])) { $form_info['no buttons'] = TRUE; } ctools_include('wizard'); $form_state = array( 'page' => $page, 'type' => 'edit', 'ajax' => $js && (!isset($operation['ajax']) || !empty($operation['ajax'])), 'rerender' => TRUE, 'trail' => $active, 'task_name' => $page->task_name, 'task_id' => $page->task_id, 'task' => $page->task, 'subtask_id' => $page->subtask_id, 'subtask' => $page->subtask, 'operation' => $operation, ); if ($active && $active[0] == 'handlers' && isset($form_state['page']->handlers[$form_state['trail'][1]])) { $form_state['handler_id'] = $form_state['trail'][1]; $form_state['handler'] = &$form_state['page']->handlers[$form_state['handler_id']]; } if ($active && $active[0] == 'actions' && $active[1] == 'configure' && isset($form_state['page']->new_handler)) { $form_state['handler_id'] = $form_state['page']->new_handler->name; $form_state['handler'] = &$form_state['page']->new_handler; } $built_form = ctools_wizard_multistep_form($form_info, $step, $form_state); $output = drupal_render($built_form); $title = empty($form_state['title']) ? $operation['title'] : $form_state['title']; $titles[] = $title; $title = implode(' » ', array_filter($titles)); // If there are messages for the form, render them. if ($messages = theme('status_messages')) { $output = $messages . $output; } $description = isset($operation['admin description']) ? $operation['admin description'] :