From cdb884ac55e65bab5b49934e2585cfafbf34f5ea Mon Sep 17 00:00:00 2001
From: smulvihill <smulvihill@795442.no-reply.drupal.org>
Date: Thu, 12 May 2022 13:02:38 +1000
Subject: [PATCH] Issue #3118516 by bgilhome, smulvih2, VladimirAus: Add an
 'exclude' xpath option

---
 config/install/toc_api.toc_type.default.yml   |  1 +
 config/install/toc_api.toc_type.full.yml      |  1 +
 .../toc_api.toc_type.full_numbered.yml        |  1 +
 config/install/toc_api.toc_type.simple.yml    |  1 +
 .../toc_api.toc_type.simple_numbered.yml      |  1 +
 config/schema/toc_api.toc_type.schema.yml     |  3 +++
 src/Toc.php                                   | 15 +++++++++++-
 src/TocTypeForm.php                           |  6 +++++
 toc_api.install                               | 23 +++++++++++++++++++
 9 files changed, 51 insertions(+), 1 deletion(-)
 create mode 100644 toc_api.install

diff --git a/config/install/toc_api.toc_type.default.yml b/config/install/toc_api.toc_type.default.yml
index 7affc29..705ec78 100644
--- a/config/install/toc_api.toc_type.default.yml
+++ b/config/install/toc_api.toc_type.default.yml
@@ -13,6 +13,7 @@ options:
   header_allowed_tags: '<em> <b> <del> <i> <mark> <s> <span> <strong> <sup> <sub> <em> <b> <del> <i> <mark> <s> <span> <strong> <sup> <sub>'
   header_id: title
   header_id_prefix: section
+  header_exclude_xpath: ''
   top_label: 'Back to top'
   top_min: 2
   top_max: 2
diff --git a/config/install/toc_api.toc_type.full.yml b/config/install/toc_api.toc_type.full.yml
index 3b43c55..dd671e8 100644
--- a/config/install/toc_api.toc_type.full.yml
+++ b/config/install/toc_api.toc_type.full.yml
@@ -13,6 +13,7 @@ options:
   header_allowed_tags: '<em> <b> <del> <i> <mark> <s> <span> <strong> <sup> <sub> <em> <b> <del> <i> <mark> <s> <span> <strong> <sup> <sub>'
   header_id: title
   header_id_prefix: section
+  header_exclude_xpath: ''
   top_label: 'Back to top'
   top_min: 2
   top_max: 2
diff --git a/config/install/toc_api.toc_type.full_numbered.yml b/config/install/toc_api.toc_type.full_numbered.yml
index 0bb82d8..61961c7 100644
--- a/config/install/toc_api.toc_type.full_numbered.yml
+++ b/config/install/toc_api.toc_type.full_numbered.yml
@@ -13,6 +13,7 @@ options:
   header_allowed_tags: '<em> <b> <del> <i> <mark> <s> <span> <strong> <sup> <sub> <em> <b> <del> <i> <mark> <s> <span> <strong> <sup> <sub>'
   header_id: title
   header_id_prefix: section
+  header_exclude_xpath: ''
   top_label: 'Back to top'
   top_min: 2
   top_max: 2
diff --git a/config/install/toc_api.toc_type.simple.yml b/config/install/toc_api.toc_type.simple.yml
index 55f2403..20bfad3 100644
--- a/config/install/toc_api.toc_type.simple.yml
+++ b/config/install/toc_api.toc_type.simple.yml
@@ -13,6 +13,7 @@ options:
   header_allowed_tags: '<em> <b> <del> <i> <mark> <s> <span> <strong> <sup> <sub> <em> <b> <del> <i> <mark> <s> <span> <strong> <sup> <sub>'
   header_id: title
   header_id_prefix: section
+  header_exclude_xpath: ''
   top_label: 'Back to top'
   top_min: 2
   top_max: 2
diff --git a/config/install/toc_api.toc_type.simple_numbered.yml b/config/install/toc_api.toc_type.simple_numbered.yml
index bff2503..bcccb13 100644
--- a/config/install/toc_api.toc_type.simple_numbered.yml
+++ b/config/install/toc_api.toc_type.simple_numbered.yml
@@ -13,6 +13,7 @@ options:
   header_allowed_tags: '<em> <b> <del> <i> <mark> <s> <span> <strong> <sup> <sub> <em> <b> <del> <i> <mark> <s> <span> <strong> <sup> <sub>'
   header_id: title
   header_id_prefix: section
+  header_exclude_xpath: ''
   top_label: 'Back to top'
   top_min: 2
   top_max: 2
diff --git a/config/schema/toc_api.toc_type.schema.yml b/config/schema/toc_api.toc_type.schema.yml
index 0966dfa..19bdb58 100644
--- a/config/schema/toc_api.toc_type.schema.yml
+++ b/config/schema/toc_api.toc_type.schema.yml
@@ -42,6 +42,9 @@ toc_api.toc_type.*:
         header_id_prefix:
           type: string
           label: 'Header ID prefix'
+        header_exclude_xpath:
+          type: string
+          label: 'Header exclude XPath'
         top_label:
           type: string
           label: 'Back to top'
diff --git a/src/Toc.php b/src/Toc.php
index 545ce8b..fa5eed4 100644
--- a/src/Toc.php
+++ b/src/Toc.php
@@ -40,6 +40,7 @@ class Toc implements TocInterface {
     'header_allowed_tags' => '<em> <b> <del> <i> <mark> <s> <span> <strong> <sup> <sub> <em> <b> <del> <i> <mark> <s> <span> <strong> <sup> <sub>',
     'header_id' => 'title',
     'header_id_prefix' => 'section',
+    'header_exclude_xpath' => '',
     'top_label' => 'Back to top',
     'top_min' => 2,
     'top_max' => 2,
@@ -135,6 +136,8 @@ class Toc implements TocInterface {
    *   - 'header_id_prefix': Prefix to be prepended to header id when
    *     'path' or 'key' is selected.
    *     Default value is 'section'
+   *   - 'header_exclude_xpath': Optional XPath for any headers to exclude from
+   *     the table of contents.
    *
    *   - 'top_min': The minimum level of header to include a back to top link.
    *     Default value is 2
@@ -203,12 +206,22 @@ class Toc implements TocInterface {
     $index_keys = $default_keys;
 
     $dom = Html::load($this->source);
+
+    // If the exclude XPath option is set, retrieve those elements.
+    $exclude_nodes = [];
+    if ($this->options['header_exclude_xpath']) {
+      $xpath = new \DOMXPath($dom);
+      foreach ($xpath->query($this->options['header_exclude_xpath']) as $exclude_node) {
+        $exclude_nodes[] = $exclude_node;
+      }
+    }
+
     // Loop through all the tags to ensure headers are found in the correct
     // order.
     $dom_nodes = $dom->getElementsByTagName('*');
     /** @var \DOMElement $dom_node */
     foreach ($dom_nodes as $dom_node) {
-      if (empty($this->options['headers'][$dom_node->tagName])) {
+      if (empty($this->options['headers'][$dom_node->tagName]) || in_array($dom_node, $exclude_nodes, TRUE)) {
         continue;
       }
 
diff --git a/src/TocTypeForm.php b/src/TocTypeForm.php
index 9f6f547..9c1012e 100644
--- a/src/TocTypeForm.php
+++ b/src/TocTypeForm.php
@@ -197,6 +197,12 @@ class TocTypeForm extends EntityForm {
       '#type' => 'textfield',
       '#default_value' => $options['header_id_prefix'],
     ];
+    $form['options']['header']['header_exclude_xpath'] = [
+      '#title' => $this->t('Header exclude XPath'),
+      '#description' => $this->t('Optional XPath for header elements to exclude from the table of contents. Example - //*[contains(@class,"toc-ignore")]'),
+      '#type' => 'textfield',
+      '#default_value' => $options['header_exclude_xpath'] ?? '',
+    ];
 
     $form['options']['top'] = [
       '#type' => 'details',
diff --git a/toc_api.install b/toc_api.install
new file mode 100644
index 0000000..e41b719
--- /dev/null
+++ b/toc_api.install
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * @file
+ * Update functions for the toc_api module.
+ */
+
+/**
+ * Add header_exclude_xpath to existing TOCs
+ */
+function toc_api_update_8001() {
+  $toc_types = \Drupal::entityQuery('toc_type')->execute();
+
+  foreach ($toc_types as $type) {
+    $entity = \Drupal::entityTypeManager()->getStorage('toc_type')->load($type);
+    $options = $entity->getOptions();
+
+    if (empty($options['header_exclude_xpath'])) {
+      $config_factory = \Drupal::configFactory();
+      $config_factory->getEditable('toc_api.toc_type.' . $type)->set('options.header_exclude_xpath', '')->save();
+    }
+  }
+}
-- 
GitLab