diff --git css/export-ui-list.css css/export-ui-list.css new file mode 100644 index 0000000..3a778ab --- /dev/null +++ css/export-ui-list.css @@ -0,0 +1,28 @@ +/* $Id: panels-item.css,v 1.1.2.1 2010/02/17 01:09:46 merlinofchaos Exp $ */ +body form#ctools-export-ui-list-form { + margin: 0 0 20px 0; +} + +#ctools-export-ui-list-form .form-item { + padding-right: 1em; /* LTR */ + float: left; /* LTR */ + margin-top: 0; + margin-bottom: 0; +} + +#ctools-export-ui-list-items { + width: 100%; +} + +#edit-order-wrapper { + clear: left; /* LTR */ +} + +#ctools-export-ui-list-form .form-submit { + margin-top: 1.65em; + float: left; /* LTR */ +} + +tr.ctools-export-ui-disabled { + color: #999; +} diff --git ctools.module ctools.module index ad3cb4e..37d45b7 100644 --- ctools.module +++ ctools.module @@ -284,7 +284,7 @@ function _ctools_passthrough(&$items, $type = 'theme') { require_once './' . $file->filename; list($tool) = explode('.', $file->name, 2); - $function = 'ctools_' . $tool . '_' . $type; + $function = 'ctools_' . str_replace ('-', '_', $tool) . '_' . $type; if (function_exists($function)) { $function($items); } @@ -760,3 +760,46 @@ function ctools_file_check_directory(&$directory, $mode = 0, $form_item = NULL) return TRUE; } + +/** + * Menu loader; Load exportables when used with export-ui. + */ +function ctools_export_ui_load($item_name, $plugin_name) { + $return = &ctools_static(__FUNCTION__, FALSE); + + if (!$return) { + ctools_include('export'); + ctools_include('export-ui'); + $plugin = ctools_get_export_ui($plugin_name); + + if ($plugin) { + // Get the load callback. + $schema = ctools_export_get_schema($plugin['schema']); + $return = call_user_func($schema['export']['load callback'], $item_name); + } + } + + return $return; +} + +/** + * Menu access callback for various tasks of export-ui. + */ +function ctools_export_ui_task_access($export, $op) { + // TODO: This needs to be more configurable. + + if (!user_access('administer site configuration')) { + return FALSE; + } + switch ($op) { + case 'revert': + return ($export->export_type & EXPORT_IN_DATABASE) && ($export->export_type & EXPORT_IN_CODE); + case 'delete': + return ($export->export_type & EXPORT_IN_DATABASE) && !($export->export_type & EXPORT_IN_CODE); + case 'disable': + return empty($export->disabled); + case 'enable': + return !empty($export->disabled); + } + return TRUE; +} diff --git includes/export-ui.admin.inc includes/export-ui.admin.inc new file mode 100644 index 0000000..ad77dbe --- /dev/null +++ includes/export-ui.admin.inc @@ -0,0 +1,213 @@ +list_page($js, $_POST); + } + else { + return t('Configuration error. No handler found.'); + } +} + +/** + * Main page callback to manipulate exportables. + * + * This simply loads the object defined in the plugin and hands it off to + * a method based upon the name of the operation in use. This can easily + * be used to add more ops. + */ +function ctools_export_ui_switcher_page($plugin_name, $op, $item = array()) { + $js = !empty($_REQUEST['ctools_ajax']); + + // Load the $plugin information + ctools_include('export'); + $plugin = ctools_get_export_ui($plugin_name); + + $handler = ctools_export_ui_get_handler($plugin); + if ($handler) { + $method = $op . '_page'; + if (method_exists($handler, $method)) { + $args = array( + 'js' => $js, + 'input' => $_POST, + 'item' => $item, + ); + return call_user_func_array(array($handler, $method), $args); + } + } + else { + return t('Configuration error. No handler found.'); + } +} + +/** + * Provide a form to confirm one of the provided actions. + */ +function ctools_export_ui_confirm(&$form_state, $plugin_name, $op = 'delete', $export) { + $plugin = ctools_get_export_ui($plugin_name); + + $form = array(); + $form['export'] = array('#type' => 'value', '#value' => $export); + $form['action'] = array('#type' => 'value', '#value' => $op); + $form['plugin'] = array('#type' => 'value', '#value' => $plugin); + + $export_key = $plugin['export']['key']; + $question = str_replace('!action', $plugin['allowed operations'][$op], $plugin['form']['string']['confirmation']['question']); + $question = str_replace('%title', $export->{$export_key}, $plugin['form']['string']['confirmation']['question']); + + $form = confirm_form($form, + $question, + ctools_export_ui_plugin_base_path($plugin['name']), + $plugin['form']['string']['confirmation'][$op], + drupal_ucfirst($plugin['allowed operations'][$op]), t('Cancel') + ); + return $form; +} + +/** + * Submit handler for the ctools_export_ui_confirm form. + */ +function ctools_export_ui_confirm_submit($form, &$form_state) { + ctools_include('export'); + $plugin = $form['plugin']['#value']; + + $export = $form_state['values']['export']; + switch ($form_state['values']['action']) { + case 'revert': + case 'delete': + $schema = ctools_export_get_schema($plugin['schema']); + call_user_func($schema['export']['delete callback'], $export); + break; + } + $form_state['redirect'] = ctools_export_ui_plugin_base_path($plugin['name']); +} + +/** + * Enable or disable an exportable. + */ +function ctools_export_ui_switcher(&$form_state, $plugin_name, $op = 'enable', $export) { + $plugin = ctools_get_export_ui($plugin_name); + $export_key = $plugin['export']['key']; + + ctools_export_set_object_status($export, $op != 'enable'); + drupal_set_message(str_replace('%title', $export->{$export_key}, $plugin['form']['string']['message'][$op])); + drupal_goto(ctools_export_ui_plugin_base_path($plugin['name'])); +} + +/** + * Page callback for import form. Switches form output to export form + * if import submission has occurred. + */ +function ctools_export_ui_import_page($plugin_name) { + if (!empty($_POST) && $_POST['form_id'] == 'ctools_export_ui_form') { + return drupal_get_form('ctools_export_ui_form', $plugin_name, 'add'); + } + return drupal_get_form('ctools_export_ui_import', $plugin_name); +} + +/** + * Import form. Provides simple helptext instructions and textarea for + * pasting a export definition. + */ +function ctools_export_ui_import($form_state, $plugin_name) { + ctools_include('export-ui'); + $plugin = ctools_get_export_ui($plugin_name); + + drupal_set_title($plugin['form']['string']['title']['import']); + + $form = array(); + $form['plugin'] = array( + '#type' => 'value', + '#value' => $plugin, + ); + + $form['help'] = array( + '#type' => 'item', + '#value' => $plugin['form']['string']['help']['import'], + ); + $form['import'] = array( + '#title' => t('@plugin object', array('@plugin' => $plugin['title'])), + '#type' => 'textarea', + '#rows' => 10, + '#required' => TRUE, + ); + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Import'), + ); + return $form; +} + +/** + * Import form submit handler. Evaluates import code and transfers to + * export definition form. + */ +function ctools_export_ui_import_submit($form, &$form_state) { + $plugin = $form['plugin']['#value']; + $export_key = $plugin['export']['key']; + + $items = array(); + if ($import = $form_state['values']['import']) { + ob_start(); + $export = eval($import); + ob_end_clean(); + } + + if (is_object($export)) { + if (!empty($export->{$export_key})) { + + $schema = ctools_export_get_schema($plugin['schema']); + + // TODO: If $schema['export']['load callback'] == FALSE we should issue + // an error - but maybe it should happen in a more generic place. + if (call_user_func($schema['export']['load callback'], $export->{$export_key})) { + drupal_set_message(t('A @plugin with this name already exists. Please remove the existing export before importing this definition.', array('@plugin' => $plugin['title'])), 'error'); + } + else { + drupal_set_title($plugin['form']['string']['title']['export']); + $output = drupal_get_form('ctools_export_ui_form', $plugin['name'], 'add', (object) $export); + print theme('page', $output); + exit; + } + } + } + else { + drupal_set_message(t('An error occurred while importing. Please check your export definition.', 'error')); + + $form_state['redirect'] = ctools_export_ui_plugin_base_path($plugin['name']); + } +} + +/** + * Provides a form with an exported export definition for use in modules. + * + * @param $cid + * A export id. + * + * @return + * A FormAPI array. + */ +function ctools_export_ui_export(&$form_state, $export, $plugin_name) { + ctools_include('export-ui'); + $plugin = ctools_get_export_ui($plugin_name); + + $export_key = $plugin['export']['key']; + + drupal_set_title(str_replace('%title', $plugin['title'], $plugin['form']['string']['title']['export'])); + + return ctools_export_form($form_state, ctools_export_ui_export_object($plugin, $export), t('Export')); +} diff --git includes/export-ui.inc includes/export-ui.inc new file mode 100644 index 0000000..32eeed0 --- /dev/null +++ includes/export-ui.inc @@ -0,0 +1,344 @@ + 'ctools_export_ui_defaults', + ); +} + +/** + * Provide defaults for an export-ui plugin. + */ +function ctools_export_ui_defaults($info, &$plugin) { + ctools_include('export'); + + $plugin += array( + 'has menu' => TRUE, + 'title' => $plugin['name'], + 'export' => array(), + 'allowed operations' => array(), + 'menu' => array(), + 'form' => array(), + 'list' => NULL, + ); + + if (empty($plugin['schema'])) { + if ($plugin['has menu']) { + // We need to issue a warning as schema is a required key. + drupal_set_message(t('The plugin definition of @plugin is missing the "schema" key.', array('@plugin' => $plugin['name'])), 'error'); + } + } + else { + $schema = ctools_export_get_schema($plugin['schema']); + + $plugin['export'] += array( + // Add the identifier key from the schema so we don't have to call + // ctools_export_get_schema() just for that. + 'key' => $schema['export']['key'], + ); + + // Add some default fields that appear often in exports + // If these use different keys they can easily be specified in the + // $plugin. + + if (empty($plugin['export']['admin_title']) && !empty($schema['fields']['admin_title'])) { + $plugin['export']['admin_title'] = 'admin_title'; + } + if (empty($plugin['export']['admin_description']) && !empty($schema['fields']['admin_description'])) { + $plugin['export']['admin_description'] = 'admin_description'; + } + } + + // Define allowed operations, and the name of the operations. + $plugin['allowed operations'] += array( + 'edit' => t('Edit'), + 'enable' => t('Enable'), + 'disable' => t('Disable'), + 'revert' => t('Revert'), + 'delete' => t('Delete'), + 'clone' => t('Clone'), + 'import' => t('Import'), + 'export' => t('Export'), + ); + + if ($plugin['has menu']) { + $plugin['menu'] += array( + 'menu item' => str_replace(' ', '-', $plugin['name']), + 'menu prefix' => 'admin/build', + ); + + $prefix_count = count(explode('/', $plugin['menu']['menu prefix'])); + + $plugin['menu'] += array( + // Default menu items that should be declared. + 'items' => array( + 'list callback' => array( + 'path' => '', + // Menu items are translated by the menu system. + // TODO: We need more flexibility in title. The title of the admin page + // is not necessarily the title of the object, plus we need + // plural, singular, proper, not proper, etc. + 'title' => $plugin['title'], + 'description' => 'List '. $plugin['title'], + 'page callback' => 'ctools_export_ui_list_items', + 'page arguments' => array($plugin['name']), + 'type' => MENU_NORMAL_ITEM, + ), + 'list' => array( + 'path' => 'list', + 'title' => 'List', + 'description' => 'List '. $plugin['title'], + 'page callback' => 'ctools_export_ui_list_items', + 'page arguments' => array( $plugin['name']), + 'type' => MENU_DEFAULT_LOCAL_TASK, + ), + 'add' => array( + 'path' => 'add', + 'title' => 'Add', + 'description' => 'Add a new '. $plugin['title'], + 'page callback' => 'ctools_export_ui_switcher_page', + 'page arguments' => array($plugin['name'], 'add'), + 'type' => MENU_LOCAL_TASK, + ), + 'edit callback' => array( + 'path' => 'list/%ctools_export_ui', + 'page callback' => 'ctools_export_ui_switcher_page', + 'page arguments' => array($plugin['name'], 'edit', $prefix_count + 2), + 'load arguments' => array($plugin['name']), + 'type' => MENU_CALLBACK, + ), + 'edit' => array( + 'path' => 'list/%ctools_export_ui/edit', + 'title' => 'Edit', + 'page callback' => 'ctools_export_ui_switcher_page', + 'page arguments' => array($plugin['name'], 'edit', $prefix_count + 2), + 'load arguments' => array($plugin['name']), + 'type' => MENU_DEFAULT_LOCAL_TASK, + ), + ), + ); + + if ($plugin['allowed operations']['import']) { + $plugin['menu']['items'] += array( + 'import' => array( + 'path' => 'import', + 'title' => 'Import', + 'description' => 'Import '. $plugin['title'] .' to your site.', + // We allow permissions to import only to users that are allowed to + // execute php. + 'access callback' => 'ctools_access_multiperm', + 'access arguments' => array('use PHP for block visibility'), + 'page callback' => 'ctools_export_ui_import_page', + 'page arguments' => array($plugin['name']), + 'type' => MENU_LOCAL_TASK, + ), + ); + } + + if ($plugin['allowed operations']['export']) { + $plugin['menu']['items'] += array( + 'export' => array( + 'path' => 'list/%ctools_export_ui/export', + 'title' => 'Export', + 'description' => 'Export '. $plugin['title'], + 'page callback' => 'drupal_get_form', + 'page arguments' => array('ctools_export_ui_export', $prefix_count + 2, $plugin['name']), + 'load arguments' => array($plugin['name']), + 'type' => MENU_LOCAL_TASK, + ), + ); + } + + if ($plugin['allowed operations']['revert']) { + $plugin['menu']['items'] += array( + 'revert' => array( + 'path' => 'list/%ctools_export_ui/revert', + 'title' => 'Revert', + 'description' => 'Revert '. $plugin['title'], + 'page callback' => 'drupal_get_form', + 'page arguments' => array('ctools_export_ui_confirm', $plugin['name'], 'revert', $prefix_count + 2), + 'load arguments' => array($plugin['name']), + 'access callback' => 'ctools_export_ui_task_access', + 'access arguments' => array($prefix_count + 2, 'revert'), + 'type' => MENU_CALLBACK, + ), + ); + } + + if ($plugin['allowed operations']['clone']) { + $plugin['menu']['items'] += array( + 'clone' => array( + 'path' => 'list/%ctools_export_ui/clone', + 'title' => 'Clone', + 'description' => 'Revert '. $plugin['title'], + 'page callback' => 'ctools_export_ui_switcher_page', + 'page arguments' => array($plugin['name'], 'clone', $prefix_count + 2), + 'load arguments' => array($plugin['name']), + 'type' => MENU_CALLBACK, + ), + ); + } + + if ($plugin['allowed operations']['delete']) { + $plugin['menu']['items'] += array( + 'delete' => array( + 'path' => 'list/%ctools_export_ui/delete', + 'title' => 'Delete', + 'description' => 'Delete '. $plugin['title'], + 'page callback' => 'drupal_get_form', + 'page arguments' => array('ctools_export_ui_confirm', $plugin['name'], 'delete', $prefix_count + 2), + 'load arguments' => array($plugin['name']), + 'access callback' => 'ctools_export_ui_task_access', + 'access arguments' => array($prefix_count + 2, 'delete'), + 'type' => MENU_CALLBACK, + ), + ); + } + + if ($plugin['allowed operations']['enable']) { + $plugin['menu']['items'] += array( + 'enable' => array( + 'path' => 'list/%ctools_export_ui/enable', + 'title' => 'Enable', + 'description' => 'Enable '. $plugin['title'], + 'page callback' => 'ctools_export_ui_switcher_page', + 'page arguments' => array($plugin['name'], 'enable', $prefix_count + 2), + 'load arguments' => array($plugin['name']), + 'access callback' => 'ctools_export_ui_task_access', + 'access arguments' => array($prefix_count + 2, 'enable'), + 'type' => MENU_CALLBACK, + ), + ); + } + + if ($plugin['allowed operations']['disable']) { + $plugin['menu']['items'] += array( + 'disable' => array( + 'path' => 'list/%ctools_export_ui/disable', + 'title' => 'Disable', + 'description' => 'Disable '. $plugin['title'], + 'page callback' => 'ctools_export_ui_switcher_page', + 'page arguments' => array($plugin['name'], 'disable', $prefix_count + 2), + 'load arguments' => array($plugin['name']), + 'access callback' => 'ctools_export_ui_task_access', + 'access arguments' => array($prefix_count + 2, 'disable'), + 'type' => MENU_CALLBACK, + ), + ); + } + } + + // Define form elements. + $plugin['form'] += array( + 'settings' => function_exists($plugin['name'] . '_form') ? $plugin['name'] . '_form' : '', + 'string' => array( + // Strings used in drupal_set_title(). + 'title' => array( + 'add' => t('Add a new @plugin', array('@plugin' => $plugin['title'])), + // The "%title" will be replaced in ctools_export_ui_form(), as in this + // stage we dont have the specific exportable object. + 'edit' => t('Editing @plugin %title', array('@plugin' => $plugin['title'])), + 'view' => t('Viewing @plugin %title', array('@plugin' => $plugin['title'])), + 'clone' => t('Cloning @plugin %title', array('@plugin' => $plugin['title'])), + + 'import' => t('Import @plugin', array('@plugin' => $plugin['title'])), + 'export' => t('Export @plugin', array('@plugin' => $plugin['title'])), + ), + // Strings used in confirmation pages. + 'confirmation' => array( + // The "!action" and "%title" will be replaced in + // ctools_export_ui_form(). + 'question' => t('Are you sure you want to !action the @plugin %title?', array('@plugin' => $plugin['title'])), + 'revert' => t('This action will permanently remove any customizations made to this export.'), + 'delete' => t('This action will remove this export permanently from your site.'), + ), + // Strings used in $forms. + 'help' => array( + 'import' => t('You can import an exported definition by pasting the exported object code into the field below.'), + ), + // Strings used in drupal_set_message(). + 'message' => array( + 'enable' => t('@plugin %title was enabled.', array('@plugin' => $plugin['title'])), + 'disable' => t('@plugin %title was disabled.', array('@plugin' => $plugin['title'])), + ), + ), + ); +} + +/** + * Get the class to handle creating a list of exportable items. + * + * @return + * Either the lister class or FALSE if one could not be had. + */ +function ctools_export_ui_get_handler($plugin) { + $cache = &ctools_static(__FUNCTION__, array()); + if (empty($cache[$plugin['name']])) { + // If a list class is not specified by the plugin, fall back to the + // default ctools_export_ui plugin instead. + if (empty($plugin['list'])) { + $default = ctools_get_export_ui('ctools_export_ui'); + $class = ctools_plugin_get_class($default, 'handler'); + } + else { + $class = ctools_plugin_get_class($plugin, 'handler'); + } + + if ($class) { + $cache[$plugin['name']] = new $class(); + $cache[$plugin['name']]->init($plugin); + } + } + return !empty($cache[$plugin['name']]) ? $cache[$plugin['name']] : FALSE; +} + +/** + * CTools export function. + */ +function ctools_export_ui_export_object($plugin, $export, $indent = '') { + $schema = ctools_export_get_schema($plugin['schema']); + + ctools_include('export'); + return ctools_export_object($plugin['schema'], $export, $indent); +} + +/** + * Get redirection path from a plugin. + * + * @param $plguin_name + * The plugin name. + * + * @return + * The menu path to the plugin's list. + */ +function ctools_export_ui_plugin_base_path($plugin_name) { + $plugin = ctools_get_export_ui($plugin_name); + + return $plugin['menu']['menu prefix'] .'/'. $plugin['menu']['menu item']; +} + +/** + * Helper function to include CTools plugins and get an export-ui exportable. + * + * @param $plugin_name + * The plugin that should be laoded. + */ +function ctools_get_export_ui($plugin_name) { + ctools_include('plugins'); + return ctools_get_plugins('ctools', 'export_ui', $plugin_name); + +} + +/** + * Helper function to include CTools plugins and get all export-ui exportables. + */ +function ctools_get_export_uis() { + ctools_include('plugins'); + return ctools_get_plugins('ctools', 'export_ui', $plugin_name); +} diff --git includes/export-ui.menu.inc includes/export-ui.menu.inc new file mode 100644 index 0000000..cdea750 --- /dev/null +++ includes/export-ui.menu.inc @@ -0,0 +1,33 @@ + array('administer site configuration'), + 'file' => 'export-ui.admin.inc', + 'file path' => drupal_get_path('module', 'ctools') .'/includes', + // Add the map, so we can get the plugin in export_ui_load(). + 'load arguments' => array('%map'), + ); + + $path = !empty($item['path']) ? $prefix .'/'. $item['path'] : $prefix; + unset($item['path']); + $items[$path] = $item; + } + } + } +} diff --git includes/export.inc includes/export.inc index b465d38..f3d2277 100644 --- includes/export.inc +++ includes/export.inc @@ -460,6 +460,13 @@ function ctools_export_get_schema($table) { 'export callback' => "$schema[module]_export_{$table}", 'list callback' => "$schema[module]_{$table}_list", 'to hook code callback' => "$schema[module]_{$table}_to_hook_code", + + // Define CRUD functions, if none are defined in the exportable + // schema. + 'create callback' => 'ctools_export_new_object', + 'save callback' => function_exists("$schema[module]_save") ? "$schema[module]_save" : FALSE, + 'delete callback' => function_exists("$schema[module]_delete") ? "$schema[module]_delete" : FALSE, + 'load callback' => function_exists("$schema[module]_load") ? "$schema[module]_load" : FALSE, ); return $schema; diff --git js/auto-submit.js js/auto-submit.js new file mode 100644 index 0000000..fa35bfb --- /dev/null +++ js/auto-submit.js @@ -0,0 +1,66 @@ +// $Id: auto-submit.js,v 1.1.2.1 2010/02/17 01:09:46 merlinofchaos Exp $ + +/** + * To make a form auto submit, all you have to do is 3 things: + * + * ctools_add_js('auto-submit'); + * + * On gadgets you want to auto-submit when changed, add the ctools-auto-submit + * class. With FAPI, add: + * @code + * '#attributes' => array('class' => 'ctools-auto-submit'), + * @endcode + * + * Finally, you have to identify which button you want clicked for autosubmit. + * The behavior of this button will be honored if it's ajaxy or not: + * @code + * '#attributes' => array('class' => 'ctools-use-ajax ctools-auto-submit-click'), + * @endcode + * + * Currently only 'select' and 'textfield' types are supported. We probably + * could use additional support for radios and checkboxes. + */ + +Drupal.behaviors.CToolsAutoSubmit = function() { + var timeoutID = 0; + + // Bind to any select widgets that will be auto submitted. + $('select.ctools-auto-submit:not(.ctools-auto-submit-processed)') + .addClass('.ctools-auto-submit-processed') + .change(function() { + $(this.form).find('.ctools-auto-submit-click').click(); + }); + + // Bind to any textfield widgets that will be auto submitted. + $('input[type=text].ctools-auto-submit:not(.ctools-auto-submit-processed)') + .addClass('.ctools-auto-submit-processed') + .keyup(function(e) { + var form = this.form; + switch (e.keyCode) { + case 16: // shift + case 17: // ctrl + case 18: // alt + case 20: // caps lock + case 33: // page up + case 34: // page down + case 35: // end + case 36: // home + case 37: // left arrow + case 38: // up arrow + case 39: // right arrow + case 40: // down arrow + case 9: // tab + case 13: // enter + case 27: // esc + return false; + default: + if (!$(form).hasClass('ctools-ajaxing')) { + if ((timeoutID)) { + clearTimeout(timeoutID); + } + + timeoutID = setTimeout(function() { $(form).find('.ctools-auto-submit-click').click(); }, 300); + } + } + }); +} diff --git plugins/export_ui/ctools_export_ui.class.php plugins/export_ui/ctools_export_ui.class.php new file mode 100644 index 0000000..0976d3c --- /dev/null +++ plugins/export_ui/ctools_export_ui.class.php @@ -0,0 +1,814 @@ +plugin = $plugin; + } + + // ------------------------------------------------------------------------ + // These methods are the API for generating the list of exportable items. + + /** + * Master entry point for handling a list. + * + * It is unlikely that a child object will need to override this method, + * unless the listing mechanism is going to be highly specialized. + */ + function list_page($js, $input) { + ctools_export_load_object_reset($this->plugin['schema']); + // TODO: Probably should be a callback in the schema to handle this in + // case the exports want a more complicated load function. + $this->items = ctools_export_load_object($this->plugin['schema'], 'all'); + + // 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['ctools_export_ui'][$this->plugin['name']]); + if (!$js) { + return drupal_goto($_GET['q']); + } + // clear everything but form id, form build id and form token: + $keys = array_keys($input); + foreach ($keys as $id) { + if (!in_array($id, array('form_id', 'form_build_id', 'form_token'))) { + unset($input[$id]); + } + } + $replace_form = TRUE; + } + + // If there is no input, check to see if we have stored input in the + // session. + if (!isset($input['form_id'])) { + if (isset($_SESSION['ctools_export_ui'][$this->plugin['name']]) && is_array($_SESSION['ctools_export_ui'][$this->plugin['name']])) { + $input = $_SESSION['ctools_export_ui'][$this->plugin['name']]; + } + } + else { + $_SESSION['ctools_export_ui'][$this->plugin['name']] = $input; + unset($_SESSION['ctools_export_ui'][$this->plugin['name']]['q']); + } + + // This is where the form will put the output. + $this->rows = array(); + $this->sorts = array(); + + $form_state = array( + 'plugin' => $this->plugin, + 'input' => $input, + 'rerender' => TRUE, + 'no_redirect' => TRUE, + 'object' => &$this, + ); + + ctools_include('form'); + $form = ctools_build_form('ctools_export_ui_list_form', $form_state); + + $output = $this->list_header($form_state) . $this->list_render($form_state) . $this->list_footer($form_state); + + if (!$js) { + $this->list_css(); + return $form . $output; + } + + ctools_include('ajax'); + $commands = array(); + $commands[] = ctools_ajax_command_replace('#ctools-export-ui-list-items', $output); + if (!empty($replace_form)) { + $commands[] = ctools_ajax_command_replace('#ctools-export-ui-list-form', $form); + } + ctools_ajax_render($commands); + } + + /** + * Create the filter/sort form at the top of a list of exports. + * + * This handles the very default conditions, and most lists are expected + * to override this and call through to parent::list_form() in order to + * get the base form and then modify it as necessary to add search + * gadgets for custom fields. + */ + function list_form(&$form, &$form_state) { + // This forces the form to *always* treat as submitted which is + // necessary to make it work. + $form['#token'] = FALSE; + if (empty($form_state['input'])) { + $form["#post"] = TRUE; + } + + // Add the 'q' in if we are not using clean URLs or it can get lost when + // using this kind of form. + if (!variable_get('clean_url', FALSE)) { + $form['q'] = array( + '#type' => 'hidden', + '#value' => $_GET['q'], + ); + } + + $all = array('all' => t('- All -')); + + $form['top row'] = array( + '#prefix' => '