Problem
While there is no UI to delete a filter format, the deletion of a filter format via API, (e.g. in update functions, drush, or custom code), may result in data loss. This is unexpected behavior and can easily happen to unintentional data loss.
This is related to #3326447: Deleting a Media view mode should not result in the deletion of an entire text format what can easily trigger this problem (and this is how I found out). Since #3326447: Deleting a Media view mode should not result in the deletion of an entire text format has since been fixed, downgraded this issue to "Major".
Steps to reproduce
* Create content type with a new formatted text field
* Edit the field. Select filter format "Restricted HTML" under allowed_formats and save it.
* Create a node with content for the formatted text field.
* Execute PHP code
\Drupal\filter\Entity\FilterFormat::load('restricted_html')->delete();
* Check the node, the new field and its content is gone.
Reason is that the field has a config dependency on the filter format, when it is configured as an allowed format. Thus, when the filter format is deleted, the field and field storage are deleted as well, even though it has data.
Proposed resolution
Deleting a filter format is problematic generally, but generally the field data is not lost when wrongly deleted. The problematic part is when the field is deleted. Thus, this is where we need to be more careful:
Implement onDependencyRemoval() so that the field config is updated, not deleted, when the filter format is deleted.
Remaining tasks
For usability review
Though deleting text formats is not allowed through the UI, it could be possible via command line, code that is not sufficiently careful to delete a text format, or a bug like #3326447: Deleting a Media view mode should not result in the deletion of an entire text format. When entity with a formatted text field is viewed, no content for the field will be displayed if the field format is set to a nonexistent format. This is a security feature per #17, because without the format, it is no longer safe to display the content.
Should there be some sort of message or markup displayed on view to people of sufficient level of access that the format is missing? The access could possibly be determined by whether the user has access to edit the formatted text field.
| Comment | File | Size | Author |
|---|
Issue fork drupal-3537624
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
godotislateI think this can be done by implementing
onDependencyRemoval(array $dependencies)inDrupal\field\Entity\FieldStorageConfig.Basically, something like:
Comment #3
godotislateComment #4
fagoWith onDependencyRemoval() we could remove the dependency and let the field and data survive, what I think would be acceptable given the deletion.
I'd prefer to deny the deletion when the field has data though, but that does not seem to be properly doable. The only documented exception for ConfigEntity:delete() is EntityStorageException what seems not suiting.
The code generating the dependency on filter-formats in text-fields is that method:
As seen, it only adds dependencies for the "allowed_formats". This seems bogus, since there is no larger harm when a filter-format this is a allowed-format is removed than when a filter-format that is not referenced is removed. When all filter-formats are allowed, no dependency is added, the filter format might be used, and it can be deleted. What happens is that the field and its data is not removed, it just gets a reference on a selected filter format.
On the other hand when a filter format is removed that is part of the "allowed-formats", since there is just one entry less that users can select. The more severe data integrity issue is the same as before.
That said, the right fix here is probably to add a config dependency on all filter-formats when the list of filter-formats is not restricted.
Then, given that we already have the issue that filter formats leads to data with invalid text-format-references when no "allowed_formats" are set, implementing onDependencyRemoval() to avoid field-data deletion in case of "allowed_formats" usage seems reasonable.
Comment #6
godotislateMy suggestion to implement
onDependencyRemoval()in #2 was in the wrong place.TextItemBasemakes more sense on where to implement it, and that's what is in MR 12823.This prevents the data from deleted, but there is a display issue, in that when the format is deleted, existing content with text fields using that filter format can not display the content. However, this can be addressed by editing the entity and setting the filter format to something else.
Leaving at NW for tests.
Comment #7
fagoThank you. I added the test case, verified it fails first, and then put it on top of your changes, to see it pass! All good, this seems ready then!
Comment #8
fagoThis actually concerns text fields provided by text module.
Comment #9
useernamee commentedComment #10
xjmI haven't read the solution in full yet, but it is extremely important that we block deletion of the filter format when stuff still uses it rather than anything else like altering config, since changing a filter format can have security implications.
Whatever solution should also have subsystem and/or framework manager review.
Comment #11
godotislateSo
FilterFormatAccessControlHandler::checkAccess()has this:I used drush to reproduce steps in the IS:
drush php-eval 'Drupal\filter\Entity\FilterFormat::load("restricted_html")->delete();', which I guess bypassescheckAccesssomehow. I haven't looked into why, and there's a note in the comment about deletion via UI and not CLI.Regardless, maybe a better approach is hardening filter format entity delete access?
ETA. I think checkAccess isn't invoked by
$entity->delete(), so it might be too late to prevent deletion once that is called.Comment #12
xjmThis is also kinda problematic, replacing data loss with data integrity problems and/or "perceived" data loss.
Comment #13
xjmOops, xpost. Reviewing #11 now.
Comment #14
xjm#11 does indeed also sound like something we should confirm or revisit. It might be worth a
git log -Lon the comment to see why the issue that added it made the choice to differentiate the UI versus API deletion in this way. (There might be useful discussion on the original issue.)Thanks @godotislate!
This will also eventually need usability review on whatever proposed solution we fix, and should be fixed in a way that's consistent with these other "config dependencies eat my config" issues.
Also, bumping to critical; nothing should be able to delete a populated storage in this way without an explicit user decision. When we uninstall a module, for example, the user has to do a bulk operation to remove module content before they can uninstall it.
Comment #15
godotislate@xjm I think I got confused in #11.
Looking at the code again now, it doesn't look like calling
$entity->delete()invokes entity access. I believe the access check needs to happen beforedelete()call. Oncedelete()is called, I don't think there's any way to stop that except by throwing an exception.Comment #16
xjm@godotislate Ah, yeah, the internal API call should probably only do what it says on the tin and not vary according to the logged-in user account or anything like that. So changing things earlier in the callstack seems like the right approach. We might not be able to stop people from making bad out-of-context direct API calls with Drush; that is a Drush "feature" I guess.
In either case, we should manually test it carefully.
Comment #17
longwaveRe #6/#12 this is a security feature, because the filter format may previously have sanitised some HTML, and once that is removed we can no longer safely display the content. I believe this is also why deleting a format from the UI is not allowed either.
Comment #18
godotislateI guess the question here is, given that filter formats are not intended to be deleted, what handling should there be in the case that a drush command, or contrib/custom code was not sufficiently careful to prevent deletion?
Comment #19
fagoFixing it on entity-access layer makes sense, but it's not solving the issue I reported here. The UI already has no way to delete filter formats, it can only be disabled. This issue is about unexpected behavior when you run the code, with drush, or via an (post)-update hook/function, that does not matter:
So when the filter format is still referenced in field config, those fields get deleted, including the content. I'd argue this is very un-intuitive behavior, since this is not visible or clear from the code executed.
Indeed, deleting the filter-format might result in data-integrity issues. But I'd argue that's an intuitive. possible consequence from deleting a filter format manually like this, but loosing not touched field-configuration including the deletion of if its data is not.
Additionally, you can already delete filter formats that are in use with code snippets like this - so that "data integrity" issue when deleting filter formats via code is already pre-existing. The described problem only applies if the format is referenced as a "allowed-filter-format" in the field. However, if no filter formats are referenced, filter-format usage is not restricted (so it might be in use) and deleting a filter-format would not delete any fields. Thus, this "data-integrity issue" is not new, but the suggested change makes the behavior consistent in both cases.
I must say, I don't see an issue with the "data-integrity" problem, as mentioned, that's intuitive to me when you delete a filter format like this. However, if we decide it is an issue that shall be tackled instead, we should also cover the case of un-referenced filter-formats which are currently not referenced in field configs at all. That sounds like a quite complicated problem to solve though.
Comment #20
godotislateAgreed with #19: if a filter format is improperly deleted somehow, this should not result in content data loss. If, for example, the project has the filter format configuration in version control, the format could be restored somewhat easily. But if all the data is gone, the restoration effort would be more difficult and would require database backups to be available.
What are next steps? Should this be submitted for usability review?
Comment #21
godotislateAdding #3326447: Deleting a Media view mode should not result in the deletion of an entire text format as related because it seems to follow that if you delete a Media view mode that deletes a text format, this could end up deleting content.
Comment #22
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #23
godotislate#2579743: Config entities implementing EntityWithPluginCollectionInterface should ask the plugins to react when their dependencies are removed is in, which resolves #3326447: Deleting a Media view mode should not result in the deletion of an entire text format and removes one vector of (accidentally) deleting filter formats.
Comment #24
ironnuts commentedThere is code inside TextEditorObjectDependentValidatorTrait.php at lines 34- 38 which seems related?: Is there scope to update any ckeditor tests as part of this issue?
Comment #25
godotislateI wonder if it would make sense to show on entity view a message that the formatted text field can not be displayed because of the missing filter format. This message would be limited to users with a specific level access, perhaps having permission to edit the field? Though I think that could be something to be done in a follow up while the data loss concern gets addressed here first.
Comment #26
ironnuts commentedRe: #25 That sounds like it would tick a few boxes test wise.
Comment #27
godotislateFixed the logic in onDependencyRemoval a bit and rebased.
Tagged for usability review to evaluate whether something like #25 is needed and updated the IS to note that as a remaining task.
Comment #28
benjifisherWe discussed this issue at #3552911: Drupal Usability Meeting 2025-10-24 (@rkoller and @simohell) and #3554117: Drupal Usability Meeting 2025-10-31 (@benjifisher, $rkoller, and @simohell). Those issues each have a link to a recording of the meeting. I am giving issue credit here to the attendees at the usability meetings.
This comment is not a usability review. Instead, it is about the results of some manual testing. (At the second meeting, we were a little confused: when we did not see data loss, we thought the issue was outdated. We forgot that we should have tested with 11.x instead of the branch from the MR.)
The issue summary refers to #3326447: Deleting a Media view mode should not result in the deletion of an entire text format. That issue was closed as a duplicate of #2579743: Config entities implementing EntityWithPluginCollectionInterface should ask the plugins to react when their dependencies are removed, which is now Fixed. We have not tested to confirm, but it seems as if the only ways to delete a filter format are using PHP code (as in the Steps to reproduce) or perhaps using the
jsonapimodule. I am adding the tag for an issue summary update and setting the status back to NW. Perhaps the priority should be downgraded from Critical, too.The Steps to reproduce are incomplete if there is more than one field using the same field storage. For example, this is the case when testing with the Standard or Umami installation profile. If only one of the field configurations is removed, then the field storage configuration is not removed, and that means that the database table is not removed. In this case, it is possible to restore the data from the deleted field:
deleted = 0for all rows (or only the ones affected by deleting the filter format, if you can think of a way to do that).Related: the issue summary states,
The field, not the field storage, has a dependency on the filter format.
I also made some comments on the MR. These are not related to the usability meeting, and they are just my own opinions.
Comment #29
godotislateThanks @benjifisher for the review and to you, @rkoller, and @simohell for looking at this in the usability meeting.
Were you able to discuss the question in the "For usability review" section in the IS? #12 raised a question about data integrity problem or perceived data loss because of the behavior not to show the content of a formatted text field on entity view when the field's text format does not exist. I proposed one solution: on entity view, display a message or markup to users who have access to edit the field value that the format is missing.
Updated the IS and downgraded to Major since #3326447: Deleting a Media view mode should not result in the deletion of an entire text format was fixed via #2579743: Config entities implementing EntityWithPluginCollectionInterface should ask the plugins to react when their dependencies are removed
Comment #30
benjifisher@godotislate:
Thanks for updating the Steps to reproduce. I made a few further tweaks and also updated the Proposed resolution. I am removing the tag for an issue summary update.
Personally, I agree with the last sentence in your Comment #25:
Perhaps we should open a follow-up issue and have the usability review there.
Still not a usability opinion (and still my personal opinion): removing a filter format (however that happens) is an edge case, so it is not worth a lot of extra code to alert site admins/editors to the problem on the View page. The code would have to worry about permissions, caching, and whether the notice would appear in other places, like listings from Views.
It is much simpler to work with the node-edit form, which is already restricted to appropriate permissions. Often, there is already some indication on that form: a Text format select list with nothing selected (and I think that will generate an error if you try to submit the form). We could add a more explicit warning in this situation, but I am still not sure it is worth the effort.
Comment #31
benjifisherUsability review
The decision at #3552911: Drupal Usability Meeting 2025-10-24 is that we should add a notification on the view page when a field has an invalid filter format. That notification might be in a status message or it might be in place of the field that is not rendered. In either case, the notification should be restricted to users with edit permission for the entity.
Following Comments #25 and #30, we can address this in a follow-up issue, so I added #3556673: Alert users with edit permission when a field is not displayed because of invalid text format.
I am removing the tag for a usability review. I am not sure this issue needs all of the other issue tags currently attached, but removing any of those is outside the scope of a usability review.
If you want more feedback from the usability team, a good way to reach out is in the #ux channel in Slack.
Comment #32
godotislateAddressed or asked for clarification for outstanding MR comments.
Putting this back into review.
This issue is scoped to prevent data loss in the case that a filter format is deleted, though that is not supposed to happen. Since #3326447: Deleting a Media view mode should not result in the deletion of an entire text format has been addressed, a possible cause for a filter format being deleted has been removed, but it is possible that there are other ways that a filter format could be deleted (such as other bugs, contrib/custom code, or CLI commands.)
Removing
Needs security reviewbecause it seems like security issue might have been addressed in #17Removing
Needs change recordbecause there's no API change here.Comment #33
benjifisher@godotislate:
I replied to your comments on the MR. I also added a draft change record in case there are contrib or custom modules that extend
TextItemBaseand overrideonDependencyRemoval.Back to NW.
Comment #34
godotislateAddressed MR comments. Back to NR.
Comment #35
benjifisher@godotislate:
Thanks for the updates.
There is still one open thread on the MR, but I do not see a specific suggestion for changes. This issue is also tagged for subsystem maintainer review, but I am not sure that is needed.
Reminder: @godotislate and I agree (Comments #25, #30) that the usability concerns raised in this issue can be postponed to the followup issue #3556673: Alert users with edit permission when a field is not displayed because of invalid text format.
I did one more round of manual testing, following the Steps to reproduce in the issue summary. As expected, the new field was not displayed (but the label was) when viewing the node, but there was no data loss: the field config, field storage config, and database table were all preserved. Editing the node and selecting a valid text format restored the text when viewing the node.
Comment #36
smustgrave commented3 subsystems and I never am needed for sub maintainer review. Benjifisher pinged me about this one. Assuming to look at the approach of using the onRemoval approach +1 for me, pretty inline with how we do other places.
Did take a lap at saving credit.
Comment #37
xjmJust reiterating for posterity: We do not defer usability reviews to followups. Usability is a core gate, and hard-blocking. 🙂
Very rarely, we will grant an exception and temporarily defer gates to followups to more quickly mitigate data loss or the like. In this case, though, user expectations about what should happen are deeply intertwined with what the user considers data loss versus intentional deletion, so it is an essential part of the issue.
Comment #38
godotislateComment #40
godotislateSetting back to NR to get it picked up for another usability review (per #37) with the tag.
Comment #41
smustgrave commentedDoes #28/#31 not count as usability? Also issue was worked on by benjifisher who is on the UX team.
Comment #42
godotislateSorry, that's on me. I missed that #31 was a follow up usability review, and for whatever reason I thought the review was going to be done in the follow up. Anyway, NW for #31. Basically, bring #3556673: Alert users with edit permission when a field is not displayed because of invalid text format back into scope here.
Comment #43
smustgrave commentedTried to address that warning part.
Comment #44
smustgrave commentedRebased
Comment #45
smustgrave commentedComment #46
godotislateAdded a comment to the MR.
Also, we should make sure that the message only shows to appropriate level users.
Comment #47
smustgrave commentedSounds like needs more work. Will try and get to it after my trip if no one beats me to it.
Comment #48
godotislateYes, sorry, meant to set back to NW.