Updated: Comment #28

Problem/Motivation

When a filter is missing (either from upgrade or after disabling a filter-providing module), you can no longer view/edit any content with that filter, or access the format admin page.

Proposed resolution

Provide a fallback filter that is used until the format is resaved

Remaining tasks

N/A

User interface changes

N/A

API changes

API addition: removeInstanceId() is moved from ImageEffectBag to PluginBag itself

N/A

Original report by @Berdir

That makes it rather hard to get rid of that filter again :)

This is similar to the block problem. It happens very easily when upgrading a real site with contrib modules to 8.x or when disabling a module that provides a filter that was enabled.

I think missing filters should be ignored as they were in 7.x (maybe only implicitly because we did module_invoke() or something and that silently skipped it.

Either we make FilterBag intelligent enough to skip missing filters or we filter them out earlier.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Berdir’s picture

Status: Active » Needs review
Issue tags: -Needs tests
FileSize
2.11 KB

This is how I would expect it to work.

Status: Needs review » Needs work

The last submitted patch, missing-filter-test-only-2015313-1.patch, failed testing.

chx’s picture

Status: Needs work » Needs review
FileSize
3.95 KB

This is a possible fix.

Edit: it fails on admin/config/content/formats/manage. Yet, it's a start.

chx’s picture

Assigned: Unassigned » tim.plunkett

For review.

chx’s picture

Perhaps use a different factory and use that to instantiate this filter_null. Probably better than messing with the Bag. Also we need to hide it from the UI... somehow.

Status: Needs review » Needs work

The last submitted patch, 2015313_3.patch, failed testing.

tim.plunkett’s picture

The bag is the correct place, but the try/catch should be in initializePlugins, see BlockPluginBag

andypost’s picture

There was a meta issue about proper support for all plugins.
Each kind of plugin needs separate handling of absence the plugin handler, we have this implementation from VDC so probably it makes sense to implement base class/interface

At least no text should be displayed if there's no filter
Same applies for image effects - we should throw 403 to frontend if there's not effect implementation
But both needs ability to remove broken plugin prom list as view does

niko-’s picture

FileSize
3.01 KB

I attach fixing concept patch for make sure if I my undestanding is right.
It based on #3 so at this momment it must be applyed after that.

Tim, check if I think right please.

In short, I replace broken filter with filter_null and inform admin in messages about filter replacing.

chx’s picture

Thanks, I think that looks good but surely you dont need that current now?

Also, } else { needs a newline.

Finally, I think that this way the null filter will show up and confuse people; we need some way to hide from the UI.

tim.plunkett’s picture

I'm pretty sure the try/catch can just go around the parent::pluginInitialize(), it should wrap the least amount of code possible.

niko-’s picture

@chx sure current in my patch come from #3 - it dont need and will be removed

As for hide broken filter in admin my idea is show in admin as disabled checkbox with title (near checkbox) something like "filter_id filter broken ". So we give ability to user delete broken filter and get config clean

jhodgdon’s picture

timplunkett says this is related to a new issue I just filed:
#2058285: Enabling php.module results in uncaught exception on Modules/Extend page

chx’s picture

Re #12 yes, that's one problem but the other problem I have is that if we add a filter_null filter then it'll show up for any format as a possible filter to be added and that's mighty confusing. We either hardwire skipping it or add a no ui capability to filters.

tim.plunkett’s picture

Title: Missing filters result in Exception when viewing text in that format, the filter tips are displayed or the filter is edited » Missing filters result in Exception when the format is used
Status: Needs work » Needs review
FileSize
11.63 KB

The tests need updating, but if a missing plugin is requested, the definition for the null filter is returned and watchdog is triggered.

tim.plunkett’s picture

  1. @@ -115,6 +116,13 @@ public function form(array $form, array &$form_state) {
    +      if ($filter instanceof FilterNull) {
    +        $filters->removeInstanceID($filter_id);
    

    We might want a d_s_m here

  2. @@ -14,18 +14,6 @@
    - * @Filter(
    - *   id = "filter_html",
    - *   module = "filter",
    - *   title = @Translation("Limit allowed HTML tags"),
    - *   type = FILTER_TYPE_HTML_RESTRICTOR,
    - *   settings = {
    - *     "allowed_html" = "<a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd> <h4> <h5> <h6>",
    - *     "filter_html_help" = 1,
    - *     "filter_html_nofollow" = 0
    - *   },
    - *   weight = -10
    - * )
    

    Oops! This was debug code :)

  3. @@ -73,6 +73,22 @@ public function testFilterFormatUpgrade() {
    +    check_markup($this->randomName(), 'format_two');
    

    This should assert that it returns an empty string.

Status: Needs review » Needs work

The last submitted patch, filter-2015313-15.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
11.21 KB
4.09 KB
2.2 KB

Here we are. Should be good to go now.

tim.plunkett’s picture

Issue tags: +API addition
  1. +++ b/core/lib/Drupal/Component/Plugin/DefaultPluginBag.php
    @@ -133,4 +133,12 @@ public function setConfiguration($instance_id, array $configuration) {
    +  /**
    +   * {@inheritdoc}
    +   */
    +  public function removeInstanceID($instance_id) {
    +    parent::removeInstanceID($instance_id);
    +    unset($this->configurations[$instance_id]);
    +  }
    
    +++ b/core/lib/Drupal/Component/Plugin/PluginBag.php
    @@ -125,6 +127,17 @@ public function setInstanceIDs(array $instance_ids) {
    +   * Removes an instance ID.
    +   *
    +   * @param string $instance_id
    +   *   An image effect instance IDs.
    +   */
    +  public function removeInstanceID($instance_id) {
    +    unset($this->instanceIDs[$instance_id]);
    +    $this->remove($instance_id);
    +  }
    
    +++ b/core/modules/image/lib/Drupal/image/ImageEffectBag.php
    @@ -26,17 +26,6 @@ public function &get($instance_id) {
    -   * Removes an instance ID.
    -   *
    -   * @param string $instance_id
    -   *   An image effect instance IDs.
    -   */
    -  public function removeInstanceID($instance_id) {
    -    unset($this->instanceIDs[$instance_id], $this->configurations[$instance_id]);
    -    $this->remove($instance_id);
    -  }
    

    For some reason when I abstracted the plugin bags, I left this method only on image effects. It is very useful, and needed here.

  2. +++ b/core/modules/filter/lib/Drupal/filter/FilterPluginManager.php
    @@ -7,6 +7,7 @@
    +use Drupal\Component\Plugin\Exception\PluginException;
    

    Unneeded, whoops

  3. +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FilterFormatUpgradePathTest.php
    @@ -73,6 +73,24 @@ public function testFilterFormatUpgrade() {
    +    // Check that missing
    

    Ugh, need to fix this comment still.

tim.plunkett’s picture

FileSize
11.27 KB
2.16 KB

Yay, fail/pass combo in 18 as expected.

Berdir’s picture

+++ b/core/modules/filter/lib/Drupal/filter/FilterFormatFormControllerBase.phpundefined
@@ -115,6 +116,14 @@ public function form(array $form, array &$form_state) {
+      // If a filter is missing, it will have been replaced by the null filter.

I don't think will have been is correct here? It already happened? has been should be enough, no?

+++ b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterNull.phpundefined
@@ -0,0 +1,67 @@
+  public function tips($long = FALSE) {
+    return t('No HTML tags allowed.');

Wondering if this could use a different description, as *nothing* is allowed :) Not sure what to publicly display in this case, though.

chx’s picture

> Wondering if this could use a different description, as *nothing* is allowed :) Not sure what to publicly display in this case, though.

I hope, that we could return just '' here cos we do not want these tips, this filter, ever to show up for the user, do we?

jhodgdon’s picture

I have a suggestion for FilterNull class documentation:

/**
 * Provides a fallback placeholder filter to use for missing filters.
 *
 * The filter system uses this filter to replace missing filters (for example, if a filter module
 * has been disabled) that are still part of defined text formats. It returns an empty string.
 *
 * (annotation)
 *   title = @Translation("Provides a fallback for missing filters. Do not use."),
*/
class FilterNull ...

(check wrapping on that paragraph)

I also noticed that in the constructor, it logs a message about the filter but doesn't set the $logged variable to TRUE. Oops! There doesn't seem to be much point in having $logged if it isn't used... but since the log message is only in the constructor, maybe this is not needed?

I also think the tips() method should return t('Missing filter. All text is removed.') or something to that effect. And maybe also "Visit the Text Filters administration page to remedy.", possibly with a link?

tim.plunkett’s picture

FileSize
2.77 KB
11.55 KB

I checked my watchdog, had way too many entries. Duh!

Responded to #21-23, thanks.

@chx Yes ideally this will never show up, but if it does we might as well explain ourselves.

jhodgdon’s picture

I am happy with the patch in #24, documentation-wise. I have not tested the functionality of the patch, but the tests appear to be adequate.

Actually, it would be nice to see a patch with just the tests to verify that it fails without the code fix portion of the patch. Oh, wait, I see that this was done in #18. (That patch did include the FilterNull class but I don't think that changes the test result, which clearly matches this issue report, so that is good.)

I'll let other reviewers set this to RTBC.

Status: Needs review » Needs work
Issue tags: -API addition

The last submitted patch, filter-2015313-24.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Needs review
Issue tags: +API addition

#24: filter-2015313-24.patch queued for re-testing.

Testbot environment failure (missing menu_links table!)

Berdir’s picture

Status: Needs review » Reviewed & tested by the community

Manually tested, enable e.g. php.module, enable that filter for a specific format, create a node with that format, disable php.module, visit node, kaboom. Same when you try to disable that filter again in the settings.

Apply patch, clear cache (so that it finds the new filter_null plugin, won't help without the patch), and it's "working" again. Logs exactly one watchdog entry per request. "working" means there is no text being displayed and the correct filter tip is displayed.

Editing the filter format displays a warning that it will be removed on save and that's exactly what happens, after a re-save, the content is displayed again.

Perfect :)

catch’s picture

I'm not sure the filter tips is enough - what about disabling the textarea on edit/create with a broken format?

tim.plunkett’s picture

Then do we have to deal with #states for the text format select list?
This fixes a bug, that sounds like a whole feature request we'd want to pull the UX team in on...

chx’s picture

Yeah I wouldn't waste a lot of UX effort on a corner case like this.

catch’s picture

Status: Reviewed & tested by the community » Fixed

Just trying to remember what we did in Drupal 7, I think we only prevented the text from being rendered so yeah, committed/pushed to 8.x.

tim.plunkett’s picture

Assigned: tim.plunkett » Unassigned

Thanks!

Berdir’s picture

We don't do anything in 7.x, that specific filter is simply skipped and doesn't run. So if you have PHP code in textfields and disable php.module, it is shown.

catch’s picture

Doh I was thinking of missing text formats which check_markup() checks for.

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

Anonymous’s picture

Issue summary: View changes

updated