diff --git a/config/install/htmlpurifier.settings.yml b/config/install/htmlpurifier.settings.yml new file mode 100644 index 0000000000000000000000000000000000000000..c3fdba8c94a894b845467952e49bdaa963223d74 --- /dev/null +++ b/config/install/htmlpurifier.settings.yml @@ -0,0 +1 @@ +cache.serializer_path: '' diff --git a/config/schema/htmlpurifier.schema.yml b/config/schema/htmlpurifier.schema.yml index 8b5b4dd7169982160b901bd3558bee1e0c211460..a2c7a57a9e0132e99bc64795911f11d7377560a2 100644 --- a/config/schema/htmlpurifier.schema.yml +++ b/config/schema/htmlpurifier.schema.yml @@ -5,3 +5,10 @@ filter_settings.htmlpurifier: htmlpurifier_configuration: type: string label: 'Configuration of HTMLPurifier' +htmlpurifier.settings: + type: config_object + label: 'HTML Purifier settings' + mapping: + cache.serializer_path: + type: string + label: 'Absolute path with no trailing slash to store serialized definitions.' diff --git a/htmlpurifier.info.yml b/htmlpurifier.info.yml index 7b5391b0bb749d4119ec4fbbc9d1bd38e0c37489..7eff3e24ab0522b6ada767d0bfe68a818e991aed 100644 --- a/htmlpurifier.info.yml +++ b/htmlpurifier.info.yml @@ -2,6 +2,4 @@ name: 'HTML Purifier' type: module description: 'Filter that removes malicious HTML and ensures standards compliant output.' package: Filter -core: 8.x -core_version_requirement: ^8 || ^9 || ^10 -php: 7.1 +core_version_requirement: ^10.2 diff --git a/src/Plugin/Filter/HtmlPurifierFilter.php b/src/Plugin/Filter/HtmlPurifierFilter.php index deb0247d55bde7af7cb15cd2c3eea749c842b758..15768eba69572baafc6dbc60f27ff5f49b9f731e 100644 --- a/src/Plugin/Filter/HtmlPurifierFilter.php +++ b/src/Plugin/Filter/HtmlPurifierFilter.php @@ -3,9 +3,13 @@ namespace Drupal\htmlpurifier\Plugin\Filter; use Drupal\Component\Serialization\Yaml; +use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\filter\FilterProcessResult; use Drupal\filter\Plugin\FilterBase; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * The implementation of HTML Purifier filter. @@ -17,7 +21,7 @@ use Drupal\filter\Plugin\FilterBase; * type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE * ) */ -class HtmlPurifierFilter extends FilterBase { +class HtmlPurifierFilter extends FilterBase implements ContainerFactoryPluginInterface { /** * Array of error messages from HTMLPurifier configuration assignments. @@ -26,16 +30,48 @@ class HtmlPurifierFilter extends FilterBase { */ protected $configErrors = []; + public function __construct( + array $configuration, + $plugin_id, + $plugin_definition, + protected FileSystemInterface $fileSystem, + protected ConfigFactoryInterface $configFactory, + ) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + } + /** * {@inheritdoc} */ - public function process($text, $langcode) { + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('file_system'), + $container->get('config.factory'), + ); + } + + /** + * {@inheritdoc} + */ + public function process($text, $langcode): FilterProcessResult { if (!empty($this->settings['htmlpurifier_configuration'])) { $purifier_config = $this->applyPurifierConfig($this->settings['htmlpurifier_configuration']); } else { $purifier_config = \HTMLPurifier_Config::createDefault(); } + + // Set Serializer path to the temporary directory, so it can be written. + $cache_serializer_path = $this->configFactory->get('htmlpurifier.settings')->get('cache.serializer_path'); + if (empty($cache_serializer_path)) { + $cache_serializer_path = $this->fileSystem->getTempDirectory() . '/htmlpurifier'; + } + $this->fileSystem->prepareDirectory($cache_serializer_path, FileSystemInterface::MODIFY_PERMISSIONS | FileSystemInterface::CREATE_DIRECTORY); + $purifier_config->set('Cache.SerializerPath', $cache_serializer_path); + $purifier = new \HTMLPurifier($purifier_config); $purified_text = $purifier->purify($text); return new FilterProcessResult($purified_text); @@ -50,13 +86,18 @@ class HtmlPurifierFilter extends FilterBase { $settings = Yaml::decode($configuration); foreach ($settings as $namespace => $directives) { - if (is_array($directives)) { - foreach ($directives as $key => $value) { - $purifier_config->set("$namespace.$key", $value); + + // Keep Cache managing out of the text formats scope. + if ($namespace !== 'Cache') { + + if (is_array($directives)) { + foreach ($directives as $key => $value) { + $purifier_config->set("$namespace.$key", $value); + } + } + else { + $this->configErrors[] = 'Invalid value for namespace $namespace, must be an array of directives.'; } - } - else { - $this->configErrors[] = 'Invalid value for namespace $namespace, must be an array of directives.'; } } @@ -66,11 +107,16 @@ class HtmlPurifierFilter extends FilterBase { /** * {@inheritdoc} */ - public function settingsForm(array $form, FormStateInterface $form_state) { + public function settingsForm(array $form, FormStateInterface $form_state): array { if (empty($this->settings['htmlpurifier_configuration'])) { /** @var \HTMLPurifier_Config $purifier_config */ $purifier_config = \HTMLPurifier_Config::createDefault(); - $default_value = Yaml::encode($purifier_config->getAll()); + $config_array = $purifier_config->getAll(); + + // Keep Cache managing out of the text formats scope. + unset($config_array['Cache']); + + $default_value = Yaml::encode($config_array); } else { $default_value = $this->settings['htmlpurifier_configuration']; @@ -121,7 +167,7 @@ class HtmlPurifierFilter extends FilterBase { /** * Custom error handler to manage invalid purifier configuration assignments. */ - public function configErrorHandler(int $errno, string $errstr) { + public function configErrorHandler(int $errno, string $errstr): void { // Do not set a validation error if the error is about a deprecated use. if ($errno < E_DEPRECATED) { // \HTMLPurifier_Config::triggerError() adds ' invoked on line ...' to the