From e9e46dc0f03176a80a079d31ad7f01adff0b00c0 Mon Sep 17 00:00:00 2001 From: Rupert Jabelman Date: Tue, 6 Feb 2024 10:57:54 +0000 Subject: [PATCH 1/6] Add auto heading id filter to the wysiwyg format on install. --- localgov_publications.install | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/localgov_publications.install b/localgov_publications.install index 61eaab2..dfeea24 100644 --- a/localgov_publications.install +++ b/localgov_publications.install @@ -1,5 +1,7 @@ getEditable('book.settings'); $allowed_types = $config->get('allowed_types'); $allowed_types[] = 'localgov_publication_page'; $config->set('allowed_types', $allowed_types); $config->save(); + + // Add our localgov-publication-cover-page-alias to + // pathauto.settings.safe_tokens. This prevents double escaping in the + // resulting URL. $path_auto_config = \Drupal::configFactory()->getEditable('pathauto.settings'); $safe_tokens = $path_auto_config->get('safe_tokens'); $safe_tokens[] = 'localgov-publication-cover-page-alias'; $path_auto_config->set('safe_tokens', $safe_tokens)->save(); + + // Add heading_id_filter from the auto_heading_ids module to the wysiwyg + // filter format. This is required for the ToC block to work consistently. + /** @var \Drupal\filter\Entity\FilterFormat $wysiwygFormat */ + $wysiwygFormat = FilterFormat::load('wysiwyg'); + $wysiwygFormat->setFilterConfig('heading_id_filter', [ + 'status' => TRUE, + ]); + $wysiwygFormat->save(); } From e0492b0349b2cf2a9c4056c06c27f56730e4a40a Mon Sep 17 00:00:00 2001 From: Rupert Jabelman Date: Tue, 6 Feb 2024 11:08:21 +0000 Subject: [PATCH 2/6] Code style --- localgov_publications.install | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/localgov_publications.install b/localgov_publications.install index dfeea24..4960cd6 100644 --- a/localgov_publications.install +++ b/localgov_publications.install @@ -1,12 +1,12 @@ Date: Tue, 6 Feb 2024 12:18:56 +0000 Subject: [PATCH 3/6] Check wysiwyg format exists before trying to update it. --- localgov_publications.install | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/localgov_publications.install b/localgov_publications.install index 4960cd6..858010d 100644 --- a/localgov_publications.install +++ b/localgov_publications.install @@ -32,8 +32,10 @@ function localgov_publications_install() { // filter format. This is required for the ToC block to work consistently. /** @var \Drupal\filter\Entity\FilterFormat $wysiwygFormat */ $wysiwygFormat = FilterFormat::load('wysiwyg'); - $wysiwygFormat->setFilterConfig('heading_id_filter', [ - 'status' => TRUE, - ]); - $wysiwygFormat->save(); + if ($wysiwygFormat) { + $wysiwygFormat->setFilterConfig('heading_id_filter', [ + 'status' => TRUE, + ]); + $wysiwygFormat->save(); + } } From 1b73ac659df109d8a5654682d8097da424b811e7 Mon Sep 17 00:00:00 2001 From: Rupert Jabelman Date: Tue, 6 Feb 2024 17:15:22 +0000 Subject: [PATCH 4/6] Changed check that we loaded filter format in install to use interface. --- localgov_publications.install | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/localgov_publications.install b/localgov_publications.install index 858010d..dc56515 100644 --- a/localgov_publications.install +++ b/localgov_publications.install @@ -6,6 +6,7 @@ */ use Drupal\filter\Entity\FilterFormat; +use Drupal\filter\FilterFormatInterface; /** * Implements hook_install(). @@ -30,9 +31,8 @@ function localgov_publications_install() { // Add heading_id_filter from the auto_heading_ids module to the wysiwyg // filter format. This is required for the ToC block to work consistently. - /** @var \Drupal\filter\Entity\FilterFormat $wysiwygFormat */ $wysiwygFormat = FilterFormat::load('wysiwyg'); - if ($wysiwygFormat) { + if ($wysiwygFormat instanceof FilterFormatInterface) { $wysiwygFormat->setFilterConfig('heading_id_filter', [ 'status' => TRUE, ]); From 5063756ab0dea9e8c77d3ac8f809f011d135a4dc Mon Sep 17 00:00:00 2001 From: Rupert Jabelman Date: Tue, 13 Feb 2024 14:47:04 +0000 Subject: [PATCH 5/6] Copied over patched filter from auto_heading_ids (See https://www.drupal.org/project/auto_heading_ids/issues/3419508) to this module. --- composer.json | 1 - localgov_publications.info.yml | 1 - localgov_publications.install | 5 +- src/Plugin/Filter/HeadingIdFilter.php | 157 ++++++++++++++++++++++++++ 4 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 src/Plugin/Filter/HeadingIdFilter.php diff --git a/composer.json b/composer.json index 3a94ea1..b7ad665 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,6 @@ } ], "require": { - "drupal/auto_heading_ids": "^2.0@beta", "localgovdrupal/localgov_core": ">=2.1.10", "localgovdrupal/localgov_paragraphs": ">=2.4.0" }, diff --git a/localgov_publications.info.yml b/localgov_publications.info.yml index 847ae9c..e793c86 100644 --- a/localgov_publications.info.yml +++ b/localgov_publications.info.yml @@ -5,7 +5,6 @@ package: LocalGov Drupal core_version_requirement: ^9 || ^10 dependencies: - - auto_heading_ids:auto_heading_ids - drupal:book - drupal:menu_ui - drupal:views diff --git a/localgov_publications.install b/localgov_publications.install index dc56515..363d5e8 100644 --- a/localgov_publications.install +++ b/localgov_publications.install @@ -33,8 +33,11 @@ function localgov_publications_install() { // filter format. This is required for the ToC block to work consistently. $wysiwygFormat = FilterFormat::load('wysiwyg'); if ($wysiwygFormat instanceof FilterFormatInterface) { - $wysiwygFormat->setFilterConfig('heading_id_filter', [ + $wysiwygFormat->setFilterConfig('localgov_publications_heading_ids', [ 'status' => TRUE, + 'settings' => [ + 'keep_existing_ids' => TRUE, + ], ]); $wysiwygFormat->save(); } diff --git a/src/Plugin/Filter/HeadingIdFilter.php b/src/Plugin/Filter/HeadingIdFilter.php new file mode 100644 index 0000000..f18053b --- /dev/null +++ b/src/Plugin/Filter/HeadingIdFilter.php @@ -0,0 +1,157 @@ +transliteration = $transliteration; + parent::__construct($configuration, $plugin_id, $plugin_definition); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('transliteration') + ); + } + + /** + * {@inheritdoc} + */ + public function process($text, $langcode) { + return new FilterProcessResult($this->filterAttributes($text)); + } + + /** + * Applies ids to headings. + * + * @param string $text + * The HTML text string to be filtered. + * + * @return string + * Filtered HTML. + */ + public function filterAttributes($text) { + $output = $text; + $html_dom = Html::load($text); + $xpath = new \DOMXPath($html_dom); + $heading_tags = '//h2|//h3|//h4|//h5|//h6'; + $keep_existing_ids = $this->settings['keep_existing_ids'] ?? FALSE; + + // Apply attribute restrictions to headings. + $headings_found = FALSE; + foreach ($xpath->query($heading_tags) as $heading_tag) { + + if ($keep_existing_ids && $heading_tag->hasAttribute('id')) { + continue; + } + + $id = $this->transformHeadingToId($heading_tag->nodeValue); + $heading_tag->setAttribute('id', Html::getUniqueId($id)); + $headings_found = TRUE; + } + + if ($headings_found) { + // Only bother serializing if something changed. + $output = Html::serialize($html_dom); + $output = trim($output); + } + + return $output; + } + + /** + * Creates a machine name based on the heading. + * + * Inspired by Drupal\migrate\Plugin\migrate\process\MachineName::transform. + * Improved by Drupal\pathauto\AliasCleaner. + * + * @param string $heading + * String to convert to id. + * + * @return mixed + * A dash separated id. + */ + public function transformHeadingToId($heading) { + // Reduce to ascii. + $new_value = $this->transliteration->transliterate($heading); + // Reduce to letters and numbers. + $new_value = preg_replace('/[^a-zA-Z0-9\/]+/', static::SEPARATOR, $new_value); + // Remove consecutive separators. + $new_value = preg_replace('/' . static::SEPARATOR . '+/', static::SEPARATOR, $new_value); + // Remove leading and trailing separators. + $new_value = trim($new_value, static::SEPARATOR); + // Convert to lowercase. + $new_value = mb_strtolower($new_value); + // Truncate to 128 chars. + return Unicode::truncate($new_value, 128, TRUE); + } + + /** + * {@inheritDoc} + */ + public function settingsForm(array $form, FormStateInterface $form_state) { + + $form['keep_existing_ids'] = [ + '#title' => 'Keep existing IDs', + '#type' => 'checkbox', + '#default_value' => $this->settings['keep_existing_ids'] ?? FALSE, + '#description' => 'When this is selected, existing IDs in content will not be changed.', + ]; + + return $form; + } + +} From 99df49d1c0147adbd50d7353abaa065a0d1c4565 Mon Sep 17 00:00:00 2001 From: Rupert Jabelman Date: Tue, 13 Feb 2024 14:52:25 +0000 Subject: [PATCH 6/6] Code style. --- src/Plugin/Filter/HeadingIdFilter.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Plugin/Filter/HeadingIdFilter.php b/src/Plugin/Filter/HeadingIdFilter.php index f18053b..b0d3c3b 100644 --- a/src/Plugin/Filter/HeadingIdFilter.php +++ b/src/Plugin/Filter/HeadingIdFilter.php @@ -48,6 +48,7 @@ class HeadingIdFilter extends FilterBase implements ContainerFactoryPluginInterf * The plugin implementation definition. * @param \Drupal\Core\Transliteration\PhpTransliteration $transliteration * The transliteration service instance. + * * @internal param \Drupal\Core\Entity\EntityManagerInterface $entity_manager An entity manager object.* An entity manager object. */ public function __construct(array $configuration, $plugin_id, $plugin_definition, PhpTransliteration $transliteration) { @@ -148,7 +149,7 @@ public function settingsForm(array $form, FormStateInterface $form_state) { '#title' => 'Keep existing IDs', '#type' => 'checkbox', '#default_value' => $this->settings['keep_existing_ids'] ?? FALSE, - '#description' => 'When this is selected, existing IDs in content will not be changed.', + '#description' => $this->t('When this is selected, existing IDs in content will not be changed.'), ]; return $form;