Skip to content

Commit

Permalink
Merge pull request #143 from localgovdrupal/fix/1.x/142-add-auto-head…
Browse files Browse the repository at this point in the history
…ing-ids-to-wysiwyg

Add auto heading id filter to the wysiwyg format on install.
  • Loading branch information
rupertj committed Feb 27, 2024
2 parents 41694be + 99df49d commit 8294884
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 2 deletions.
1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
}
],
"require": {
"drupal/auto_heading_ids": "^2.0@beta",
"localgovdrupal/localgov_core": ">=2.1.10",
"localgovdrupal/localgov_paragraphs": ">=2.4.0"
},
Expand Down
1 change: 0 additions & 1 deletion localgov_publications.info.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 23 additions & 0 deletions localgov_publications.install
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,40 @@
* Install, update and uninstall functions for the LocalGov Publications module.
*/

use Drupal\filter\Entity\FilterFormat;
use Drupal\filter\FilterFormatInterface;

/**
* Implements hook_install().
*/
function localgov_publications_install() {

// Add our localgov_publication_page content type to
// book.settings.allowed_types. This lets editors create publication pages.
$config = \Drupal::configFactory()->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.
$wysiwygFormat = FilterFormat::load('wysiwyg');
if ($wysiwygFormat instanceof FilterFormatInterface) {
$wysiwygFormat->setFilterConfig('localgov_publications_heading_ids', [
'status' => TRUE,
'settings' => [
'keep_existing_ids' => TRUE,
],
]);
$wysiwygFormat->save();
}
}
158 changes: 158 additions & 0 deletions src/Plugin/Filter/HeadingIdFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<?php

namespace Drupal\localgov_publications\Plugin\Filter;

use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Transliteration\PhpTransliteration;
use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
* Apply identifiers (anchors) to headings in content.
*
* This filter is copied from the auto_heading_ids module.
*
* @Filter(
* id = "localgov_publications_heading_ids",
* title = @Translation("Automatically apply identifiers (anchors) to headings in content"),
* type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE,
* weight = 10
* )
*/
class HeadingIdFilter extends FilterBase implements ContainerFactoryPluginInterface {

/**
* Word separator.
*/
const SEPARATOR = '-';

/**
* Transliteration service.
*
* @var \Drupal\Core\Transliteration\PhpTransliteration
*/
protected $transliteration;

/**
* Constructs a \Drupal\editor\Plugin\Filter\EditorFileReference object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* 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) {
$this->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' => $this->t('When this is selected, existing IDs in content will not be changed.'),
];

return $form;
}

}

0 comments on commit 8294884

Please sign in to comment.