diff --git a/core/modules/filter/src/Entity/FilterFormat.php b/core/modules/filter/src/Entity/FilterFormat.php index 4b00af1..59acedb 100644 --- a/core/modules/filter/src/Entity/FilterFormat.php +++ b/core/modules/filter/src/Entity/FilterFormat.php @@ -7,6 +7,7 @@ namespace Drupal\filter\Entity; +use Drupal\Component\Plugin\PluginInspectionInterface; use Drupal\Core\Config\Entity\ConfigEntityBase; use Drupal\Core\Entity\EntityWithPluginCollectionInterface; use Drupal\Core\Entity\EntityStorageInterface; @@ -387,4 +388,42 @@ public function getHtmlRestrictions() { } } + /** + * {@inheritdoc} + */ + public function removeFilter($instance_id) { + unset($this->filters[$instance_id]); + $this->filterCollection->removeInstanceId($instance_id); + } + + /** + * {@inheritdoc} + */ + public function onDependencyRemoval(array $dependencies) { + $changed = FALSE; + $filters = $this->filters(); + foreach ($filters as $filter) { + if (in_array($filter->provider, $dependencies['module'])) { + $this->removeFilter($filter->getPluginId()); + $changed = TRUE; + } + } + if ($changed) { + $this->save(); + } + } + + /** + * {@inheritdoc} + */ + protected function calculatePluginDependencies(PluginInspectionInterface $instance) { + // Only add dependencies for plugins that are actually configured. This is + // necessary because the filter plugin collection will return all available + // filter plugins. + // @see \Drupal\filter\FilterPluginCollection::getConfiguration() + if (isset($this->filters[$instance->getPluginId()])) { + parent::calculatePluginDependencies($instance); + } + } + } diff --git a/core/modules/filter/src/FilterFormatInterface.php b/core/modules/filter/src/FilterFormatInterface.php index b5e75ce..b9c558b 100644 --- a/core/modules/filter/src/FilterFormatInterface.php +++ b/core/modules/filter/src/FilterFormatInterface.php @@ -84,4 +84,12 @@ public function getFilterTypes(); */ public function getHtmlRestrictions(); + /** + * Removes a filter. + * + * @param string $instance_id + * The ID of a filter plugin to be removed. + */ + public function removeFilter($instance_id); + } diff --git a/core/modules/filter/src/Tests/FilterAPITest.php b/core/modules/filter/src/Tests/FilterAPITest.php index 588546e..fc755ec 100644 --- a/core/modules/filter/src/Tests/FilterAPITest.php +++ b/core/modules/filter/src/Tests/FilterAPITest.php @@ -386,4 +386,34 @@ public function assertFilterFormatViolation(ConstraintViolationListInterface $vi } $this->assertTrue($filter_format_violation_found, format_string('Validation violation for invalid value "%invalid_value" found', array('%invalid_value' => $invalid_value))); } + + /** + * Tests that dependency removal works. + * + * Uninstalling a module that provides a filter should not remove a text + * format that uses the filter. + */ + public function testDependencyRemoval() { + $this->installSchema('user', array('users_data')); + $filter_format = \Drupal\filter\Entity\FilterFormat::load('filtered_html'); + + // Enable the filter_test_restrict_tags_and_attributes filter. + $filter_config = [ + 'weight' => 10, + 'status' => 1, + ]; + $filter_format->setFilterConfig('filter_test_restrict_tags_and_attributes', $filter_config)->save(); + + // Verify that a dependency exists on the module that provides the filter. + $this->assertEqual(['module' => ['filter_test']], $filter_format->getDependencies()); + + // Uninstall the module. + \Drupal::moduleHandler()->uninstall(array('filter_test')); + \Drupal::entityManager()->getStorage('filter_format')->resetCache(); + + // Verify the text format still exists but the dependency and filter is gone. + $filter_format = \Drupal\filter\Entity\FilterFormat::load('filtered_html'); + $this->assertEqual([], $filter_format->getDependencies()); + } + }