Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CTDA9-290: Add normalizers. #1

Merged
merged 8 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,49 @@
# iiif_presentation_api
# IIIF Presentation API

## Introduction

Provides serialization to IIIF Presentation API.

## Requirements

This module requires the following modules/libraries:

* Serialization (part of Drupal core)

## Installation

Install as usual, see
[this](https://www.drupal.org/docs/extending-drupal/installing-modules) for
further information.

## Configuration

Out of the box the module provides minimal implementation and expects other
modules to either decorate or provide their own implementation by extending the
provided normalizers.

For any content entity that should be exposed the format will need to be
configured as [documented by Drupal][1].

## Troubleshooting/Issues

Having problems or solved one? contact
[discoverygarden](http://support.discoverygarden.ca).

## Maintainers/Sponsors

Current maintainers:

* [discoverygarden](http://www.discoverygarden.ca)

## Development

If you would like to contribute to this module create an issue, pull request
and or contact
[discoverygarden](http://support.discoverygarden.ca).

## License

[GPLv3](http://www.gnu.org/licenses/gpl-3.0.txt)

[1]: https://www.drupal.org/docs/drupal-apis/restful-web-services-api/restful-web-services-api-overview#s-api-features]
7 changes: 7 additions & 0 deletions iiif_presentation_api.info.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: IIIF Presentation API
type: module
description: 'Provides a IIIF Presentation API implementation for Drupal entities.'
package: DGI
core_version_requirement: ^9 || ^10
dependencies:
- drupal:serialization
18 changes: 18 additions & 0 deletions iiif_presentation_api.services.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
services:
serializer.encoder.iiif_presentation_api.iiif_p_v3.encoder:
class: 'Drupal\iiif_presentation_api\Encoder\V3\IiifP'
tags:
- { name: encoder, priority: 20, format: 'iiif-p-v3' }
serializer.normalizer.iiif_presentation_api.iiif_p_v3.content_entity:
class: 'Drupal\iiif_presentation_api\Normalizer\V3\ContentEntityNormalizer'
tags:
- { name: normalizer, priority: 10 }
arguments: ['@current_user']
serializer.normalizer.iiif_presentation_api.iiif_p_v3.field_item_list:
class: 'Drupal\iiif_presentation_api\Normalizer\V3\FieldItemListNormalizer'
tags:
- { name: normalizer, priority: 10 }
serializer.normalizer.iiif_presentation_api.iiif_p_v3.field_item:
class: 'Drupal\iiif_presentation_api\Normalizer\V3\FieldItemNormalizer'
tags:
- { name: normalizer, priority: 10 }
19 changes: 19 additions & 0 deletions src/Encoder/V3/IiifP.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace Drupal\iiif_presentation_api\Encoder\V3;

use Drupal\serialization\Encoder\JsonEncoder;

/**
* IIIF Presentation v3 encoder.
*/
class IiifP extends JsonEncoder {

/**
* The format that this encoder supports.
*
* @var array
*/
protected static $format = ['iiif-p-v3'];

}
19 changes: 19 additions & 0 deletions src/IiifPresentationApiServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace Drupal\iiif_presentation_api;

use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceModifierInterface;

class IiifPresentationApiServiceProvider implements ServiceModifierInterface {

/**
* {@inheritDoc}
*/
public function alter(ContainerBuilder $container) {
if ($container->has('http_middleware.negotiation') && is_a($container->getDefinition('http_middleware.negotiation')->getClass(), '\Drupal\Core\StackMiddleware\NegotiationMiddleware', TRUE)) {
$container->getDefinition('http_middleware.negotiation')->addMethodCall('registerFormat', ['iiif-p-v3', ['application/json']]);
}
}

}
53 changes: 53 additions & 0 deletions src/Normalizer/EntityUriTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

namespace Drupal\iiif_presentation_api\Normalizer;

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Url;

/**
* Provides a trait for generating entity URIs.
*/
trait EntityUriTrait {

use RouterProviderTrait;

/**
* Constructs the entity URI.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity.
* @param array $context
* Normalization/serialization context.
*
* @return string
* The entity URI.
*/
protected function getEntityUri(EntityInterface $entity, array $context = []): string {
// Some entity types don't provide a canonical link template.
if ($entity->isNew()) {
return '';
}

$route_name = 'rest.entity.' . $entity->getEntityTypeId() . '.GET';
if ($entity->hasLinkTemplate('canonical')) {
$url = $entity->toUrl('canonical');
}
elseif ($this->getRouteProvider()->getRoutesByNames([$route_name])) {
$url = Url::fromRoute('rest.entity.' . $entity->getEntityTypeId() . '.GET', [$entity->getEntityTypeId() => $entity->id()]);
}
else {
return '';
}

$url->setAbsolute();
if (!$url->isExternal()) {
$url->setRouteParameter('_format', 'iiif-p-v3');
}
$generated_url = $url->toString(TRUE);
$this->addCacheableDependency($context, $generated_url);

return $generated_url->getGeneratedUrl();
}

}
41 changes: 41 additions & 0 deletions src/Normalizer/RouterProviderTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Drupal\iiif_presentation_api\Normalizer;

use Drupal\Core\Routing\RouteProviderInterface;

/**
* Provides a trait for injecting the route provider service.
*/
trait RouterProviderTrait {
/**
* The route provider service.
*
* @var \Drupal\Core\Routing\RouteProviderInterface
*/
protected RouteProviderInterface $routeProvider;

/**
* Get the route provider service.
*
* @return \Drupal\Core\Routing\RouteProviderInterface
* The route provider service.
*/
protected function getRouteProvider(): RouteProviderInterface {
if (!isset($this->routeProvider)) {
$this->routeProvider = \Drupal::service('router.route_provider');
}
return $this->routeProvider;
}

/**
* Set the route provider service.
*
* @param \Drupal\Core\Routing\RouteProviderInterface $routeProvider
* The route provider service.
*/
protected function setRouteProvider(RouteProviderInterface $routeProvider): void {
$this->routeProvider = $routeProvider;
}

}
116 changes: 116 additions & 0 deletions src/Normalizer/V3/ContentEntityNormalizer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php

namespace Drupal\iiif_presentation_api\Normalizer\V3;

use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Routing\RouteProviderInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\iiif_presentation_api\Normalizer\EntityUriTrait;

/**
* Normalizer for content entities.
*/
class ContentEntityNormalizer extends NormalizerBase {

use EntityUriTrait;

/**
* {@inheritDoc}
*/
protected $supportedInterfaceOrClass = ContentEntityInterface::class;

/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected AccountInterface $user;

/**
* Constructor for the ContentEntityNormalizer.
*
* @param \Drupal\Core\Session\AccountInterface $user
* The current user.
*/
public function __construct(AccountInterface $user) {
$this->user = $user;
}

/**
* {@inheritDoc}
*/
public function normalize($object, $format = NULL, array $context = []) {

$normalized = [];
if (!isset($context['base-depth'])) {
$context['base-depth'] = TRUE;
$normalized['@context'] = 'http://iiif.io/api/presentation/3/context.json';
}
else {
$context['base-depth'] = FALSE;
}
$normalized += [
'id' => $this->getEntityUri($object, $context),
'type' => $context['base-depth'] ? 'Manifest' : 'Canvas',
'label' => [
'none' => [$object->label()],
],
];

$context += [
'account' => $this->user,
];

if (isset($context[static::SERIALIZATION_CONTEXT_CACHEABILITY])) {
$context[static::SERIALIZATION_CONTEXT_CACHEABILITY]->addCacheContexts(['user.roles']);
}

$context['parent'] = [
'type' => $normalized['type'],
'id' => $normalized['id'],
];
return $this->normalizeEntityFields($object, $format, $context, $normalized);
}

/**
* Normalizes all fields present on a content entity.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $object
* The entity being normalized.
* @param string $format
* The format being serialized.
* @param array $context
* An array containing any context being passed to the normalizers.
* @param array $normalized
* An array representing the normalized entity to be rendered.
*
* @return array
* The normalized representation of the entity.
*/
protected function normalizeEntityFields(ContentEntityInterface $object, string $format, array $context, array $normalized): array {
$this->addCacheableDependency($context, $object);
foreach ($object->getFields() as $field) {
$access = $field->access('view', $context['account'], TRUE);
$this->addCacheableDependency($context, $access);
if (!$access->isAllowed()) {
continue;
}
$normalized_property = $this->serializer->normalize($field, $format, $context);
if (!empty($normalized_property)) {
$normalized = NestedArray::mergeDeep($normalized, (array) $normalized_property);
}
}
return $normalized;
}

/**
* {@inheritDoc}
*/
public function getSupportedTypes(?string $format): array {
return [
ContentEntityInterface::class => TRUE,
];
}

}
26 changes: 26 additions & 0 deletions src/Normalizer/V3/FieldItemListNormalizer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace Drupal\iiif_presentation_api\Normalizer\V3;

use Drupal\Core\Field\FieldItemListInterface;

/**
* Normalizer for field item lists.
*/
class FieldItemListNormalizer extends NormalizerBase {

/**
* {@inheritDoc}
*/
protected $supportedInterfaceOrClass = FieldItemListInterface::class;

/**
* {@inheritDoc}
*/
public function getSupportedTypes(?string $format): array {
return [
FieldItemListInterface::class => TRUE,
];
}

}
26 changes: 26 additions & 0 deletions src/Normalizer/V3/FieldItemNormalizer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace Drupal\iiif_presentation_api\Normalizer\V3;

use Drupal\Core\Field\FieldItemInterface;

/**
* Normalizer for field items.
*/
class FieldItemNormalizer extends NormalizerBase {

/**
* {@inheritDoc}
*/
protected $supportedInterfaceOrClass = FieldItemInterface::class;

/**
* {@inheritDoc}
*/
public function getSupportedTypes(?string $format): array {
return [
FieldItemInterface::class => FALSE,
];
}

}
Loading