Convert existing links to data-entity

Last updated on
5 September 2023

Some editors might want existing links added prior to installing Linkit to be stored as Drupal URLs, with <code>data-entity</code> metadata. This feature request was originally documented in #3008159: Convert existing links to data-entity.

The maintainers feel this should not automatically happen in the Linkit module. Therefore, developers wishing to perform this conversion can use an update hook to target specific entities. A solution by wweibel is shown below.

Note that the use of Url::fromUri() can cause memory issues if called in a loop, as per #3132913: Allow different uri protocols for more effecient url generation.  If running this code in a loop, you may also want to considering using \Drupal::service('entity.memory_cache')->deleteAll() or  \Drupal::service('entity_type.manager')->getStorage('...')->resetCache();

<?php

  /**
   * Convert hyperlinks to entity links.
   */
  protected function convertHyperlinks(FieldableEntityInterface &$entity, $content, $field) {
    $doc = Html::load($content);

    foreach ($doc->getElementsByTagName('a') as $element) {
      $href = parse_url($element->getAttribute('href'));
      if (!(empty($href['host']) || str_contains(strtolower($href['host']), 'YOUR-SITE-HOSTNAME'))) {
        // Not the healthcare site.
        continue;
      }

      if (!empty($href['scheme']) && $href['scheme'] == 'mailto') {
        // Content still using `mailto` links.
        continue;
      }

      if (empty($href['path']) || $href['path'] === '/' || str_starts_with($href['path'], '#')) {
        // Nothing, link to the homepage, or anchor.
        continue;
      }

      $path = $href['path'];
      if (!str_starts_with($path, '/')) {
        $path = '/' . $path;
      }

      try {
        // Would prefer not to use try-catch but 'bad' href could sneak through.
        $url = Url::fromUri("internal:" . $path);
      }
      catch (InvalidArgumentException $e) {
        continue;
      }

      if (!($url->isRouted() && str_starts_with($url->getRouteName(), 'entity.'))) {
        // Not an entity route.
        continue;
      }

      $route = $url->getRouteParameters();
      $entity_type = key($route);
      if (!in_array($entity_type, ['node', 'media'])) {
        // Not an entity type to link.
        continue;
      }

      $linked_entity = $this->entityTypeManager->getStorage($entity_type)->load($route[$entity_type]);
      if ($entity) {
        // Add data attributes same as when created in CKEditor.
        // Linkit.
        $element->setAttribute('data-entity-type', $entity_type);
        $element->setAttribute('data-entity-substitution', 'canonical');
        $element->setAttribute('data-entity-uuid', $linked_entity->uuid());
        // Custom.
        $element->setAttribute('data-entity-id', $linked_entity->id());
        $element->setAttribute('data-entity-bundle', $linked_entity->bundle());
        $element->setAttribute('data-internal-href', $this->pathAliasManager->getPathByAlias($path));
        // Update to Drupal aware path.
        $element->setAttribute('href', $this->pathAliasManager->getAliasByPath($path));
      }
    }

    $content = $doc->saveHTML();
    $content = Html::decodeEntities(Html::serialize($doc));
    $entity->$field->value = $content;
  }

?>

Help improve this page

Page status: No known problems

You can: