Commit f406799a authored by alexpott's avatar alexpott

Issue #1935548 by linclark, c4rl: Remove JSON-LD module.

parent 9a53a21b
......@@ -255,9 +255,6 @@ Help module
Image module
- Nathan Haug 'quicksketch' http://drupal.org/user/35821
JSON-LD module
- Lin Clark 'linclark' http://drupal.org/user/396253
Locale module
- Gábor Hojtsy 'Gábor Hojtsy' http://drupal.org/user/4166
......
name: JSON-LD
type: module
description: 'Serializes entities using JSON-LD format.'
package: Core
version: VERSION
core: 8.x
dependencies:
- rdf
- serialization
<?php
/**
* @file
* Enables entity serialization in JSON-LD.
*/
/**
* Implements hook_help().
*/
function jsonld_help($path, $args) {
switch ($path) {
case 'admin/help#jsonld':
$output = '';
$output .= '<p>' . t('The JSON-LD module serializes entities to the <a href="@jsonld_org">JSON-LD</a> data format. To request JSON-LD instead of HTML, a client should add an Accept header to the request. This module will respond with JSON-LD if the Accept header value is one of the following: application/ld+json, which returns an easy-to-use data structure which is compatible with many external schemas, and application/vnd.drupal.ld+json, which is more expressive and is appropriate for content staging.', array(
'@jsonld_org' => 'http://json-ld.org/',
)) . '</p>';
return $output;
}
}
# Normalizers can be specified to support a particular class and format in
# Normalizer::supportsNormalization(). Since the first matching Normalizer
# is used, Normalizers should be ordered from most specific to least
# specific.
services:
serializer.normalizer.entity_reference.jsonld:
class: Drupal\jsonld\JsonldEntityReferenceNormalizer
tags:
- { name: normalizer, priority: 5 }
arguments: ['@rdf.site_schema_manager', '@rdf.mapping_manager']
serializer.normalizer.field_item.jsonld:
class: Drupal\jsonld\JsonldFieldItemNormalizer
tags:
- { name: normalizer, priority: 5 }
arguments: ['@rdf.site_schema_manager', '@rdf.mapping_manager']
serializer.normalizer.entity.jsonld:
class: Drupal\jsonld\JsonldEntityNormalizer
tags:
- { name: normalizer, priority: 5 }
arguments: ['@rdf.site_schema_manager', '@rdf.mapping_manager']
serializer.normalizer.rdf_schema.jsonld:
class: Drupal\jsonld\JsonldRdfSchemaNormalizer
tags:
- { name: normalizer, priority: 5 }
arguments: ['@rdf.site_schema_manager', '@rdf.mapping_manager']
# Add the encoder to the service container. Encoders can only specify which
# format they support in Encoder::supportsEncoding().
serializer.encoder.jsonld:
class: Drupal\jsonld\JsonldEncoder
tags:
- { name: encoder, priority: 5, format: jsonld }
serializer.encoder.drupal_jsonld:
class: Drupal\jsonld\JsonldEncoder
tags:
- { name: encoder, priority: 5, format: drupal_jsonld }
jsonld.subscriber:
class: Drupal\jsonld\EventSubscriber\JsonldSubscriber
tags:
- { name: event_subscriber }
<?php
/**
* @file
* Contains Drupal\jsonld\EventSubscriber\JsonldSubscriber.
*/
namespace Drupal\jsonld\EventSubscriber;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Subscribes to the kernel request event to add JSON-LD media formats.
*/
class JsonldSubscriber implements EventSubscriberInterface {
/**
* Registers JSON-LD formats with the Request class.
*
* @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
* The event to process.
*/
public function onKernelRequest(GetResponseEvent $event) {
$request = $event->getRequest();
$request->setFormat('drupal_jsonld', 'application/vnd.drupal.ld+json');
$request->setFormat('jsonld', 'application/ld+json');
}
/**
* Registers the methods in this class that should be listeners.
*
* @return array
* An array of event listener definitions.
*/
static function getSubscribedEvents() {
$events[KernelEvents::REQUEST][] = array('onKernelRequest', 40);
return $events;
}
}
<?php
/**
* @file
* Definition of Drupal\jsonld\JsonldEncoder.
*/
namespace Drupal\jsonld;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
/**
* Encodes JSON-LD data.
*
* Simply respond to JSON-LD requests using the JSON encoder.
*/
class JsonldEncoder extends JsonEncoder {
/**
* The formats that this Encoder supports.
*
* @var array
*/
static protected $format = array('jsonld', 'drupal_jsonld');
/**
* Overrides \Symfony\Component\Serializer\Encoder\JsonEncoder::supportsEncoding()
*/
public function supportsEncoding($format) {
return in_array($format, static::$format);
}
/**
* Overrides \Symfony\Component\Serializer\Encoder\JsonEncoder::supportsDecoding()
*/
public function supportsDecoding($format) {
return in_array($format, static::$format);
}
}
<?php
/**
* @file
* Definition of Drupal\jsonld\JsonldEntityNormalizer.
*/
namespace Drupal\jsonld;
use Drupal\jsonld\JsonldNormalizerBase;
use Drupal\rdf\RdfMappingException;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
/**
* Converts the Drupal entity object structure to JSON-LD array structure.
*/
class JsonldEntityNormalizer extends JsonldNormalizerBase implements DenormalizerInterface {
/**
* The interface or class that this Normalizer supports.
*
* @var string
*/
protected $supportedInterfaceOrClass = 'Drupal\Core\Entity\EntityInterface';
/**
* Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize()
*/
public function normalize($entity, $format = NULL, array $context = array()) {
$entity_wrapper = new JsonldEntityWrapper($entity, $format, $this->serializer, $this->siteSchemaManager);
$attributes = $entity_wrapper->getProperties();
$attributes = array(
'@id' => $entity_wrapper->getId(),
'@type' => $entity_wrapper->getTypeUri(),
) + $attributes;
return $attributes;
}
/**
* Implements \Symfony\Component\Serializer\Normalizer\DenormalizerInterface::denormalize()
*
* @throws \Symfony\Component\Serializer\Exception\UnexpectedValueException
*/
public function denormalize($data, $class, $format = null, array $context = array()) {
if (!isset($data['@type'])) {
throw new UnexpectedValueException('JSON-LD @type parameter must be included.');
}
// Every bundle has a type, identified by URI. The incoming data should
// either include a type URI from this site's schema, or one of the type
// URIs in the incoming data must map to a site schema URI when passed
// through the RDF mapping manager.
$type_uris = is_array($data['@type']) ? $data['@type'] : array($data['@type']);
// If the RDF mapping manager can find a match to a site schema URI, it
// will return the corresponding Typed Data ids. Otherwise, throw an
// exception.
// @todo The @types might be CURIEs or aliases. Expand before trying to map.
try {
$typed_data_ids = $this->rdfMappingManager->getTypedDataIdsFromTypeUris($type_uris);
}
catch (RdfMappingException $e) {
throw new UnexpectedValueException($e->getMessage(), 0, $e);
}
$values = array(
'type' => $typed_data_ids['bundle'],
);
// If the data specifies a default language, use it to create the entity.
if (isset($data['langcode'])) {
$values['langcode'] = $data['langcode'][LANGUAGE_NOT_SPECIFIED][0]['value'];
}
// Otherwise, if the default language is not specified but there are
// translations of field values, explicitly set the entity's default
// language to the site's default language. This is required to enable
// field translation on this entity.
else if ($this->containsTranslation($data)) {
$values['langcode'] = language(LANGUAGE_TYPE_CONTENT)->langcode;
}
$entity = entity_create($typed_data_ids['entity_type'], $values);
// Make sure all empty entity fields default to NULL, so that afterwards it
// is possible to determine which fields were part of the data (even if they
// are empty).
foreach ($entity as $name => $field) {
if ($field->isEmpty()) {
$field->setValue(NULL);
}
}
// For each attribute in the JSON-LD, add the values as fields to the newly
// created entity. It is assumed that the JSON attribute names are the same
// as the site's field names.
// @todo Possibly switch to URI expansion of attribute names.
foreach ($data as $fieldName => $incomingFieldValues) {
// Skip the JSON-LD specific terms, which start with '@'.
if ($fieldName[0] === '@') {
continue;
}
// If the incoming value is an empty array we set the property to mark it
// for deletion.
if (empty($incomingFieldValues) && is_array($incomingFieldValues)) {
$entity->{$fieldName} = array();
}
// Figure out the designated class for this field type, which is used by
// the Serializer to determine which Denormalizer to use.
// @todo Is there a better way to get the field type's associated class?
$fieldItemClass = get_class($entity->get($fieldName)->offsetGet(0));
// Iterate through the language keyed values and add them to the entity.
// The vnd.drupal.ld+json mime type will always use language keys, per
// http://drupal.org/node/1838700.
foreach ($incomingFieldValues as $langcode => $incomingFieldItems) {
$fieldValue = $this->serializer->denormalize($incomingFieldItems, $fieldItemClass, $format);
$entity->getTranslation($langcode)
->set($fieldName, $fieldValue);
}
}
return $entity;
}
/**
* Determine whether incoming data contains translated content.
*
* @param array $data
* The incoming data.
*
* @return bool
* Whether or not this data contains translated content.
*/
protected function containsTranslation($data) {
// Langcodes which do not represent a translation of the entity.
$defaultLangcodes = array(
LANGUAGE_DEFAULT,
LANGUAGE_NOT_SPECIFIED,
LANGUAGE_NOT_APPLICABLE,
language(LANGUAGE_TYPE_CONTENT)->langcode,
);
// Combine the langcodes from the field value keys in a single array.
$fieldLangcodes = array();
foreach ($data as $propertyName => $property) {
//@todo Once @context has been added, check whether this property
// corresponds to an annotation instead. This will allow us to support
// incoming data that doesn't use language annotations.
if ('@' !== $propertyName[0]) {
$fieldLangcodes += array_keys($property);
}
}
$translationLangcodes = array_diff($fieldLangcodes, $defaultLangcodes);
return !empty($translationLangcodes);
}
}
<?php
/**
* @file
* Definition of Drupal\jsonld\JsonldEntityReferenceNormalizer.
*/
namespace Drupal\jsonld;
use Drupal\Core\Cache\DatabaseBackend;
use Drupal\Core\Entity\EntityNG;
use Drupal\jsonld\JsonldNormalizerBase;
use Drupal\rdf\SiteSchema\SiteSchemaManager;
use ReflectionClass;
use Symfony\Component\Serializer\Exception\RuntimeException;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
/**
* Converts an EntityReferenceItem to a JSON-LD array structure.
*/
class JsonldEntityReferenceNormalizer extends JsonldNormalizerBase implements DenormalizerInterface {
/**
* The interface or class that this Normalizer supports.
*
* @var string
*/
protected $supportedInterfaceOrClass = 'Drupal\Core\Entity\Field\Type\EntityReferenceItem';
/**
* Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize()
*/
public function normalize($object, $format = NULL, array $context = array()) {
// @todo If an $options parameter is added to the serialize signature, as
// requested in https://github.com/symfony/symfony/pull/4938, then instead
// of creating the array of properties, we could simply call normalize and
// pass in the referenced entity with a flag that ensures it is rendered as
// a node reference and not a node definition.
$entity_wrapper = new JsonldEntityWrapper($object->entity, $format, $this->serializer, $this->siteSchemaManager);
return array(
'@id' => $entity_wrapper->getId(),
);
}
/**
* Implements \Symfony\Component\Serializer\Normalizer\DenormalizerInterface::denormalize()
*/
public function denormalize($data, $class, $format = null, array $context = array()) {
// @todo Support denormalization for Entity Reference.
return array();
}
/**
* Overrides \Drupal\jsonld\JsonldNormalizerBase::supportsDenormalization()
*/
public function supportsDenormalization($data, $type, $format = NULL) {
$reflection = new ReflectionClass($type);
return in_array($format, static::$format) && ($reflection->getName() == $this->supportedInterfaceOrClass || $reflection->isSubclassOf($this->supportedInterfaceOrClass));
}
}
<?php
/**
* @file
* Definition of Drupal\jsonld\JsonldEntityWrapper.
*/
namespace Drupal\jsonld;
use Drupal\Core\Entity\Entity;
use Drupal\rdf\SiteSchema\SiteSchema;
use Drupal\rdf\SiteSchema\SiteSchemaManager;
use Symfony\Component\Serializer\Serializer;
/**
* Provide an interface for JsonldNormalizer to get required properties.
*
* @todo Eventually, this class should be removed. It allows both the
* EntityNormalizer and the EntityReferenceNormalizer to have access to the
* same functions. If an $options parameter is added to the serialize
* signature, as requested in https://github.com/symfony/symfony/pull/4938,
* then the EntityReferenceNormalizer could simply call
* EntityNormalizer::normalize(), passing in the referenced entity.
*/
class JsonldEntityWrapper {
/**
* The entity that this object wraps.
*
* @var \Drupal\Core\Entity\EntityNG
*/
protected $entity;
/**
* The requested format.
*
* @var string
*/
protected $format;
/**
* The serializer.
*
* @var \Symfony\Component\Serializer\Serializer
*/
protected $serializer;
/**
* The site schema manager.
*
* @var \Drupal\rdf\SiteSchema\SiteSchemaManager
*/
protected $siteSchemaManager;
/**
* Constructor.
*
* @param \Drupal\Core\Entity\EntityNG $entity
* The Entity API entity
* @param string $format.
* The format.
* @param \Symfony\Component\Serializer\Serializer $serializer
* The serializer, provided by the SerializerAwareNormaizer.
* @param \Drupal\rdf\SiteSchema\SiteSchemaManager $site_schema_manager
* The site schema manager.
*/
public function __construct(Entity $entity, $format, Serializer $serializer, SiteSchemaManager $site_schema_manager) {
$this->entity = $entity;
$this->format = $format;
$this->serializer = $serializer;
$this->siteSchemaManager = $site_schema_manager;
}
/**
* Get the Entity's URI for the @id attribute.
*
* @return string
* The URI of the entity.
*/
public function getId() {
$uri_info = $this->entity->uri();
return url($uri_info['path'], array('absolute' => TRUE));
}
/**
* Get the type URI.
*
* @todo Once RdfMappingManager has a mapOutputTypes event, use that instead
* of simply returning the site schema URI.
*/
public function getTypeUri() {
$entity_type = $this->entity->entityType();
$bundle = $this->entity->bundle();
switch ($this->format) {
case 'drupal_jsonld':
$schema_path = SiteSchema::CONTENT_DEPLOYMENT;
break;
case 'jsonld':
$schema_path = SiteSchema::SYNDICATION;
}
$schema = $this->siteSchemaManager->getSchema($schema_path);
return $schema->bundle($entity_type, $bundle)->getUri();
}
/**
* Get properties, excluding JSON-LD specific properties.
*
* @return array
* An array of properties structured as in JSON-LD.
*/
public function getProperties() {
// Properties to skip.
$skip = array('id');
$properties = array();
// Create language map property structure.
foreach ($this->entity->getTranslationLanguages() as $langcode => $language) {
foreach ($this->entity->getTranslation($langcode) as $name => $field) {
$definition = $this->entity->getPropertyDefinition($name);
$langKey = empty($definition['translatable']) ? LANGUAGE_NOT_SPECIFIED : $langcode;
if (!$field->isEmpty()) {
$properties[$name][$langKey] = $this->serializer->normalize($field, $this->format);
}
}
}
// Only return properties which are not in the $skip array.
return array_diff_key($properties, array_fill_keys($skip, ''));
}
}
<?php
/**
* @file
* Definition of Drupal\jsonld\JsonldFieldItemNormalizer.
*/
namespace Drupal\jsonld;
use Drupal\Core\Entity\Field\FieldItemInterface;
use Drupal\jsonld\JsonldNormalizerBase;
use Symfony\Component\Serializer\Exception\RuntimeException;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
/**
* Converts the Drupal entity object structure to JSON-LD array structure.
*/
class JsonldFieldItemNormalizer extends JsonldNormalizerBase implements DenormalizerInterface {
/**
* The interface or class that this Normalizer supports.
*
* @var string
*/
protected $supportedInterfaceOrClass = 'Drupal\Core\Entity\Field\FieldItemInterface';
/**
* Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize()
*/
public function normalize($object, $format = NULL, array $context = array()) {
return $object->getPropertyValues();
}
/**
* Implements \Symfony\Component\Serializer\Normalizer\DenormalizerInterface::denormalize()
*/
public function denormalize($data, $class, $format = null, array $context = array()) {
// For most fields, the field items array should simply be returned as is.
return $data;
}
}
<?php
/**
* @file
* Definition of Drupal\jsonld\JsonldNormalizerBase.
*/
namespace Drupal\jsonld;
use ReflectionClass;
use Drupal\rdf\RdfMappingManager;
use Drupal\rdf\SiteSchema\SiteSchemaManager;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Normalizer\SerializerAwareNormalizer;
/**
* Provide a base class for JSON-LD Normalizers.
*/
abstract class JsonldNormalizerBase extends SerializerAwareNormalizer implements NormalizerInterface {
/**
* The interface or class that this Normalizer supports.
*
* @var string
*/
protected $supportedInterfaceOrClass;
/**
* The formats that this Normalizer supports.
*
* @var array
*/
static protected $format = array('jsonld', 'drupal_jsonld');
/**
* The site schema manager.
*
* @var \Drupal\rdf\SiteSchema\SiteSchemaManager
*/
protected $siteSchemaManager;
/**
* The RDF mapping manager.
*
* @var \Drupal\rdf\RdfMappingManager
*/
protected $rdfMappingManager;
/**
* Constructor.
*
* @param \Drupal\rdf\SiteSchema\SiteSchemaManager $site_schema_manager
* The site schema manager.
* @param \Drupal\rdf\RdfMappingManager $rdf_mapping_manager
* The RDF mapping manager.
*/
public function __construct(SiteSchemaManager $site_schema_manager, RdfMappingManager $rdf_mapping_manager) {
$this->siteSchemaManager = $site_schema_manager;
$this->rdfMappingManager = $rdf_mapping_manager;
}
/**
* Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize()
*/
public function supportsNormalization($data, $format = NULL) {
return is_object($data) && in_array($format, static::$format) && ($data instanceof $this->supportedInterfaceOrClass);
}
/**
* Implements \Symfony\Component\Serializer\Normalizer\DenormalizerInterface::supportsDenormalization()
*
* This class doesn't implement DenormalizerInterface, but most of its child
* classes do, so this method is implemented at this level to reduce code
* duplication.
*/
public function supportsDenormalization($data, $type, $format = NULL) {
$reflection = new ReflectionClass($type);
return in_array($format, static::$format) && $reflection->implementsInterface($this->supportedInterfaceOrClass);
}
}
<?php
/**
* @file
* Contains JsonldRdfSchemaNormalizer.
*/
namespace Drupal\jsonld;
use Drupal\jsonld\JsonldNormalizerBase;
use Drupal\rdf\RdfConstants;
/**
* Converts the Drupal entity object structure to JSONLD array structure.
*/
class JsonldRdfSchemaNormalizer extends JsonldNormalizerBase {
/**
* The interface or class that this Normalizer supports.
*
* @var string
*/
protected $supportedInterfaceOrClass = 'Drupal\rdf\SiteSchema\SchemaTermBase';
/**
* Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize()
*/
public function normalize($data, $format = NULL, array $context = array()) {
$normalized = array();
$graph = $data->getGraph();
foreach ($graph as $term_uri => $properties) {
// JSON-LD uses the @type keyword as a stand-in for rdf:type. Replace any
// use of rdf:type and move the type to the front of the property array.
if (isset($properties[RdfConstants::RDF_TYPE])) {
$properties = array(
'@type' => $properties[RdfConstants::RDF_TYPE],
) + $properties;
}
unset($properties[RdfConstants::RDF_TYPE]);
// Add the @id keyword to the front of the array.
$normalized[] = array(
'@id' => $term_uri,
) + $properties;
}
return $normalized;
}
}
<?php
/**
* @file
* Contains JsonldTestSetupHelper.
*/
namespace Drupal\jsonld\Tests;
use Drupal\jsonld\JsonldEncoder;
use Drupal\jsonld\JsonldEntityNormalizer;
use Drupal\jsonld\JsonldEntityReferenceNormalizer;
use Drupal\jsonld\JsonldFieldItemNormalizer;
use Drupal\rdf\RdfMappingManager;