diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php index f9f56a4..6b51220 100644 --- a/core/lib/Drupal/Core/Form/FormBuilder.php +++ b/core/lib/Drupal/Core/Form/FormBuilder.php @@ -664,9 +664,22 @@ public function prepareForm($form_id, &$form, &$form_state) { if ($form_state['method'] == 'get' && !isset($form['#method'])) { $form['#method'] = 'get'; } + elseif (!isset($form['#method'])) { + $form['#method'] = 'post'; + } + // Prevent an undefined index error. + if (!isset($form['#token'])) { + $form['#token'] = NULL; + } // For certain forms like a search for or exposed filters, we want to make // a clean GET request without hidden values as query parameters. - $clean_get = $form['#method'] == 'get' && $form['#token'] === FALSE && $form['#build_id'] === FALSE; + if ($form['#method'] == 'get' && ($form['#token'] === FALSE || !empty($form_state['always_process']))) { + $clean_get = TRUE; + $form['#token'] = FALSE; + } + else { + $clean_get = FALSE; + } // Generate a new #build_id for this form, if none has been set already. // The form_build_id is used as key to cache a particular build of the form. @@ -677,8 +690,7 @@ public function prepareForm($form_id, &$form, &$form_state) { if (!isset($form['#build_id'])) { $form['#build_id'] = 'form-' . Crypt::randomBytesBase64(); } - // Form constructors may explicitly set #build_id to FALSE when processing - // and validation are irrelevant to the form, such as GET search forms. + // If we are making a clean GET request, omit this hidden element. if (!$clean_get) { $form['form_build_id'] = array( '#type' => 'hidden', @@ -703,7 +715,7 @@ public function prepareForm($form_id, &$form, &$form_state) { if ($user && $user->isAuthenticated() && !$form_state['programmed']) { // Form constructors may explicitly set #token to FALSE when cross site // request forgery is irrelevant to the form, such as search forms. - if (isset($form['#token']) && $form['#token'] === FALSE) { + if ($form['#token'] === FALSE) { unset($form['#token']); } // Otherwise, generate a public token based on the form id. @@ -721,6 +733,7 @@ public function prepareForm($form_id, &$form, &$form_state) { } } + // If we are making a clean GET request, omit this hidden element. if (isset($form_id) && !$clean_get) { $form['form_id'] = array( '#type' => 'hidden', diff --git a/core/modules/views/lib/Drupal/views/Form/ViewsExposedForm.php b/core/modules/views/lib/Drupal/views/Form/ViewsExposedForm.php index ae3da03..fcb6cab 100644 --- a/core/modules/views/lib/Drupal/views/Form/ViewsExposedForm.php +++ b/core/modules/views/lib/Drupal/views/Form/ViewsExposedForm.php @@ -116,6 +116,10 @@ public function buildForm(array $form, array &$form_state) { $form['#action'] = url($view->display_handler->getUrl()); $form['#theme'] = $view->buildThemeFunctions('views_exposed_form'); $form['#id'] = drupal_clean_css_identifier('views_exposed_form-' . String::checkPlain($view->storage->id()) . '-' . String::checkPlain($display['id'])); + + // Since the exposed form is a GET form, we don't want it to send a wide + // variety of information. + $form['#token'] = FALSE; // $form['#attributes']['class'] = array('views-exposed-form'); /** @var \Drupal\views\Plugin\views\exposed_form\ExposedFormPluginBase $exposed_form_plugin */ diff --git a/core/modules/views/views.module b/core/modules/views/views.module index 944c0c9..3abd329 100644 --- a/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -879,18 +879,6 @@ function views_pre_render_views_form_views_form($element) { } /** - * Implement hook_form_alter for the exposed form. - * - * Since the exposed form is a GET form, we don't want it to send a wide - * variety of information. - */ -function views_form_views_exposed_form_alter(&$form, &$form_state) { - $form['form_build_id']['#access'] = FALSE; - $form['form_token']['#access'] = FALSE; - $form['form_id']['#access'] = FALSE; -} - -/** * Implements hook_query_TAG_alter(). * * This is the hook_query_alter() for queries tagged by Views and is used to diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php index 487f4c8..c18d1da 100644 --- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php +++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php @@ -125,6 +125,119 @@ public function testGetFormIdWithBaseForm() { } /** + * Tests the prepareForm() method with a post request. + */ + public function testPrepareFormPost() { + // By default the user is anonymous, so the form_token is ot emitted. + $this->formBuilder->setCurrentUser($this->account); + $form = array(); + $form_state = $this->formBuilder->getFormStateDefaults(); + $form = (new TestForm())->buildForm($form, $form_state); + $this->formBuilder->prepareForm('my_module_form_id', $form, $form_state); + + $this->assertEquals('post', $form['#method']); + $this->assertTrue(isset($form['form_build_id'])); + $this->assertTrue(isset($form['form_id'])); + $this->assertFalse(isset($form['form_token'])); + return; + + $form = array(); + $form_state = $this->formBuilder->getFormStateDefaults(); + $form = (new TestForm())->buildForm($form, $form_state); + $this->formBuilder->prepareForm('my_module_form_id', $form, $form_state); + + $this->assertEquals('post', $form['#method']); + $this->assertTrue(isset($form['form_build_id'])); + $this->assertFalse(isset($form['form_build_id']['#access'])); + $this->assertTrue(isset($form['form_id'])); + $this->assertFalse(isset($form['form_id']['#access'])); + $this->assertTrue(isset($form['form_token'])); + $this->assertFalse(isset($form['form_token']['#access'])); + + // Make sure the form ID and build ID are still set even with no token. + $form = array(); + $form_state = $this->formBuilder->getFormStateDefaults(); + $form = (new TestForm())->buildForm($form, $form_state); + // Opt out of the token checking. + $form['#token'] = FALSE; + $this->formBuilder->prepareForm('my_module_form_id', $form, $form_state); + + $this->assertEquals('post', $form['#method']); + $this->assertTrue(isset($form['form_build_id'])); + $this->assertTrue(isset($form['form_id'])); + $this->assertFalse(isset($form['form_token'])); + } + + /** + * Tests the prepareForm() method with a get request with token checking. + * + * You can explicitly remove form token checking by using #token = FALSE. + */ + public function testPrepareFormGetWithTokenChecking() { + $form = array(); + $form_state = array( + 'method' => 'get', + ); + $form_state += $this->formBuilder->getFormStateDefaults(); + $form = (new TestForm())->buildForm($form, $form_state); + $this->formBuilder->prepareForm('my_module_form_id', $form, $form_state); + + $this->assertEquals('get', $form['#method']); + $this->assertFalse(isset($form['form_build_id']['#access'])); + $this->assertFalse(isset($form['form_id']['#access'])); + $this->assertFalse(isset($form['form_token']['#access'])); + } + + /** + * Tests the prepareForm() method with a get request without token checking. + */ + public function testPrepareFormGetWithoutTokenChecking() { + $form = array(); + $form_state = array( + 'method' => 'get', + ); + $form_state += $this->formBuilder->getFormStateDefaults(); + $form = (new TestForm())->buildForm($form, $form_state); + // Opt out of the token checking. + $form['#token'] = FALSE; + + $this->formBuilder->prepareForm('my_module_form_id', $form, $form_state); + + $this->assertEquals('get', $form['#method']); + $this->assertFalse(isset($form['form_build_id'])); + $this->assertFalse(isset($form['form_id'])); + $this->assertFalse(isset($form['form_token'])); + + // Second variant. + $form_state = $this->formBuilder->getFormStateDefaults(); + $form = (new TestForm())->buildForm($form, $form_state); + // Opt out of the token checking. + $form['#token'] = FALSE; + $form['#method'] = 'get'; + + $this->formBuilder->prepareForm('my_module_form_id2', $form, $form_state); + + $this->assertEquals('get', $form['#method']); + $this->assertFalse(isset($form['form_build_id'])); + $this->assertFalse(isset($form['form_id'])); + $this->assertFalse(isset($form['form_token'])); + + // Third variant. + $form_state = array( + 'method' => 'get', + 'always_process' => TRUE, + ); + $form = (new TestForm())->buildForm($form, $form_state); + + $this->formBuilder->prepareForm('my_module_form_id3', $form, $form_state); + + $this->assertEquals('get', $form['#method']); + $this->assertFalse(isset($form['form_build_id'])); + $this->assertFalse(isset($form['form_id'])); + $this->assertFalse(isset($form['form_token'])); + } + + /** * Tests the redirectForm() method when a redirect is expected. * * @param array $form_state