Text formats include an ordered list of filters which can serve different purposes such as limitation what HTML can be placed into content, replacing a pattern, adding extra CSS to the page, etc. The filters are invoked in order if they are used to filter text.
Starting from Drupal 8 new filters can be defined using plugins with specification of their type.
Filter plugins \Drupal\filter\Plugin\FilterInterface
- The plugin should be located into the proper place to adhere to PSR-4 standards. The folder structure should be /src/Plugin/Filter.
- The plugin should be annotated with the @Filter annotation.
- The plugin should specify a filter type in its annotation:
- FilterInterface::TYPE_HTML_RESTRICTOR - HTML tag and attribute restricting filters.
- FilterInterface::TYPE_MARKUP_LANGUAGE - The filter converts something that's not HTML to HTML in a way that is not compatible with WYSIWYG editing.
- FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE - Irreversible transformation filters. The filter performs a transform for which a WYSIWYG plugin does not exist to perform the transformation client-side
- FilterInterface::TYPE_TRANSFORM_REVERSIBLE - Reversible transformation filters. The filter performs a transform for which a WYSIWYG plugin exists to perform the same transformation (and its reverse) client-side.
- \Drupal\filter\Plugin\FilterBase provides a default implementation so the plugin don't need to implement every method.
Example filter plugin "Wrap table"
Let's build an example filter plugin to provide a filter to wrap a table element to an extra wrapper with the class "tablefield-wrapper" to improve its displaying on different devices. We plan on taking existing HTML markup and a little modernise it, so the type FilterInterface::TYPE_TRANSFORM_REVERSIBLE fits.
namespace Drupal\MODULE_NAME\Plugin\Filter;
use Drupal\Component\Utility\Html;
use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
/**
* Provides a filter to wrap a table into an extra wrapper.
*
* @Filter(
* id = "MODULE_NAME_table_wrap",
* title = @Translation("Wrap tables"),
* description = @Translation("Uses an extra <code>div</code> tag to wrap tables."),
* type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE
* )
*/
class FilterTableWrap extends FilterBase {
/**
* {@inheritdoc}
*/
public function process($text, $langcode) {
$result = new FilterProcessResult($text);
// If there are no tables, return early.
if (stripos($text, '<table') === FALSE) {
return $result;
}
return $result->setProcessedText($this->wrapTables($text));
}
/**
* Transform markup of tables to add the wrapper.
*
* @param string $text
* The markup to transform.
*
* @return string
* The transformed text.
*/
private function wrapTables(string $text): string {
$dom = Html::load($text);
// Create a wrapper element.
$wrapper = $dom->createElement('div');
$wrapper->setAttribute('class', 'tablefield-wrapper');
$tables = $dom->getElementsByTagName('table');
foreach ($tables as $table) {
// Clone the wrapper div.
$wrapper_clone = $wrapper->cloneNode();
// Replace the table with this wrapper div.
$table->parentNode->replaceChild($wrapper_clone, $table);
// Append the table to wrapper div.
$wrapper_clone->appendChild($table);
}
return Html::serialize($dom);
}
}
We can add this new filter "Wrap tables" via text editor admin UI:
After this we can add a table using the text editor in usual way:
But the table will have an extra div wrapper with the specific class on front-end: