#193191 by chx: allow form elements to enable form caching - allows custom form elements to leverage the AHAH framework

6.x
Gábor Hojtsy 2007-11-19 19:23:28 +00:00
parent c1555f33bc
commit 4615c96d59
1 changed files with 96 additions and 29 deletions

View File

@ -51,6 +51,7 @@ function drupal_get_form($form_id) {
$form_state = array('storage' => NULL, 'submitted' => FALSE); $form_state = array('storage' => NULL, 'submitted' => FALSE);
$args = func_get_args(); $args = func_get_args();
$cacheable = FALSE;
if (isset($_SESSION['batch_form_state'])) { if (isset($_SESSION['batch_form_state'])) {
// We've been redirected here after a batch processing : the form has // We've been redirected here after a batch processing : the form has
@ -84,11 +85,10 @@ function drupal_get_form($form_id) {
$form_build_id = 'form-'. md5(mt_rand()); $form_build_id = 'form-'. md5(mt_rand());
$form['#build_id'] = $form_build_id; $form['#build_id'] = $form_build_id;
drupal_prepare_form($form_id, $form, $form_state); drupal_prepare_form($form_id, $form, $form_state);
if (!empty($form['#cache'])) { // Store a copy of the unprocessed form for caching and indicate that it
// By not sending the form state, we avoid storing the storage which // is cacheable if #cache will be set.
// won't have been touched yet. $original_form = $form;
form_set_cache($form_build_id, $form, NULL); $cacheable = TRUE;
}
unset($form_state['post']); unset($form_state['post']);
} }
$form['#post'] = $_POST; $form['#post'] = $_POST;
@ -99,6 +99,12 @@ function drupal_get_form($form_id) {
// altering the $form_state variable, which is passed into them by // altering the $form_state variable, which is passed into them by
// reference. // reference.
drupal_process_form($form_id, $form, $form_state); drupal_process_form($form_id, $form, $form_state);
if ($cacheable && !empty($form['#cache'])) {
// Caching is done past drupal_process_form so #process callbacks can
// set #cache. By not sending the form state, we avoid storing
// $form_state['storage'].
form_set_cache($form_build_id, $original_form, NULL);
}
} }
// Most simple, single-step forms will be finished by this point -- // Most simple, single-step forms will be finished by this point --
@ -116,27 +122,7 @@ function drupal_get_form($form_id) {
// other variables passed into drupal_get_form(). // other variables passed into drupal_get_form().
if (!empty($form_state['rebuild']) || !empty($form_state['storage'])) { if (!empty($form_state['rebuild']) || !empty($form_state['storage'])) {
array_shift($args); $form = drupal_rebuild_form($form_id, $form_state, $args);
array_unshift($args, $form_state);
array_unshift($args, $form_id);
$form = call_user_func_array('drupal_retrieve_form', $args);
// We need a new build_id for the new version of the form.
$form_build_id = 'form-'. md5(mt_rand());
$form['#build_id'] = $form_build_id;
drupal_prepare_form($form_id, $form, $form_state);
// 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.
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,
// then process the form for rendering.
$_POST = array();
$form['#post'] = array();
drupal_process_form($form_id, $form, $form_state);
} }
// If we haven't redirected to a new location by now, we want to // If we haven't redirected to a new location by now, we want to
@ -144,6 +130,75 @@ function drupal_get_form($form_id) {
return drupal_render_form($form_id, $form); return drupal_render_form($form_id, $form);
} }
/**
* Retrieves a form, caches it and processes it with an empty $_POST.
*
* This function clears $_POST and passes the empty $_POST to the form_builder.
* To preserve some parts from $_POST, pass them in $form_state.
*
* If your AHAH callback simulates the pressing of a button, then your AHAH
* callback will need to do the same as what drupal_get_form would do when the
* button is pressed: get the form from the cache, run drupal_process_form over
* it and then if it needs rebuild, run drupal_rebuild_form over it. Then send
* back a part of the returned form.
* $form_state['clicked_button']['#array_parents'] will help you to find which
* part.
*
* @param $form_id
* The unique string identifying the desired form. If a function
* with that name exists, it is called to build the form array.
* Modules that need to generate the same form (or very similar forms)
* using different $form_ids can implement hook_forms(), which maps
* different $form_id values to the proper form constructor function. Examples
* may be found in node_forms(), search_forms(), and user_forms().
* @param $form_state
* A keyed array containing the current state of the form. Most
* important is the $form_state['storage'] collection.
* @param $args
* Any additional arguments are passed on to the functions called by
* drupal_get_form(), plus the original form_state in the beginning. If you
* are getting a form from the cache, use $form['#parameters'] to shift off
* the $form_id from its beginning then the resulting array can be used as
* $arg here.
* @param $form_build_id
* If the AHAH callback calling this function only alters part of the form,
* then pass in the existing form_build_id so we can re-cache with the same
* csid.
* @return
* The newly built form.
*/
function drupal_rebuild_form($form_id, &$form_state, $args, $form_build_id = NULL) {
// Remove the first argument. This is $form_id.when called from
// drupal_get_form and the original $form_state when called from some AHAH
// callback. Neither is needed.
array_shift($args);
// Put in the current state.
array_unshift($args, $form_state);
// And the form_id.
array_unshift($args, $form_id);
$form = call_user_func_array('drupal_retrieve_form', $args);
if (!isset($form_build_id)) {
// We need a new build_id for the new version of the form.
$form_build_id = 'form-'. md5(mt_rand());
}
$form['#build_id'] = $form_build_id;
drupal_prepare_form($form_id, $form, $form_state);
// 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.
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,
// then process the form for rendering.
$_POST = array();
$form['#post'] = array();
drupal_process_form($form_id, $form, $form_state);
return $form;
}
/** /**
* Fetch a form from cache. * Fetch a form from cache.
*/ */
@ -738,7 +793,7 @@ function form_error(&$element, $message = '') {
* $_POST data. * $_POST data.
*/ */
function form_builder($form_id, $form, &$form_state) { function form_builder($form_id, $form, &$form_state) {
static $complete_form; static $complete_form, $cache;
// Initialize as unprocessed. // Initialize as unprocessed.
$form['#processed'] = FALSE; $form['#processed'] = FALSE;
@ -784,6 +839,9 @@ function form_builder($form_id, $form, &$form_state) {
// Check to see if a tree of child elements is present. If so, // Check to see if a tree of child elements is present. If so,
// continue down the tree if required. // continue down the tree if required.
$form[$key]['#parents'] = $form[$key]['#tree'] && $form['#tree'] ? array_merge($form['#parents'], array($key)) : array($key); $form[$key]['#parents'] = $form[$key]['#tree'] && $form['#tree'] ? array_merge($form['#parents'], array($key)) : array($key);
$array_parents = isset($form['#array_parents']) ? $form['#array_parents'] : array();
$array_parents[] = $key;
$form[$key]['#array_parents'] = $array_parents;
} }
// Assign a decimal placeholder weight to preserve original array order. // Assign a decimal placeholder weight to preserve original array order.
@ -815,6 +873,15 @@ function form_builder($form_id, $form, &$form_state) {
// After handling the special IE case, we no longer need the buttons collection. // After handling the special IE case, we no longer need the buttons collection.
unset($form_state['buttons']); unset($form_state['buttons']);
// If some callback set #cache, we need to flip a static flag so later it
// can be found.
if (isset($form['#cache'])) {
$cache = $form['#cache'];
}
// We are on the top form, we can copy back #cache if it's set.
if (isset($form['#type']) && $form['#type'] == 'form' && isset($cache)) {
$form['#cache'] = TRUE;
}
return $form; return $form;
} }
@ -913,8 +980,7 @@ function _form_builder_handle_input_element($form_id, &$form, &$form_state, $com
if (isset($form['#process']) && !$form['#processed']) { if (isset($form['#process']) && !$form['#processed']) {
foreach ($form['#process'] as $process) { foreach ($form['#process'] as $process) {
if (function_exists($process)) { if (function_exists($process)) {
$args = array_merge(array($form), array(isset($edit) ? $edit : NULL), array($form_state), array($complete_form)); $form = $process($form, isset($edit) ? $edit : NULL, $form_state, $complete_form);
$form = call_user_func_array($process, $args);
} }
} }
$form['#processed'] = TRUE; $form['#processed'] = TRUE;
@ -1669,6 +1735,7 @@ function form_expand_ahah($element) {
drupal_add_js(array('ahah' => array($element['#id'] => $ahah_binding)), 'setting'); drupal_add_js(array('ahah' => array($element['#id'] => $ahah_binding)), 'setting');
$js_added[$element['#id']] = TRUE; $js_added[$element['#id']] = TRUE;
$element['#cache'] = TRUE;
} }
return $element; return $element;
} }