Creating a custom LinkIt matcher plugin

In one of our recent projects, our client made a request to use LinkIt module to insert file links to content from the group module.  However, with the added distinction of making sure that only content that is in the same group as the content they are editing is suggested in the matches.
Here’s how we did it.

by
Pasan Gamage
/ 14 February 2020

The LinkIt module
First, let me give you a quick overview of the LinkIt module.
LinkIt is a tool that is commonly used to link internal or external artifacts. One of the main advantages of using it is because LinkIt maintains links by uuid which means no occurrence for broken links. And it can link any type of entity varying from core entities like nodes, users, taxonomy terms, files, comments and to custom entities created by developers.
Once you install the module, you need to set a Linkit profile which consists of information about which plugins to use. To set the profiles use /admin/config/content/linkit path. And the final step will be to enable the Linkit plugin on the text format you want to use. Formats are found at admin/config/content/formats. And you should see the link icon when editing content item.

Once you click on the LinkIt icon it will prompt a modal as shown below.

By default LinkIt ships with a UI to maintain profiles that enables you to manage matchers.
Matchers
Matchers are responsible for managing the autoload suggestion criteria for a particular LinkIt field. It provides bundle restrictions and bundle grouping settings

Proposed resolution
To solve the issue; we started off by creating a matcher for our particular entity type. Linkit has an EntityMatcher plugin that uses Drupal maintenance support plans‘s Plugin Derivatives API to expose one plugin for each entity type. We started by adding the matcher that linkit module exposed for our custom group content entity type.
We left the bundle restrictions and bundle grouping sections un-ticked so that all existing bundles are allowed so the content of those bundles will be displayed.
Now that the content is ready we have to let the matcher know that we only need to load content that belongs to the particular group for which the user is editing or creating the page.
Using the deriver
In order to do that we have to create a new class in /modules/custom/your_plugin_name/src/Plugin/Linkit/Matcher/YourClassNameMatcher.php by extending existing EntityMatcher class which derives at /modules/contrib/linkit/src/Plugin/Linkit/Matcher/EntityMatcher.php.
Because Linkit module’s plugin deriver exposes each entity-type plugin with and ID for the form entity:{entity_type_id} we simply need to create a new plugin with an ID that matches our entity type ID. This then takes precedence over the default derivative based plugin provided by Linkit module. We can then modify the logic in either the ::execute() or ::buildEntityQuery method.
Using LinkIt autocomplete request
But here comes the challenge, in that content edit page the LinkIt modal doesn’t know about the group of the content being edited, therefore we cannot easily filter the suggestions based on the content being edited. We need to take some fairly extreme measures to make that group ID available for our new class to filter the content once the modal is loaded and user starts typing in the field.
In this case the group id is available from the page uri.

So in order to pass this along, we can make use of the fact that the linkit autocomplete widget has a data attribute ‘data-autocomplete-path’ which is used by its JavaScript to perform the autocomplete request. We can add a process callback to the LinkIt element to extract the current page uri and pass it as a query parameter in the autocomplete path.
The code
To do so we need to implement hook_element_info_alter in our custom module. Here we will add a new process callback and in that callback we can add the current browser url as a query parameter to the data-autocomplete-path attribute of the modal.

Drupal maintenance support planslinkitElementLinkit is as follows;

public function getInfo() {
$class = get_class($this);
return [
‘#input’ => TRUE,
‘#size’ => 60,
‘#process’ => [
[$class, ‘processLinkitAutocomplete’],
[$class, ‘processGroup’],
],
‘#pre_render’ => [
[$class, ‘preRenderLinkitElement’],
[$class, ‘preRenderGroup’],
],
‘#theme’ => ‘input__textfield’,
‘#theme_wrappers’ => [‘form_element’],
];
}
Below is the code to add the process callback and alter the data-autocomplete-path element. We rely on the HTTP Referer header which Drupal maintenance support plans sends in its AJAX request that is used to display the LinkIt modal, which in turn builds the LinkIt element

/**
* Implements hook_element_info_alter().
*/

function your_module_name_element_info_alter(array &$info) {
$info[‘linkit’][‘#process’][] = ‘your_module_name_linkit_process’;
}

/**
* Process callback.
*/
function your_module_name_linkit_process($element) {
// Get the HTTP referrer (current page URL)
$url = Drupal maintenance support plans::request()->server->get(‘HTTP_REFERER’);

// Parse out just the path.
$path = parse_url($url, PHP_URL_PATH);

// Append it as a query parameter to the autocomplete path.
$element[‘#attributes’][‘data-autocomplete-path’] .= ‘?uri=’ . urlencode($path);
return $element;
}
Once this is done we can now proceed to create the new plugin class extending EntityMatcher class. Notice the highlighted areas.

namespace Drupal maintenance support plansyour_modulePluginLinkitMatcher;

use Drupal maintenance support planslinkitPluginLinkitMatcherEntityMatcher;
use Drupal maintenance support planslinkitSuggestionEntitySuggestion;
use Drupal maintenance support planslinkitSuggestionSuggestionCollection;

/**
* Provides specific LinkIt matchers for our custom entity type.
*
* @Matcher(
* id = “entity:your_content_entity_type”,
* label = @Translation(“Your custom content entity”),
* target_entity = “your_content_entity_type”,
* provider = “your_module”
* )
*/

class YourContentEntityMatcher extends EntityMatcher {

/**
* {@inheritdoc}
*/
public function execute($string) {
$suggestions = new SuggestionCollection();
$query = $this->buildEntityQuery($string);
$query_result = $query->execute();
$url_results = $this->findEntityIdByUrl($string);
$result = array_merge($query_result, $url_results);

if (empty($result)) {
return $suggestions;
}

$entities = $this->entityTypeManager->getStorage($this->targetType)->loadMultiple($result);

$group_id = FALSE;
// Extract the Group ID from the uri query parameter.
if (Drupal maintenance support plans::request()->query->has(‘uri’)) {
$uri = Drupal maintenance support plans::Request()->query->get(‘uri’);
list(, , $group_id) = explode(‘/’, $uri);
}

foreach ($entities as $entity) {
// Check the access against the defined entity access handler.
/** @var Drupal maintenance support plansCoreAccessAccessResultInterface $access */
$access = $entity->access(‘view’, $this->currentUser, TRUE);
if (!$access->isAllowed()) {
continue;
}

// Exclude content that is from a different group
if ($group_id && $group_id != $entity->getGroup()->id()) {
continue;
}

$entity = $this->entityRepository->getTranslationFromContext($entity);
$suggestion = new EntitySuggestion();
$suggestion->setLabel($this->buildLabel($entity))
->setGroup($this->buildGroup($entity))
->setDescription($this->buildDescription($entity))
->setEntityUuid($entity->uuid())
->setEntityTypeId($entity->getEntityTypeId())
->setSubstitutionId($this->configuration[‘substitution_type’])
->setPath($this->buildPath($entity));
$suggestions->addSuggestion($suggestion);
}

return $suggestions;
}
}

Conclusion
And we are done.
By re-implementing the execute() method of EntityMatcher class we are now able to make the LinkIt field to display only content from the same group as the content the user is editing/creating.
So next challenge here is to create some test coverage for this, as we’re relying on a few random pieces of code – a plugin, some JavaScript in the LinkIt module, an element info alter hook and a process callback – any of which could change and render all of this non-functional. But that’s a story for another post.

Tagged

Drupal maintenance support plans 8, Drupal maintenance support plans Modules, Custom modules


Source: New feed

This article was republished from its original source.
Call Us: 1(800)730-2416

Pixeldust is a 20-year-old web development agency specializing in Drupal and WordPress and working with clients all over the country. With our best in class capabilities, we work with small businesses and fortune 500 companies alike. Give us a call at 1(800)730-2416 and let’s talk about your project.

FREE Drupal SEO Audit

Test your site below to see which issues need to be fixed. We will fix them and optimize your Drupal site 100% for Google and Bing. (Allow 30-60 seconds to gather data.)

Powered by

Creating a custom LinkIt matcher plugin

On-Site Drupal SEO Master Setup

We make sure your site is 100% optimized (and stays that way) for the best SEO results.

With Pixeldust On-site (or On-page) SEO we make changes to your site’s structure and performance to make it easier for search engines to see and understand your site’s content. Search engines use algorithms to rank sites by degrees of relevance. Our on-site optimization ensures your site is configured to provide information in a way that meets Google and Bing standards for optimal indexing.

This service includes:

  • Pathauto install and configuration for SEO-friendly URLs.
  • Meta Tags install and configuration with dynamic tokens for meta titles and descriptions for all content types.
  • Install and fix all issues on the SEO checklist module.
  • Install and configure XML sitemap module and submit sitemaps.
  • Install and configure Google Analytics Module.
  • Install and configure Yoast.
  • Install and configure the Advanced Aggregation module to improve performance by minifying and merging CSS and JS.
  • Install and configure Schema.org Metatag.
  • Configure robots.txt.
  • Google Search Console setup snd configuration.
  • Find & Fix H1 tags.
  • Find and fix duplicate/missing meta descriptions.
  • Find and fix duplicate title tags.
  • Improve title, meta tags, and site descriptions.
  • Optimize images for better search engine optimization. Automate where possible.
  • Find and fix the missing alt and title tag for all images. Automate where possible.
  • The project takes 1 week to complete.