ckeditor_mentions-8.x-2.x-dev/src/MentionEventDispatcher.php
src/MentionEventDispatcher.php
<?php
namespace Drupal\ckeditor_mentions;
use Drupal\ckeditor_mentions\Events\CKEditorMentionsEvent;
use Drupal\ckeditor_mentions\MentionsType\MentionsTypeManagerInterface;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Masterminds\HTML5;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* Class MentionService.
*
* @package Drupal\ckeditor_mentions
* @internal
*/
class MentionEventDispatcher {
public function __construct(
protected EventDispatcherInterface $eventDispatcher,
protected EntityTypeManagerInterface $entityTypeManager,
protected MentionsTypeManagerInterface $mentionsTypeManager,
protected EntityRepositoryInterface $entityRepository,
protected CacheBackendInterface $cache,
) {}
/**
* Triggers the Mention Event.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* Trigger the mention event.
* @param string $event_name
* The name of the event.
*/
public function dispatchMentionEvent(EntityInterface $entity, string $event_name): void {
foreach ($this->getMentionsFromEntity($entity) as $mentioned_entity) {
$event = new CKEditorMentionsEvent($entity, $mentioned_entity['entity'], $mentioned_entity['plugin']);
$this->eventDispatcher->dispatch($event, $event_name);
}
}
/**
* Reads all the fields from an entity and return all the users mentioned.
*
* The array returned has this format:
*
* [entity_id] => [
* 'uuid' => $uuid,
* 'id' => $id,
* 'field_name' => [
* 'delta' => [
* 0 => 0,
* 1 => 1,
* 2 => 2,
* ]
* ]
* ];
*
* The first key is the user id, the next key is the field_name where the
* user was mentioned and finally the deltas of the fields.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity from which will get the mentions.
*
* @return array
* The users mentioned.
*/
public function getMentionsFromEntity(EntityInterface $entity): array {
$mentioned_entities = [];
// Check if some of the fields is using the CKEditor editor.
if (!$entity instanceof FieldableEntityInterface) {
return $mentioned_entities;
}
foreach (_editor_get_formatted_text_fields($entity) as $field_name) {
$items = $entity->get($field_name);
$main_property = $items->getFieldDefinition()->getFieldStorageDefinition()->getMainPropertyName();
/** @var \Drupal\Core\Field\FieldItemInterface $item */
foreach ($items as $delta => $item) {
$format_id = $item->get('format')->getValue();
if (!$format_id || !$this->isFormatMentionable($format_id)) {
continue;
}
foreach ($this->getMentionedEntities($item->get($main_property)->getValue()) as $mentioned_entity_information) {
$mentioned_item = [
'field_info' => [
$field_name => [
'delta' => [
$delta => $delta,
],
],
],
];
$mentioned_item += $mentioned_entity_information;
$mentioned_entities[] = $mentioned_item;
}
}
}
return $mentioned_entities;
}
/**
* Checks if a given text format supports CKEditor mentions.
*
* @param string $format_id
* The text format ID to check.
*
* @return bool
* TRUE if the format supports mentions, FALSE otherwise.
*/
public function isFormatMentionable(string $format_id): bool {
// Load the editor only if its status is not yet cached, and cache it.
$cid = "ckeditor_mentions:mentionable_formats:$format_id";
if ($cache = $this->cache->get($cid)) {
$is_mentionable = $cache->data;
}
else {
// Editor entities use the "format" property as the "id"
// in "entity_keys", i.e., the format ID and the editor ID always
// match, so we can load the editor by the format ID.
$editor_storage = $this->entityTypeManager->getStorage('editor');
/** @var \Drupal\editor\EditorInterface|null $editor */
$editor = $editor_storage->load($format_id);
$is_mentionable = FALSE;
if ($editor?->getEditor() === 'ckeditor5') {
$mention_types = $editor->getSettings()['plugins']['ckeditor_mentions_mentions']['plugins'] ?? [];
foreach ($mention_types as $mention_type) {
if ($mention_type['enable'] ?? FALSE) {
$is_mentionable = TRUE;
break;
}
}
}
$this->cache->set($cid, $is_mentionable, tags: $editor_storage->getEntityType()->getListCacheTags());
}
return $is_mentionable;
}
/**
* Returns an with information about mentioned entities.
*
* @param string $field_value
* The field text $field_text.
*
* @return array
* Array with information about mentioned entities.
*/
public function getMentionedEntities(string $field_value): array {
$mentioned_entities = [];
$plugins = [];
if (empty($field_value)) {
return $mentioned_entities;
}
// Instantiate the HTML5 parser, but without the HTML5 namespace being
// added to the DOM document.
$html5 = new HTML5(['disable_html_ns' => TRUE]);
$dom = $html5->loadHTML($field_value);
$anchors = $dom->getElementsByTagName('a');
foreach ($anchors as $anchor) {
/** @var @todo BC LAYER, with data-entity-id **/
$plugin = NULL;
$entity_uuid = $anchor->getAttribute('data-entity-uuid');
$plugin_id = $anchor->getAttribute('data-plugin');
if (empty($entity_uuid) || empty($plugin_id)) {
continue;
}
/** @var \Drupal\ckeditor_mentions\MentionsType\MentionsTypeBase $plugin */
$plugin = $plugins[$plugin_id] ??= $this->mentionsTypeManager->createInstance($plugin_id);
$mentioned_entities[] = [
'plugin' => $plugin,
'entity' => $this->entityRepository->loadEntityByUuid($plugin->getPluginDefinition()['entity_type'], $entity_uuid),
];
}
return $mentioned_entities;
}
}
