diff --git a/core/includes/batch.inc b/core/includes/batch.inc index 8bd33b7..be3c103 100644 --- a/core/includes/batch.inc +++ b/core/includes/batch.inc @@ -17,6 +17,7 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Component\Utility\Timer; use Drupal\Core\Batch\Percentage; +use Drupal\Core\Form\FormState; use Drupal\Core\Page\DefaultHtmlPageRenderer; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -442,6 +443,9 @@ function _batch_finished() { } // Determine the target path to redirect to. + if (!isset($_batch['form_state'])) { + $_batch['form_state'] = new FormState(); + } if (!isset($_batch['form_state']['redirect'])) { if (isset($_batch['redirect'])) { $_batch['form_state']['redirect'] = $_batch['redirect']; diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php index a0f9e2f..afe1077 100644 --- a/core/lib/Drupal/Core/Form/FormBuilder.php +++ b/core/lib/Drupal/Core/Form/FormBuilder.php @@ -211,14 +211,13 @@ public function buildForm($form_id, FormStateInterface &$form_state) { $form = $this->retrieveForm($form_id, $form_state); $this->prepareForm($form_id, $form, $form_state); - // self::setCache() removes uncacheable $form_state keys defined in - // \Drupal\Core\Form\FormState::getUncacheableKeys() in order for - // multi-step forms to work properly. This means that form processing - // logic for single-step forms using $form_state['cache'] may depend on - // data stored in those keys during - // self::retrieveForm()/self::prepareForm(), but form processing should - // not depend on whether the form is cached or not, so $form_state is - // adjusted to match what it would be after a + // self::setCache() removes uncacheable $form_state keys (see properties + // in \Drupal\Core\Form\FormState) in order for multi-step forms to work + // properly. This means that form processing logic for single-step forms + // using $form_state['cache'] may depend on data stored in those keys + // during self::retrieveForm()/self::prepareForm(), but form processing + // should not depend on whether the form is cached or not, so $form_state + // is adjusted to match what it would be after a // self::setCache()/self::getCache() sequence. These exceptions are // allowed to survive here: // - always_process: Does not make sense in conjunction with form caching diff --git a/core/lib/Drupal/Core/Form/FormState.php b/core/lib/Drupal/Core/Form/FormState.php index 8f75d41..6b21377 100644 --- a/core/lib/Drupal/Core/Form/FormState.php +++ b/core/lib/Drupal/Core/Form/FormState.php @@ -11,6 +11,8 @@ /** * Stores information about the state of a form. + * + * @todo Remove usage of \ArrayAccess. */ class FormState implements FormStateInterface, \ArrayAccess { @@ -25,186 +27,391 @@ class FormState implements FormStateInterface, \ArrayAccess { * The internal storage of the form state. * * @var array - * An array which stores information about the form. The following - * parameters may be set in $form_state to affect how the form is rendered: - * - build_info: Internal. An associative array of information stored by - * Form API that is necessary to build and rebuild the form from cache - * when the original context may no longer be available: - * - callback: The actual callback to be used to retrieve the form array. - * Can be any callable. If none is provided $form_id is used as the name - * of a function to call instead. - * - args: A list of arguments to pass to the form constructor. - * - files: An optional array defining include files that need to be - * loaded for building the form. Each array entry may be the path to a - * file or another array containing values for the parameters 'type', - * 'module' and 'name' as needed by module_load_include(). The files - * listed here are automatically loaded by form_get_cache(). By default - * the current menu router item's 'file' definition is added, if any. - * Use form_load_include() to add include files from a form constructor. - * - form_id: Identification of the primary form being constructed and - * processed. - * - base_form_id: Identification for a base form, as declared in the form - * class's \Drupal\Core\Form\BaseFormIdInterface::getBaseFormId() method. - * - rebuild_info: Internal. Similar to 'build_info', but pertaining to - * self::rebuildForm(). - * - rebuild: Normally, after the entire form processing is completed and - * submit handlers have run, a form is considered to be done and - * self::redirectForm() will redirect the user to a new page using a GET - * request (so a browser refresh does not re-submit the form). However, if - * 'rebuild' has been set to TRUE, then a new copy of the form is - * immediately built and sent to the browser, instead of a redirect. This - * is used for multi-step forms, such as wizards and confirmation forms. - * Normally, $form_state['rebuild'] is set by a submit handler, since its - * is usually logic within a submit handler that determines whether a form - * is done or requires another step. However, a validation handler may - * already set $form_state['rebuild'] to cause the form processing to - * bypass submit handlers and rebuild the form instead, even if there are - * no validation errors. - * - response: Used when a form needs to return some kind of a - * \Symfony\Component\HttpFoundation\Response object, e.g., a - * \Symfony\Component\HttpFoundation\BinaryFileResponse when triggering a - * file download. If you use the $form_state['redirect'] key, it will be - * used to build a \Symfony\Component\HttpFoundation\RedirectResponse and - * will populate this key. - * - redirect: Used to redirect the form on submission. It may either be a - * string containing the destination URL, or an array of arguments - * compatible with url(). See url() for complete information. - * - no_redirect: If set to TRUE the form will NOT perform a redirect, - * even if 'redirect' is set. - * - method: The HTTP form method to use for finding the input for this - * form. May be 'post' or 'get'. Defaults to 'post'. Note that 'get' - * method forms do not use form ids so are always considered to be - * submitted, which can have unexpected effects. The 'get' method should - * only be used on forms that do not change data, as that is exclusively - * the domain of 'post.' - * - cache: If set to TRUE the original, unprocessed form structure will be - * cached, which allows the entire form to be rebuilt from cache. A - * typical form workflow involves two page requests; first, a form is - * built and rendered for the user to fill in. Then, the user fills the - * form in and submits it, triggering a second page request in which the - * form must be built and processed. By default, $form and $form_state are - * built from scratch during each of these page requests. Often, it is - * necessary or desired to persist the $form and $form_state variables - * from the initial page request to the one that processes the submission. - * 'cache' can be set to TRUE to do this. A prominent example is an - * Ajax-enabled form, in which ajax_process_form() enables form caching - * for all forms that include an element with the #ajax property. (The - * Ajax handler has no way to build the form itself, so must rely on the - * cached version.) Note that the persistence of $form and $form_state - * happens automatically for (multi-step) forms having the 'rebuild' flag - * set, regardless of the value for 'cache'. - * - no_cache: If set to TRUE the form will NOT be cached, even if 'cache' - * is set. - * - values: An associative array of values submitted to the form. The - * validation functions and submit functions use this array for nearly all - * their decision making. (Note that #tree determines whether the values - * are a flat array or an array whose structure parallels the $form array. - * See the @link forms_api_reference.html Form API reference @endlink for - * more information.) - * - input: The array of values as they were submitted by the user. These - * are raw and unvalidated, so should not be used without a thorough - * understanding of security implications. In almost all cases, code - * should use the data in the 'values' array exclusively. The most common - * use of this key is for multi-step forms that need to clear some of the - * user input when setting 'rebuild'. The values correspond to - * \Drupal::request()->request or \Drupal::request()->query, depending on - * the 'method' chosen. - * - always_process: If TRUE and the method is GET, a form_id is not - * necessary. This should only be used on RESTful GET forms that do NOT - * write data, as this could lead to security issues. It is useful so that - * searches do not need to have a form_id in their query arguments to - * trigger the search. - * - must_validate: Ordinarily, a form is only validated once, but there are - * times when a form is resubmitted internally and should be validated - * again. Setting this to TRUE will force that to happen. This is most - * likely to occur during Ajax operations. - * - programmed: If TRUE, the form was submitted programmatically, usually - * invoked via self::submitForm(). Defaults to FALSE. - * - programmed_bypass_access_check: If TRUE, programmatic form submissions - * are processed without taking #access into account. Set this to FALSE - * when submitting a form programmatically with values that may have been - * input by the user executing the current request; this will cause - * #access to be respected as it would on a normal form submission. - * Defaults to TRUE. - * - process_input: Boolean flag. TRUE signifies correct form submission. - * This is always TRUE for programmed forms coming from self::submitForm() - * (see 'programmed' key), or if the form_id coming from the - * \Drupal::request()->request data is set and matches the current form_id. - * - submitted: If TRUE, the form has been submitted. Defaults to FALSE. - * - executed: If TRUE, the form was submitted and has been processed and - * executed. Defaults to FALSE. - * - triggering_element: (read-only) The form element that triggered - * submission, which may or may not be a button (in the case of Ajax - * forms). This key is often used to distinguish between various buttons - * in a submit handler, and is also used in Ajax handlers. - * - has_file_element: Internal. If TRUE, there is a file element and Form - * API will set the appropriate 'enctype' HTML attribute on the form. - * - groups: Internal. An array containing references to details elements to - * render them within vertical tabs. - * - storage: $form_state['storage'] is not a special key, and no specific - * support is provided for it in the Form API. By tradition it was - * the location where application-specific data was stored for - * communication between the submit, validation, and form builder - * functions, especially in a multi-step-style form. Form implementations - * may use any key(s) within $form_state (other than the keys listed here - * and other reserved ones used by Form API internals) for this kind of - * storage. The recommended way to ensure that the chosen key doesn't - * conflict with ones used by the Form API or other modules is to use the - * module name as the key name or a prefix for the key name. For example, - * the entity form classes use $this->entity in entity forms, - * or $form_state['controller']->getEntity() outside the controller, to - * store information about the entity being edited, and this information - * stays available across successive clicks of the "Preview" button (if - * available) as well as when the "Save" button is finally clicked. - * - buttons: A list containing copies of all submit and button elements in - * the form. - * - complete_form: A reference to the $form variable containing the - * complete form structure. #process, #after_build, #element_validate, and - * other handlers being invoked on a form element may use this reference - * to access other information in the form the element is contained in. - * - temporary: An array holding temporary data accessible during the - * current page request only. All $form_state properties that are not - * reserved keys (see form_state_keys_no_cache()) persist throughout a - * multistep form sequence. Form API provides this key for modules to - * communicate information across form-related functions during a single - * page request. It may be used to temporarily save data that does not - * need to or should not be cached during the whole form workflow; e.g., - * data that needs to be accessed during the current form build process - * only. There is no use-case for this functionality in Drupal core. */ - protected $internalStorage = array( - 'rebuild' => FALSE, - 'rebuild_info' => array(), - 'redirect' => NULL, - 'build_info' => array( - 'args' => array(), - 'files' => array(), - ), - 'temporary' => array(), - 'validation_complete' => FALSE, - 'submitted' => FALSE, - 'executed' => FALSE, - 'programmed' => FALSE, - 'programmed_bypass_access_check' => TRUE, - 'cache'=> FALSE, - 'method' => 'post', - 'groups' => array(), - 'buttons' => array(), - 'errors' => array(), - 'limit_validation_errors' => NULL, - 'storage' => array(), - ); + protected $internalStorage = array(); /** * The complete form structure. * + * #process, #after_build, #element_validate, and other handlers being invoked + * on a form element may use this reference to access other information in the + * form the element is contained in. + * * @see self::getCompleteForm() * + * This property is uncacheable. + * * @var array */ protected $complete_form; /** + * An associative array of information stored by Form API that is necessary to + * build and rebuild the form from cache when the original context may no + * longer be available: + * - callback: The actual callback to be used to retrieve the form array. + * Can be any callable. If none is provided $form_id is used as the name + * of a function to call instead. + * - args: A list of arguments to pass to the form constructor. + * - files: An optional array defining include files that need to be loaded + * for building the form. Each array entry may be the path to a file or + * another array containing values for the parameters 'type', 'module' and + * 'name' as needed by module_load_include(). The files listed here are + * automatically loaded by form_get_cache(). By default the current menu + * router item's 'file' definition is added, if any. Use + * form_load_include() to add include files from a form constructor. + * - form_id: Identification of the primary form being constructed and + * processed. + * - base_form_id: Identification for a base form, as declared in the form + * class's \Drupal\Core\Form\BaseFormIdInterface::getBaseFormId() method. + * + * @var array + */ + protected $build_info = array( + 'args' => array(), + 'files' => array(), + ); + + /** + * Similar to self::$build_info, but pertaining to + * \Drupal\Core\Form\FormBuilderInterface::rebuildForm(). + * + * This property is uncacheable. + * + * @var array + */ + protected $rebuild_info = array(); + + /** + * Normally, after the entire form processing is completed and submit handlers + * have run, a form is considered to be done and + * \Drupal\Core\Form\FormSubmitterInterface::redirectForm() will redirect the + * user to a new page using a GET request (so a browser refresh does not + * re-submit the form). However, if 'rebuild' has been set to TRUE, then a new + * copy of the form is immediately built and sent to the browser, instead of a + * redirect. This is used for multi-step forms, such as wizards and + * confirmation forms. Normally, $form_state['rebuild'] is set by a submit + * handler, since its is usually logic within a submit handler that determines + * whether a form is done or requires another step. However, a validation + * handler may already set $form_state['rebuild'] to cause the form processing + * to bypass submit handlers and rebuild the form instead, even if there are + * no validation errors. + * + * This property is uncacheable. + * + * @var bool + */ + protected $rebuild = FALSE; + + /** + * Used when a form needs to return some kind of a + * \Symfony\Component\HttpFoundation\Response object, e.g., a + * \Symfony\Component\HttpFoundation\BinaryFileResponse when triggering a + * file download. If you use the $form_state['redirect'] key, it will be used + * to build a \Symfony\Component\HttpFoundation\RedirectResponse and will + * populate this key. + * + * @var \Symfony\Component\HttpFoundation\Response|null + */ + protected $response; + + /** + * Used to redirect the form on submission. It may either be a string + * containing the destination URL, or an array of arguments compatible with + * url(). See url() for complete information. + * + * This property is uncacheable. + * + * @var string|array|null + */ + protected $redirect; + + /** + * Used for route-based redirects. + * + * This property is uncacheable. + * + * @var \Drupal\Core\Url|array + */ + protected $redirect_route; + + /** + * If set to TRUE the form will NOT perform a redirect, even if + * self::$redirect is set. + * + * This property is uncacheable. + * + * @var bool + */ + protected $no_redirect; + + /** + * The HTTP form method to use for finding the input for this form. + * + * May be 'post' or 'get'. Defaults to 'post'. Note that 'get' method forms do + * not use form ids so are always considered to be submitted, which can have + * unexpected effects. The 'get' method should only be used on forms that do + * not change data, as that is exclusively the domain of 'post.' + * + * This property is uncacheable. + * + * @var string + */ + protected $method = 'post'; + + /** + * If set to TRUE the original, unprocessed form structure will be cached, + * which allows the entire form to be rebuilt from cache. A typical form + * workflow involves two page requests; first, a form is built and rendered + * for the user to fill in. Then, the user fills the form in and submits it, + * triggering a second page request in which the form must be built and + * processed. By default, $form and $form_state are built from scratch during + * each of these page requests. Often, it is necessary or desired to persist + * the $form and $form_state variables from the initial page request to the + * one that processes the submission. 'cache' can be set to TRUE to do this. + * A prominent example is an Ajax-enabled form, in which ajax_process_form() + * enables form caching for all forms that include an element with the #ajax + * property. (The Ajax handler has no way to build the form itself, so must + * rely on the cached version.) Note that the persistence of $form and + * $form_state happens automatically for (multi-step) forms having the + * self::$rebuild flag set, regardless of the value for self::$cache. + * + * @var bool + */ + protected $cache = FALSE; + + /** + * If set to TRUE the form will NOT be cached, even if 'cache' is set. + * + * @var bool + */ + protected $no_cache; + + /** + * An associative array of values submitted to the form. + * + * The validation functions and submit functions use this array for nearly all + * their decision making. (Note that #tree determines whether the values are a + * flat array or an array whose structure parallels the $form array. See the + * @link forms_api_reference.html Form API reference @endlink for more + * information.) + * + * This property is uncacheable. + * + * @var array + */ + protected $values; + + /** + * The array of values as they were submitted by the user. + * + * These are raw and unvalidated, so should not be used without a thorough + * understanding of security implications. In almost all cases, code should + * use the data in the 'values' array exclusively. The most common use of this + * key is for multi-step forms that need to clear some of the user input when + * setting 'rebuild'. The values correspond to \Drupal::request()->request or + * \Drupal::request()->query, depending on the 'method' chosen. + * + * This property is uncacheable. + * + * @var array + */ + protected $input; + + /** + * If TRUE and the method is GET, a form_id is not necessary. + * + * This should only be used on RESTful GET forms that do NOT write data, as + * this could lead to security issues. It is useful so that searches do not + * need to have a form_id in their query arguments to trigger the search. + * + * This property is uncacheable. + * + * @var bool + */ + protected $always_process; + + /** + * Ordinarily, a form is only validated once, but there are times when a form + * is resubmitted internally and should be validated again. Setting this to + * TRUE will force that to happen. This is most likely to occur during Ajax + * operations. + * + * This property is uncacheable. + * + * @var bool + */ + protected $must_validate; + + /** + * If TRUE, the form was submitted programmatically, usually invoked via + * \Drupal\Core\Form\FormBuilderInterface::submitForm(). Defaults to FALSE. + * + * @var bool + */ + protected $programmed = FALSE; + + /** + * If TRUE, programmatic form submissions are processed without taking #access + * into account. Set this to FALSE when submitting a form programmatically + * with values that may have been input by the user executing the current + * request; this will cause #access to be respected as it would on a normal + * form submission. Defaults to TRUE. + * + * @var bool + */ + protected $programmed_bypass_access_check = TRUE; + + /** + * TRUE signifies correct form submission. This is always TRUE for programmed + * forms coming from \Drupal\Core\Form\FormBuilderInterface::submitForm() (see + * 'programmed' key), or if the form_id coming from the + * \Drupal::request()->request data is set and matches the current form_id. + * + * @var bool + */ + protected $process_input; + + /** + * If TRUE, the form has been submitted. Defaults to FALSE. + * + * This property is uncacheable. + * + * @var bool + */ + protected $submitted = FALSE; + + /** + * If TRUE, the form was submitted and has been processed and executed. + * + * This property is uncacheable. + * + * @var bool + */ + protected $executed = FALSE; + + /** + * The form element that triggered submission, which may or may not be a + * button (in the case of Ajax forms). This key is often used to distinguish + * between various buttons in a submit handler, and is also used in Ajax + * handlers. + * + * This property is uncacheable. + * + * @var array|null + */ + protected $triggering_element; + + /** + * If TRUE, there is a file element and Form API will set the appropriate + * 'enctype' HTML attribute on the form. + * + * @var bool + */ + protected $has_file_element; + + /** + * Contains references to details elements to render them within vertical tabs. + * + * This property is uncacheable. + * + * @var array + */ + protected $groups = array(); + + /** + * This is not a special key, and no specific support is provided for it in + * the Form API. By tradition it was the location where application-specific + * data was stored for communication between the submit, validation, and form + * builder functions, especially in a multi-step-style form. Form + * implementations may use any key(s) within $form_state (other than the keys + * listed here and other reserved ones used by Form API internals) for this + * kind of storage. The recommended way to ensure that the chosen key doesn't + * conflict with ones used by the Form API or other modules is to use the + * module name as the key name or a prefix for the key name. For example, the + * entity form classes use $this->entity in entity forms, or + * $form_state['controller']->getEntity() outside the controller, to store + * information about the entity being edited, and this information stays + * available across successive clicks of the "Preview" button (if available) + * as well as when the "Save" button is finally clicked. + * + * @var array + */ + protected $storage = array(); + + /** + * A list containing copies of all submit and button elements in the form. + * + * This property is uncacheable. + * + * @var array + */ + protected $buttons = array(); + + /** + * Holds temporary data accessible during the current page request only. + * + * All $form_state properties that are not reserved keys (see + * other properties marked as uncacheable) persist throughout a multistep form + * sequence. Form API provides this key for modules to communicate information + * across form-related functions during a single page request. It may be used + * to temporarily save data that does not need to or should not be cached + * during the whole form workflow; e.g., data that needs to be accessed during + * the current form build process only. There is no use-case for this + * functionality in Drupal core. + * + * This property is uncacheable. + * + * @var array + */ + protected $temporary; + + /** + * Tracks if the form has finished validation. + * + * This property is uncacheable. + * + * @var bool + */ + protected $validation_complete = FALSE; + + /** + * Contains errors for this form. + * + * This property is uncacheable. + * + * @var array + */ + protected $errors = array(); + + /** + * Stores which errors should be limited during validation. + * + * This property is uncacheable. + * + * @var array|null + */ + protected $limit_validation_errors; + + /** + * Stores the gathered validation handlers. + * + * This property is uncacheable. + * + * @var array|null + */ + protected $validate_handlers; + + /** + * Stores the gathered submission handlers. + * + * This property is uncacheable. + * + * @var array|null + */ + protected $submit_handlers; + + /** * @param array $form_state_additions */ public function __construct(array $form_state_additions = array()) { @@ -225,40 +432,21 @@ public function setFormState(array $form_state_additions) { * {@inheritdoc} */ public function getCacheableArray($allowed_keys = array()) { - $uncacheable_keys = array_diff($this->getUncacheableKeys(), $allowed_keys); - return array_diff_key($this->internalStorage, array_flip($uncacheable_keys)); - } - - /** - * Returns an array of $form_state keys that shouldn't be cached. - */ - protected function getUncacheableKeys() { - return array( - // Public properties defined by form constructors and form handlers. - 'always_process', - 'must_validate', - 'rebuild', - 'rebuild_info', - 'redirect', - 'redirect_route', - 'no_redirect', - 'temporary', - // Internal properties defined by form processing. - 'buttons', - 'triggering_element', - 'complete_form', - 'groups', - 'input', - 'method', - 'submit_handlers', - 'validation_complete', - 'submitted', - 'executed', - 'validate_handlers', - 'values', - 'errors', - 'limit_validation_errors', - ); + $cacheable_array = array( + 'build_info' => $this->build_info, + 'response' => $this->response, + 'cache' => $this->cache, + 'no_cache' => $this->no_cache, + 'programmed' => $this->programmed, + 'programmed_bypass_access_check' => $this->programmed_bypass_access_check, + 'process_input' => $this->process_input, + 'has_file_element' => $this->has_file_element, + 'storage' => $this->storage, + ) + $this->internalStorage; + foreach ($allowed_keys as $allowed_key) { + $cacheable_array[$allowed_key] = $this->get($allowed_key); + } + return $cacheable_array; } /** @@ -280,18 +468,13 @@ public function &getCompleteForm() { * {@inheritdoc} */ public function offsetExists($offset) { - return isset($this->internalStorage[$offset]); + return isset($this->{$offset}) || isset($this->internalStorage[$offset]); } /** * {@inheritdoc} */ public function &offsetGet($offset) { - // @todo Remove usages of $form_state['complete_form']. - if ($offset == 'complete_form') { - return $this->getCompleteForm(); - } - $value = &$this->get($offset); return $value; } @@ -307,6 +490,9 @@ public function offsetSet($offset, $value) { * {@inheritdoc} */ public function offsetUnset($offset) { + if (property_exists($this, $offset)) { + $this->{$offset} = NULL; + } unset($this->internalStorage[$offset]); } @@ -314,7 +500,12 @@ public function offsetUnset($offset) { * {@inheritdoc} */ public function setIfNotExists($property, $value) { - if (!array_key_exists($property, $this->internalStorage)) { + if (property_exists($this, $property)) { + if ($this->{$property} === NULL) { + $this->set($property, $value); + } + } + else if (!array_key_exists($property, $this->internalStorage)) { $this->set($property, $value); } return $this; @@ -324,17 +515,27 @@ public function setIfNotExists($property, $value) { * {@inheritdoc} */ public function &get($property) { - if (!isset($this->internalStorage[$property])) { - $this->internalStorage[$property] = NULL; + if (property_exists($this, $property)) { + return $this->{$property}; + } + else { + if (!isset($this->internalStorage[$property])) { + $this->internalStorage[$property] = NULL; + } + return $this->internalStorage[$property]; } - return $this->internalStorage[$property]; } /** * {@inheritdoc} */ public function set($property, $value) { - $this->internalStorage[$property] = $value; + if (property_exists($this, $property)) { + $this->{$property} = $value; + } + else { + $this->internalStorage[$property] = $value; + } return $this; } diff --git a/core/lib/Drupal/Core/Form/FormSubmitter.php b/core/lib/Drupal/Core/Form/FormSubmitter.php index 97a9a20..4c5d1c7 100644 --- a/core/lib/Drupal/Core/Form/FormSubmitter.php +++ b/core/lib/Drupal/Core/Form/FormSubmitter.php @@ -94,11 +94,11 @@ public function doSubmitForm(&$form, FormStateInterface &$form_state) { */ public function executeSubmitHandlers(&$form, FormStateInterface &$form_state) { // If there was a button pressed, use its handlers. - if (isset($form_state['submit_handlers'])) { + if (!empty($form_state['submit_handlers'])) { $handlers = $form_state['submit_handlers']; } // Otherwise, check for a form-level handler. - elseif (isset($form['#submit'])) { + elseif (!empty($form['#submit'])) { $handlers = $form['#submit']; } else { diff --git a/core/modules/block/src/BlockBase.php b/core/modules/block/src/BlockBase.php index fb6a9dd..304b07b 100644 --- a/core/modules/block/src/BlockBase.php +++ b/core/modules/block/src/BlockBase.php @@ -348,9 +348,11 @@ public function validateConfigurationForm(array &$form, FormStateInterface $form foreach ($this->getVisibilityConditions() as $condition_id => $condition) { // Allow the condition to validate the form. $condition_values = new FormState(array( - 'values' => &$form_state['values']['visibility'][$condition_id], + 'values' => $form_state['values']['visibility'][$condition_id], )); $condition->validateConfigurationForm($form, $condition_values); + // Update the original form values. + $form_state['values']['visibility'][$condition_id] = $condition_values['values']; } $this->blockValidate($form, $form_state); @@ -379,9 +381,11 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s foreach ($this->getVisibilityConditions() as $condition_id => $condition) { // Allow the condition to submit the form. $condition_values = new FormState(array( - 'values' => &$form_state['values']['visibility'][$condition_id], + 'values' => $form_state['values']['visibility'][$condition_id], )); $condition->submitConfigurationForm($form, $condition_values); + // Update the original form values. + $form_state['values']['visibility'][$condition_id] = $condition_values['values']; } $this->blockSubmit($form, $form_state); } diff --git a/core/modules/block/src/BlockForm.php b/core/modules/block/src/BlockForm.php index de7aa10..704f3d1 100644 --- a/core/modules/block/src/BlockForm.php +++ b/core/modules/block/src/BlockForm.php @@ -150,10 +150,12 @@ public function validate(array $form, FormStateInterface $form_state) { // The Block Entity form puts all block plugin form elements in the // settings form element, so just pass that to the block for validation. $settings = new FormState(array( - 'values' => &$form_state['values']['settings'] + 'values' => $form_state['values']['settings'] )); // Call the plugin validate handler. $this->entity->getPlugin()->validateConfigurationForm($form, $settings); + // Update the original form values. + $form_state['values']['settings'] = $settings['values']; } /** @@ -167,12 +169,13 @@ public function submit(array $form, FormStateInterface $form_state) { // settings form element, so just pass that to the block for submission. // @todo Find a way to avoid this manipulation. $settings = new FormState(array( - 'values' => &$form_state['values']['settings'], - 'errors' => $form_state['errors'], + 'values' => $form_state['values']['settings'], )); // Call the plugin submit handler. $entity->getPlugin()->submitConfigurationForm($form, $settings); + // Update the original form values. + $form_state['values']['settings'] = $settings['values']; // Save the settings of the plugin. $entity->save(); diff --git a/core/modules/image/src/Form/ImageEffectFormBase.php b/core/modules/image/src/Form/ImageEffectFormBase.php index b819568..6f3268e 100644 --- a/core/modules/image/src/Form/ImageEffectFormBase.php +++ b/core/modules/image/src/Form/ImageEffectFormBase.php @@ -107,9 +107,11 @@ public function validateForm(array &$form, FormStateInterface $form_state) { // The image effect configuration is stored in the 'data' key in the form, // pass that through for validation. $effect_data = new FormState(array( - 'values' => &$form_state['values']['data'] + 'values' => $form_state['values']['data'], )); $this->imageEffect->validateConfigurationForm($form, $effect_data); + // Update the original form values. + $form_state['values']['data'] = $effect_data['values']; } /** @@ -121,9 +123,12 @@ public function submitForm(array &$form, FormStateInterface $form_state) { // The image effect configuration is stored in the 'data' key in the form, // pass that through for submission. $effect_data = new FormState(array( - 'values' => &$form_state['values']['data'] + 'values' => $form_state['values']['data'], )); $this->imageEffect->submitConfigurationForm($form, $effect_data); + // Update the original form values. + $form_state['values']['data'] = $effect_data['values']; + $this->imageEffect->setWeight($form_state['values']['weight']); if (!$this->imageEffect->getUuid()) { $this->imageStyle->addImageEffect($this->imageEffect->getConfiguration());