Browse Source
* Add media headers * Add and fix up tests * Rebase and code review * Remove ?_format=jsonld from edit-media linkpull/756/head
Jared Whiklo
7 years ago
committed by
Natkeeran
8 changed files with 322 additions and 92 deletions
@ -0,0 +1,124 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\islandora\EventSubscriber; |
||||||
|
|
||||||
|
use Drupal\Core\Entity\EntityFieldManager; |
||||||
|
use Drupal\Core\Routing\RouteMatchInterface; |
||||||
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface; |
||||||
|
use Symfony\Component\HttpFoundation\Response; |
||||||
|
use Symfony\Component\HttpKernel\Event\FilterResponseEvent; |
||||||
|
use Symfony\Component\HttpKernel\KernelEvents; |
||||||
|
|
||||||
|
/** |
||||||
|
* Class LinkHeaderSubscriber. |
||||||
|
* |
||||||
|
* @package Drupal\islandora\EventSubscriber |
||||||
|
*/ |
||||||
|
abstract class LinkHeaderSubscriber implements EventSubscriberInterface { |
||||||
|
|
||||||
|
/** |
||||||
|
* The entity field manager. |
||||||
|
* |
||||||
|
* @var \Drupal\Core\Entity\EntityFieldManager |
||||||
|
*/ |
||||||
|
protected $entityFieldManager; |
||||||
|
|
||||||
|
/** |
||||||
|
* The route match object. |
||||||
|
* |
||||||
|
* @var \Drupal\Core\Routing\RouteMatchInterface |
||||||
|
*/ |
||||||
|
protected $routeMatch; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* |
||||||
|
* @param \Drupal\Core\Entity\EntityFieldManager $entity_field_manager |
||||||
|
* The entity field manager. |
||||||
|
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match |
||||||
|
* The route match object. |
||||||
|
*/ |
||||||
|
public function __construct( |
||||||
|
EntityFieldManager $entity_field_manager, |
||||||
|
RouteMatchInterface $route_match |
||||||
|
) { |
||||||
|
$this->entityFieldManager = $entity_field_manager; |
||||||
|
$this->routeMatch = $route_match; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public static function getSubscribedEvents() { |
||||||
|
// Run this early so the headers get cached. |
||||||
|
$events[KernelEvents::RESPONSE][] = ['onResponse', 129]; |
||||||
|
|
||||||
|
return $events; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the Node | Media | File. |
||||||
|
* |
||||||
|
* @param \Symfony\Component\HttpFoundation\Response $response |
||||||
|
* The current response object. |
||||||
|
* @param string $object_type |
||||||
|
* The type of entity to look for. |
||||||
|
* |
||||||
|
* @return Drupal\Core\Entity\ContentEntityBase|bool |
||||||
|
* A node or media entity or FALSE if we should skip out. |
||||||
|
*/ |
||||||
|
protected function getObject(Response $response, $object_type) { |
||||||
|
if ($object_type != 'node' |
||||||
|
&& $object_type != 'media' |
||||||
|
) { |
||||||
|
return FALSE; |
||||||
|
} |
||||||
|
|
||||||
|
// Exit early if the response is already cached. |
||||||
|
if ($response->headers->get('X-Drupal-Dynamic-Cache') == 'HIT') { |
||||||
|
return FALSE; |
||||||
|
} |
||||||
|
|
||||||
|
if (!$response->isOk()) { |
||||||
|
return FALSE; |
||||||
|
} |
||||||
|
|
||||||
|
// Hack the node out of the route. |
||||||
|
$route_object = $this->routeMatch->getRouteObject(); |
||||||
|
if (!$route_object) { |
||||||
|
return FALSE; |
||||||
|
} |
||||||
|
|
||||||
|
$methods = $route_object->getMethods(); |
||||||
|
$is_get = in_array('GET', $methods); |
||||||
|
$is_head = in_array('HEAD', $methods); |
||||||
|
if (!($is_get || $is_head)) { |
||||||
|
return FALSE; |
||||||
|
} |
||||||
|
|
||||||
|
$route_contexts = $route_object->getOption('parameters'); |
||||||
|
if (!$route_contexts) { |
||||||
|
return FALSE; |
||||||
|
} |
||||||
|
if (!isset($route_contexts[$object_type])) { |
||||||
|
return FALSE; |
||||||
|
} |
||||||
|
|
||||||
|
$object = $this->routeMatch->getParameter($object_type); |
||||||
|
|
||||||
|
if (!$object) { |
||||||
|
return FALSE; |
||||||
|
} |
||||||
|
|
||||||
|
return $object; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds resource-specific link headers to appropriate responses. |
||||||
|
* |
||||||
|
* @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event |
||||||
|
* Event containing the response. |
||||||
|
*/ |
||||||
|
abstract public function onResponse(FilterResponseEvent $event); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\islandora\EventSubscriber; |
||||||
|
|
||||||
|
use Drupal\Core\Entity\EntityFieldManager; |
||||||
|
use Drupal\Core\Entity\EntityTypeManagerInterface; |
||||||
|
use Drupal\Core\Entity\FieldableEntityInterface; |
||||||
|
use Drupal\Core\Routing\RouteMatchInterface; |
||||||
|
use Drupal\Core\Url; |
||||||
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface; |
||||||
|
use Symfony\Component\HttpKernel\Event\FilterResponseEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* Class MediaLinkHeaderSubscriber. |
||||||
|
* |
||||||
|
* @package Drupal\islandora\EventSubscriber |
||||||
|
*/ |
||||||
|
class MediaLinkHeaderSubscriber extends LinkHeaderSubscriber implements EventSubscriberInterface { |
||||||
|
|
||||||
|
/** |
||||||
|
* Media storage interface. |
||||||
|
* |
||||||
|
* @var \Drupal\Core\Entity\EntityStorageInterface |
||||||
|
*/ |
||||||
|
protected $mediaBundleStorage; |
||||||
|
|
||||||
|
/** |
||||||
|
* MediaLinkHeaderSubscriber constructor. |
||||||
|
* |
||||||
|
* @param \Drupal\Core\Entity\EntityFieldManager $entity_field_manager |
||||||
|
* The entity field manager. |
||||||
|
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match |
||||||
|
* The route match object. |
||||||
|
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager |
||||||
|
* The entity type manager. |
||||||
|
*/ |
||||||
|
public function __construct( |
||||||
|
EntityFieldManager $entity_field_manager, |
||||||
|
RouteMatchInterface $route_match, |
||||||
|
EntityTypeManagerInterface $entity_type_manager) { |
||||||
|
$this->mediaBundleStorage = $entity_type_manager->getStorage('media_bundle'); |
||||||
|
parent::__construct($entity_field_manager, $route_match); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function onResponse(FilterResponseEvent $event) { |
||||||
|
$response = $event->getResponse(); |
||||||
|
|
||||||
|
$entity = $this->getObject($response, 'media'); |
||||||
|
|
||||||
|
if ($entity === FALSE) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
$media_bundle = $this->mediaBundleStorage->load($entity->bundle()); |
||||||
|
|
||||||
|
$type_configuration = $media_bundle->getTypeConfiguration(); |
||||||
|
|
||||||
|
if (!isset($type_configuration['source_field'])) { |
||||||
|
return; |
||||||
|
} |
||||||
|
$source_field = $type_configuration['source_field']; |
||||||
|
|
||||||
|
if (empty($source_field) || |
||||||
|
!$entity instanceof FieldableEntityInterface || |
||||||
|
!$entity->hasField($source_field) |
||||||
|
) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Collect file links for the media. |
||||||
|
$links = []; |
||||||
|
foreach ($entity->get($source_field)->referencedEntities() as $referencedEntity) { |
||||||
|
if ($entity->access('view')) { |
||||||
|
$file_url = $referencedEntity->url('canonical', ['absolute' => TRUE]); |
||||||
|
$edit_media_url = Url::fromRoute('islandora.media_source_update', ['media' => $referencedEntity->id()]) |
||||||
|
->setAbsolute() |
||||||
|
->toString(); |
||||||
|
$links[] = "<$file_url>; rel=\"describes\"; type=\"{$referencedEntity->getMimeType()}\""; |
||||||
|
$links[] = "<$edit_media_url>; rel=\"edit-media\""; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Exit early if there aren't any. |
||||||
|
if (empty($links)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Add the link headers to the response. |
||||||
|
$response->headers->set('Link', $links, FALSE); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\Tests\islandora\Functional; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests the MediaLinkHeader event subscriber. |
||||||
|
* |
||||||
|
* @group islandora |
||||||
|
*/ |
||||||
|
class MediaLinkHeaderTest extends IslandoraFunctionalTestBase { |
||||||
|
|
||||||
|
/** |
||||||
|
* @covers \Drupal\islandora\EventSubscriber\MediaLinkHeaderSubscriber |
||||||
|
*/ |
||||||
|
public function testMediaLinkHeaders() { |
||||||
|
// Create a test user. |
||||||
|
$account = $this->drupalCreateUser([ |
||||||
|
'view media', |
||||||
|
'create media', |
||||||
|
'update media', |
||||||
|
]); |
||||||
|
$this->drupalLogin($account); |
||||||
|
|
||||||
|
$urls = $this->createThumbnailWithFile(); |
||||||
|
|
||||||
|
$this->drupalGet($urls['media'], [], ['Cache-Control: no-cache']); |
||||||
|
|
||||||
|
$this->assertTrue( |
||||||
|
$this->validateLinkHeaderWithUrl('describes', $urls['file']['file'], '', 'image/png') == 1, |
||||||
|
"Malformed 'describes' link header" |
||||||
|
); |
||||||
|
$this->assertTrue( |
||||||
|
$this->validateLinkHeaderWithUrl('edit-media', $urls['file']['rest'], '', '') == 1, |
||||||
|
"Malformed 'edit-media' link header" |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue