Skip to content

Instantly share code, notes, and snippets.

@ptmkenny
Created January 18, 2023 10:50
Show Gist options
  • Save ptmkenny/b532f569cecc6d2496e849a3b213d5d9 to your computer and use it in GitHub Desktop.
Save ptmkenny/b532f569cecc6d2496e849a3b213d5d9 to your computer and use it in GitHub Desktop.
Drupal 10 filter plugin (mymodule/src/Plugin/Filter) for importing media by Feeds GUID
<?php
namespace Drupal\my_module\Plugin\Filter;
use Drupal\Component\Utility\Html;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\Core\Security\TrustedCallbackInterface;
use Drupal\filter\FilterProcessResult;
use Drupal\media\MediaInterface;
use Drupal\media\Plugin\Filter\MediaEmbed;
/**
* Provides a filter to embed media items using a custom tag.
*
* @Filter(
* id = "media_feeds_embed",
* title = @Translation("Embed feeds media"),
* description = @Translation("Embeds media items using a custom tag, <code>&lt;feeds-media&gt;</code>. If used in conjunction with the 'Align/Caption' filters, make sure this filter is configured to run after them."),
* type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE,
* settings = {
* "default_view_mode" = "default",
* "allowed_view_modes" = {},
* "allowed_media_types" = {},
* },
* weight = 100,
* )
*
* @internal
*/
class MediaFeedsEmbed extends MediaEmbed implements TrustedCallbackInterface {
/**
* {@inheritdoc}
*/
public function process($text, $langcode) {
$result = new FilterProcessResult($text);
if (stristr($text, '<feeds-media') === FALSE) {
return $result;
}
$dom = Html::load($text);
$xpath = new \DOMXPath($dom);
foreach ($xpath->query('//feeds-media[normalize-space(@data-entity-guid)!=""]') as $node) {
/** @var \DOMElement $node */
$feeds_guid = $node->getAttribute('data-entity-guid');
$view_mode_id = $node->getAttribute('data-view-mode') ?: $this->settings['default_view_mode'];
// Delete the consumed attributes.
$node->removeAttribute('data-entity-guid');
$node->removeAttribute('data-view-mode');
$media_load_by_properties = $this->entityTypeManager->getStorage('media')
->loadByProperties(['feeds_item.guid' => $feeds_guid]);
// loadByProperties() can return multiple.
// It will only return a single value in this case (because only 1 GUID),
// but we need to reset it anyway b/c Drupal doesn't know that.
$media = reset($media_load_by_properties);
assert($media === NULL || $media instanceof MediaInterface, "Failed to load guid $feeds_guid for rendering.");
if (!$media) {
$this->loggerFactory->get('media')->error('During rendering of embedded media: the media item with UUID "@feeds_guid" does not exist.', ['@feeds_guid' => $feeds_guid]);
}
else {
$media = $this->entityRepository->getTranslationFromContext($media, $langcode);
$media = clone $media;
$this->applyPerEmbedMediaOverrides($node, $media);
}
$view_mode = NULL;
if ($view_mode_id !== EntityDisplayRepositoryInterface::DEFAULT_DISPLAY_MODE) {
$view_mode = $this->entityRepository->loadEntityByConfigTarget('entity_view_mode', "media.$view_mode_id");
if (!$view_mode) {
$this->loggerFactory->get('media')->error('During rendering of embedded media: the view mode "@view-mode-id" does not exist.', ['@view-mode-id' => $view_mode_id]);
}
}
$build = $media && ($view_mode || $view_mode_id === EntityDisplayRepositoryInterface::DEFAULT_DISPLAY_MODE)
? $this->renderMedia($media, $view_mode_id, $langcode)
: $this->renderMissingMediaIndicator();
if (empty($build['#attributes']['class'])) {
$build['#attributes']['class'] = [];
}
// Any attributes not consumed by the filter should be carried over to the
// rendered embedded entity. For example, `data-align` and `data-caption`
// should be carried over, so that even when embedded media goes missing,
// at least the caption and visual structure won't get lost.
foreach ($node->attributes as $attribute) {
if ($attribute->nodeName == 'class') {
// We don't want to overwrite the existing CSS class of the embedded
// media (or if the media entity can't be loaded, the missing media
// indicator). But, we need to merge in CSS classes added by other
// filters, such as filter_align, in order for those filters to work
// properly.
$build['#attributes']['class'] = array_unique(array_merge($build['#attributes']['class'], explode(' ', $attribute->nodeValue)));
}
else {
$build['#attributes'][$attribute->nodeName] = $attribute->nodeValue;
}
}
$this->renderIntoDomNode($build, $node, $result);
}
$result->setProcessedText(Html::serialize($dom));
return $result;
}
/**
* {@inheritdoc}
*/
public function tips($long = FALSE) {
if ($long) {
return $this->t('
<p>You can embed media items:</p>
<ul>
<li>Choose which media item to embed: <code>&lt;feeds-media data-entity-guid="my-feeds-id" /&gt;</code></li>
<li>Optionally also choose a view mode: <code>data-view-mode="tiny_embed"</code>, otherwise the default view mode is used.</li>
</ul>');
}
else {
return $this->t('You can embed media items (using the <code>&lt;feeds-media&gt;</code> tag).');
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment