bynder-4.0.0-beta1/modules/bynder_usage/src/EventSubscriber/UsageEventSubscriber.php
modules/bynder_usage/src/EventSubscriber/UsageEventSubscriber.php
<?php
namespace Drupal\bynder_usage\EventSubscriber;
use Drupal\bynder\BynderApiInterface;
use Drupal\bynder\Plugin\media\Source\Bynder;
use Drupal\bynder_usage\Exception\UnableToAddUsageException;
use Drupal\bynder_usage\Exception\UnableToDeleteUsageException;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Url;
use Drupal\Core\Utility\Error;
use Drupal\entity_usage\Events\EntityUsageEvent;
use Drupal\entity_usage\Events\Events;
use Drupal\media\MediaInterface;
use Drupal\paragraphs\ParagraphInterface;
use GuzzleHttp\Exception\RequestException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Listens for the usage events from Entity Usage module.
*/
class UsageEventSubscriber implements EventSubscriberInterface {
/**
* Bynder api service.
*
* @var \Drupal\bynder\BynderApiInterface
* Bynder api service.
*/
protected $bynderApi;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The request stack.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* UsageEventSubscriber constructor.
*
* @param \Drupal\bynder\BynderApiInterface $bynder_api_service
* Bynder api service.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
* The request stack.
*/
public function __construct(BynderApiInterface $bynder_api_service, EntityTypeManagerInterface $entity_type_manager, RequestStack $request_stack) {
$this->bynderApi = $bynder_api_service;
$this->entityTypeManager = $entity_type_manager;
$this->requestStack = $request_stack;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
$events[Events::USAGE_REGISTER][] = ['onUsageRegister'];
$events[Events::DELETE_BY_SOURCE_ENTITY][] = ['onDeleteBySourceEntity'];
$events[Events::DELETE_BY_TARGET_ENTITY][] = ['onDeleteByTargetEntity'];
return $events;
}
/**
* Returns the remote media ID.
*
* @param \Drupal\media\MediaInterface $media
* The media to get the remote ID for.
*
* @return mixed|null
* The remote media ID or NULL if not found.
*/
protected function getRemoteMediaId(MediaInterface $media) {
$source_plugin = $media->getSource();
if (!$source_plugin instanceof Bynder) {
return NULL;
}
return $source_plugin->getSourceFieldValue($media);
}
/**
* Returns the canonical URL for the given entity.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The source entity.
*
* @return \Drupal\Core\Url|null
* The absolute URL of the given entity.
*/
protected function getEntityUrl(EntityInterface $entity) {
// If the entity is a paragraph, attempt to recursively load the parent.
while ($entity && $entity instanceof ParagraphInterface) {
$entity = $entity->getParentEntity();
}
// If the entity exists and has a canonical link template, get the URI.
return $entity && $entity->hasLinkTemplate('canonical') ? $entity->toUrl('canonical')
->setOption('path_processing', FALSE)
->setAbsolute() : NULL;
}
/**
* Auxiliary function to get media information for asset usage operations.
*
* @param \Drupal\entity_usage\Events\EntityUsageEvent $event
*
* @return array|null
*/
private function getUsageEventMediainformation(EntityUsageEvent $event) {
if ($event->getTargetEntityType() !== 'media') {
return NULL;
}
/** @var \Drupal\media\MediaInterface $media */
$media = $this->entityTypeManager->getStorage('media')
->load($event->getTargetEntityId());
if (!isset($media)) {
return NULL;
}
$source_plugin = $media->getSource();
if (!$source_plugin instanceof Bynder) {
return NULL;
}
$url = NULL;
if ($source_id = $event->getSourceEntityId()) {
if ($entity = $this->entityTypeManager->getStorage($event->getSourceEntityType())->load($source_id)) {
$url = $this->getEntityUrl($entity);
}
}
if ($url) {
return [
'mediaId' => $source_plugin->getSourceFieldValue($media),
'url' => $url,
];
}
}
/**
* Returns whether the given remote ID and URI have a remote usage.
*
* @param string $remote_id
* The remote ID.
* @param string $uri
* The URI this asset was used on.
*
* @return bool
* TRUE if there is a remote usage for the ID and URI. Otherwise, FALSE.
*/
protected function hasRemoteUsageByUri($remote_id, $uri) {
$usages = $this->bynderApi->getAssetUsages($remote_id);
// No remote usages.
if (empty($usages)) {
return FALSE;
}
foreach ($usages as $usage) {
// There is a remote usage on the given URI.
if ($usage['uri'] === $uri) {
return TRUE;
}
}
return FALSE;
}
/**
* Triggers when a usage is registered (create or update) for a Bynder asset.
*
* @param \Drupal\entity_usage\Events\EntityUsageEvent $event
* The event to process.
*/
public function onUsageRegister(EntityUsageEvent $event) {
$mediaInfo = $this->getUsageEventMediainformation($event);
if (isset($mediaInfo)) {
try {
// Add a usage only once if the count is positive and there are no
// usages registered on this URI.
if ($event->getCount() > 0) {
$usage_url = $mediaInfo['url'];
if (!$this->hasRemoteUsageByUri($mediaInfo['mediaId'], $usage_url->toString())) {
$this->bynderApi->addAssetUsage(
$mediaInfo['mediaId'],
$usage_url,
date(DATE_ATOM, \Drupal::time()->getRequestTime()), 'Added asset by user ' . \Drupal::currentUser()->getAccountName() . '.'
);
}
}
// If the count is zero, the item was removed.
else {
$this->bynderApi->removeAssetUsage($mediaInfo['mediaId'], $mediaInfo['url']->toString());
}
}
catch (RequestException $e) {
Error::logException(\Drupal::logger('bynder'), $e);
(new UnableToAddUsageException($e->getMessage()))->logException()->displayMessage();
}
}
}
/**
* Triggers when the source entity is deleted.
*
* @param \Drupal\entity_usage\Events\EntityUsageEvent $event
* The event to process.
*/
public function onDeleteBySourceEntity(EntityUsageEvent $event) {
// Skip if a non-default revision or translation is deleted.
if ($event->getSourceEntityRevisionId() || $event->getSourceEntityLangcode()) {
return;
}
$storage = $this->entityTypeManager->getStorage($event->getSourceEntityType());
$entity = $storage->load($event->getSourceEntityId());
if (!$entity) {
return;
}
$usage_url = $this->getEntityUrl($entity);
if (!$usage_url) {
return;
}
// At this point, the entity usages are already deleted so we can't query
// the entity usage table to find the relevant media usages. Instead, loop
// over the references of the source entity and delete all usages of the
// Bynder media assets on this URI.
foreach ($entity->referencedEntities() as $referenced_entity) {
if ($referenced_entity instanceof MediaInterface && ($remote_id = $this->getRemoteMediaId($referenced_entity))) {
try {
$this->bynderApi->removeAssetUsage($remote_id, $usage_url->toString());
}
catch (RequestException $e) {
Error::logException(\Drupal::logger('bynder'), $e);
(new UnableToDeleteUsageException($e->getMessage()))->logException()->displayMessage();
}
}
}
}
/**
* Triggers if the target (media) entity is deleted. Remove all Bynder usages.
*
* @param \Drupal\entity_usage\Events\EntityUsageEvent $event
* The entity usage event.
*/
public function onDeleteByTargetEntity(EntityUsageEvent $event) {
if ($event->getTargetEntityType() === 'media') {
/** @var \Drupal\media\MediaInterface $media */
$media = $this->entityTypeManager->getStorage('media')->load($event->getTargetEntityId());
if ($media) {
$remote_id = $this->getRemoteMediaId($media);
if ($remote_id) {
try {
// The Bynder media entity is deleted from the system. Delete all
// remote usages for this asset.
$usages = $this->bynderApi->getAssetUsages($remote_id);
$base_url = Url::fromUri('base:/')->setAbsolute()->toString();
foreach ($usages as $usage) {
// If the usage appears to be on this site remove all the usages.
if (strpos($usage['uri'], $base_url) === 0) {
$this->bynderApi->removeAssetUsage($remote_id, $usage['uri']);
}
}
}
catch (RequestException $e) {
Error::logException(\Drupal::logger('bynder'), $e);
(new UnableToDeleteUsageException($e->getMessage()))->logException()->displayMessage();
}
}
}
}
}
}
