Problem/Motivation

Any field of type 'Text (formatted)' simply does not save the data entered into it when a module is enabled that alters the element info for #type text_format and adds an additional #pre_render callback.

Possible data loss: It could simply cause data loss as the user will simply fail to notice the entered data is not saved.

Impossible to submit data: In case the field is set as 'required', the user will not be able to submit the data at all.

How to reproduce?

  1. Install Drupal 8 with the standard profile.
  2. Create a field of type 'Text (formatted)' with default configuration.
  3. Try to save a value for that field. e.g. when you create a content.

Note: This is about a "text" field (as in Field API) not a "textfield" (as in @FormElement("textfield")).

Proposed resolution

Technical reason:
The filter module copies selected element info properties from the top-level #type text_format element to the lower level #type textfield element, and then augments the lower level #type textfield element with its default values using the + $array operator. The + $array operator will not override existing keys.
When text module is enabled on its own, there is no value for #type textfield in #pre_render and so the lower level #type textfield receives the default $array value from the textfield element info.
When editor module is enabled, it alters the element info for #type text_format, adding a #pre_render entry, this is copied into the lower level #type textfield and prevents the default from being added via the default when + $array is used.
The solution in the patch is to make sure that #pre_render values form #type text_format are not copied into the lower level #type textfield element, ensuring the defaults are set.

Before

Before screenshot

After

After screenshot

Remaining tasks

User interface changes

None.

API changes

Unknown.

Original report by @Kartagis

I noticed that fields created via Field API or otherwise (only on registration form) don't save information. I first noticed this when I created a field via my module and attached it to user entity_type. I then tested it by manually creating a formatted text field with Field UI, and noticed that behaviour too.
Regards,

Files: 
CommentFileSizeAuthor
#41 text-format-busted-2348459.pass_.patch4.38 KBlarowlan
PASSED: [[SimpleTest]]: [PHP 5.4 MySQL] 81,769 pass(es).
[ View ]
#41 text-format-busted-2348459.fail_.patch3.79 KBlarowlan
FAILED: [[SimpleTest]]: [PHP 5.4 MySQL] 81,724 pass(es), 1 fail(s), and 0 exception(s).
[ View ]
#41 interdiff.txt4.3 KBlarowlan
#40 text-format-busted-2348459.fail3_.patch6.81 KBlarowlan
FAILED: [[SimpleTest]]: [PHP 5.4 MySQL] 81,744 pass(es), 1 fail(s), and 0 exception(s).
[ View ]
#40 interdiff.txt1.14 KBlarowlan
#39 text-format-busted-2348459.fail2_.patch6.78 KBlarowlan
PASSED: [[SimpleTest]]: [PHP 5.4 MySQL] 81,752 pass(es).
[ View ]
#39 interdiff.txt3.35 KBlarowlan
#31 text-format-busted-2348459.fail_.patch3.91 KBlarowlan
PASSED: [[SimpleTest]]: [PHP 5.4 MySQL] 81,735 pass(es).
[ View ]
#24 drupal-formatted-text-breaks-textfield-2348459-24.patch701 bytesalexarpen
PASSED: [[SimpleTest]]: [PHP 5.4 MySQL] 81,103 pass(es).
[ View ]
#12 drupal-formatted-text-breaks-textfield-2348459-11.patch700 bytesalexarpen
PASSED: [[SimpleTest]]: [PHP 5.4 MySQL] 78,995 pass(es).
[ View ]
#9 2348459-after.png123.38 KBalexarpen
#9 2348459-before.png118.28 KBalexarpen
#9 drupal-fix-formatted-textfield-9215589-0.patch483 bytesalexarpen
PASSED: [[SimpleTest]]: [PHP 5.4 MySQL] 78,932 pass(es).
[ View ]

Comments

Kartagis’s picture

Issue summary:View changes
.John’s picture

Status:Active» Postponed (maintainer needs more info)

Unable to recreate this issue in the latest dev when adding a field through the UI. Can you provide steps on how to create the issue from a fresh installation of the latest dev?

Kartagis’s picture

Status:Postponed (maintainer needs more info)» Active

Step 1: Browsed to /admin/config/people/accounts/fields
Step 2: Created a field.
Step 3: Verified that the table for the field has been created.
Step 4: Filled in some value and save.
Step 5: Checked if there is any data in the table.

There is one additional behaviour that I noticed. If you make the field required and fill in some value, I get Foo field is required..

Regards,
K.

lokapujya’s picture

Status:Active» Postponed (maintainer needs more info)

Tried those steps. It works fine. Anything different about your setup?

lokapujya’s picture

Status:Postponed (maintainer needs more info)» Active

seeing it now that I used formatted text field.

Kartagis’s picture

You are right, it works on plain text.

alexarpen’s picture

Title:Fields created don't saveText fields with filtered text enabled do not save values
Issue summary:View changes
Priority:Normal» Major
Issue tags:+textfield, +Amsterdam2014
alexarpen’s picture

The simple text field did not used to have the option to be filtered in Drupal 7 and I couldn't find any evidence it does in Drupal 8 on purpose.

This feature is simply implemented in class TextItemBase and is not overridden in class TextItem.

The question is that 'Should simple text field be able to be a filtered text at all?'

Some reference to check:
https://api.drupal.org/api/drupal/developer!topics!forms_api_reference.h...

alexarpen’s picture

StatusFileSize
new483 bytes
PASSED: [[SimpleTest]]: [PHP 5.4 MySQL] 78,932 pass(es).
[ View ]
new118.28 KB
new123.38 KB

The problem is that the input field has no name attribute therefore the data is not sent to the server upon form submission.

alexarpen’s picture

Status:Active» Needs review
Berdir’s picture

Component:field system» forms system
Issue tags:+Needs tests

So we're starting to figure this out.

The current preprocess logic for textfields sits in \Drupal\Core\Render\Element\Textfield::preRenderTextfield(). But somehow, this is not called when we go through a text_format type that then switches to a textfield. Textarea doesn't have the prerender method, it uses preprocess for this. Why is this different?

alexarpen’s picture

Component:forms system» filter.module
StatusFileSize
new700 bytes
PASSED: [[SimpleTest]]: [PHP 5.4 MySQL] 78,995 pass(es).
[ View ]

The problem was caused by filter module. It was making the text field not be able to run #pre_render functions.

Though there is no problem with textarea. The reason is that textarea is using template_preprocess_* function (the old way) rather than using #pre_render methods.

There is a need for another issue saying that textarea should use #pre_render rather than *_preprocess_* function.

There is also another problem would be noticeable after applying this path:
Text fields with formatted text enabled turn into Textarea in front end. This should be discussed in another issue.

There is also a big question remaining: Is does not seem to be a mistake by filter.module because it is simply mobing #pre_render to the child element. Why it's not / should it be applied for the child element?

alexarpen’s picture

A new issue is created to make Textarea use #pre_render rather than proprocess function:
https://www.drupal.org/node/2350931

graindor’s picture

Wim Leers’s picture

This issue is very confusing. Please update the IS to be incredibly explicit about a "text" field (as in Field API) versus a "textfield" (as in @FormElement("textfield")).

This may have been introduced during the conversion of elements into annotated classes.

Wim Leers’s picture

Berdir asked:

As soon as we add the #id to the attributes, ckeditor makes our textfield a textarea-editor-thingy. is that by design?

Yes, CKEditor will happily replace input[type=text] also AFAIK/IIRC.

The real question/problem is: why does Drupal allow associating a text format with a input[type=text]? (Or, probably, why does Drupal allow using a input[type=text] Field API Widget with a filtered text field?)

Berdir’s picture

text fields with a format were always (as in, since 7.x at least) supported.

This is not field API specific. Field API text fields use the standard form API #type => text_format with #base_type => 'textfield', that is broken.

And yes, I guess it is related to the @RenderElement changes.

beejeebus’s picture

ouch, this will lead to data loss.

we definitely need tests so that we don't reintroduce bugs that lose user-submitted data.

kattekrab’s picture

I'll have a go at updating the issue summary.

kattekrab’s picture

Issue summary:View changes
kattekrab’s picture

Title:Text fields with filtered text enabled do not save valuesText fields with formatted text enabled do not save values

title change for clarity

kattekrab’s picture

Issue summary:View changes
alexarpen’s picture

Issue summary:View changes

@beejeebus, the problem is a bit more dramatic than not having a test for that. Actually there is a test for that but for an unknown reason, the test passes while it should fail.
@kattekrab, thanks. It is much more clear now!

I am re-submitting the patch as the dev version has changed a bit since then.

alexarpen’s picture

Title:Text fields with formatted text enabled do not save valuesFields of type 'Text (formatted)' do NOT save values.
Issue summary:View changes
StatusFileSize
new701 bytes
PASSED: [[SimpleTest]]: [PHP 5.4 MySQL] 81,103 pass(es).
[ View ]

Text field uses '#pre_render' functions to add attributes. In D7 this was being done by template_preprocess_* functions.
The problem is that filter module removes '#pre_render' from $element['value'] which prevents the text field to do the preprocess tasks.

Please review it and move it to RTBC.

I update the issue summary to explain how to reproduce the problem in simple steps.

lokapujya’s picture

" the test passes while it should fail." - Which test is that? How hard would it be to modify the test so that i fails without the patch?

alexarpen’s picture

The problem with the test is actually another issue. I will create another issue for that and will explain things there. This patch has nothing to do with the test. It just fixed the bug.

lokapujya’s picture

Anytime we fix a bug (even more so, for a data loss), we should also add a test or fix the broken test.

Since this bug is only visible when another module (Filter module) is enabled, one way of testing this would be to add a test that enables all Core modules and then runs a form test. Is there a better way? Another way, would be to mimic the way filter module removes '#pre_render'.

alexarpen’s picture

@lokapujya Could you explain more about this line: 'Another way, would be to mimic the way filter module removes '#pre_render'.'?

lokapujya’s picture

We could create a dummy test module that does (whatever it is that filter module does) that is preventing the text field to do preprocess tasks AND then enable it during a test. Maybe it's easier just to enable the filter module?

kattekrab’s picture

@alexar - I think that what @beejeebus and @lokapujya are saying is that YOU need to write and add tests to show your patch fixes the bug. And the tests help prevent regressions in future.

larowlan’s picture

StatusFileSize
new3.91 KB
PASSED: [[SimpleTest]]: [PHP 5.4 MySQL] 81,735 pass(es).
[ View ]

So here's a test, but the thing is - it passes - without the patch.
Can someone take a look and tell me what I'm missing.

larowlan’s picture

So if the test passes - does that point to something in Field API?

tim.plunkett’s picture

+++ b/core/modules/filter/src/Tests/TextFormatElementFormTest.php
@@ -0,0 +1,136 @@
+    $form_state = new FormState();
+    $form_state->setBuildInfo(['callback_object' => $this, 'args' => []]);
...
+    $form = $form_builder->buildForm($this, $form_state);

This could just be $form_builder->getForm($this);

alexarpen’s picture

@larowlan and @tim.plunkett

I created another issue regarding the test and put the current test which has a problem there:
#2382601: Tests which pass while they should fail!

I also updated the issue summary.

catch’s picture

Priority:Major» Critical

I think we should handle #2382601: Tests which pass while they should fail! in here.

Bumping this to critical now anyway.

alexarpen’s picture

@catch I think they are different issues and should be discussed differently because even though we fix the test issue this problem still persists and needs the patch get applied.

larowlan’s picture

larowlan’s picture

larowlan’s picture

StatusFileSize
new3.35 KB
new6.78 KB
PASSED: [[SimpleTest]]: [PHP 5.4 MySQL] 81,752 pass(es).
[ View ]

Here's another test that follows the exact steps to reproduce - but passes :(
*BUT* the exact steps to reproduce applied manually actually reproduce the bug
*SO* I assume it is something in standard install or another enabled module

digging down that path now

larowlan’s picture

StatusFileSize
new1.14 KB
new6.81 KB
FAILED: [[SimpleTest]]: [PHP 5.4 MySQL] 81,744 pass(es), 1 fail(s), and 0 exception(s).
[ View ]

So yep, its something in standard - looking at editor module first (already looked at quickedit)

Patch to demo fail

larowlan’s picture

StatusFileSize
new4.3 KB
new3.79 KB
FAILED: [[SimpleTest]]: [PHP 5.4 MySQL] 81,724 pass(es), 1 fail(s), and 0 exception(s).
[ View ]
new4.38 KB
PASSED: [[SimpleTest]]: [PHP 5.4 MySQL] 81,769 pass(es).
[ View ]

Ok, getting somewhere - the cause is indeed editor module.

Fail/Pass patches.

Also fixes #33

Fix is #24

Blew away the web-test added in #39, the kernel test is obviously quicker.

larowlan’s picture

Issue summary:View changes

Updated issue summary with tech details

The last submitted patch, 40: text-format-busted-2348459.fail3_.patch, failed testing.

The last submitted patch, 41: text-format-busted-2348459.fail_.patch, failed testing.

beejeebus’s picture

larowlan++

tim.plunkett’s picture

Status:Needs review» Reviewed & tested by the community
Issue tags:-textfield, -Needs tests

Nice sleuthing!

alexpott’s picture

Status:Reviewed & tested by the community» Fixed

Committed 2b60900 and pushed to 8.0.x. Thanks!

  • alexpott committed 2b60900 on 8.0.x
    Issue #2348459 by larowlan, alexarpen: Fields of type 'Text (formatted...
Wim Leers’s picture

Wow! Awesome detective work!

Also: thanks for the follow-up; this is indeed brittleness we should avoid.

Status:Fixed» Closed (fixed)

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