Commit faca27de authored by catch's avatar catch

Issue #2015313 by tim.plunkett, Berdir, chx, niko-: Fixed Missing filters...

Issue #2015313 by tim.plunkett, Berdir, chx, niko-: Fixed Missing filters result in Exception when the format is used.
parent 8abed813
......@@ -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]);
}
}
......@@ -101,7 +101,9 @@ public function remove($instance_id) {
* The ID of the plugin instance to add.
*/
public function addInstanceID($id) {
$this->instanceIDs[$id] = $id;
if (!isset($this->instanceIDs[$id])) {
$this->instanceIDs[$id] = $id;
}
}
/**
......@@ -124,6 +126,17 @@ public function setInstanceIDs(array $instance_ids) {
$this->instanceIDs = $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);
}
/**
* Implements \Iterator::current().
*/
......
......@@ -7,10 +7,8 @@
namespace Drupal\filter;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Plugin\DefaultPluginBag;
use Drupal\Component\Utility\String;
/**
* A collection of filters.
......@@ -48,6 +46,8 @@ public function getAll() {
// Retrieve all available filter plugin definitions.
if (!$this->definitions) {
$this->definitions = $this->manager->getDefinitions();
// Do not allow the null filter to be used directly, only as a fallback.
unset($this->definitions['filter_null']);
}
// Ensure that there is an instance of all available filters.
......@@ -68,25 +68,17 @@ public function getAll() {
protected function initializePlugin($instance_id) {
// Filters have a 1:1 relationship to text formats and can be added and
// instantiated at any time.
$definition = $this->manager->getDefinition($instance_id);
if (isset($definition)) {
// $configuration is the whole filter plugin instance configuration, as
// contained in the text format configuration. The default configuration
// is the filter plugin definition.
// @todo Configuration should not be contained in definitions. Move into a
// FilterBase::init() method.
$configuration = $definition;
// Merge the actual configuration into the default configuration.
if (isset($this->configurations[$instance_id])) {
$configuration = NestedArray::mergeDeep($configuration, $this->configurations[$instance_id]);
}
$this->configurations[$instance_id] = $configuration;
parent::initializePlugin($instance_id);
}
else {
throw new PluginException(String::format("Unknown filter plugin ID '@filter'.", array('@filter' => $instance_id)));
// @todo $configuration is the whole filter plugin instance configuration,
// as contained in the text format configuration. The default
// configuration is the filter plugin definition. Configuration should not
// be contained in definitions. Move into a FilterBase::init() method.
$configuration = $this->manager->getDefinition($instance_id);
// Merge the actual configuration into the default configuration.
if (isset($this->configurations[$instance_id])) {
$configuration = NestedArray::mergeDeep($configuration, $this->configurations[$instance_id]);
}
$this->configurations[$instance_id] = $configuration;
parent::initializePlugin($instance_id);
}
/**
......
......@@ -12,6 +12,7 @@
use Drupal\Core\Entity\EntityFormController;
use Drupal\Core\Entity\Query\QueryFactory;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\filter\Plugin\Filter\FilterNull;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
......@@ -115,6 +116,14 @@ public function form(array $form, array &$form_state) {
// Create filter plugin instances for all available filters, including both
// enabled/configured ones as well as new and not yet unconfigured ones.
$filters = $format->filters()->sort();
foreach ($filters as $filter_id => $filter) {
// When a filter is missing, it is replaced by the null filter. Remove it
// here, so that saving the form will remove the missing filter.
if ($filter instanceof FilterNull) {
drupal_set_message(t('The %filter filter is missing, and will be removed once this format is saved.', array('%filter' => $filter_id)), 'warning');
$filters->removeInstanceID($filter_id);
}
}
// Filter status.
$form['filters']['status'] = array(
......
......@@ -40,4 +40,13 @@ public function __construct(\Traversable $namespaces) {
$this->factory = new ContainerFactory($this);
}
/**
* {@inheritdoc}
*/
public function getDefinition($plugin_id) {
$plugins = $this->getDefinitions();
// If the requested filter is missing, use the null filter.
return isset($plugins[$plugin_id]) ? $plugins[$plugin_id] : $plugins['filter_null'];
}
}
<?php
/**
* @file
* Contains \Drupal\filter\Plugin\Filter\FilterNull.
*/
namespace Drupal\filter\Plugin\Filter;
use Drupal\filter\Annotation\Filter;
use Drupal\Core\Annotation\Translation;
use Drupal\filter\Plugin\FilterBase;
/**
* 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.
*
* @Filter(
* id = "filter_null",
* module = "filter",
* title = @Translation("Provides a fallback for missing filters. Do not use."),
* type = FILTER_TYPE_HTML_RESTRICTOR,
* weight = -10
* )
*/
class FilterNull extends FilterBase {
/**
* Tracks if an alert about this filter has been logged.
*
* @var bool
*/
protected $logged = FALSE;
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, array $plugin_definition) {
// Once per filter, log that a filter plugin was missing.
if (!$this->logged) {
$this->logged = TRUE;
watchdog('filter', 'Missing filter plugin: %filter.', array('%filter' => $plugin_id), WATCHDOG_ALERT);
}
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public function process($text, $langcode, $cache, $cache_id) {
return '';
}
/**
* {@inheritdoc}
*/
public function getHTMLRestrictions() {
// Nothing is allowed.
return array('allowed' => array());
}
/**
* {@inheritdoc}
*/
public function tips($long = FALSE) {
return t('Missing filter. All text is removed');
}
}
......@@ -25,17 +25,6 @@ public function &get($instance_id) {
return parent::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);
}
/**
* Updates the configuration for an image effect instance.
*
......
......@@ -73,6 +73,23 @@ public function testFilterFormatUpgrade() {
// Check that role 5 cannot access to the defined text formats
$this->assertNoFieldChecked('edit-5-use-text-format-format-one', 'Use text format format_one permission for role is set correctly.');
$this->assertNoFieldChecked('edit-5-use-text-format-format-two', 'Use text format format_two permission for role is set correctly.');
// Check that the format has the missing filter.
$two = filter_format_load('format_two');
$this->assertTrue($two->filters()->has('missing_filter'));
// Try to use a filter format with a missing filter, this should not throw
// an exception.
$empty_markup = check_markup($this->randomName(), 'format_two');
$this->assertIdentical($empty_markup, '', 'The filtered text is empty while a filter is missing.');
// Editing and saving the format should drop the missing filter.
$this->drupalGet('admin/config/content/formats/manage/format_two');
$this->assertRaw(t('The %filter filter is missing, and will be removed once this format is saved.', array('%filter' => 'missing_filter')));
$this->drupalPost(NULL, array(), t('Save configuration'));
filter_formats_reset();
$two = filter_format_load('format_two');
$this->assertFalse($two->filters()->has('missing_filter'));
}
}
......@@ -132,6 +132,14 @@
'status' => '1',
'settings' => 'a:1:{s:17:"filter_url_length";s:2:"72";}',
))
->values(array(
'format' => 'format_two',
'module' => 'missing_module',
'name' => 'missing_filter',
'weight' => '0',
'status' => '1',
'settings' => 'a:0:{}',
))
->execute();
// Define which roles can use the text formats.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment