Changing field settings, such as the label, after data has been entered into a content-type using that field, is not working.

The error is,

FieldUpdateForbiddenException: field_sql_storage cannot change the schema for an existing field with data. in field_sql_storage_field_update_forbid() (line 229 of /Users/Shai/Sites/mampsites/drupal/modules/field/modules/field_sql_storage/field_sql_storage.module).

Shai

Comments

yched’s picture

Priority: Normal » Critical

Indeed. After a quick look, seems to be some FAPI-related mess up.

Er, critical.

yched’s picture

That affects text fields.

That's because the 'Maximum length' input at the bottom of the form is '#disabled' (which, BTW, Seven doesn't really hint : #690980: Disabled form elements not properly rendered) - see text_field_settings_form().

When the form is submitted, $form_state['values'] contains an empty string for the element, instead of the '#default_value'.
That empty string is taken as the new setting for the 'Maximum length', and since you're not supposed to be able to change this at this point (there's already data in the field, Field API doesn't support changing the db schema), this raises an exception.

I don't know when this broke. Definitely worked at some point earlier.
This shows we lack tests on Field UI, but I'm a bit surprised that a #disabled element is handed back by FAPI as having actually changed ;-).

Need feedback from FAPI gurus here.

yched’s picture

When a textfield element is #disabled, _form_builder_handle_input_element() does:

$input_exists = FALSE;
$input = NULL;
      if (!$input_exists && !$form_state['rebuild'] && !$form_state['programmed']) {
        // We leverage the internal logic of form_set_value() to change the
        // input values by passing $form_state['input'] instead of the usual
        // $form_state['values']. In effect, this adds the necessary parent keys
        // to $form_state['input'] and sets the element's input value to NULL.
        _form_set_value($form_state['input'], $element, $element['#parents'], NULL);
        $input_exists = TRUE;
      }

comes back with NULL in $form_state['values'], then

        if (function_exists($value_callback)) {
          $element['#value'] = $value_callback($element, $input, $form_state);
        }

runs form_type_textfield_value(), which puts back '' (empty string) in $element['#value'].

Last refactor in this area was #622922: User input keeping during multistep forms, crossposting over there.

effulgentsia’s picture

Title: Changing Field Settings Throws Error and Fails » User input should not be processed for disabled elements
Component: field_ui.module » forms system
Status: Active » Needs review
Issue tags: +Security
StatusFileSize
new2.34 KB

@yched: can you confirm that this fixes the original issue reported? If it does, since this touches on security, a FAPI maintainer should review this prior to it being RTBC.

Shai’s picture

I applied the patch in #4 to an install of of D7 - alpha 1. The patch applied cleanly. I went to an existing text field, with nodes using that field in the db, and changed the label. The new field label updated without error.

Shai

yched’s picture

@effulgentsia: Yup, I confirm it fixes it.

katrienc’s picture

I've also tested this and confirm that the patch on #4 solves the issue.

effulgentsia’s picture

Version: 7.0-alpha1 » 7.x-dev

Now we just need chx or another FAPI maintainer to approve the change (or identify why it's completely wrong).

effulgentsia’s picture

StatusFileSize
new3.5 KB

This one adds one more hunk for the form_type_textfield_value() that's commented with why. If FAPI maintainers disagree with the change introduced by #4, then just this patch's form_type_textfield_value() hunk alone can be committed (with a modified comment) and it will fix the specific use-case this issue was started with, but I continue to think this full patch is in order. It makes no sense to me that there is no way currently to set something as not being available to user input, but still visible. There was a recent comment posted to the D6 FAPI reference to this effect as well: http://api.drupal.org/api/drupal/developer--topics--forms_api_reference..... It makes a lot more sense to me to treat #disabled as meaning not just default the browser to having it disabled, but make the server-side code treat it as disabled too. If someone doesn't want an element disabled in this sense, they can set #attributes['disabled']='disabled' instead of setting #disabled, and this would mean: only disable it client-side, and if client-side code enables the element, then user input can be accepted by the server.

effulgentsia’s picture

Issue tags: -Security +Security improvements
StatusFileSize
new3.3 KB

Changed so that even programmatically submitted forms can't bypass #disabled restrictions (note, this is how checkboxes work in HEAD, so this patch just applies the same rule to all elements), and removed the @todo from #9. I'm quite confident this is now correct and ready to fly, but it needs someone familiar enough with FAPI and security to RTBC.

arhak’s picture

If someone doesn't want an element disabled in this sense, they can set #attributes['disabled']='disabled' ...

would be nice to have it documented

effulgentsia’s picture

Yep, I think the most logical place, if this patch goes in, would be in http://api.drupal.org/api/drupal/developer--topics--forms_api_reference..... For better or worse, that seems to be where Form API properties are explained. Since this is also an API change from D6 (in terms of making the #disabled property mean more than it meant in D6), this will also need to be mentioned in http://drupal.org/update/modules/6/7 (again, assuming the patch gets committed).

David_Rothstein’s picture

This seems related to (or maybe a duplicate of) #426056: Server-side enforcement of #disabled is inconsistent?

effulgentsia’s picture

Title: User input should not be processed for disabled elements » Disabled textfields should use default value, not be forced to empty string
Issue tags: -Security improvements
StatusFileSize
new857 bytes

Ok, since there's the other issue, this one is just a patch to fix the original issue reported. This one's a no-brainer, but still needs someone to RTBC (though doesn't have to be a FAPI guru).

effulgentsia’s picture

Title: Disabled textfields should use default value, not be forced to empty string » When disabled textfields are submitted, their values are forced to empty string instead of the default value

Better title given that this is a regression that occurred due to #622922: User input keeping during multistep forms, and all this issue does is fix that regression.

eric_a’s picture

Is this really a regression due to #622922: User input keeping during multistep forms? Or let me put it this way: I got bitten by this in D6. In simple forms I would set the #disabled property on textfields with default values and was left with empty strings after submission. At the time I decided this was just the way FAPI worked and that I either had to set the values myself in a submit handler or set #access FALSE and display a disabled copy of the original textfield.

effulgentsia’s picture

@Eric_A: You're correct. It's not a regression. I looked at the D6 code and at the D7 code prior to #622922: User input keeping during multistep forms, and both have the same bug. I assumed it was a regression because of comment #2, but I guess there's some other reason FieldUI worked before and now doesn't.

I still think it's a critical bug, and the current behavior makes no sense, because, for example, #type='select' and #type='textarea' do not have this bug. It should at least be fixed for D7, and #14 can be easily rolled for D6 too if that's desired.

The best way to hack around this limitation of textfields until this issue is fixed is to set #value on the element which is the way to force a value on any element regardless of user input. In fact, until #426056: Server-side enforcement of #disabled is inconsistent is fixed, I highly recommend doing this on all #disabled elements that the form doesn't want input for, since currently, nothing prevents javascript from enabling a disabled element.

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community

Looks proper to me. I've been bitten by this too.

yched’s picture

Aw, sorry for wrongly assigning blame, effulgentsia.

Confirming that #14 fixes the bug.

webchick’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs tests

Let's get some tests for this.

Shai’s picture

What would the appropriate tests be?

I know that in my original example, the problem only occurred if there was data in the database for the particular field. So the test would create a content-type, and then a text field, and then add a node with data in the text field, and then try to change the label in the content type. Is that right?

What other tests should be created?

Re: writing the tests... how do we get someone to jump in. I think with the limited time I have to work on core my time would be better spent in doing things I already know how to do. However, if it is deemed that there is a big shortage of test writers... then maybe I would jump in. For me or others, can someone leave a link for the best "writing tests for core" tutorial.

Shai

sun’s picture

effulgentsia’s picture

Status: Needs work » Postponed

I'd like #426056: Server-side enforcement of #disabled is inconsistent to go in first. There's a dependency between that issue and proper testing of this one. If that issue weren't on the table, this one could proceed, but since that one has reached consensus, and is just awaiting bot to return and someone to RTBC it, it makes sense to get that one in first.

@Shai: once that issue goes in, I'll write the test for this one. It'll be pretty simple: it can leverage existing FAPI tests.

effulgentsia’s picture

Assigned: Unassigned » effulgentsia

.

effulgentsia’s picture

Assigned: effulgentsia » Unassigned
Status: Postponed » Fixed

This got fixed as part of #335035: drupalPost() incorrectly submits input for disabled elements. The test for this already existed as part of FormsTestCase::testDisabledElements(), but the test wasn't working correctly due to that bug.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

ledut’s picture

subscribing