Updated: Comment #42

Problem/Motivation

This bug is known to affect files, checkboxes, dates, boolean and probably other fields. Multistep forms are especially affected because any files (or field settings) added in previous steps will be lost when the form is finally submitted on the final step. This is because fields which are not in the active step are hidden with #access=false.

Steps to reproduce

1. Create a new content type with an image field and give it a default image.
2. Add a node of that content and upload an image different from the default.
3. Create a new module and in the .module file, add the following code (note, this module is named `devmodule`):

<?php

function devmodule_menu() {
  $items['devmodule'] = array(
    'page callback' => 'devmodule_callback',
    'access callback' => TRUE,
  );  
  return $items;
}

function devmodule_callback() {
  module_load_include('inc', 'node', 'node.pages');
  $node = node_load(1); // Use the nid of the node you created in Step #2.
  $form = node_page_edit($node);
  $form['field_image']['#access'] = FALSE; // 'field_image' is the name of the image field added in Step #1.
  return render($form);
}

4. Enable the module and clear the cache.
5. Navigate to the menu path defined in the module (i.e. http://d8.local/devmodule) where you should see the node edit form without the image field.
7. Save the form.
8. Result: The image file is deleted and replaced by the default image.

Proposed resolution

The following code exists in file.module:

  // Process any input and save new uploads.
  if ($input !== FALSE) {
    $return = $input;

    // Uploads take priority over all other values.
    if ($file = file_managed_file_save_upload($element)) {
      $fid = $file->fid;

If file is #access=false, we have $input = NULL (which is !== false), thus, $fid is set to 0.

Changing

  if ($input !== FALSE) {

to

  if ($input !== FALSE && $input !== NULL) {

fixes the issue. No side effects were confirmed so far.

Remaining tasks

Determine if the test is adding new test coverage, if so add it.

User interface changes

API changes

Data model changes

Release notes snippet

Comments

webchick’s picture

Status: Active » Needs review
xjm’s picture

Tagging issues not yet using summary template.

geek-merlin’s picture

Component: file system » file.module
Gman’s picture

I too was using the multistep module and noticed my images disappearing when saving other steps which did not include the images. When investigating this issue, I came across this patch.

I can report that the submitted patch resolves the issue (no files/images silently deleted from the node).

I would suggest changing the title to "File(s) silently deleted when #access=false" as that more generally represents the issue at hand.

xjm’s picture

Title: file silently deleted in multistep form » File(s) silently deleted when #access=false

Anyone can change the title. :)

geek-merlin’s picture

Status: Needs review » Reviewed & tested by the community

so rtbc as of #4

webchick’s picture

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

Let's get some test coverage on this. Looks like extending file_module_test_form() in modules/file/tests/file_module_test.module to include an $access parameter might be a place to look.

stefan freudenberg’s picture

Assigned: Unassigned » stefan freudenberg
catch’s picture

Issue tags: +Needs backport to D7

Tagging for backport.

Yuri’s picture

I tested this patch on D7.12 with the current Multistep dev 2011-June-27, and confirm that files stay saved in multiple steps. As far as I experience now there are no side effects.

geek-merlin’s picture

Assigned: stefan freudenberg » Unassigned
Status: Needs work » Reviewed & tested by the community

so this patch still works unchanged for d7 and d8 as reported in the OP and is rtbc.

we still need test coverage as pointed out in #7

webchick’s picture

Status: Reviewed & tested by the community » Needs work

Still needs test coverage != RTBC. :)

xjm’s picture

It's not RTBC until it includes those tests. :)

Edit: Crosspost. :P

geek-merlin’s picture

ups sorry i meant to revert that myself.

stefan freudenberg’s picture

Hi. Sorry for failing to report back. I am having trouble making my test fail without the patch D7. Can you share your form axel.rutz?

star-szr’s picture

StatusFileSize
new1.72 KB

I'm trying to put together a test case for this, from my understanding we need to use a multi-step form to test whether the patch fixes the bug, my approach so far has been to add another button to the file_module_test_form() form. I've attached my work-in-progress file_module_test.module patch, I know it's not pretty. So far I've just been testing file_module_test.module directly in the browser by removing hidden = TRUE from file_module_test.info.

Here is my testing procedure, after enabling the 'File test' module:

  1. Visit file/test
  2. Upload a file, then click 'Continue submit'.
  3. Once the page reloads, click 'Save'.
  4. Verify that the file has been uploaded to the files directory and is in the file_managed table.

With this test case, the patch doesn't seem to make a difference, so I think I'm definitely missing something.

xjm’s picture

Maybe we could try creating a form with the multistep module, with which the patch reportedly solves the issue, and inspect that form's structure?

stefan freudenberg’s picture

We have to simulate the way it does multistep. It's not a core module, so we cannot use it here. Every step in multistep form module is actually a different URL.

webchick’s picture

Priority: Major » Normal

Since this is showing up only in a contributed module and so far not replicable by core, degrading to normal.

It would be nice if the maintainers of said module could handle creating the test case. Core developers have so far been unsuccessful in creating it by sticking to common patterns in core, so I'm guessing multistep is doing something a bit out of the ordinary.

star-szr’s picture

From what I saw when testing the Multistep module, the module seems to alter existing forms (it doesn't create forms of its own), and stores the step in the URL query string.

As the original issue stated, the Multistep module sets #access = FALSE for fields not on the current page. The fact that this is done through a field_group.module hook - hook_field_group_pre_render() complicates the issue even further, and makes it harder to reproduce this behavior in a comprehensible test case in core.

It was also pointed out when discussing this issue on IRC that the existing condition (below) was likely written that way for a reason, so we definitely need tests (and comments in the code) to prove that this change is sound.

if ($input !== FALSE) {

martijn houtman’s picture

I bumped into this problem while creating a block (in a custom module) that grabs the user_profile_form, which contains an image-field and setting this field's #access to FALSE: after submit the previously-set image is deleted.

So I just wanted to state that this does not _just_ happen when using the Multistep module, it happens whenever you set a file-field or image-field to #access FALSE. The test-case would most likely be easier to follow my steps.


function any_menu_callback() {
  $account = user_load('...');
  $form = drupal_get_form('user_profile_form', $account); // Or any other form that contains a file-field (I think, untested!)
  $form['field_image']['#access'] = FALSE; // Any file field that currently contains a file
  return render($form);
}

And now save the form. In the current situation, the image will be deleted.

I can confirm changing ($input !== FALSE) to just ($input) fixes the problem, while maintaining the ability to unset a file with the 'remove' button, so I think this fix is correct.

mrharolda’s picture

It seems checkboxes have the same issue...

martijn houtman’s picture

The fix (removing !== FALSE) does have a side-effect: with an image field that has a default image selected, the fix prevent you from unsetting the default image. Going to look for a better fix ...

ACF’s picture

Assigned: Unassigned » ACF
yesct’s picture

Assigned: ACF » Unassigned

No work for a few weeks. Making it unassigned so it can be picked up by anyone. ACF, reassign it back if you want, no worries. :)

dags’s picture

Assigned: Unassigned » dags
dags’s picture

Issue summary: View changes

typo

dags’s picture

I moved this comment into the issue summary.

dags’s picture

I did some investigating and found this snippet under file_managed_file_submit($form, &$form_state) around line 1259 in file.module.

  if ($button_key == 'remove_button') {
    // If it's a temporary file we can safely remove it immediately, otherwise
    // it's up to the implementing module to remove usages of files to have them
    // removed.
    if ($element['#file'] && $element['#file']->status == 0) { 
      file_delete($element['#file']->fid);
    }    

Removing the if statement around file_delete() solves the problem with the default images not being removed. But that doesn't seem like a reasonable solution.

dags’s picture

Issue summary: View changes

Adding new information to the issue summary.

dags’s picture

Priority: Normal » Major

In comment #19, webchick degraded the priority from major to normal as it was believed this was a bug in the multistep module. Setting back to major because this has been reproduced in core.

dags’s picture

Assigned: dags » Unassigned

I'm also unassigning this from myself in case anyone else wants to jump in.

xjm’s picture

Thanks @dags.

Let's get an automated test for this based on the STR in the summary.

dags’s picture

Status: Needs work » Needs review
StatusFileSize
new1.45 KB

First draft of a test.

Status: Needs review » Needs work

The last submitted patch, 1205822-files-access-test.patch, failed testing.

xjm’s picture

Nice work, thanks @dags! That test looks good.

A few suggestions:

  1. --- a/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php
    +++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.phpundefined
    
    @@ -20,6 +20,31 @@ public static function getInfo() {
    +    $type_name = 'article';
    +    $field_name = strtolower($this->randomName());
    +    $this->createFileField($field_name, $type_name);
    +    $field = field_info_field($field_name);
    +    $instance = field_info_instance('node', $field_name, $type_name);
    

    Let's add an inline comment above this to make it more scannable, something like "Add a file field to the article content type."

    Also, ideally, we should use a test content type rather than article for better decoupling. (See LocaleContentTest for an example.) If FileFieldWidgetTest does not currently use a test bundle elsewhere, let's introduce it here first and then file a followup issue to use that test bundle throughout this class (or possibly throughout all classes extending FileFieldTestBase, depending).

    In an ideal world, we'd use a test entity instead of node, but that's probably out of scope here. (We wouldn't be able to use uploadNodeFile(), and the test entity class is in flux with the EntityNG conversion, plus #1822000: Remove Drupal\field_test\Plugin\Entity\Type\TestEntity in favor of EntityTest.)

  2. +++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.phpundefined
    @@ -20,6 +20,31 @@ public static function getInfo() {
    +    $test_file = $this->getTestFile('image');
    +    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
    +    $node = node_load($nid, TRUE);
    

    Another inline comment here would be good, like "Create a node with an uploaded file in the field."

  3. +++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.phpundefined
    @@ -20,6 +20,31 @@ public static function getInfo() {
    +    // Save the node and ensure it does not have the file.
    

    I think this should say "and ensure the file is not deleted"?

  4. +++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.phpundefined
    @@ -20,6 +20,31 @@ public static function getInfo() {
    +    $this->assertFileExists($node_file, t('File still exists.'));
    

    The t() can be removed from around the assertion message here. See: http://drupal.org/simpletest-tutorial-drupal7#t

raycascella’s picture

Assigned: Unassigned » raycascella

Working on ticket at Portland2013 sprint. Going to combine patch and test, then reroll after a manual test on my local.

raycascella’s picture

Looks like the patch created before may have gone stale. Initial tests show the test passing with or without the update. I'm going to keep looking, though. As I suspect it may have been human error on my part while testing.

raycascella’s picture

Issue summary: View changes

Adding links to related issues

geek-merlin’s picture

#32: good test @dags!

+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.phpundefined
@@ -20,6 +20,31 @@ public static function getInfo() {
+    $this->drupalPost("node/$nid/edit", $edit, t('Save'));

Nice test, but it looks like this cannot work:
WebTestBase::handleForm (called by ebTestBase::drupalPost / WebTestBase::handleForm
) only can generate a post request to insert values into a already rendered form.
It can NOT do a form alter.

How it CAN work:
* write a tiny helper module that does the form alter
* enable it before your test (not forget to disable it afterwards)

geek-merlin’s picture

Assigned: raycascella » Unassigned

@ RayCascella:
still sprinting?
if yes feel free to reassign you!
if not i just unblock this.
;-)

slashrsm’s picture

Assigned: Unassigned » slashrsm
Status: Needs work » Needs review
StatusFileSize
new3.36 KB

I combined all 3 patches and re-rolled against 8.x.

Will work on other issues based on this patch.

Status: Needs review » Needs work

The last submitted patch, 1205822_39.patch, failed testing.

slashrsm’s picture

Assigned: slashrsm » Unassigned
Status: Needs work » Needs review
StatusFileSize
new5.66 KB
new3.49 KB

Attached patch includes comments from #34 and #37. I also changed

  if ($input) {

to

  if ($input !== FALSE && !== NULL) {

I have feeling that we can get array() as an input in here which must processed. Initial patch would ignore this case and display default value instead. I may be wrong, so we probably need to investigate this a bit more.

About bug mentioned in #23... It looks like it also exists in HEAD, which means that was not introduced by this patch. I'll do some more investigation and post another bug if I confirm my assumptions.

slashrsm’s picture

Confirming default image issue as unrelated bug. Bug report: #2051213: Unable to remove default image from image field/instance.

slashrsm’s picture

Issue summary: View changes

Formatting issue summary so it follows the template.

slashrsm’s picture

Issue summary was updated to reflect latest info.

dags’s picture

@slashrsm: Patch looks good. One minor issue:

+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.phpundefined
@@ -30,6 +30,40 @@ public static function getInfo() {
+    debug($type);

Remove this line? I'm not sure if coding standards actually require debug() lines to be removed from tests...

Also, could you post the test separately from the patch? So we can see it fail without the patch and pass with it.

slashrsm’s picture

StatusFileSize
new657 bytes
new3.47 KB
new3.02 KB

I definitely didn't want to leave debug() in there :).

slashrsm’s picture

Any other comments? Could we RTBC it?

slashrsm’s picture

Issue summary: View changes

Update summary with latest info.

bmango’s picture

I manually applied the patch in #45 and it worked well for me. I was having problems saving images with the Multistep module.

Many thanks!

ts145nera’s picture

StatusFileSize
new351 bytes

Hello and thanks,
is correct apply only this?

Status: Needs review » Needs work

The last submitted patch, 48: save_multistep_files_1205822.patch, failed testing.

ts145nera’s picture

Version: 8.x-dev » 7.26
Issue tags: -Needs backport to D7
StatusFileSize
new351 bytes

I'm sorry, I've forget to select D7

marthinal’s picture

Version: 7.26 » 8.x-dev

We need to fix this for d8 and then backport to d7.

ts145nera’s picture

I'm sorry, but I don't work with D8...
I'll try to port this patch in D8 early.

marthinal’s picture

Status: Needs work » Needs review
Issue tags: +DrupalCampSpain
StatusFileSize
new3.12 KB

Rerolled. Cannot reproduce manually. Also, this test should fail and works. Anyway, I think we could add this test.

ryan.ryan’s picture

Assigned: Unassigned » ryan.ryan

Assigning for testing

Jalandhar’s picture

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

Patch needs reroll

babruix’s picture

Status: Needs work » Needs review
StatusFileSize
new3.13 KB
new3.88 KB

Re-rolled.

babruix’s picture

Issue tags: -Needs reroll

Status: Needs review » Needs work

The last submitted patch, 56: 1205822-56.patch, failed testing.

yesct’s picture

Assigned: ryan.ryan » Unassigned

Issue was assigned in May. Unassigning so it is clear anyone can work on this.

catch’s picture

catch queued 56: 1205822-56.patch for re-testing.

The last submitted patch, 56: 1205822-56.patch, failed testing.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Oakleeey_97’s picture

Shot in the dark here..

I've tried applying the patch in the description and it doesn't make a difference at all.

As soon as I press next / previous to go back onto the page I uploaded the file, the file's deleted.

Any help would be massively appreciated.

jonathanshaw’s picture

Issue tags: +Needs reroll
deepakaryan1988’s picture

Assigned: Unassigned » deepakaryan1988
deepakaryan1988’s picture

Assigned: deepakaryan1988 » Unassigned
Status: Needs work » Needs review
StatusFileSize
new3.08 KB

Tried to reroll this and it was working on my local. Let's hope it would not break in auto testing.

Status: Needs review » Needs work

The last submitted patch, 67: file_s_silently-1205822-67.patch, failed testing.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

manuel garcia’s picture

Issue tags: -Needs reroll

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.8.x-dev

Drupal 8.6.x will not receive any further development aside from security fixes. Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.2.x-dev

Drupal 8 is end-of-life as of November 17, 2021. There will not be further changes made to Drupal 8. Bugfixes are now made to the 9.3.x and higher branches only. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.15 was released on June 1st, 2022 and is the final full bugfix release for the Drupal 9.3.x series. Drupal 9.3.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.4.x-dev branch from now on, and new development or disruptive changes should be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.9 was released on December 7, 2022 and is the final full bugfix release for the Drupal 9.4.x series. Drupal 9.4.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.5.x-dev branch from now on, and new development or disruptive changes should be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

quietone’s picture

This was a bugsmash target yesterday. Lendude and myself discussed this.

The last report that this problem was occurring and the patch fixed it was in #47, 9 years ago. Then there was a break for 2 years. After that,
in #53 a patch was made to reproduce the problem. That patch passed tests and it was reported that the problem could not be reproduced. The same comment suggest the test could be added. However, if the problem not longer exists, perhaps there is an existing test and this one does not need to be added. Since then there has been no activity.

What remains is to determine if the test is worth adding. I have updated the issue summary.

FWIW, I updated the patch and yes it passes.

quietone’s picture

Category: Bug report » Task

And no longer a bug report.

pooja saraah’s picture

StatusFileSize
new3.14 KB
new1.27 KB

Fixed failed commands on #81
Attached patch against Drupal 9.5.x

b-prod’s picture

@quietone I don't think this issue is definitively closed, as this bug still appear if you set the access to FALSE on a parent element of the file managed element, but not on the file managed element itself.

Actually the point above seems to be caused by late alteration of the form, so this is not related to the current issue.

Version: 9.5.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.