From 80ff5109a774a69473e2a9d74a49b8dc4da03e8f Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sun, 29 Jul 2007 17:28:23 +0000 Subject: [PATCH] - Patch #162708 by Eaton and Earl: add support for image buttons to FAPI3. This is a small form API extension (doesn't break existing code) that facilitates the Drupal 6 upgrade of the Views module. A good example of why it can be beneficial to start upgrading your modules early on in the code freeze. ;) --- includes/bootstrap.inc | 2 +- includes/common.inc | 3 + includes/form.inc | 147 +++++++++++++++++++------- modules/forum/forum-submitted.tpl.php | 6 +- modules/forum/forums.tpl.php | 2 +- modules/menu/menu.module | 2 +- modules/system/system.module | 3 +- 7 files changed, 121 insertions(+), 44 deletions(-) diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index c5792998223..52bfc5fa5c9 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -1101,6 +1101,6 @@ function ip_address() { $remote_ip = $ip_array[0]; } } - + return $remote_ip; } diff --git a/includes/common.inc b/includes/common.inc index 98b65431686..c07fc1bee59 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -2774,6 +2774,9 @@ function drupal_common_themes() { 'button' => array( 'arguments' => array('element' => NULL), ), + 'image_button' => array( + 'arguments' => array('element' => NULL), + ), 'hidden' => array( 'arguments' => array('element' => NULL), ), diff --git a/includes/form.inc b/includes/form.inc index 865c1fecdee..5ab3d885f49 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -44,7 +44,6 @@ */ function drupal_get_form($form_id) { $form_state = array('storage' => NULL, 'submitted' => FALSE); - $expire = max(ini_get('session.cookie_lifetime'), 86400); $args = func_get_args(); @@ -62,12 +61,7 @@ function drupal_get_form($form_id) { // form_state data from a previous step, we'll retrieve it so it can // be passed on to the form processing code. if (isset($_POST['form_id']) && $_POST['form_id'] == $form_id && !empty($_POST['form_build_id'])) { - if ($cached = cache_get('form_'. $_POST['form_build_id'], 'cache_form')) { - $form = $cached->data; - if ($cached = cache_get('storage_'. $_POST['form_build_id'], 'cache_form')) { - $form_state['storage'] = $cached->data; - } - } + $form = form_get_cache($_POST['form_build_id'], $form_state); } // If the previous bit of code didn't result in a populated $form @@ -84,7 +78,9 @@ function drupal_get_form($form_id) { $form['#build_id'] = $form_build_id; drupal_prepare_form($form_id, $form, $form_state); if (!empty($form['#cache'])) { - cache_set('form_'. $form_build_id, $form, 'cache_form', $expire); + // By not sending the form state, we avoid storing the storage which + // won't have been touched yet. + form_set_cache($form_build_id, $form, NULL); } unset($form_state['post']); } @@ -126,10 +122,7 @@ function drupal_get_form($form_id) { // Now, we cache the form structure so it can be retrieved later for // validation. If $form_state['storage'] is populated, we'll also cache // it so that it can be used to resume complex multi-step processes. - cache_set('form_'. $form_build_id, $form, 'cache_form', $expire); - if (!empty($form_state['storage'])) { - cache_set('storage_'. $form_build_id, $form_state['storage'], 'cache_form', $expire); - } + form_set_cache($form_build_id, $form, $form_state); // Clear out all post data, as we don't want the previous step's // data to pollute this one and trigger validate/submit handling, @@ -144,6 +137,31 @@ function drupal_get_form($form_id) { return drupal_render_form($form_id, $form); } +/** + * Fetch a form from cache. + */ +function form_get_cache($form_build_id, &$form_state) { + if ($cached = cache_get('form_'. $form_build_id, 'cache_form')) { + $form = $cached->data; + if ($cached = cache_get('storage_'. $form_build_id, 'cache_form')) { + $form_state['storage'] = $cached->data; + } + return $form; + } +} + +/** + * Store a form in the cache + */ +function form_set_cache($form_build_id, $form, $form_state) { + $expire = max(ini_get('session.cookie_lifetime'), 86400); + + cache_set('form_'. $form_build_id, $form, 'cache_form', $expire); + if (!empty($form_state['storage'])) { + cache_set('storage_'. $form_build_id, $form_state['storage'], 'cache_form', $expire); + } +} + /** * Retrieves a form using a form_id, populates it with $form_state['values'], * processes it, and returns any validation errors encountered. This @@ -840,13 +858,7 @@ function _form_builder_handle_input_element($form_id, &$form, &$form_state) { $button_type = $form['#executes_submit_callback'] ? 'submit' : 'button'; $form_state['buttons'][$button_type][] = $form; - // See if a submit button was clicked. In Internet Explorer, if ONLY - // one submit button is present, AND the enter key is used to submit - // the form, no form value is sent for it and we'll never detect a - // match. In most cases, though, the following code will properly handle - // finding the clicked button and storing any custom validate and - // submit handlers it has defined. - if (isset($form['#post'][$form['#name']]) && $form['#post'][$form['#name']] == $form['#value']) { + if (_form_button_was_clicked($form)) { $form_state['submitted'] = $form_state['submitted'] || $form['#executes_submit_callback']; // In most cases, we want to use form_set_value() to manipulate @@ -877,6 +889,64 @@ function _form_builder_handle_input_element($form_id, &$form, &$form_state) { form_set_value($form, $form['#value'], $form_state); } +/** + * Helper function to handle the sometimes-convoluted logic of button + * click detection. + * + * In Internet Explorer, if ONLY one submit button is present, AND the + * enter key is used to submit the form, no form value is sent for it + * and we'll never detect a match. That special case is handled by + * _form_builder_ie_cleanup(). + */ +function _form_button_was_clicked($form) { + // First detect normal 'vanilla' button clicks. Traditionally, all + // standard buttons on a form share the same name (usually 'op'), + // and the specific return value is used to determine which was + // clicked. This ONLY works as long as $form['#name'] puts the + // value at the top level of the tree of $_POST data. + if (isset($form['#post'][$form['#name']]) && $form['#post'][$form['#name']] == $form['#value']) { + return TRUE; + } + // When image buttons are clicked, browsers do NOT pass the form element + // value in $_POST. Instead they pass an integer representing the + // coordinates of the click on the button image. This means that image + // buttons MUST have unique $form['#name'] values, but the details of + // their $_POST data should be ignored. + elseif (!empty($form['#has_garbage_value']) && isset($form['#value']) && $form['#value'] !== '') { + return TRUE; + } + return FALSE; +} + +/** + * In IE, if only one submit button is present, AND the enter key is + * used to submit the form, no form value is sent for it and our normal + * button detection code will never detect a match. We call this + * function after all other button-detection is complete to check + * for the proper conditions, and treat the single button on the form + * as 'clicked' if they are met. + */ +function _form_builder_ie_cleanup($form, &$form_state) { + // Quick check to make sure we're always looking at the full form + // and not a sub-element. + if (!empty($form['#type']) && $form['#type'] == 'form') { + // If we haven't recognized a submission yet, and there's a single + // submit button, we know that we've hit the right conditions. Grab + // the first one and treat it as the clicked button. + if (empty($form_state['submitted']) && !empty($form_state['buttons']['submit']) && empty($form_state['buttons']['button'])) { + $button = $form_state['buttons']['submit'][0]; + + // Set up all the $form_state information that would have been + // populated had the button been recognized earlier. + $form_state['submitted'] = TRUE; + $form_state['submit_handlers'] = empty($button['#submit']) ? NULL : $button['#submit']; + $form_state['validate_handlers'] = empty($button['#validate']) ? NULL : $button['#validate']; + $form_state['values'][$button['#name']] = $button['#value']; + $form_state['clicked_button'] = $button; + } + } +} + /** * Helper function to determine the value for a checkbox form element. * @@ -999,24 +1069,6 @@ function form_type_token_value($form, $edit = FALSE) { } } -/** - * Handle the special Internet Explorer one-button-form hit-enter- - * instead-of-clicking scenario. - */ -function _form_builder_ie_cleanup($form, &$form_state) { - if (!empty($form['#type']) && $form['#type'] == 'form') { - // If the 'submitted' flag isn't tripped, but there is only one submit button... - if (empty($form_state['submitted']) && !empty($form_state['buttons']['submit']) && empty($form_state['buttons']['button'])) { - $button = $form_state['buttons']['submit'][0]; - $form_state['submitted'] = TRUE; - $form_state['submit_handlers'] = empty($button['#submit']) ? NULL : $button['#submit']; - $form_state['validate_handlers'] = empty($button['#validate']) ? NULL : $button['#validate']; - $form_state['values'][$button['#name']] = $button['#value']; - $form_state['clicked_button'] = $button; - } - } -} - /** * Use this function to make changes to form values in the form validate * phase, so they will be available in the submit phase in $form_state. @@ -1598,6 +1650,27 @@ function theme_button($element) { return '\n"; } +/** + * Theme an image button. + */ +function theme_image_button($element) { + // Make sure not to overwrite classes. + if (isset($element['#attributes']['class'])) { + $element['#attributes']['class'] = 'form-'. $element['#button_type'] .' '. $element['#attributes']['class']; + } + else { + $element['#attributes']['class'] = 'form-'. $element['#button_type']; + } + + return '\n"; +} + /** * Format a hidden form field. * diff --git a/modules/forum/forum-submitted.tpl.php b/modules/forum/forum-submitted.tpl.php index 79580c3aa3a..747cf83752b 100644 --- a/modules/forum/forum-submitted.tpl.php +++ b/modules/forum/forum-submitted.tpl.php @@ -1,10 +1,10 @@ - by !author', array( - '@time' => $time, + '@time' => $time, '!author' => $author, )); ?> diff --git a/modules/forum/forums.tpl.php b/modules/forum/forums.tpl.php index 1b8264a72e3..0b8e1dfb690 100644 --- a/modules/forum/forums.tpl.php +++ b/modules/forum/forums.tpl.php @@ -1,4 +1,4 @@ -'. format_plural($num_links, 'Warning: There is currently 1 menu item in %title. It will be deleted (system-defined items will be reset).', 'Warning: There are currently @count menu items in %title. They will be deleted (system-defined items will be reset).', array('%title' => $menu['title'])) .'

'; } - $caption .= '

'. t('This action cannot be undone.') .'

'; + $caption .= '

'. t('This action cannot be undone.') .'

'; return confirm_form($form, t('Are you sure you want to delete the custom menu %title?', array('%title' => $menu['title'])), 'admin/build/menu-customize/'. $menu['menu_name'], $caption, t('Delete')); } diff --git a/modules/system/system.module b/modules/system/system.module index 0ebaaea517b..c856ea93070 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -105,6 +105,7 @@ function system_elements() { // Inputs $type['submit'] = array('#input' => TRUE, '#name' => 'op', '#button_type' => 'submit', '#executes_submit_callback' => TRUE, '#ahah_event' => 'submit', '#process' => array('form_expand_ahah')); $type['button'] = array('#input' => TRUE, '#name' => 'op', '#button_type' => 'submit', '#executes_submit_callback' => FALSE, '#ahah_event' => 'submit', '#process' => array('form_expand_ahah')); + $type['image_button'] = array('#input' => TRUE, '#button_type' => 'submit','#executes_submit_callback' => TRUE, '#ahah_event' => 'submit', '#process' => array('form_expand_ahah'), '#has_garbage_value' => TRUE, '#image' => NULL); $type['textfield'] = array('#input' => TRUE, '#size' => 60, '#maxlength' => 128, '#autocomplete_path' => FALSE); $type['password'] = array('#input' => TRUE, '#size' => 60, '#maxlength' => 128); $type['password_confirm'] = array('#input' => TRUE, '#process' => array('expand_password_confirm')); @@ -3126,4 +3127,4 @@ function system_goto_action_submit($form, $form_state) { function system_goto_action($object, $context) { drupal_goto($context['url']); -} \ No newline at end of file +}