Index: webform_hooks.php =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/webform/webform_hooks.php,v retrieving revision 1.9.2.2 diff -u -r1.9.2.2 webform_hooks.php --- webform_hooks.php 11 Mar 2010 01:40:15 -0000 1.9.2.2 +++ webform_hooks.php 29 Mar 2010 02:53:12 -0000 @@ -15,6 +15,30 @@ */ /** + * Define callbacks that can be used as select list options. + * + * @return + * An array of callbacks that can be used for select list options. This array + * should be keyed by the "name" of the pre-defined list. The values should + * be an array with the following additional keys: + * - title: The translated title for this list. + * - options callback: The name of the function that will return the list. + * - file: Optional. The file containing the options callback, relative to + * the module root. + */ +function hook_webform_options_info() { + $items = array(); + + $items['days'] = array( + 'title' => t('Days of the week'), + 'options callback' => 'webform_options_days', + 'file' => 'includes/webform.options.inc', + ); + + return $items; +} + +/** * Respond to the loading of Webform submissions. * * @param $submissions Index: webform.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/webform/webform.module,v retrieving revision 1.196.2.12 diff -u -r1.196.2.12 webform.module --- webform.module 27 Mar 2010 23:51:15 -0000 1.196.2.12 +++ webform.module 29 Mar 2010 02:53:12 -0000 @@ -195,6 +195,16 @@ 'type' => MENU_LOCAL_TASK, ); + // AJAX callback for loading select list options. + $items['webform/ajax/options/%webform_menu'] = array( + 'load arguments' => array(3), + 'page callback' => 'webform_select_options_ajax', + 'access callback' => 'node_access', + 'access arguments' => array('update', 3), + 'file' => 'components/select.inc', + 'type' => MENU_CALLBACK, + ); + // Node webform results. $items['node/%webform_menu/webform-results'] = array( 'title' => 'Results', @@ -356,7 +366,7 @@ module_load_include('inc', 'webform', 'includes/webform.components'); if ($cid == 'new') { $components = webform_components(); - $component = in_array($type, array_keys($components)) ? array('type' => $type, 'name' => $_GET['name'], 'mandatory' => $_GET['mandatory'], 'pid' => $_GET['pid'], 'weight' => $_GET['weight']) : FALSE; + $component = in_array($type, array_keys($components)) ? array('type' => $type, 'nid' => $nid, 'name' => $_GET['name'], 'mandatory' => $_GET['mandatory'], 'pid' => $_GET['pid'], 'weight' => $_GET['weight']) : FALSE; } else { $node = node_load($nid); @@ -747,6 +757,14 @@ } /** + * Implementation of hook_webform_select_options_info(). + */ +function webform_webform_select_options_info() { + module_load_include('inc', 'webform', 'includes/webform.options'); + return _webform_options_info(); +} + +/** * Implementation of hook_file_download(). * * Only allow users with view webform submissions to download files. Index: components/select.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/webform/components/select.inc,v retrieving revision 1.39.2.9 diff -u -r1.39.2.9 select.inc --- components/select.inc 25 Mar 2010 01:00:03 -0000 1.39.2.9 +++ components/select.inc 29 Mar 2010 02:53:12 -0000 @@ -27,6 +27,7 @@ 'other_text' => t('Other...'), 'description' => '', 'custom_keys' => FALSE, + 'options_source' => '', ), ); } @@ -48,7 +49,28 @@ function _webform_edit_select($component) { $form = array(); + drupal_add_js(drupal_get_path('module', 'webform') . '/components/select.js', 'module', 'header', FALSE, TRUE, FALSE); + drupal_add_js(array('webform' => array('selectOptionsUrl' => url('webform/ajax/options/' . $component['nid']))), 'setting'); + $other = array(); + if ($info = _webform_select_options_info()) { + $options = array('' => t('None')); + foreach ($info as $name => $source) { + $options[$name] = $source['title']; + } + + $other['options_source'] = array( + '#title' => t('Load a pre-built option list'), + '#type' => 'select', + '#options' => $options, + '#default_value' => $component['extra']['options_source'], + '#weight' => 1, + '#description' => t('Use a pre-built list of options rather than entering options manually. Options will not be editable if using pre-built list.'), + '#parents' => array('extra', 'options_source'), + '#weight' => 5, + ); + } + if (module_exists('select_or_other')) { $other['other_option'] = array( '#type' => 'checkbox', @@ -56,6 +78,7 @@ '#default_value' => $component['extra']['other_option'], '#description' => t('Check this option if you want to allow users to enter an option not on the list.'), '#parents' => array('extra', 'other_option'), + '#weight' => 2, ); $other['other_text'] = array( '#type' => 'textfield', @@ -63,11 +86,12 @@ '#default_value' => $component['extra']['other_text'], '#description' => t('If allowing other options, enter text to be used for other-enabling option.'), '#parents' => array('extra', 'other_text'), + '#weight' => 3, ); } if (module_exists('options_element')) { - $options = _webform_select_options($component['extra']['items']); + $options = _webform_select_options($component); $form['items'] = array( '#type' => 'fieldset', @@ -75,6 +99,7 @@ '#collapsible' => TRUE, '#attributes' => array('class' => 'webform-options-element'), '#element_validate' => array('_webform_edit_validate_options'), + '#weight' => 2, ); $form['items']['options'] = array( @@ -88,6 +113,7 @@ '#key_type' => 'mixed', '#key_type_toggle' => t('Customize keys (Advanced)'), '#key_type_toggled' => $component['extra']['custom_keys'], + '#disabled' => !empty($component['extra']['options_source']), '#weight' => 1, ); @@ -103,6 +129,7 @@ '#rows' => 5, '#weight' => -2, '#required' => TRUE, + '#attributes' => array('readonly' => !empty($component['extra']['options_source'])), '#element_validate' => array('_webform_edit_validate_select'), ); $form['extra'] = array_merge($form['extra'], $other); @@ -120,6 +147,7 @@ '#title' => t('Multiple'), '#default_value' => $component['extra']['multiple'], '#description' => t('Check this option if the user should be allowed to choose multiple values.'), + '#weight' => 0, ); } @@ -233,7 +261,7 @@ // Convert the user-entered options list into an array. $default_value = $filter ? _webform_filter_values($component['value'], NULL, NULL, NULL, FALSE) : $component['value']; - $options = _webform_select_options($component['extra']['items'], !$component['extra']['aslist'], $filter); + $options = _webform_select_options($component, $filter); if ($component['extra']['optrand']) { _webform_shuffle_options($options); @@ -395,7 +423,7 @@ * Convert FAPI 0/1 values into something saveable. */ function _webform_submit_select($component, $value) { - $options = drupal_map_assoc(array_flip(_webform_select_options($component['extra']['items'], TRUE))); + $options = drupal_map_assoc(array_flip(_webform_select_options($component))); $return = NULL; if (is_array($value)) { @@ -431,7 +459,7 @@ $component = $element['#component']; // Convert submitted 'safe' values to un-edited, original form. - $options = _webform_select_options($component['extra']['items'], TRUE); + $options = _webform_select_options($component); $items = array(); if ($component['extra']['multiple']) { @@ -483,7 +511,7 @@ * Implementation of _webform_analysis_component(). */ function _webform_analysis_select($component, $sids = array(), $single = FALSE) { - $options = _webform_select_options($component['extra']['items'], TRUE); + $options = _webform_select_options($component); $show_other_results = $single; $sid_placeholders = count($sids) ? array_fill(0, count($sids), "'%s'") : array(); @@ -568,7 +596,7 @@ if ($component['extra']['multiple'] && $export_options['select_format'] == 'separate') { $headers[0][] = ''; $headers[1][] = $component['name']; - $items = _webform_select_options($component['extra']['items'], TRUE); + $items = _webform_select_options($component); if ($component['extra']['other_option']) { $other_label = !empty($component['extra']['other_text']) ? check_plain($component['extra']['other_text']) : t('Other...'); $items[$other_label] = $other_label; @@ -596,7 +624,7 @@ * Implementation of _webform_csv_data_component(). */ function _webform_csv_data_select($component, $export_options, $value) { - $options = _webform_select_options($component['extra']['items'], TRUE); + $options = _webform_select_options($component); $return = array(); if ($component['extra']['multiple']) { @@ -628,6 +656,92 @@ } /** + * Menu callback; Return a predefined list of select options as JSON. + */ +function webform_select_options_ajax($source_name = '') { + $info = _webform_select_options_info(); + + $component['extra']['options_source'] = $source_name; + if ($source_name && isset($info[$source_name])) { + $options = _webform_select_options_to_text(_webform_select_options($component, FALSE)); + } + else { + $options = ''; + } + + $return = array( + 'elementId' => module_exists('options_element') ? 'edit-items-options-options-field-widget' : 'edit-extra-items', + 'options' => $options, + ); + + drupal_json($return); +} + + +/** + * Generate a list of options for a select list. + */ +function _webform_select_options($component, $filter = TRUE) { + if ($component['extra']['options_source']) { + $options = _webform_select_options_callback($component['extra']['options_source'], 'options', $component); + } + else { + $options = _webform_select_options_from_text($component['extra']['items'], !$component['extra']['aslist'], $filter); + } + + return isset($options) ? $options : array(); +} + +/** + * Load Webform select option info from 3rd party modules. + */ +function _webform_select_options_info() { + static $info; + if (!isset($info)) { + $info = array(); + + foreach (module_implements('webform_select_options_info') as $module) { + $additions = module_invoke($module, 'webform_select_options_info'); + foreach ($additions as $key => $addition) { + $additions[$key]['module'] = $module; + } + $info = array_merge($info, $additions); + } + drupal_alter('webform_select_options_info', $info); + } + return $info; +} + +/** + * Execute a select option callback. + * + * @param $name + * The name of the options group. + * @param $type + * The callback to be executed, with "form" or "options". + * @param $component + * The full Webform component. + * @param $filter + * Whether information returned should be sanitized. Defaults to TRUE. + */ +function _webform_select_options_callback($name, $type, $component, $filter = TRUE) { + $info = _webform_select_options_info(); + + // Include any necessary files. + if (isset($info[$name]['file'])) { + $pathinfo = pathinfo($info[$name]['file']); + $path = ($pathinfo['dirname'] ? $pathinfo['dirname'] . '/' : '') . basename($pathinfo['basename'], '.' . $pathinfo['extension']); + module_load_include($pathinfo['extension'], $info[$name]['module'], $path); + } + + // Execute the callback function. + if (isset($info[$name][$type . ' callback']) && function_exists($info[$name][$type . ' callback'])) { + $function = $info[$name][$type . ' callback']; + return $function($component, $filter); + } +} + +/** * Utility function to split user-entered values from new-line seperated * text into an array of options. * @@ -638,7 +752,7 @@ * @param $filter * Optional. Whether or not to filter returned values. */ -function _webform_select_options($text, $flat = FALSE, $filter = TRUE) { +function _webform_select_options_from_text($text, $flat = FALSE, $filter = TRUE) { static $option_cache = array(); // Keep each processed option block in an array indexed by the MD5 hash of @@ -687,6 +801,40 @@ return $option_cache[$flat][$md5]; } + +/** + * Convert an array of options into text. + */ +function _webform_select_options_to_text($options) { + $output = ''; + $previous_key = false; + + foreach ($options as $key => $value) { + // Convert groups. + if (is_array($value)) { + $output .= '<' . $key . '>' . "\n"; + foreach ($value as $subkey => $subvalue) { + $output .= $subkey . '|' . $subvalue . "\n"; + } + $previous_key = $key; + } + // Typical key|value pairs. + else { + // Exit out of any groups. + if (isset($options[$previous_key]) && is_array($options[$previous_key])) { + $output .= "<>\n"; + } + // Skip empty rows. + if ($options[$key] !== '') { + $output .= $key . '|' . $value . "\n"; + } + $previous_key = $key; + } + } + + return $output; +} + /** * Utility function to shuffle an array while preserving key-value pairs. */ Index: components/grid.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/webform/components/grid.inc,v retrieving revision 1.14.2.4 diff -u -r1.14.2.4 grid.inc --- components/grid.inc 25 Mar 2010 01:00:03 -0000 1.14.2.4 +++ components/grid.inc 29 Mar 2010 02:53:12 -0000 @@ -64,7 +64,7 @@ ); $form['options']['options'] = array( '#type' => 'options', - '#options' => _webform_select_options($component['extra']['options'], TRUE), + '#options' => _webform_select_options_from_text($component['extra']['options'], TRUE), '#optgroups' => FALSE, '#default_value' => FALSE, '#optgroups' => FALSE, @@ -83,7 +83,7 @@ ); $form['questions']['options'] = array( '#type' => 'options', - '#options' => _webform_select_options($component['extra']['questions'], TRUE), + '#options' => _webform_select_options_from_text($component['extra']['questions'], TRUE), '#optgroups' => FALSE, '#default_value' => FALSE, '#optgroups' => FALSE, @@ -148,8 +148,8 @@ '#suffix' => '', ); - $questions = _webform_select_options($component['extra']['questions'], TRUE); - $options = _webform_select_options($component['extra']['options'], TRUE); + $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE); + $options = _webform_select_options_from_text($component['extra']['options'], TRUE); if ($component['extra']['optrand']) { _webform_shuffle_options($options); @@ -185,8 +185,8 @@ * Implementation of _webform_display_component(). */ function _webform_display_grid($component, $value, $format = 'html') { - $questions = _webform_select_options($component['extra']['questions'], TRUE); - $options = _webform_select_options($component['extra']['options'], TRUE); + $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE); + $options = _webform_select_options_from_text($component['extra']['options'], TRUE); $element = array( '#title' => $component['name'], @@ -261,8 +261,8 @@ */ function _webform_analysis_grid($component, $sids = array()) { // Generate the list of options and questions. - $options = _webform_select_options($component['extra']['options'], TRUE); - $questions = _webform_select_options($component['extra']['questions'], TRUE); + $options = _webform_select_options_from_text($component['extra']['options'], TRUE); + $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE); // Generate a lookup table of results. $placeholders = count($sids) ? array_fill(0, count($sids), "'%s'") : array(); @@ -306,7 +306,7 @@ * Implementation of _webform_table_component(). */ function _webform_table_grid($component, $value) { - $questions = _webform_select_options($component['extra']['questions'], TRUE); + $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE); $output = ''; // Set the value as a single string. if (is_array($value)) { @@ -329,7 +329,7 @@ $header = array(); $header[0] = array(''); $header[1] = array($component['name']); - $items = _webform_select_options($component['extra']['questions'], TRUE); + $items = _webform_select_options_from_text($component['extra']['questions'], TRUE); $count = 0; foreach ($items as $key => $item) { // Empty column per sub-field in main header. @@ -349,7 +349,7 @@ * Implementation of _webform_csv_data_component(). */ function _webform_csv_data_grid($component, $export_options, $value) { - $questions = _webform_select_options($component['extra']['questions'], TRUE); + $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE); $return = array(); foreach ($questions as $key => $question) { $return[] = isset($value[$key]) ? $question : ''; Index: includes/webform.options.inc =================================================================== RCS file: includes/webform.options.inc diff -N includes/webform.options.inc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ includes/webform.options.inc 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,134 @@ + t('Days of the week'), + 'options callback' => 'webform_options_days', + 'file' => 'includes/webform.options.inc', + ); + + if (function_exists('countries_api_get_array')) { + $items['countries'] = array( + 'title' => t('Countries'), + 'options callback' => 'webform_options_countries', + 'file' => 'includes/webform.options.inc', + ); + } + + $items['united_states'] = array( + 'title' => t('US states'), + 'options callback' => 'webform_options_united_states', + 'file' => 'includes/webform.options.inc', + ); + + return $items; +} + +/** + * Option list containing the days of the week. + */ +function webform_options_days() { + $days = array( + 'sunday' => t('Sunday'), + 'monday' => t('Monday'), + 'tuesday' => t('Tuesday'), + 'wednesday' => t('Wednesday'), + 'thursday' => t('Thursday'), + 'friday' => t('Friday'), + 'saturday' => t('Saturday'), + ); + + // Order according to site settings for first day. + if ($first_day = variable_get('date_first_day', 0)) { + $week = array_splice($days, $first_day); + $days = array_merge($week, $days); + } + + return $days; +} + +/** + * Options list containing country names. + */ +function webform_options_countries() { + return countries_api_get_array(); +} + +/** + * Options list containing United States states and territories. + */ +function webform_options_united_states() { + return array( + 'AL' => t('Alabama'), + 'AK' => t('Alaska'), + 'AS' => t('American Samoa'), + 'AZ' => t('Arizona'), + 'AR' => t('Arkansas'), + 'CA' => t('California'), + 'CO' => t('Colorado'), + 'CT' => t('Connecticut'), + 'DE' => t('Delaware'), + 'DC' => t('District of Columbia'), + 'FL' => t('Florida'), + 'GA' => t('Georgia'), + 'GU' => t('Guam'), + 'HI' => t('Hawaii'), + 'ID' => t('Idaho'), + 'IL' => t('Illinois'), + 'IN' => t('Indiana'), + 'IA' => t('Iowa'), + 'KS' => t('Kansas'), + 'KY' => t('Kentucky'), + 'LA' => t('Louisiana'), + 'ME' => t('Maine'), + 'MH' => t('Marshall Islands'), + 'MD' => t('Maryland'), + 'MA' => t('Massachusetts'), + 'MI' => t('Michigan'), + 'MN' => t('Minnesota'), + 'MS' => t('Mississippi'), + 'MO' => t('Missouri'), + 'MT' => t('Montana'), + 'NE' => t('Nebraska'), + 'NV' => t('Nevada'), + 'NH' => t('New Hampshire'), + 'NJ' => t('New Jersey'), + 'NM' => t('New Mexico'), + 'NY' => t('New York'), + 'NC' => t('North Carolina'), + 'ND' => t('North Dakota'), + 'MP' => t('Northern Marianas Islands'), + 'OH' => t('Ohio'), + 'OK' => t('Oklahoma'), + 'OR' => t('Oregon'), + 'PW' => t('Palau'), + 'PA' => t('Pennsylvania'), + 'PR' => t('Puerto Rico'), + 'RI' => t('Rhode Island'), + 'SC' => t('South Carolina'), + 'SD' => t('South Dakota'), + 'TN' => t('Tennessee'), + 'TX' => t('Texas'), + 'UT' => t('Utah'), + 'VT' => t('Vermont'), + 'VI' => t('Virgin Islands'), + 'VA' => t('Virginia'), + 'WA' => t('Washington'), + 'WV' => t('West Virginia'), + 'WI' => t('Wisconsin'), + 'WY' => t('Wyoming'), + ); +} Index: components/select.js =================================================================== RCS file: components/select.js diff -N components/select.js --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ components/select.js 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,56 @@ +// $Id$ + +/** + * @file + * Enhancements for select list configuration options. + */ + +(function ($) { + +Drupal.behaviors.webformSelectLoadOptions = function(context) { + settings = Drupal.settings; + + $('#edit-extra-options-source', context).change(function() { + var url = settings.webform.selectOptionsUrl + '/' + this.value; + $.ajax({ + url: url, + success: Drupal.webform.selectOptionsLoad, + dataType: 'json' + }); + }); +} + +Drupal.webform = Drupal.webform || {}; + +Drupal.webform.selectOptionsOriginal = false; +Drupal.webform.selectOptionsLoad = function(result) { + if (Drupal.optionsElement) { + if (result.options) { + // Save the current select options the first time a new list is chosen. + if (Drupal.webform.selectOptionsOriginal === false) { + Drupal.webform.selectOptionsOriginal = $(Drupal.optionElements[result.elementId].manualOptionsElement).val(); + } + $(Drupal.optionElements[result.elementId].manualOptionsElement).val(result.options); + Drupal.optionElements[result.elementId].disable(); + Drupal.optionElements[result.elementId].updateWidgetElements(); + } + else { + Drupal.optionElements[result.elementId].enable(); + if (Drupal.webform.selectOptionsOriginal) { + $(Drupal.optionElements[result.elementId].manualOptionsElement).val(Drupal.webform.selectOptionsOriginal); + Drupal.optionElements[result.elementId].updateWidgetElements(); + Drupal.webform.selectOptionsOriginal = false; + } + } + } + else { + if (result.options) { + $('#' + result.elementId).val(result.options).attr('readonly', 'readonly'); + } + else { + $('#' + result.elementId).attr('readonly', ''); + } + } +} + +})(jQuery);