Updated: Comment #0

Problem/Motivation

Vertical tabs currently save things such as visibility__active_tab as form values, which corresponds to open tab when pressing the save button. This is semantically wrong and can cause problems when we iterate over all form values to save them, such as in settings_form for example.

Proposed resolution

Add $form_state->valuesCleanKeys. Any values added to that array will be unset by form_state_values_clean(). Update vertical tabs to set it's items to be cleaned using this.

Remaining tasks

Review

User interface changes

None

API changes

Adds $form_state->valuesCleanKeys. Any values added to that array will be unset by form_state_values_clean().

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

tstoeckler’s picture

webchick’s picture

+1, this is probably a better approach. :)

Xano’s picture

Not to sound ignorant, but why would we ever want to iterate over form values and save those? We should be iterating over what we know can be saved, and then get the values from the submitted form data instead, so things don't break like they do here, or when people alter forms to add new elements.

markhalliwell’s picture

markhalliwell’s picture

Issue tags: +Needs backport to D7

Forgot tag

frankcarey’s picture

This is a patch (for D7) which adds $form_state['values_clean']. Any values added to that array will be unset by form_state_values_clean(). Seems like a clean and intuitive way to do it instead of creating another element just for vertical tabs. I wonder if the button logic could be greatly simplified as well in form_state_values_clean() using this technique? Looking for feedback on this approach before I port to D8.

To be clearer about the issue, the results of this bug is that **__active_tab variables show up in the variables tables and can cause features that export them to be considered overridden unnecessarily. They aren't even used when you return to a page or anything. This most likely happens on settings_form forms since they blindly save all values as variables. (< @Xano this is why it's an issue.)

frankcarey’s picture

FileSize
1.14 KB
frankcarey’s picture

Status: Active » Needs review

Status: Needs review » Needs work

The last submitted patch, 7: drupal-1805254-2-D7.patch, failed testing.

markhalliwell’s picture

Until the version field is switched to 7.x, this patch will fail testing. Hiding the file for now, 8.0.x will need to be fixed first regardless.

frankcarey’s picture

Assigned: Unassigned » frankcarey

Roger, porting the D7 patch to D8 now while I have it in my head.

frankcarey’s picture

Status: Needs work » Needs review
FileSize
2.79 KB

Here is the D8 patch which does things very similarly. I searched around for the special code that was added in #2035725: Remove visibility__active_tab from block.schema.yml but it looks like it was refactored out in commit 1228438e65d [1228438e65d].

Status: Needs review » Needs work

The last submitted patch, 12: drupal-clean-values-2190895-12.patch, failed testing.

frankcarey’s picture

Status: Needs work » Needs review
FileSize
2.79 KB

Oops, can use 'string' for php type-hinting.

markhalliwell’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

Awesome, this looks good to me. Just setting to CNW since this will also need additional tests (may already have some similar existing ones somewhere).

tim.plunkett’s picture

  1. +++ b/core/lib/Drupal/Core/Form/FormState.php
    @@ -197,6 +197,24 @@ class FormState implements FormStateInterface {
    +  protected $clean_values = array(
    
    @@ -974,6 +992,30 @@ public function setValueForElement(array $element, $value) {
    +    return $this->clean_values;
    

    Some of the old property names are snake_case due to since-removed ArrayAccess, but all new properties should be lowerCamelCase

  2. +++ b/core/lib/Drupal/Core/Form/FormState.php
    @@ -974,6 +992,30 @@ public function setValueForElement(array $element, $value) {
    +  public function getCleanValues() {
    ...
    +  public function setCleanValues(array $clean_values) {
    ...
    +  public function addCleanValue($clean_value) {
    

    These aren't the values themselves, they're the keys.

  3. +++ b/core/lib/Drupal/Core/Form/FormState.php
    @@ -1164,12 +1206,9 @@ public function getFormObject() {
    +    foreach($this->getCleanValues() as $value) {
    

    missing a space

frankcarey’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
FileSize
3.97 KB

Cool, here it is with tests and tim's suggested fixes. Names now are like addCleanValueKey() instead of addCleanValue().

frankcarey’s picture

I assume we should add the new methods to FormStateInterface as well? Here is a patch that adds that. FYI, I think this would actually be my first core-commit ..kinda long overdue :P

frankcarey’s picture

More functional tests, including for vertical tabs itself.

frankcarey’s picture

Issue summary: View changes
idebr’s picture

  1. +++ b/core/lib/Drupal/Core/Form/FormStateInterface.php
    @@ -1046,4 +1046,30 @@ public function isValidationComplete();
    +   * @return string $key
    

    I think this should be
    @param string $key

  2. +++ b/core/lib/Drupal/Core/Form/FormStateInterface.php
    @@ -1046,4 +1046,30 @@ public function isValidationComplete();
    +   *   The key if the form value to be cleaned.
    

    Suggestion: The form value key to be cleaned.

tim.plunkett’s picture

This is really close!

  1. +++ b/core/lib/Drupal/Core/Form/FormState.php
    @@ -197,6 +197,24 @@ class FormState implements FormStateInterface {
    +  protected $cleanValueKeys = array(
    
    +++ b/core/modules/system/src/Tests/Form/ElementsVerticalTabsTest.php
    @@ -66,4 +67,12 @@ function testDefaultTab() {
    +    $values = Json::decode($this->drupalPostForm('form_test/form-state-values-clean', array(), t('Submit')));
    +    $this->assertFalse(isset($values['vertical_tabs__active_tab']), format_string('%element was removed.', array('%element' => 'vertical_tabs__active_tab')));
    

    Here, and elsewhere, please use [] instead of array()

  2. +++ b/core/lib/Drupal/Core/Form/FormStateInterface.php
    @@ -1046,4 +1046,30 @@ public function isValidationComplete();
    +  public function setCleanValueKeys(array $values);
    ...
    +  public function getCleanValueKeys();
    

    Might as well flip the order of these, to match the order in FormState.

  3. +++ b/core/lib/Drupal/Core/Form/FormStateInterface.php
    @@ -1046,4 +1046,30 @@ public function isValidationComplete();
    +   * @return array $keys
    

    @return array

  4. +++ b/core/lib/Drupal/Core/Form/FormStateInterface.php
    @@ -1046,4 +1046,30 @@ public function isValidationComplete();
    +   * @return string $key
    

    @param string $key

    @return $this

  5. +++ b/core/modules/system/src/Tests/Form/StateValuesCleanTest.php
    @@ -49,6 +49,9 @@ function testFormStateValuesClean() {
    +    $this->assertFalse(isset($values['wine']), format_string('%element was removed.', array('%element' => 'wine')));
    

    Here, and elsewhere, use String::format() instead of format_string()

  6. +++ b/core/tests/Drupal/Tests/Core/Form/FormStateTest.php
    @@ -530,6 +530,23 @@ public function testTemporaryValue() {
    +  /**
    +  * @covers ::getCleanValues
    +  * @covers ::setCleanValues
    +  * @covers ::addCleanValue
    +  * @covers ::cleanValues
    +  */
    +  public function testCleanValues() {
    

    Please split these into four test methods, one for each @covers

    Consider using @depends to build from one method to the next.

  7. +++ b/core/tests/Drupal/Tests/Core/Form/FormStateTest.php
    @@ -530,6 +530,23 @@ public function testTemporaryValue() {
    +    $form_state = New FormState();
    

    new FormState()

Also, please include interdiffs when making changes between patches. Thanks!

idebr’s picture

Status: Needs review » Needs work

I believe the feedback in #22 means this issue needs work :)

frankcarey’s picture

Huh, I thought I'd updated this :/ I have all the fixes and just needed to upload the diffs I think.. @todo

frankcarey’s picture

Status: Needs work » Needs review
FileSize
10.14 KB
9.74 KB

Here are the files w/ the interdiff

Status: Needs review » Needs work

The last submitted patch, 25: drupal-clean-values-2190895-25.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Reviewed & tested by the community
FileSize
10.35 KB
963 bytes

Sorry, I wasn't specific about the String::format thing, fixing for you just to get this done.

As that's my only contribution here, I feel comfortable RTBCing this.

Thanks @frankcarey!

markhalliwell’s picture

Minor nit pick, but shouldn't the manual unset that was introduced in #2035725: Remove visibility__active_tab from block.schema.yml be removed here since it's already removed from the array by this patch?

This patch:

+++ b/core/lib/Drupal/Core/Render/Element/VerticalTabs.php
@@ -100,6 +100,9 @@ public static function processVerticalTabs(&$element, FormStateInterface $form_s
+    // Clean up the active tab value so it's not accidentally stored in
+    // settings forms.
+    $form_state->addCleanValueKey($name . '__active_tab');

From other issue (which has the @todo this patch fixes):

+++ b/core/modules/block/lib/Drupal/block/BlockFormController.php
@@ -359,6 +360,21 @@ public function delete(array $form, array &$form_state) {
+  public function buildEntity(array $form, array &$form_state) {
+    $entity = parent::buildEntity($form, $form_state);
+
+    // visibility__active_tab is Form API state and not configuration.
+    // @todo Fix vertical tabs.
+    $visibility = $entity->get('visibility');
+    unset($visibility['visibility__active_tab']);
+    $entity->set('visibility', $visibility);
+
+    return $entity;
+  }
tim.plunkett’s picture

alexpott’s picture

Category: Task » Bug report

This is a bug.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 27: revamp_vertical_tabs_to-2190895-27.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Reviewed & tested by the community
FileSize
1.47 KB
10.37 KB

Yay, the automated @covers checker works!

alexpott’s picture

Version: 8.0.x-dev » 7.x-dev
Status: Reviewed & tested by the community » Patch (to be ported)

This issue is a normal bug fix, and doesn't include any disruptive changes, so it is allowed per https://www.drupal.org/core/beta-changes. Committed 1218fdf and pushed to 8.0.x. Thanks!

  • alexpott committed 1218fdf on 8.0.x
    Issue #2190895 by frankcarey, tim.plunkett: Revamp vertical tabs to not...

The last submitted patch, 12: drupal-clean-values-2190895-12.patch, failed testing.

markhalliwell’s picture

Status: Patch (to be ported) » Needs work
Issue tags: -Needs backport to D7

Awesome :)

@frankcarey, I've re-queued #7, but figured you may want to revisit the 7.x patch so it's a little more inline with the final patch that got into 8.x?

  • alexpott committed 1218fdf on 8.1.x
    Issue #2190895 by frankcarey, tim.plunkett: Revamp vertical tabs to not...

  • alexpott committed 1218fdf on 8.3.x
    Issue #2190895 by frankcarey, tim.plunkett: Revamp vertical tabs to not...

  • alexpott committed 1218fdf on 8.3.x
    Issue #2190895 by frankcarey, tim.plunkett: Revamp vertical tabs to not...

  • alexpott committed 1218fdf on 8.4.x
    Issue #2190895 by frankcarey, tim.plunkett: Revamp vertical tabs to not...

  • alexpott committed 1218fdf on 8.4.x
    Issue #2190895 by frankcarey, tim.plunkett: Revamp vertical tabs to not...
frankcarey’s picture

frankcarey’s picture

Re uploading the same D7 patch to try to get tests running for it again (old one doesn't seem to work anymore)