- Issue created by @mark_fullmer
- πΊπΈUnited States lhridley
No only is this desirable, it is critical, backwards compatibility issue.
I have a client that has a site built with Layout Builder and embedded (inline) blocks that have multiple embedded links to PDFs. Over 3,000 blocks, some with dozens of links, all of which are now breaking. They are using Linkit version 6.1.4.
Finding the pages with the inline blocks for over 3,000 affected pieces of content will take more resources than this client has available.
Using "find and replace" to query the database and hope you get them all is not an option.
- π©πͺGermany mrshowerman Munich
+1 for implementing this change and dropping (or at least ignoring) the
data-entity-substitution
attribute.We have a global setting that controls the substitution type, and I would expect all (existing and newly created) links to update their generated URLs when I change that setting. There is no point in storing the substitution type in every single link element if editors can't change it.
I came across this issue today when I tried to create a custom substitution type that extends the
media
type, and does not get applied to existing links because they all decide to stick with the old one. The only chance I have is to use the same plugin ID (feels like a bad idea). - πΊπΈUnited States lhridley
For those of you that need to update Linkit links in inline blocks placed with Layout Builder (which was the issue that our client was having to deal with), here is the code we used to update the embedded links in the inline block entities.
In our case, the blocks were placed with Layout Builder, and the content of the blocks was created with CKEditor. The embedded links were in the "body" field of the inline block.
The client was dealing with over 3,000 links to PDFs that were embedded in the wysiwyg field of the inline blocks.
This code was deployed as a hook_update.
Context:
- The block type that contains the Linkit links is `utc_text_block
- The selection criteria to select the blocks that "might" contain links to update is the occurrence of a `data-entity-substitution` string in the body field
- The affected change was to change the value of the `data-entity-substitution` from `canonical` to `media` for any inline ` ` tags that had:
- a `data-entity-substitution` attribute with a value of `canonical`
- a `data-entity-type` attribute with a value of `media`
Code for the hook_update:
// Selects all blocks of type utc_text_block containing // 'data-entity-substitution="canonical"'. $query = \Drupal::entityQuery('block_content') ->condition('type', 'utc_text_block') ->condition('body', '%data-entity-substitution="canonical"%', 'LIKE') ->accessCheck(FALSE); $results = $query->execute(); // Sets a counter to 0. $blocks_updated = 0; // Loops through blocks selected in query above. foreach ($results as $block) { $replaced = FALSE; $block_content = BlockContent::load($block); $body_field = $block_content->body->getValue(); $body_text = $block_content->body->getValue()[0]['value']; $body_format = $block_content->body->getValue()[0]['format']; $body_summary = $block_content->body->getValue()[0]['summary']; // Setting up new DOMDocument for extracting tags to replace. $doc = new DOMDocument(); libxml_use_internal_errors(true); // Ignore parsing errors due to malformed HTML // Loads the body text into the DOMDocument without the <html>, <body> and doc def tags. $doc->loadHTML($body_text, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); // Clear any libXML internal errors that were suppressed. libxml_clear_errors(); // Gets iterable DOMNodeList object made up of matches. $links = $doc->getElementsByTagName('a'); // Loop through the number of elements that match the tag. foreach ($links as $link) { // If link contains attributes data-entity-substitution="canonical" and // data-entity-type="media", change the value for data-entity-substitution // to "media" also. if($link->getAttribute('data-entity-substitution') == 'canonical' && $link->getAttribute('data-entity-type') == 'media') { // Clone the link node. $newLink = $link->cloneNode(true); // Set a new attributes value. $newLink->setAttribute('data-entity-substitution', 'media'); // Replace the original link node with the new link node. $link->replaceWith($newLink); // Set $replaced flag to true. $replaced = TRUE; } } // New body text from DOMdocument. $new_body_text = $doc->saveHTML(); // Set values for summary, format, and new body text. $new_body_data = [ 'value' => $new_body_text, 'summary' => $body_summary, 'format' => $body_format, ]; // Save the revised block if $replaced = TRUE. if ($replaced) { $block_content->set('body', $new_body_data); // We do not want to create a new revision because the nodes with the embedded blocks have stored // the revision ID. // Commented out this line: $block_content->setNewRevision(); $block_content->save(); $blocks_updated++; } } // When processing is complete, return the number of updated blocks. return t("Updated @blockcount blocks", ["@blockcount" => $blocks_updated]);
The same concept could be applied to nodes with links in body fields as well (or any other entity for that matter), you'd just need to change the entityQuery type from `block_content` to `node`. Unlike the blocks, however, where we had to preserve the revision ID so that the nodes created with Layout Builder would reflect the changes, you could create a new node revision.
Hopefully this will save someone some time.
- πΊπΈUnited States lhridley
A followup to the above hook_update: Don't forget to also change the configuration for the LinkIt module configuration to change the matcher substitution from `canonical` to `media` (direct link to media file). This makes future Linkit-inserted media document links go directly to the PDF file address instead of to the Media entity display.