diff --git a/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/MappingSubscriber.php b/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/MappingSubscriber.php index 764c2f9..2552870 100644 --- a/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/MappingSubscriber.php +++ b/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/MappingSubscriber.php @@ -6,7 +6,7 @@ namespace Drupal\rdf\EventSubscriber; -use Drupal\rdf\RdfMappingEvents; +use Drupal\rdf\Mapping\RdfMappingEvents; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -21,10 +21,10 @@ class MappingSubscriber implements EventSubscriberInterface { * mapping is unnecessary. Mapping is only necessary if the incoming type URI * is from an external vocabulary. * - * @param \Drupal\rdf\MapTypesFromInputEvent $event + * @param \Drupal\rdf\Mapping\MapTypesFromInputEvent $event * The mapping event. */ - public function mapTypesFromInput(\Drupal\rdf\MapTypesFromInputEvent $event) { + public function mapTypesFromInput(\Drupal\rdf\Mapping\MapTypesFromInputEvent $event) { $input_uris = $event->getInputUris(); $site_schema_types = $event->getSiteSchemaTypes(); foreach ($input_uris as $input_uri) { @@ -35,11 +35,46 @@ public function mapTypesFromInput(\Drupal\rdf\MapTypesFromInputEvent $event) { } } + public function mapBundleForOutput(\Drupal\rdf\Mapping\MapBundleForOutputEvent $event) { + $term_schema = $event->getTermSchema(); + + $config = $term_schema->getMappingConfig(); + $types = $config->get('types'); + if (!empty($types)) { + $event->addTypes($types); + } + } + + public function mapFieldForOutput(\Drupal\rdf\Mapping\MapFieldForOutputEvent $event) { + $term_schema = $event->getTermSchema(); + + $config = $term_schema->getMappingConfig(); + $properties = $config->get('properties'); + if (!empty($properties)) { + $event->addPredicates($properties); + $datatype = $config->get('datatype'); + $datatype_callback = $config->get('datatype_callback'); + $mapping_type = $config->get('mapping_type'); + + if (isset($datatype)) { + $event->setDatatype($datatype); + } + if (isset($datatype_callback)) { + $event->setDatatypeCallback($datatype_callback); + } + if (isset($mapping_type)) { + $event->setMappingType($mapping_type); + } + } + } + /** * Implements EventSubscriberInterface::getSubscribedEvents(). */ static function getSubscribedEvents() { $events[RdfMappingEvents::MAP_TYPES_FROM_INPUT] = 'mapTypesFromInput'; + $events[RdfMappingEvents::MAP_BUNDLE_FOR_OUTPUT] = 'mapBundleForOutput'; + $events[RdfMappingEvents::MAP_FIELD_FOR_OUTPUT] = 'mapFieldForOutput'; return $events; } } diff --git a/core/modules/rdf/lib/Drupal/rdf/Mapping/MapBundleForOutputEvent.php b/core/modules/rdf/lib/Drupal/rdf/Mapping/MapBundleForOutputEvent.php new file mode 100644 index 0000000..9a359b0 --- /dev/null +++ b/core/modules/rdf/lib/Drupal/rdf/Mapping/MapBundleForOutputEvent.php @@ -0,0 +1,63 @@ +termSchema = $term_schema; + $this->types = array(); + } + + public function getTermSchema() { + return $this->termSchema; + } + + /** + * Add RDF types to the CURIEs array. + * + * @param array $curies + * + * @todo Add namespace IDs. + */ + public function addTypes($curies) { + if (!is_array($curies)) { + $curies = array($curies); + } + $this->types = array_merge($this->types, $curies); + } + + /** + * @return array + * An array of CURIE strings/arrays. + */ + public function getTypes() { + return $this->types; + } +} diff --git a/core/modules/rdf/lib/Drupal/rdf/Mapping/MapFieldForOutputEvent.php b/core/modules/rdf/lib/Drupal/rdf/Mapping/MapFieldForOutputEvent.php new file mode 100644 index 0000000..c9bab59 --- /dev/null +++ b/core/modules/rdf/lib/Drupal/rdf/Mapping/MapFieldForOutputEvent.php @@ -0,0 +1,105 @@ +termSchema = $term_schema; + $this->predicates = array(); + } + + public function getTermSchema() { + return $this->termSchema; + } + + /** + * Add CURIEs to the predicates array. + * + * @param array $curies + */ + public function addPredicates($curies) { + if (!is_array($curies)) { + $curies = array($curies); + } + $this->predicates = array_merge($this->predicates, $curies); + } + + /** + * Set the datatype. + * + * @param string|array $curie + */ + public function setDatatype($curie) { + if (is_array($curie)) { + $curie = reset($curie); + } + $this->datatype = $curie; + } + + /** + * Set the datatype callback. + * + * @param callable $callable + */ + public function setDatatypeCallback($callable) { + $this->datatypeCallback = $callable; + } + + /** + * Set the mapping type. + * + * @param string $mapping_type + */ + public function setMappingType($mapping_type) { + $this->mappingType = $mapping_type; + } + + public function getPredicates() { + return $this->predicates; + } + + public function getDatatype() { + return $this->datatype; + } + + public function getDatatypeCallback() { + return $this->datatypeCallback; + } + + public function getMappingType() { + return $this->mappingType; + } +} diff --git a/core/modules/rdf/lib/Drupal/rdf/Mapping/MapTypesFromInputEvent.php b/core/modules/rdf/lib/Drupal/rdf/Mapping/MapTypesFromInputEvent.php new file mode 100644 index 0000000..7ca6ea4 --- /dev/null +++ b/core/modules/rdf/lib/Drupal/rdf/Mapping/MapTypesFromInputEvent.php @@ -0,0 +1,91 @@ +inputUris = $input_uris; + $this->siteSchemaTypes = $site_schema_types; + $this->siteSchemaUri = FALSE; + } + + /** + * Gets the input URI. + * + * @return array + * The array of incoming RDF type URIs. + */ + public function getInputUris() { + return $this->inputUris; + } + + /** + * Gets the cache of internal site schema types. + * + * @return array + * The cached site schema type array. + */ + public function getSiteSchemaTypes() { + return $this->siteSchemaTypes; + } + + /** + * Gets the site schema URI. + * + * @return string|bool + * The site schema type URI if set, FALSE if otherwise. + */ + public function getSiteSchemaUri() { + return $this->siteSchemaUri; + } + + /** + * Sets the site schema URI. + * + * @param string $uri + * The site schema type URI. + */ + public function setSiteSchemaUri($uri) { + $this->siteSchemaUri = $uri; + } +} diff --git a/core/modules/rdf/lib/Drupal/rdf/Mapping/RdfMappingEvents.php b/core/modules/rdf/lib/Drupal/rdf/Mapping/RdfMappingEvents.php new file mode 100644 index 0000000..5fea94a --- /dev/null +++ b/core/modules/rdf/lib/Drupal/rdf/Mapping/RdfMappingEvents.php @@ -0,0 +1,65 @@ +dispatcher = $dispatcher; + $this->siteSchemaManager = $site_schema_manager; + } + + /** + * Convert an array of RDF type URIs to the corresponding TypedData IDs. + * + * @param array $input_rdf_types + * An array of URIs for the type. + * + * @return array + * An array containing entity_type and bundle. + * + * @throws \Drupal\rdf\Mapping\RdfMappingException + */ + public function getTypedDataIdsFromTypeUris($input_rdf_types) { + // Get the cache of site schema types. + $site_schema_types = $this->siteSchemaManager->getTypes(); + // Map the RDF type from the incoming data to an RDF type defined in the + // internal site schema. + $type_uri = $this->mapTypesFromInput($input_rdf_types); + // If no site schema URI has been determined, then it's impossible to know + // what entity type to create. Throw an exception. + if ($type_uri == FALSE) { + throw new RdfMappingException(sprintf('No mapping to a site schema type URI found for incoming types (%s).', implode(',', $input_rdf_types))); + } + // Use the mapped RDF type URI to get the TypedData API ids the rest of the + // system uses (entity type and bundle). + return $site_schema_types[$type_uri]; + } + + /** + * Convert a bundle's Typed Data IDs to an RDF mapping array. + * + * Bundles will have different mappings based on whether the data is to be + * used for content deployment or for syndication. The site schema id is used + * to specify which to return. + * + * @param string $entity_type + * The entity type of the bundle which the mapping corresponds to. + * @param string $bundle + * The name of the bundle which the mapping corresponds to. + * @param string $schema_id + * The id of the site schema which is being mapped to. + * + * @return array + * The RDF mapping. + */ + public function getBundleMapping($entity_type, $bundle, $schema_id = SiteSchema::SYNDICATION) { + $site_schema = $this->siteSchemaManager->getSchema($schema_id); + $term_schema = $site_schema->bundle($entity_type, $bundle); + // Get the mapping, which contains the RDF types used when publishing data. + return $this->mapBundleForOutput($term_schema); + } + + /** + * Convert a field instance's Typed Data IDs to an RDF mapping array. + * + * Field instances will have different mappings based on whether the data is + * to be used for content deployment or for syndication. The site schema id + * is used to specify which to return. + * + * @param string $entity_type + * The entity type of the field instance which the mapping corresponds to. + * @param string $bundle + * The bundle of the field instance which the mapping corresponds to. + * @param string field_name + * The field name of the field instance which the mapping corresponds to. + * @param string $schema_id + * The id of the site schema which is being mapped to. + * + * @return array + * The RDF mapping. + */ + public function getFieldMapping($entity_type, $bundle, $field_name, $schema_id = SiteSchema::SYNDICATION) { + $site_schema = $this->siteSchemaManager->getSchema($schema_id); + $term_schema = $site_schema->field($entity_type, $bundle, $field_name); + return $this->mapFieldForOutput($term_schema); + } + + /** + * Get the mapping config entity for a bundle. + * + * Bundles will have different mappings based on whether the data is to be + * used for content deployment or for syndication. The site schema id is used + * to specify which to return. + * + * @param string $entity_type + * The entity type of the bundle which the mapping corresponds to. + * @param string $bundle + * The name of the bundle which the mapping corresponds to. + * @param string $schema_id + * The id of the site schema which is being mapped to. + * + * @return array + * The RDF mapping config entity. + */ + public function getBundleMappingConfig($entity_type, $bundle, $schema_id = SiteSchema::SYNDICATION) { + $bundle_schema = $this->siteSchemaManager->getSchema($schema_id)->bundle($entity_type, $bundle); + return $bundle_schema->getMappingConfig(); + } + + /** + * Get the field mapping config entity for a field instance. + * + * Field instances will have different mappings based on whether the data is + * to be used for content deployment or for syndication. The site schema id + * is used to specify which to return. + * + * @param string $entity_type + * The entity type of the field instance which the mapping corresponds to. + * @param string $bundle + * The bundle of the field instance which the mapping corresponds to. + * @param string field_name + * The field name of the field instance which the mapping corresponds to. + * @param string $schema_id + * The id of the site schema which is being mapped to. + * + * @return array + * The RDF mapping. + */ + public function getFieldMappingConfig($entity_type, $bundle, $field_name, $schema_id = SiteSchema::SYNDICATION) { + $field_schema = $this->siteSchemaManager->getSchema($schema_id)->field($entity_type, $bundle, $field_name); + return $field_schema->getMappingConfig(); + } + + /** + * Map an array of incoming URIs to an internal site schema URI. + * + * @param array $input_rdf_types + * An array of RDF type URIs. + * + * @return string + * The corresponding site schema type URI. + */ + protected function mapTypesFromInput($input_rdf_types) { + // Create the event using the array of incoming RDF type URIs and the cache + // of internal site schema URIs. + $site_schema_types = $this->siteSchemaManager->getTypes(); + $mapping_event = new MapTypesFromInputEvent($input_rdf_types, $site_schema_types); + + // Allow other modules to map the incoming type URIs to an internal site + // schema type URI. For example, a content deployment module could take + // URIs from the staging site's schema and map them to the corresponding + // URI in the live site's schema. + $this->dispatcher->dispatch(RdfMappingEvents::MAP_TYPES_FROM_INPUT, $mapping_event); + + return $mapping_event->getSiteSchemaUri(); + } + + /** + * Map a bundle's site schema term to external vocabularies. + * + * @param \Drupal\rdf\SiteSchema\SchemaTermInterface $term_schema + * The term object. + * + * @return string + * The corresponding RDF mapping. + */ + protected function mapBundleForOutput($term_schema) { + $mapping = array(); + $map_event = new MapBundleForOutputEvent($term_schema); + $this->dispatcher->dispatch(RdfMappingEvents::MAP_BUNDLE_FOR_OUTPUT, $map_event); + $types = $map_event->getTypes(); + if (!empty($types)) { + $mapping['types'] = $types; + } + return $mapping; + } + + /** + * Map a field instance's site schema term to external vocabularies. + * + * @param \Drupal\rdf\SiteSchema\SchemaTermInterface $term_schema + * The term object. + * + * @return string + * The corresponding RDF mapping. + */ + protected function mapFieldForOutput($term_schema) { + $mapping = array(); + $map_event = new MapFieldForOutputEvent($term_schema); + $this->dispatcher->dispatch(RdfMappingEvents::MAP_FIELD_FOR_OUTPUT, $map_event); + + $predicates = $map_event->getPredicates(); + if (!empty($predicates)) { + $mapping['properties'] = $predicates; + $mapping['datatype'] = $map_event->getDatatype(); + $mapping['datatype_callback'] = $map_event->getDatatypeCallback(); + $mapping['mapping_type'] = $map_event->getMappingType(); + } + return array_filter($mapping); + } + +} diff --git a/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/BundleRdfMapping.php b/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/BundleRdfMapping.php new file mode 100644 index 0000000..dc7cfef --- /dev/null +++ b/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/BundleRdfMapping.php @@ -0,0 +1,71 @@ +mid; + } +} diff --git a/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/FieldRdfMapping.php b/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/FieldRdfMapping.php new file mode 100644 index 0000000..b71922d --- /dev/null +++ b/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/FieldRdfMapping.php @@ -0,0 +1,101 @@ +mid; + } +} diff --git a/core/modules/rdf/lib/Drupal/rdf/RdfConstants.php b/core/modules/rdf/lib/Drupal/rdf/RdfConstants.php index b64ae91..4ac035f 100644 --- a/core/modules/rdf/lib/Drupal/rdf/RdfConstants.php +++ b/core/modules/rdf/lib/Drupal/rdf/RdfConstants.php @@ -12,8 +12,9 @@ */ abstract class RdfConstants { const RDF_TYPE = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'; + const RDF_PROPERTY = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Property'; // RDF Schema terms. - const RDFS_CLASS = 'http://www.w3.org/2000/01/rdf-schema#class'; + const RDFS_CLASS = 'http://www.w3.org/2000/01/rdf-schema#Class'; const RDFS_DOMAIN = 'http://www.w3.org/2000/01/rdf-schema#domain'; const RDFS_IS_DEFINED_BY = 'http://www.w3.org/2000/01/rdf-schema#isDefinedBy'; const RDFS_RANGE = 'http://www.w3.org/2000/01/rdf-schema#range'; diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/BundleSchema.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/BundleSchema.php index 2c92696..ff79559 100644 --- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/BundleSchema.php +++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/BundleSchema.php @@ -22,6 +22,8 @@ class BundleSchema extends EntitySchema { */ public static $uriPattern = '{entity_type}/{bundle}'; + protected $configPrefix = 'rdf.mapping.bundle'; + /** * The bundle that this term identifies. * @@ -45,11 +47,14 @@ public function __construct($site_schema, $entity_type, $bundle) { } /** - * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getUri(). + * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getPath(). */ - public function getUri() { - $path = str_replace(array('{entity_type}', '{bundle}'), array($this->entityType, $this->bundle), static::$uriPattern); - return $this->siteSchema->getUri() . $path; + public function getPath() { + return $this->siteSchema->getPath() . $this->prepareUriPattern(); + } + + protected function prepareUriPattern() { + return str_replace(array('{entity_type}', '{bundle}'), array($this->entityType, $this->bundle), static::$uriPattern); } /** @@ -61,4 +66,10 @@ public function getProperties() { return $properties; } + public function getTermIds() { + $keys = parent::getTermIds(); + $keys['bundle'] = $this->bundle; + return $keys; + } + } diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/EntitySchema.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/EntitySchema.php index 48a69fc..5d4dd7b 100644 --- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/EntitySchema.php +++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/EntitySchema.php @@ -54,11 +54,11 @@ public function getGraph() { } /** - * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getUri(). + * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getPath(). */ - public function getUri() { + public function getPath() { $path = str_replace('{entity_type}', $this->entityType , static::$uriPattern); - return $this->siteSchema->getUri() . $path; + return $this->siteSchema->getPath() . $path; } /** @@ -70,4 +70,11 @@ public function getProperties() { return $properties; } + public function getTermIds() { + return array( + 'schema_id' => $this->siteSchema->getId(), + 'entity_type' => $this->entityType, + ); + } + } diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/FieldSchema.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/FieldSchema.php new file mode 100644 index 0000000..e7c9425 --- /dev/null +++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/FieldSchema.php @@ -0,0 +1,105 @@ +entityType = $entity_type; + $this->bundle = $bundle; + $this->fieldName = $field_name; + } + + /** + * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getGraph(). + * + * @todo Loop through all fields and add their RDF descriptions. + */ + public function getGraph() { + // @todo Implement this. + } + + /** + * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getPath(). + */ + public function getPath() { + return $this->siteSchema->getPath() . $this->prepareUriPattern(); + } + + protected function prepareUriPattern() { + return str_replace(array('{entity_type}', '{bundle}', '{field_name}'), array($this->entityType, $this->bundle, $this->fieldName), static::$uriPattern); + } + + /** + * Overrides \Drupal\rdf\SiteSchema\SchemaTermBase::getProperties(). + */ + public function getProperties() { + $properties = parent::getProperties(); + $properties[RdfConstants::RDF_TYPE] = RdfConstants::RDF_PROPERTY; + return $properties; + } + + public function getTermIds() { + return array( + 'schema_id' => $this->siteSchema->getId(), + 'entity_type' => $this->entityType, + 'bundle' => $this->bundle, + 'field_name' => $this->fieldName, + ); + } + +} diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermBase.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermBase.php index d7afd1c..86ecf19 100644 --- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermBase.php +++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermBase.php @@ -28,6 +28,8 @@ */ protected $siteSchema; + protected $configPrefix; + /** * Constructor. * @@ -39,6 +41,17 @@ public function __construct($site_schema) { } /** + * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getUri(). + */ + function getUri() { + return url($this->getPath(), array('absolute' => TRUE)); + } + + public function getCurie() { + return $this->siteSchema->getPrefix() . ':' . $this->prepareUriPattern(); + } + + /** * Implements \Drupal\rdf\SiteSchema\SchemaTermInterface::getProperties(). */ public function getProperties() { @@ -47,4 +60,31 @@ public function getProperties() { ); } + /** + * Get the mapping config object. + * + * Each site schema term can be mapped to external RDF terms. This config + * entity stores such mappings. + * + * @return \Drupal\Core\Config\Config + * The mapping config object corresponding to this site schema term + */ + public function getMappingConfig() { + $config_id = implode('.', $this->getTermIds()); + $config = config($this->configPrefix . '.' . $config_id); + + // If the entity type isn't set, then the config didn't exist before and the + // config factory created a new config entity. Fill in the keys and save it. + $entity_type = $config->get('entity_type'); + if (empty($entity_type)) { + $config->set('mid', $config_id); + foreach ($this->getTermIds() as $key => $value) { + $config->set($key, $value); + } + $config->save(); + } + + return $config; + } + } diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermInterface.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermInterface.php index 342b51e..239126e 100644 --- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermInterface.php +++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermInterface.php @@ -41,4 +41,15 @@ public function getProperties(); * The URI of the term. */ public function getUri(); + + /** + * Get the relative path of the term. + * + * Implementations of this method will use the URI patterns defined in + * $uriPattern static variables and replace placeholders with actual values. + * + * @return string + * The relative path of the term. + */ + public function getPath(); } diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchema.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchema.php index e3153d1..dbb36d1 100644 --- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchema.php +++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchema.php @@ -19,8 +19,8 @@ class SiteSchema { // are not intended to be extensible. If a site wants to use external // vocabulary terms, the appropriate way to do this is to use the RDF mapping // system. - const CONTENT_DEPLOYMENT = 'site-schema/content-deployment/'; - const SYNDICATION = 'site-schema/syndication/'; + const CONTENT_DEPLOYMENT = 'content-deployment'; + const SYNDICATION = 'syndication'; /** * The relative base path of the instantiated schema. @@ -29,20 +29,26 @@ class SiteSchema { */ protected $schemaPath; + protected $schemaId; + + protected $prefix; + /** * Constructor. * - * @param string $schema_path + * @param string $schema_id * The schema path constant, used to determine which schema to instantiate. * * @throws \UnexpectedValueException */ - public function __construct($schema_path) { + public function __construct($schema_id) { $valid_paths = array(self::CONTENT_DEPLOYMENT, self::SYNDICATION); - if (!in_array($schema_path, $valid_paths)) { - throw new \UnexpectedValueException(sprintf('%s is not a valid site schema path. Schema path must be one of %s.'), $schema_path, implode(', ', $valid_paths)); + if (!in_array($schema_id, $valid_paths)) { + throw new \UnexpectedValueException(sprintf('%s is not a valid site schema path. Schema path must be one of %s.'), $schema_id, implode(', ', $valid_paths)); } - $this->schemaPath = $schema_path; + $this->schemaId = $schema_id; + $this->schemaPath = 'site-schema/' . $schema_id . '/'; + $this->prefix = ($schema_id == self::CONTENT_DEPLOYMENT) ? 'site-cd' : 'site-syn'; } /** @@ -60,6 +66,13 @@ public function bundle($entity_type, $bundle) { } /** + * Get a field instance's term definition in this vocabulary. + */ + public function field($entity_type, $bundle, $field_name) { + return new FieldSchema($this, $entity_type, $bundle, $field_name); + } + + /** * Get the URI of the schema. * * @return string @@ -69,6 +82,10 @@ public function getUri() { return url($this->schemaPath, array('absolute' => TRUE)); } + public function getId() { + return $this->schemaId; + } + /** * Get the relative base path of the schema. */ @@ -76,6 +93,10 @@ public function getPath() { return $this->schemaPath; } + public function getPrefix() { + return $this->prefix; + } + /** * Get the routes for the types of terms defined in this schema. * diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchemaManager.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchemaManager.php index e42b868..a89c417 100644 --- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchemaManager.php +++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchemaManager.php @@ -67,8 +67,8 @@ public function getSchemas() { return $this->siteSchemas; } - public function getSchema($schema_path) { - return $this->siteSchemas[$schema_path]; + public function getSchema($schema_id) { + return $this->siteSchemas[$schema_id]; } /** diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/CommentAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/CommentAttributesTest.php index dbdba31..1fae064 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/CommentAttributesTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/CommentAttributesTest.php @@ -48,6 +48,8 @@ public function setUp() { // Prepares commonly used URIs. $this->base_uri = url('', array('absolute' => TRUE)); $this->node_uri = url('node/' . $this->node->nid, array('absolute' => TRUE)); + + rdf_save_standard_profile_mappings(); } /** @@ -235,7 +237,12 @@ function _testBasicCommentRdfaMarkup($graph, $comment, $account = array()) { else { // The author is expected to be a blank node. $author_uri = $graph->get($comment_uri, ''); - $this->assertTrue($author_uri->isBnode(), 'Comment relation to author found in RDF output (sioc:has_creator).'); + if (!is_object($author_uri)) { + $this->fail('Comment relation to author found in RDF output (sioc:has_creator).'); + } + else { + $this->assertTrue($author_uri->isBnode(), 'Comment relation to author found in RDF output (sioc:has_creator).'); + } } // Author name. diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/CrudTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/CrudTest.php index 3383f94..7a89158 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/CrudTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/CrudTest.php @@ -7,19 +7,19 @@ namespace Drupal\rdf\Tests; -use Drupal\simpletest\WebTestBase; +use Drupal\simpletest\DrupalUnitTestBase; /** * Tests the RDF mapping CRUD functions. */ -class CrudTest extends WebTestBase { +class CrudTest extends DrupalUnitTestBase { /** * Modules to enable. * * @var array */ - public static $modules = array('rdf', 'rdf_test'); + public static $modules = array('entity_test', 'rdf', 'system'); public static function getInfo() { return array( @@ -33,53 +33,57 @@ public static function getInfo() { * Tests inserting, loading, updating, and deleting RDF mappings. */ function testCRUD() { - // Verify loading of a default mapping. - $mapping = _rdf_mapping_load('test_entity', 'test_bundle'); - $this->assertTrue(count($mapping), 'Default mapping was found.'); + $entity_type = $bundle = 'entity_test'; + $mapping_manager = drupal_container()->get('rdf.mapping_manager'); + $bundle_mapping_config_name = "rdf.mapping.bundle.syndication.$entity_type.$bundle"; + $user_id_mapping_config_name = "rdf.mapping.field.syndication.$entity_type.$bundle.user_id"; - // Verify saving a mapping. - $mapping = array( - 'type' => 'crud_test_entity', - 'bundle' => 'crud_test_bundle', - 'mapping' => array( - 'rdftype' => array('sioc:Post'), - 'title' => array( - 'predicates' => array('dc:title'), - ), - 'uid' => array( - 'predicates' => array('sioc:has_creator', 'dc:creator'), - 'type' => 'rel', - ), - ), - ); - $this->assertTrue(rdf_mapping_save($mapping) === SAVED_NEW, 'Mapping was saved.'); - - // Read the raw record from the {rdf_mapping} table. - $result = db_query('SELECT * FROM {rdf_mapping} WHERE type = :type AND bundle = :bundle', array(':type' => $mapping['type'], ':bundle' => $mapping['bundle'])); - $stored_mapping = $result->fetchAssoc(); - $stored_mapping['mapping'] = unserialize($stored_mapping['mapping']); - $this->assertEqual($mapping, $stored_mapping, 'Mapping was stored properly in the {rdf_mapping} table.'); + // Save bundle mapping config. + $bundle_mapping = $mapping_manager->getBundleMappingConfig($entity_type, $bundle); + $bundle_mapping->set('types', array('sioc:Post'))->save(); + // Save field mapping config. + $user_id_mapping = $mapping_manager->getFieldMappingConfig($entity_type, $bundle, 'user_id'); + $user_id_mapping->set('properties', array('sioc:has_creator', 'dc:creator'))->save(); - // Verify loading of saved mapping. - $this->assertEqual($mapping['mapping'], _rdf_mapping_load($mapping['type'], $mapping['bundle']), 'Saved mapping loaded successfully.'); + // Test that config files were saved. + $bundle_mapping_configs = config_get_storage_names_with_prefix('rdf.mapping.bundle'); + $this->assertTrue(in_array($bundle_mapping_config_name, $bundle_mapping_configs), 'Bundle mapping config saved.'); + $field_mapping_configs = config_get_storage_names_with_prefix('rdf.mapping.field'); + $this->assertTrue(in_array($user_id_mapping_config_name, $field_mapping_configs), 'Field mapping config saved.'); - // Verify updating of mapping. - $mapping['mapping']['title'] = array( - 'predicates' => array('dc2:bar2'), - ); - $this->assertTrue(rdf_mapping_save($mapping) === SAVED_UPDATED, 'Mapping was updated.'); + // Test that config can be loaded. + $loaded_bundle_config = $mapping_manager->getBundleMappingConfig($entity_type, $bundle); + $bundle_config_array = $loaded_bundle_config->get(); + $this->assertTrue(!empty($bundle_config_array), 'Bundle mapping config loaded.'); + $loaded_field_config = $mapping_manager->getFieldMappingConfig($entity_type, $bundle, 'user_id'); + $field_config_array = $loaded_field_config->get(); + $this->assertTrue(!empty($field_config_array), 'Field mapping config loaded.'); + // Test that the proper IDs were set. + $this->assertEqual($bundle_config_array['mid'], "syndication.$entity_type.$bundle", 'Bundle mapping config has correct id.'); + $this->assertEqual($field_config_array['mid'], "syndication.$entity_type.$bundle.user_id", 'Field mapping config has correct id.'); - // Read the raw record from the {rdf_mapping} table. - $result = db_query('SELECT * FROM {rdf_mapping} WHERE type = :type AND bundle = :bundle', array(':type' => $mapping['type'], ':bundle' => $mapping['bundle'])); - $stored_mapping = $result->fetchAssoc(); - $stored_mapping['mapping'] = unserialize($stored_mapping['mapping']); - $this->assertEqual($mapping, $stored_mapping, 'Updated mapping was stored properly in the {rdf_mapping} table.'); + // Test that the values were saved correctly. + $types = config($bundle_mapping_config_name)->get('types'); + $this->assertTrue(in_array('sioc:Post', $types), 'Bundle mapping config values set properly.'); + $properties = config($user_id_mapping_config_name)->get('properties'); + $this->assertTrue(in_array('sioc:has_creator', $properties) && in_array('dc:creator', $properties), 'Field mapping config values set properly.'); - // Verify loading of saved mapping. - $this->assertEqual($mapping['mapping'], _rdf_mapping_load($mapping['type'], $mapping['bundle']), 'Saved mapping loaded successfully.'); + // Test that mapping can be updated. + $bundle_mapping->set('types', array('schema:Thing'))->save(); + $loaded_bundle_config = $mapping_manager->getBundleMappingConfig($entity_type, $bundle); + $types = $loaded_bundle_config->get('types'); + $this->assert(in_array('schema:Thing', $types) && !in_array('sioc:Post', $types), 'Bundle mapping was updated.'); + $user_id_mapping->set('properties', array('schema:author'))->save(); + $loaded_config = $mapping_manager->getFieldMappingConfig($entity_type, $bundle, 'user_id'); + $properties = $loaded_config->get('properties'); + $this->assert(in_array('schema:author', $properties) && !in_array('sioc:has_creator', $properties), 'Field mapping was updated.'); - // Verify deleting of mapping. - $this->assertTrue(rdf_mapping_delete($mapping['type'], $mapping['bundle']), 'Mapping was deleted.'); - $this->assertFalse(_rdf_mapping_load($mapping['type'], $mapping['bundle']), 'Deleted mapping is no longer found in the database.'); + // Test that the mapping can be deleted. + $bundle_mapping->delete(); + $bundle_mapping_configs = config_get_storage_names_with_prefix('rdf.mapping.bundle'); + $this->assertFalse(in_array($bundle_mapping_config_name, $bundle_mapping_configs), 'Bundle mapping config deleted.'); + $user_id_mapping->delete(); + $field_mapping_configs = config_get_storage_names_with_prefix('rdf.mapping.field'); + $this->assertFalse(in_array($user_id_mapping_config_name, $field_mapping_configs), 'Field mapping config deleted.'); } } diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/FileFieldAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/FileFieldAttributesTest.php new file mode 100644 index 0000000..a996b34 --- /dev/null +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/FileFieldAttributesTest.php @@ -0,0 +1,104 @@ + 'RDFa markup for files', + 'description' => 'Tests the RDFa markup of filefields.', + 'group' => 'RDF', + ); + } + + public function setUp() { + parent::setUp(); + $this->field_name = strtolower($this->randomName()); + + $this->mappingManager = RdfTestHelper::getMockMappingManager(); + + $type_name = 'article'; + $field_settings = array( + 'display_field' => '1', + 'display_default' => '1', + ); + $instance_settings = array( + 'description_field' => '1', + ); + $widget_settings = array(); + $this->createFileField($this->field_name, $type_name, $field_settings, $instance_settings, $widget_settings); + + // Set the teaser display to show this field. + entity_get_display('node', 'article', 'teaser') + ->setComponent($this->field_name, array( + 'type' => 'file_default', + )) + ->save(); + + // Set the RDF mapping for the new field. + $config = $this->mappingManager->getFieldMappingConfig('node', 'article', $this->field_name); + $field_mapping = array_merge($config->get(), array('properties' => array('rdfs:seeAlso'), 'mapping_type' => 'rel')); + $config->setData($field_mapping)->save(); + + $test_file = $this->getTestFile('text'); + + // Create a new node with the uploaded file. + $nid = $this->uploadNodeFile($test_file, $this->field_name, $type_name); + + $this->node = node_load($nid, TRUE); + $this->file = file_load($this->node->{$this->field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['fid']); + + } + + /** + * Tests if file fields in teasers have correct resources. + * + * Ensure that file fields have the correct resource as the object in RDFa + * when displayed as a teaser. + */ + function testNodeTeaser() { + $node = $this->node; + $node_file = $this->file; + + // Render the teaser. + $node_render_array = entity_view_multiple(array($node), 'teaser'); + $html = drupal_render($node_render_array); + + // Parses front page where the node is displayed in its teaser form. + $parser = new \EasyRdf_Parser_Rdfa(); + $graph = new \EasyRdf_Graph(); + $base_uri = url('', array('absolute' => TRUE)); + $parser->parse($graph, $html, 'rdfa', $base_uri); + + $node_uri = url('node/' . $node->nid, array('absolute' => TRUE)); + $file_uri = file_create_url($node_file->uri); + + // Node relation to attached file. + $expected_value = array( + 'type' => 'uri', + 'value' => $file_uri, + ); + $this->assertTrue($graph->hasProperty($node_uri, 'http://www.w3.org/2000/01/rdf-schema#seeAlso', $expected_value), 'Node to file relation found in RDF output (rdfs:seeAlso).'); + $this->drupalGet('node'); + } +} diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/GetNamespacesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/GetNamespacesTest.php index a133230..0457ee4 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/GetNamespacesTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/GetNamespacesTest.php @@ -19,7 +19,7 @@ class GetNamespacesTest extends WebTestBase { * * @var array */ - public static $modules = array('rdf', 'rdf_test'); + public static $modules = array('rdf', 'rdf_test_namespaces'); public static function getInfo() { return array( diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/GetRdfNamespacesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/GetRdfNamespacesTest.php index 362a416..b2e881c 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/GetRdfNamespacesTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/GetRdfNamespacesTest.php @@ -19,7 +19,7 @@ class GetRdfNamespacesTest extends WebTestBase { * * @var array */ - public static $modules = array('rdf', 'rdf_test'); + public static $modules = array('rdf', 'rdf_test_namespaces'); public static function getInfo() { return array( diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/ImageFieldAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/ImageFieldAttributesTest.php new file mode 100644 index 0000000..42e4a4f --- /dev/null +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/ImageFieldAttributesTest.php @@ -0,0 +1,102 @@ + 'RDFa markup for imagefield', + 'description' => 'Tests the RDFa markup of imagefields.', + 'group' => 'RDF', + ); + } + + public function setUp() { + parent::setUp(); + + $this->field_name = 'field_image'; + + $this->mappingManager = RdfTestHelper::getMockMappingManager(); + + // Create the image field. + $this->createImageField($this->field_name, 'article'); + + // Set the RDF mapping for the new field. + $config = $this->mappingManager->getFieldMappingConfig('node', 'article', $this->field_name); + $field_mapping = array_merge($config->get(), array('properties' => array('og:image'), 'mapping_type' => 'rel')); + $config->setData($field_mapping)->save(); + + // Get the test image that simpletest provides. + $image = current($this->drupalGetTestFiles('image')); + + // Save a node with the image. + $nid = $this->uploadNodeImage($image, $this->field_name, 'article'); + $this->node = node_load($nid); + $this->file = file_load($this->node->{$this->field_name}[Language::LANGCODE_NOT_SPECIFIED][0]['fid']); + } + + /** + * Tests that image fields in teasers have correct resources. + */ + function testNodeTeaser() { + $node = $this->node; + $node_file = $this->file; + + // Set the display options for the teaser. + $display_options = array( + 'type' => 'image', + 'settings' => array('image_style'=> 'medium', 'image_link' => 'content'), + ); + $display = entity_get_display('node', 'article', 'teaser'); + $display->setComponent($this->field_name, $display_options) + ->save(); + + // Render the teaser. + $node_render_array = node_view($node, 'teaser'); + $html = drupal_render($node_render_array); + + // Parse the teaser. + $parser = new \EasyRdf_Parser_Rdfa(); + $graph = new \EasyRdf_Graph(); + $base_uri = url('', array('absolute' => TRUE)); + $parser->parse($graph, $html, 'rdfa', $base_uri); + + // Construct the node and image URIs for testing. + $node_uri = url('node/' . $node->nid, array('absolute' => TRUE)); + $image_uri = image_style_url('medium', $node_file->uri); + + // Test relations from node to image. + $expected_value = array( + 'type' => 'uri', + 'value' => $image_uri, + ); + $this->assertTrue($graph->hasProperty($node_uri, 'http://ogp.me/ns#image', $expected_value), 'Node to file relation found in RDF output (og:image).'); + + // Test image type. + $expected_value = array( + 'type' => 'uri', + 'value' => 'http://xmlns.com/foaf/0.1/Image', + ); + $this->assertTrue($graph->hasProperty($image_uri, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', $expected_value), 'Image type found in RDF output (foaf:Image).'); + } +} diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/NodeAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/NodeAttributesTest.php index e8df665..4e2bad2 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/NodeAttributesTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/NodeAttributesTest.php @@ -19,7 +19,7 @@ class NodeAttributesTest extends NodeTestBase { * * @var array */ - public static $modules = array('rdf'); + public static $modules = array('rdf', 'rdf_test'); public static function getInfo() { return array( diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/RdfMappingEventTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfMappingEventTest.php index c099a9a..8ee12f8 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/RdfMappingEventTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfMappingEventTest.php @@ -6,13 +6,14 @@ namespace Drupal\rdf\Tests; -use Drupal\rdf\RdfMappingManager; +use Drupal\Core\Cache\DatabaseBackend; +use Drupal\rdf\Mapping\RdfMappingManager; use Drupal\rdf\EventSubscriber\MappingSubscriber; -use Drupal\rdf_test_mapping\EventSubscriber\TestMappingSubscriber; +use Drupal\rdf_test_mapping_event\EventSubscriber\TestMappingSubscriber; use Drupal\rdf\SiteSchema\BundleSchema; use Drupal\rdf\SiteSchema\SiteSchema; use Drupal\rdf\SiteSchema\SiteSchemaManager; -use Drupal\simpletest\WebTestBase; +use Drupal\simpletest\DrupalUnitTestBase; use Symfony\Component\EventDispatcher\EventDispatcher; /** @@ -20,14 +21,21 @@ * * This is implemented as a WebTest because it depends on entity info. */ -class RdfMappingEventTest extends WebTestBase { +class RdfMappingEventTest extends DrupalUnitTestBase { /** * Modules to enable. * * @var array */ - public static $modules = array('rdf', 'rdf_test_mapping', 'entity_test'); + public static $modules = array('system', 'rdf', 'rdf_test_mapping_event', 'entity_test'); + + /** + * The mock RDF mapping manager. + * + * @var \Drupal\rdf\Mapping\RdfMappingManager + */ + protected $mappingManager; public static function getInfo() { return array( @@ -38,26 +46,65 @@ public static function getInfo() { } /** - * Test that other modules can define incoming type mappings. + * Overrides \Drupal\simpletest\DrupalUnitTestBase::setUp(). */ - public function testMapInputType() { + public function setUp() { + parent::setUp(); + $this->enableModules(array('rdf', 'rdf_test_mapping_event', 'system')); + + // Set up the mock mapping manager. $dispatcher = new EventDispatcher(); $dispatcher->addSubscriber(new MappingSubscriber()); $dispatcher->addSubscriber(new TestMappingSubscriber()); - $site_schema_manager = new SiteSchemaManager($this->container->get('cache.cache')); - $mapping_manager = new RdfMappingManager($dispatcher, $site_schema_manager); + $site_schema_manager = new SiteSchemaManager(new DatabaseBackend('cache')); + $this->mappingManager = new RdfMappingManager($dispatcher, $site_schema_manager); + } + /** + * Test that other modules can define incoming type mappings. + */ + public function testMapTypesFromInput() { // Test that a site schema URI is mapped to itself. This is the default // behavior. $schema = new SiteSchema(SiteSchema::CONTENT_DEPLOYMENT); $bundle_schema = $schema->bundle('entity_test', 'entity_test'); $site_schema_type = $bundle_schema->getUri(); - $typed_data_ids = $mapping_manager->getTypedDataIdsFromTypeUris(array($site_schema_type)); - $this->assertTrue($typed_data_ids['bundle'] == 'entity_test', 'An internal site schema type URI is properly handled.'); + $typed_data_ids = $this->mappingManager->getTypedDataIdsFromTypeUris(array($site_schema_type)); + $this->assertTrue($typed_data_ids['bundle'] == 'entity_test', 'An internal site schema type URI is properly handled on input.'); // Test that a module can map an external URI to a site schema URI. - $typed_data_ids = $mapping_manager->getTypedDataIdsFromTypeUris(array(TestMappingSubscriber::STAGING_SITE_TYPE_URI)); - $this->assertTrue($typed_data_ids['bundle'] == 'entity_test', 'Modules can map external type URIs to a site schema type.'); + $typed_data_ids = $this->mappingManager->getTypedDataIdsFromTypeUris(array(TestMappingSubscriber::STAGING_SITE_TYPE_URI)); + $this->assertTrue($typed_data_ids['bundle'] == 'entity_test', 'Modules can map external type URIs to a site schema type on input.'); + } + + /** + * Test that other modules can define output mappings for bundles. + */ + public function testMapBundleForOutput() { + $mapping = $this->mappingManager->getBundleMapping('entity_test', 'entity_test'); + + // Test that the site schema URI is automatically added in the mapping. + $schema = new SiteSchema(SiteSchema::SYNDICATION); + $bundle_schema = $schema->bundle('entity_test', 'entity_test'); + $this->assertTrue(in_array($bundle_schema->getCurie(), $mapping['types']), 'Internal site schema type included in mapping on output.'); + + // Test that a module can add types to a mapping via an event. + $this->assertTrue(in_array(TestMappingSubscriber::STAGING_SITE_TYPE_URI, $mapping['types']), 'Modules can map site schema types to external type URIs on output.'); + } + + /** + * Test that other modules can define output mappings for fields. + */ + public function testMapFieldForOutput() { + $mapping = $this->mappingManager->getFieldMapping('entity_test', 'entity_test', 'user_id'); + + // Test that the site schema URI is automatically added in the mapping. + $schema = new SiteSchema(SiteSchema::SYNDICATION); + $field_schema = $schema->field('entity_test', 'entity_test', 'user_id'); + $this->assertTrue(in_array($field_schema->getCurie(), $mapping['properties']), 'Internal site schema property included in mapping on output.'); + + // Test that a module can add types to a mapping via an event. + $this->assertTrue(in_array(TestMappingSubscriber::STAGING_SITE_PROPERTY_URI, $mapping['properties']), 'Modules can map site schema types to external property URIs on output.'); } } diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/RdfTestHelper.php b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfTestHelper.php new file mode 100644 index 0000000..b8b0f97 --- /dev/null +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfTestHelper.php @@ -0,0 +1,28 @@ +addSubscriber(new MappingSubscriber()); + $site_schema_manager = new SiteSchemaManager(new MemoryBackend('cache')); + return new RdfMappingManager($dispatcher, $site_schema_manager); + } + +} diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaAttributesTest.php new file mode 100644 index 0000000..5ebdf5e --- /dev/null +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/RdfaAttributesTest.php @@ -0,0 +1,151 @@ + 'RDFa attributes', + 'description' => 'Test RDFa attribute generation from RDF mapping.', + 'group' => 'RDF', + ); + } + + public function setUp() { + parent::setUp(); + // Set up the mock mapping manager. + $dispatcher = new EventDispatcher(); + $dispatcher->addSubscriber(new MappingSubscriber()); + $site_schema_manager = new SiteSchemaManager(new MemoryBackend('cache')); + $this->mappingManager = new RdfMappingManager($dispatcher, $site_schema_manager); + } + + /** + * Test attribute creation for mappings which use 'property'. + */ + function testProperty() { + $properties = array('dc:title'); + + $mapping = array( + 'properties' => $properties, + ); + $expected_attributes = array( + 'property' => $properties, + ); + + $this->_testAttributes($expected_attributes, $mapping); + } + + /** + * Test attribute creation for mappings which use 'datatype'. + */ + function testDatatype() { + $properties = array('foo:bar1'); + $datatype = 'foo:bar1type'; + + $mapping = array( + 'datatype' => $datatype, + 'properties' => $properties, + ); + $expected_attributes = array( + 'datatype' => $datatype, + 'property' => $properties, + ); + + $this->_testAttributes($expected_attributes, $mapping); + } + + /** + * Test attribute creation for mappings which override human-readable content. + */ + function testDatatypeCallback() { + $properties = array('dc:created'); + $datatype = 'xsd:dateTime'; + + $date = 1252750327; + $isoDate = date('c', $date); + + $mapping = array( + 'datatype' => $datatype, + 'properties' => $properties, + 'datatype_callback' => 'date_iso8601', + ); + $expected_attributes = array( + 'datatype' => $datatype, + 'property' => $properties, + 'content' => $isoDate, + ); + + $this->_testAttributes($expected_attributes, $mapping, $date); + } + + /** + * Test attribute creation for mappings which use 'rel'. + */ + function testRel() { + $properties = array('sioc:has_creator', 'dc:creator'); + + $mapping = array( + 'properties' => $properties, + 'mapping_type' => 'rel', + ); + $expected_attributes = array( + 'rel' => $properties, + ); + + $this->_testAttributes($expected_attributes, $mapping); + } + + /** + * Helper function to test attribute generation. + * + * @param array $field_mapping + * The field mapping to merge into the RDF mapping config. + * @param array $expected_attributes + * The expected return of rdf_rdfa_attributes. + */ + protected function _testAttributes($expected_attributes, $field_mapping, $data = NULL) { + $this->saveMapping($field_mapping); + + $mapping = $this->mappingManager->getFieldMapping('node', 'article', 'field_test'); + $attributes = rdf_rdfa_attributes($mapping, $data); + ksort($expected_attributes); + ksort($attributes); + $this->assertEqual($expected_attributes, $attributes); + } + + /** + * Helper function to save mapping using config system. + * + * @param array $field_mapping + * The field mapping. + */ + protected function saveMapping($field_mapping) { + $config = $this->mappingManager->getFieldMappingConfig('node', 'article', 'field_test'); + $field_mapping = array_merge($config->get(), $field_mapping); + $config->setData($field_mapping)->save(); + } +} diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/SiteSchemaTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/SiteSchemaTest.php index 0cfe07f..b8d20ce 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/SiteSchemaTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/SiteSchemaTest.php @@ -44,7 +44,7 @@ function testSiteSchema() { $bundle_uri = url("$schema_path$entity_type/$bundle", array('absolute' => TRUE)); $bundle_properties = array( 'http://www.w3.org/2000/01/rdf-schema#isDefinedBy' => url($schema_path, array('absolute' => TRUE)), - 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' => 'http://www.w3.org/2000/01/rdf-schema#class', + 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' => 'http://www.w3.org/2000/01/rdf-schema#Class', 'http://www.w3.org/2000/01/rdf-schema#subClassOf' => url("$schema_path$entity_type", array('absolute' => TRUE)), ); diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyAttributesTest.php index 4b26a04..f304288 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyAttributesTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyAttributesTest.php @@ -29,6 +29,32 @@ public static function getInfo() { ); } + function setUp() { + parent::setUp(); + + // Create a default vocabulary named "Tags", enabled for the 'article' content type. + $description = st('Use tags to group articles on similar topics into categories.'); + $help = st('Enter a comma-separated list of words to describe your content.'); + $vocabulary = entity_create('taxonomy_vocabulary', array( + 'name' => st('Tags'), + 'description' => $description, + 'vid' => 'tags', + 'langcode' => language_default()->langcode, + 'help' => $help, + )); + taxonomy_vocabulary_save($vocabulary); + + // Configure RDF mapping for Tags bundle. + $rdf_mapping_manager = drupal_container()->get('rdf.mapping_manager'); + $config = $rdf_mapping_manager->getBundleMappingConfig('taxonomy_term', 'tags'); + $bundle_mapping = array_merge($config->get(), array('types' => array('skos:Concept'))); + $config->setData($bundle_mapping)->save(); + // Configure RDF mapping for name field on Tags bundle. + $config = $rdf_mapping_manager->getFieldMappingConfig('taxonomy_term', 'tags', 'name'); + $field_mapping = array_merge($config->get(), array('properties' => array('rdfs:label', 'skos:prefLabel'))); + $config->setData($field_mapping)->save(); + } + /** * Creates a random term and ensures the RDF output is correct. */ diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyTermFieldAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyTermFieldAttributesTest.php new file mode 100644 index 0000000..1eb373b --- /dev/null +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyTermFieldAttributesTest.php @@ -0,0 +1,189 @@ + 'RDFa markup for taxonomy term fields', + 'description' => 'Tests the RDFa markup of taxonomy term fields.', + 'group' => 'RDF', + ); + } + + public function setUp() { + parent::setUp(); + + $this->mappingManager = RdfTestHelper::getMockMappingManager(); + + $web_user = $this->drupalCreateUser(array('bypass node access', 'administer taxonomy')); + $this->drupalLogin($web_user); + $this->vocabulary = $this->createVocabulary(); + + // Setup a field and instance. + $this->field_name = 'field_taxonomy_test'; + + // Create the field. + $this->createTaxonomyTermReferenceField($this->field_name, $this->vocabulary); + + // Set the RDF mapping for the new field. + $config = $this->mappingManager->getFieldMappingConfig('node', 'article', $this->field_name); + $field_mapping = array_merge($config->get(), array('properties' => array('dc:subject'), 'mapping_type' => 'rel')); + $config->setData($field_mapping)->save(); + + // Set RDF mapping for term bundle. + $config = $this->mappingManager->getBundleMappingConfig('taxonomy_term', $this->vocabulary->id()); + $bundle_mapping = array_merge($config->get(), array('types' => array('skos:Concept'))); + $config->setData($bundle_mapping)->save(); + // Set RDF mapping for name field on term bundle. + $config = $this->mappingManager->getFieldMappingConfig('taxonomy_term', $this->vocabulary->id(), 'name'); + $field_mapping = array_merge($config->get(), array('properties' => array('rdfs:label'))); + $config->setData($field_mapping)->save(); + + } + + /** + * Tests if file fields in teasers have correct resources. + * + * Ensure that file fields have the correct resource as the object in RDFa + * when displayed as a teaser. + */ + function testNodeTeaser() { + // Set the teaser display to show this field. + entity_get_display('node', 'article', 'teaser') + ->setComponent($this->field_name, array( + 'type' => 'taxonomy_term_reference_link', + )) + ->save(); + + // Create a term in each vocabulary. + $term1 = $this->createTerm($this->vocabulary); + $term2 = $this->createTerm($this->vocabulary); + $taxonomy_term_1_uri = url('taxonomy/term/' . $term1->id(), array('absolute' => TRUE)); + $taxonomy_term_2_uri = url('taxonomy/term/' . $term2->id(), array('absolute' => TRUE)); + + // Submit an entity with both terms. + $langcode = Language::LANGCODE_NOT_SPECIFIED; + $this->drupalGet('node/add/article'); + $this->assertFieldByName("{$this->field_name}[$langcode][]", '', 'Widget is displayed'); + $edit = array( + "title" => $this->randomName(), + "{$this->field_name}[$langcode][]" => array($term1->id(), $term2->id()), + ); + $this->drupalPost(NULL, $edit, t('Save')); + preg_match('|node/(\d+)|', $this->url, $match); + $id = $match[1]; + + // Render the teaser. + $node = node_load($id); + $node_uri = url('node/' . $node->nid, array('absolute' => TRUE)); + $node_render_array = entity_view_multiple(array($node), 'teaser'); + $html = drupal_render($node_render_array); + + // Parse the teaser. + $parser = new \EasyRdf_Parser_Rdfa(); + $graph = new \EasyRdf_Graph(); + $base_uri = url('', array('absolute' => TRUE)); + $parser->parse($graph, $html, 'rdfa', $base_uri); + + // Node relations to taxonomy terms. + $expected_value = array( + 'type' => 'uri', + 'value' => $taxonomy_term_1_uri, + ); + $this->assertTrue($graph->hasProperty($node_uri, 'http://purl.org/dc/terms/subject', $expected_value), 'Node to term relation found in RDF output (dc:subject).'); + $expected_value = array( + 'type' => 'uri', + 'value' => $taxonomy_term_2_uri, + ); + $this->assertTrue($graph->hasProperty($node_uri, 'http://purl.org/dc/terms/subject', $expected_value), 'Node to term relation found in RDF output (dc:subject).'); + // Taxonomy terms triples. + // Term 1. + $expected_value = array( + 'type' => 'uri', + 'value' => 'http://www.w3.org/2004/02/skos/core#Concept', + ); + $this->assertTrue($graph->hasProperty($taxonomy_term_1_uri, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', $expected_value), 'Taxonomy term type found in RDF output (skos:Concept).'); + $expected_value = array( + 'type' => 'literal', + 'value' => $term1->label(), + 'lang' => 'en', + ); + $this->assertTrue($graph->hasProperty($taxonomy_term_1_uri, 'http://www.w3.org/2000/01/rdf-schema#label', $expected_value), 'Taxonomy term name found in RDF output (rdfs:label).'); + // Term 2. + $expected_value = array( + 'type' => 'uri', + 'value' => 'http://www.w3.org/2004/02/skos/core#Concept', + ); + $this->assertTrue($graph->hasProperty($taxonomy_term_2_uri, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', $expected_value), 'Taxonomy term type found in RDF output (skos:Concept).'); + $expected_value = array( + 'type' => 'literal', + 'value' => $term2->label(), + 'lang' => 'en', + ); + $this->assertTrue($graph->hasProperty($taxonomy_term_2_uri, 'http://www.w3.org/2000/01/rdf-schema#label', $expected_value), 'Taxonomy term name found in RDF output (rdfs:label).'); + } + + /** + * Create the taxonomy term reference field for testing. + * + * @param $field_name + * The name of the field to create. + * @param $vocabulary + * The vocabulary that the field should use. + * + * @todo Move this to TaxonomyTestBase, like the other field modules. + */ + protected function createTaxonomyTermReferenceField($field_name, $vocabulary) { + $field = array( + 'field_name' => $field_name, + 'type' => 'taxonomy_term_reference', + 'cardinality' => FIELD_CARDINALITY_UNLIMITED, + 'settings' => array( + 'allowed_values' => array( + array( + 'vocabulary' => $vocabulary->id(), + 'parent' => '0', + ), + ), + ) + ); + field_create_field($field); + $instance = array( + 'field_name' => $field_name, + 'entity_type' => 'node', + 'bundle' => 'article', + ); + field_create_instance($instance); + entity_get_form_display('node', 'article', 'default') + ->setComponent($field_name, array( + 'type' => 'options_select', + )) + ->save(); + entity_get_display('node', 'article', 'full') + ->setComponent($field_name, array( + 'type' => 'taxonomy_term_reference_link', + )) + ->save(); + } +} diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/TrackerAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/TrackerAttributesTest.php index 0e6e4bb..6bc9a52 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/TrackerAttributesTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/TrackerAttributesTest.php @@ -37,6 +37,54 @@ function setUp() { // Creates article content type. $this->drupalCreateContentType(array('type' => 'article', 'name' => t('Article'))); + // Set bundle RDF mapping config for article. + $rdf_mapping_manager = drupal_container()->get('rdf.mapping_manager'); + $config = $rdf_mapping_manager->getBundleMappingConfig('node', 'article'); + $bundle_mapping = array_merge($config->get(), array('types' => array('foaf:Document', 'sioc:Item'))); + $config->setData($bundle_mapping)->save(); + + // Set fields RDF mapping config for article. + $node_shared_field_mappings = array( + 'title' => array( + 'properties' => array('dc:title'), + ), + 'created' => array( + 'properties' => array('dc:date', 'dc:created'), + 'datatype' => 'xsd:dateTime', + 'datatype_callback' => 'date_iso8601', + ), + 'changed' => array( + 'properties' => array('dc:modified'), + 'datatype' => 'xsd:dateTime', + 'datatype_callback' => 'date_iso8601', + ), + 'body' => array( + 'properties' => array('content:encoded'), + ), + 'uid' => array( + 'properties' => array('sioc:has_creator'), + 'mapping_type' => 'rel', + ), + 'name' => array( + 'properties' => array('foaf:name'), + ), + 'comment_count' => array( + 'properties' => array('sioc:num_replies'), + 'datatype' => 'xsd:integer', + ), + 'last_activity' => array( + 'properties' => array('sioc:last_activity_date'), + 'datatype' => 'xsd:dateTime', + 'datatype_callback' => 'date_iso8601', + ), + ); + // Iterate over field mappings and save. + foreach ($node_shared_field_mappings as $field_name => $field_mapping) { + $config = $rdf_mapping_manager->getFieldMappingConfig('node', 'article', $field_name); + $field_mapping = array_merge($config->get(), $field_mapping); + $config->setData($field_mapping)->save(); + } + // Enables anonymous posting of content. user_role_change_permissions(DRUPAL_ANONYMOUS_RID, array( 'create article content' => TRUE, diff --git a/core/modules/rdf/rdf.api.php b/core/modules/rdf/rdf.api.php index b3f95ba..a1a7c18 100644 --- a/core/modules/rdf/rdf.api.php +++ b/core/modules/rdf/rdf.api.php @@ -68,11 +68,11 @@ function hook_rdf_mapping() { ), 'pid' => array( 'predicates' => array('sioc:reply_of'), - 'type' => 'rel', + 'mapping_type' => 'rel', ), 'uid' => array( 'predicates' => array('sioc:has_creator'), - 'type' => 'rel', + 'mapping_type' => 'rel', ), 'name' => array( 'predicates' => array('foaf:name'), diff --git a/core/modules/rdf/rdf.install b/core/modules/rdf/rdf.install index 10d3f8d..be2cde1 100644 --- a/core/modules/rdf/rdf.install +++ b/core/modules/rdf/rdf.install @@ -4,46 +4,3 @@ * @file * Install, update and uninstall functions for the rdf module. */ - -/** - * Implements hook_schema(). - */ -function rdf_schema() { - $schema['rdf_mapping'] = array( - 'description' => 'Stores custom RDF mappings for user defined content types or overriden module-defined mappings', - 'fields' => array( - 'type' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => TRUE, - 'description' => 'The name of the entity type a mapping applies to (node, user, comment, etc.).', - ), - 'bundle' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => TRUE, - 'description' => 'The name of the bundle a mapping applies to.', - ), - 'mapping' => array( - 'description' => 'The serialized mapping of the bundle type and fields to RDF terms.', - 'type' => 'blob', - 'not null' => FALSE, - 'size' => 'big', - 'serialize' => TRUE, - ), - ), - 'primary key' => array('type', 'bundle'), - ); - - return $schema; -} - -/** - * Implements hook_install(). - */ -function rdf_install() { - // Collect any RDF mappings that were declared by modules installed before - // this one. - $modules = module_implements('rdf_mapping'); - rdf_modules_installed($modules); -} diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module index aaea6bb..22ed7af 100644 --- a/core/modules/rdf/rdf.module +++ b/core/modules/rdf/rdf.module @@ -43,23 +43,23 @@ function rdf_help($path, $arg) { * 'type' => 'node', * 'bundle' => RDF_DEFAULT_BUNDLE, * 'mapping' => array( - * 'rdftype' => array('sioc:Item', 'foaf:Document'), + * 'types' => array('sioc:Item', 'foaf:Document'), * 'title' => array( - * 'predicates' => array('dc:title'), + * 'properties' => array('dc:title'), * ), * 'created' => array( - * 'predicates' => array('dc:date', 'dc:created'), + * 'properties' => array('dc:date', 'dc:created'), * 'datatype' => 'xsd:dateTime', - * 'callback' => 'date_iso8601', + * 'datatype_callback' => 'date_iso8601', * ), * 'body' => array( - * 'predicates' => array('content:encoded'), + * 'properties' => array('content:encoded'), * ), * 'uid' => array( - * 'predicates' => array('sioc:has_creator'), + * 'properties' => array('sioc:has_creator'), * ), * 'name' => array( - * 'predicates' => array('foaf:name'), + * 'properties' => array('foaf:name'), * ), * ), * ); @@ -67,16 +67,6 @@ function rdf_help($path, $arg) { */ /** - * RDF bundle flag: Default bundle. - * - * Implementations of hook_rdf_mapping() should use this constant for the - * 'bundle' key when defining a default set of RDF mappings for an entity type. - * Each bundle will inherit the default mappings defined for the entity type - * unless the bundle defines its own specific mappings. - */ -const RDF_DEFAULT_BUNDLE = ''; - -/** * Implements hook_rdf_namespaces(). */ function rdf_rdf_namespaces() { @@ -113,167 +103,15 @@ function rdf_get_namespaces() { } /** - * Returns the mapping for attributes of a given entity type/bundle pair. - * - * @param $type - * An entity type. - * @param $bundle - * (optional) A bundle name. - * - * @return - * The mapping corresponding to the requested entity type/bundle pair or an - * empty array. - */ -function rdf_mapping_load($type, $bundle = RDF_DEFAULT_BUNDLE) { - // Retrieves the bundle-specific mapping from the entity info. - $bundles = entity_get_bundles($type); - if (!empty($bundles[$bundle]['rdf_mapping'])) { - return $bundles[$bundle]['rdf_mapping']; - } - // If there is no mapping defined for this bundle, we return the default - // mapping that is defined for this entity type. - else { - return _rdf_get_default_mapping($type); - } -} - -/** * @} End of "defgroup rdf". */ /** - * Gets the default RDF mapping for a given entity type. - * - * @param $type - * An entity type, e.g. 'node' or 'comment'. - * - * @return - * The RDF mapping or an empty array if no mapping is defined for this entity - * type. - */ -function _rdf_get_default_mapping($type) { - $default_mappings = &drupal_static(__FUNCTION__); - - if (!isset($default_mappings)) { - // Get all of the modules that implement hook_rdf_mapping(). - $modules = module_implements('rdf_mapping'); - - // Only consider the default entity mapping definitions. - foreach ($modules as $module) { - $mappings = module_invoke($module, 'rdf_mapping'); - foreach ($mappings as $mapping) { - if ($mapping['bundle'] === RDF_DEFAULT_BUNDLE) { - $default_mappings[$mapping['type']] = $mapping['mapping']; - } - } - } - } - - return isset($default_mappings[$type]) ? $default_mappings[$type] : array(); -} - -/** - * Retrieves an RDF mapping from the database. - * - * @param $type - * The entity type the mapping refers to. - * @param $bundle - * The bundle the mapping refers to. - * - * @return - * An RDF mapping structure or, FALSE if the mapping does not exist. - */ -function _rdf_mapping_load($type, $bundle) { - $mappings = _rdf_mapping_load_multiple($type, array($bundle)); - return $mappings ? reset($mappings) : FALSE; -} - -/** - * Helper function to retrieve a set of RDF mappings from the database. - * - * @param $type - * The entity type of the mappings. - * @param $bundles - * The bundles the mappings refer to. - * - * @return - * An array of RDF mapping structures, or an empty array. - */ -function _rdf_mapping_load_multiple($type, array $bundles) { - $mappings = db_select('rdf_mapping') - ->fields(NULL, array('bundle', 'mapping')) - ->condition('type', $type) - ->condition('bundle', $bundles) - ->execute() - ->fetchAllKeyed(); - - foreach ($mappings as $bundle => $mapping) { - $mappings[$bundle] = unserialize($mapping); - } - return $mappings; -} - -/** * @addtogroup rdf * @{ */ /** - * Saves an RDF mapping to the database. - * - * Takes a mapping structure returned by hook_rdf_mapping() implementations - * and creates or updates a record mapping for each encountered entity - * type/bundle pair. If available, adds default values for non-existent mapping - * keys. - * - * @param $mapping - * The RDF mapping to save. - * - * @return - * MergeQuery object that indicates the outcome of the operation. - */ -function rdf_mapping_save($mapping) { - // In the case where a field has a mapping defined in the default entity - // mapping, but a mapping is not specified in the bundle-specific mapping, - // then use the default mapping for that field. - $mapping['mapping'] += _rdf_get_default_mapping($mapping['type']); - - $status = db_merge('rdf_mapping') - ->key(array( - 'type' => $mapping['type'], - 'bundle' => $mapping['bundle'], - )) - ->fields(array( - 'mapping' => serialize($mapping['mapping']), - )) - ->execute(); - - entity_info_cache_clear(); - - return $status; -} - -/** - * Deletes the mapping for the given bundle from the database. - * - * @param $type - * The entity type the mapping refers to. - * @param $bundle - * The bundle the mapping refers to. - * - * @return - * TRUE if the mapping is deleted, FALSE if not. - */ -function rdf_mapping_delete($type, $bundle) { - $num_rows = db_delete('rdf_mapping') - ->condition('type', $type) - ->condition('bundle', $bundle) - ->execute(); - - return (bool) ($num_rows > 0); -} - -/** * Builds an array of RDFa attributes for a given mapping. * * This array will typically be passed through Drupal\Core\Template\Attribute @@ -282,13 +120,13 @@ function rdf_mapping_delete($type, $bundle) { * field-specific $item_attributes variables. * * @param $mapping - * An array containing a mandatory 'predicates' key and optional 'datatype', - * 'callback' and 'type' keys. For example: + * An array containing a mandatory 'properties' key and optional 'datatype', + * 'datatype_callback' and 'type' keys. For example: * @code * array( - * 'predicates' => array('dc:created'), + * 'properties' => array('dc:created'), * 'datatype' => 'xsd:dateTime', - * 'callback' => 'date_iso8601', + * 'datatype_callback' => 'date_iso8601', * ), * ); * @endcode @@ -303,22 +141,22 @@ function rdf_mapping_delete($type, $bundle) { */ function rdf_rdfa_attributes($mapping, $data = NULL) { // The type of mapping defaults to 'property'. - $type = isset($mapping['type']) ? $mapping['type'] : 'property'; + $type = isset($mapping['mapping_type']) ? $mapping['mapping_type'] : 'property'; switch ($type) { // The mapping expresses the relationship between two resources. case 'rel': case 'rev': - $attributes[$type] = $mapping['predicates']; + $attributes[$type] = $mapping['properties']; break; // The mapping expresses the relationship between a resource and some // literal text. case 'property': - $attributes['property'] = $mapping['predicates']; + $attributes['property'] = $mapping['properties']; // Convert $data to a specific format as per the callback function. - if (isset($data) && isset($mapping['callback'])) { - $callback = $mapping['callback']; + if (isset($data) && isset($mapping['datatype_callback'])) { + $callback = $mapping['datatype_callback']; $attributes['content'] = $callback($data); } if (isset($mapping['datatype'])) { @@ -335,88 +173,18 @@ function rdf_rdfa_attributes($mapping, $data = NULL) { */ /** - * Implements hook_modules_installed(). - * - * Checks if the installed modules have any RDF mapping definitions to declare - * and stores them in the rdf_mapping table. - * - * While both default entity mappings and specific bundle mappings can be - * defined in hook_rdf_mapping(), default entity mappings are not stored in the - * database. Only overridden mappings are stored in the database. The default - * entity mappings can be overriden by specific bundle mappings which are - * stored in the database and can be altered via the RDF CRUD mapping API. - */ -function rdf_modules_installed($modules) { - foreach ($modules as $module) { - $function = $module . '_rdf_mapping'; - if (function_exists($function)) { - foreach ($function() as $mapping) { - // Only the bundle mappings are saved in the database. - if ($mapping['bundle'] !== RDF_DEFAULT_BUNDLE) { - rdf_mapping_save($mapping); - } - } - } - } -} - -/** - * Implements hook_modules_uninstalled(). - */ -function rdf_modules_uninstalled($modules) { - // @todo Remove RDF mappings of uninstalled modules. -} - -/** - * Implements hook_entity_bundle_info_alter(). - * - * Adds the proper RDF mapping to each entity type/bundle pair. - * - * @todo May need to move the comment below to another place. - * This hook should not be used by modules to alter the bundle mappings. The UI - * should always be authoritative. UI mappings are stored in the database and - * if hook_entity_info_alter() was used to override module defined mappings, it - * would override the user defined mapping as well. - * - */ -function rdf_entity_bundle_info_alter(&$bundles) { - // Loop through each entity type and its bundles. - foreach ($bundles as $entity_type => $bundle_info) { - $bundle_names = array_keys($bundle_info); - $mappings = _rdf_mapping_load_multiple($entity_type, $bundle_names); - - foreach ($bundle_names as $bundle) { - if (isset($mappings[$bundle])) { - $bundles[$entity_type][$bundle]['rdf_mapping'] = $mappings[$bundle]; - } - else { - // If no mapping was found in the database, assign the default RDF - // mapping for this entity type. - $bundles[$entity_type][$bundle]['rdf_mapping'] = _rdf_get_default_mapping($entity_type); - } - } - } -} - -/** - * Implements hook_entity_load(). - */ -function rdf_entity_load($entities, $type) { - foreach ($entities as $entity) { - // Extracts the bundle of the entity being loaded. - $entity->rdf_mapping = rdf_mapping_load($type, $entity->bundle()); - } -} - -/** * Implements hook_comment_load(). + * + * @todo refactor to take advantage of the new Drupal 8 APIs. */ function rdf_comment_load($comments) { foreach ($comments as $comment) { // Pages with many comments can show poor performance. This information // isn't needed until rdf_preprocess_comment() is called, but set it here // to optimize performance for websites that implement an entity cache. - $comment->rdf_data['date'] = rdf_rdfa_attributes($comment->rdf_mapping['created'], $comment->created->value); + $mapping_manager = drupal_container()->get('rdf.mapping_manager'); + $created_mapping = $mapping_manager->getFieldMapping('comment', $comment->bundle(), 'created'); + $comment->rdf_data['date'] = rdf_rdfa_attributes($created_mapping, $comment->created->value); $comment->rdf_data['nid_uri'] = url('node/' . $comment->nid->target_id); if ($comment->pid->target_id) { $comment->rdf_data['pid_uri'] = url('comment/' . $comment->pid->target_id, array('fragment' => 'comment-' . $comment->pid->target_id)); @@ -499,14 +267,18 @@ function rdf_preprocess_node(&$variables) { // URI of the resource described within the HTML element, while the @typeof // attribute indicates its RDF type (e.g., foaf:Document, sioc:Person, and so // on.) + $mapping_manager = drupal_container()->get('rdf.mapping_manager'); + $bundle = $variables['node']->bundle(); + $bundle_mapping = $mapping_manager->getBundleMapping('node', $bundle); $variables['attributes']['about'] = empty($variables['node_url']) ? NULL: $variables['node_url']; - $variables['attributes']['typeof'] = empty($variables['node']->rdf_mapping['rdftype']) ? NULL : $variables['node']->rdf_mapping['rdftype']; + $variables['attributes']['typeof'] = empty($bundle_mapping['types']) ? NULL : $bundle_mapping['types']; // Adds RDFa markup to the title of the node. Because the RDFa markup is // added to the

tag which might contain HTML code, we specify an empty // datatype to ensure the value of the title read by the RDFa parsers is a // literal. - $variables['title_attributes']['property'] = empty($variables['node']->rdf_mapping['title']['predicates']) ? NULL : $variables['node']->rdf_mapping['title']['predicates']; + $title_mapping = $mapping_manager->getFieldMapping('node', $bundle, 'title'); + $variables['title_attributes']['property'] = empty($title_mapping['properties']) ? NULL : $title_mapping['properties']; $variables['title_attributes']['datatype'] = ''; // In full node mode, the title is not displayed by node.html.twig so it is @@ -519,35 +291,40 @@ function rdf_preprocess_node(&$variables) { 'about' => $variables['node_url'], ), ); - if (!empty($variables['node']->rdf_mapping['title']['predicates'])) { - $element['#attributes']['property'] = $variables['node']->rdf_mapping['title']['predicates']; + if (!empty($title_mapping['properties'])) { + $element['#attributes']['property'] = $title_mapping['properties']; } drupal_add_html_head($element, 'rdf_node_title'); } // Adds RDFa markup for the date. - if (!empty($variables['node']->rdf_mapping['created'])) { - $date_attributes = rdf_rdfa_attributes($variables['node']->rdf_mapping['created'], $variables['node']->created); + $created_mapping = $mapping_manager->getFieldMapping('node', $bundle, 'created'); + if (!empty($created_mapping)) { + $date_attributes = rdf_rdfa_attributes($created_mapping, $variables['node']->created); $variables['rdf_template_variable_attributes']['date'] = $date_attributes; if ($variables['submitted']) { $variables['rdf_template_variable_attributes']['submitted'] = $date_attributes; } } // Adds RDFa markup for the relation between the node and its author. - if (!empty($variables['node']->rdf_mapping['uid'])) { - $variables['rdf_template_variable_attributes']['name']['rel'] = $variables['node']->rdf_mapping['uid']['predicates']; + $uid_mapping = $mapping_manager->getFieldMapping('node', $bundle, 'uid'); + if (!empty($uid_mapping)) { + $variables['rdf_template_variable_attributes']['name']['rel'] = $uid_mapping['properties']; if ($variables['submitted']) { - $variables['rdf_template_variable_attributes']['submitted']['rel'] = $variables['node']->rdf_mapping['uid']['predicates']; + $variables['rdf_template_variable_attributes']['submitted']['rel'] = $uid_mapping['properties']; } } // Adds RDFa markup annotating the number of comments a node has. - if (isset($variables['node']->comment_count) && !empty($variables['node']->rdf_mapping['comment_count']['predicates'])) { + $comment_count_mapping = $mapping_manager->getFieldMapping('node', $bundle, 'comment_count'); + if (isset($variables['node']->comment_count) && !empty($comment_count_mapping['properties'])) { // Annotates the 'x comments' link in teaser view. if (isset($variables['content']['links']['comment']['#links']['comment-comments'])) { - $comment_count_attributes['property'] = $variables['node']->rdf_mapping['comment_count']['predicates']; + $comment_count_attributes['property'] = $comment_count_mapping['properties']; $comment_count_attributes['content'] = $variables['node']->comment_count; - $comment_count_attributes['datatype'] = $variables['node']->rdf_mapping['comment_count']['datatype']; + if (isset($comment_count_mapping['datatype'])) { + $comment_count_attributes['datatype'] = $comment_count_mapping['datatype']; + } // According to RDFa parsing rule number 4, a new subject URI is created // from the href attribute if no rel/rev attribute is present. To get the // original node URL from the about attribute of the parent container we @@ -564,9 +341,9 @@ function rdf_preprocess_node(&$variables) { '#tag' => 'meta', '#attributes' => array( 'about' => $variables['node_url'], - 'property' => $variables['node']->rdf_mapping['comment_count']['predicates'], + 'property' => $comment_count_mapping['properties'], 'content' => $variables['node']->comment_count, - 'datatype' => $variables['node']->rdf_mapping['comment_count']['datatype'], + 'datatype' => isset($comment_count_mapping['datatype']) ? $comment_count_mapping['datatype'] : NULL, ), ); drupal_add_html_head($element, 'rdf_node_comment_count'); @@ -578,13 +355,16 @@ function rdf_preprocess_node(&$variables) { * Implements hook_preprocess_HOOK() for field.tpl.php. */ function rdf_preprocess_field(&$variables) { + $mapping_manager = drupal_container()->get('rdf.mapping_manager'); $element = $variables['element']; - $mapping = rdf_mapping_load($element['#entity_type'], $element['#bundle']); + $entity_type = $element['#entity_type']; + $bundle = $element['#bundle']; $field_name = $element['#field_name']; + $field_mapping = $mapping_manager->getFieldMapping($entity_type, $bundle, $field_name); - if (!empty($mapping) && !empty($mapping[$field_name])) { + if (!empty($field_mapping)) { foreach ($element['#items'] as $delta => $item) { - $variables['item_attributes'][$delta] = rdf_rdfa_attributes($mapping[$field_name], $item); + $variables['item_attributes'][$delta] = rdf_rdfa_attributes($field_mapping, $item); // If this field is an image, RDFa will not output correctly when the // image is in a containing tag. If the field is a file, RDFa will // not output correctly if the filetype icon comes before the link to the @@ -608,11 +388,13 @@ function rdf_preprocess_field(&$variables) { function rdf_preprocess_user(&$variables) { $account = $variables['elements']['#user']; $uri = $account->uri(); + $mapping_manager = drupal_container()->get('rdf.mapping_manager'); + $bundle_mapping = $mapping_manager->getBundleMapping('user', 'user'); // Adds RDFa markup to the user profile page. Fields displayed in this page // will automatically describe the user. - if (!empty($account->rdf_mapping['rdftype'])) { - $variables['attributes']['typeof'] = $account->rdf_mapping['rdftype']; + if (!empty($bundle_mapping['types'])) { + $variables['attributes']['typeof'] = $bundle_mapping['types']; $variables['attributes']['about'] = url($uri['path'], $uri['options']); } // If we are on the user account page, add the relationship between the @@ -629,17 +411,20 @@ function rdf_preprocess_user(&$variables) { ); // Adds the markup for username as language neutral literal, see // rdf_preprocess_username(). - $username_meta = array( - '#tag' => 'meta', - '#attributes' => array( - 'about' => url($uri['path'], $uri['options']), - 'property' => $account->rdf_mapping['name']['predicates'], - 'content' => $account->name, - 'lang' => '', - ) - ); + $name_mapping = $mapping_manager->getFieldMapping('user', 'user', 'name'); + if (!empty($name_mapping['properties'])) { + $username_meta = array( + '#tag' => 'meta', + '#attributes' => array( + 'about' => url($uri['path'], $uri['options']), + 'property' => $name_mapping['properties'], + 'content' => $account->name, + 'lang' => '', + ) + ); + drupal_add_html_head($username_meta, 'rdf_user_username'); + } drupal_add_html_head($account_holder_meta, 'rdf_user_account_holder'); - drupal_add_html_head($username_meta, 'rdf_user_username'); } } @@ -656,16 +441,8 @@ function rdf_preprocess_username(&$variables) { $variables['attributes']['lang'] = ''; } - // $variables['account'] is a pseudo account object, and as such, does not - // contain the RDF mappings for the user. In the case of nodes and comments, - // it contains the mappings for the node or comment object instead. However, - // while the RDF mappings are available from a full user_load(), this should - // be avoided for performance reasons. Since the type and bundle for users is - // already known, call rdf_mapping_load() directly. - $rdf_mapping = rdf_mapping_load('user', 'user'); - // The profile URI is used to identify the user account. The about attribute - // is used to set the URI as the default subject of the predicates embedded + // is used to set the URI as the default subject of the properties embedded // as RDFa in the child elements. Even if the user profile is not accessible // to the current user, we use its URI in order to identify the user in RDF. // We do not use this attribute for the anonymous user because we do not have @@ -676,21 +453,24 @@ function rdf_preprocess_username(&$variables) { } $attributes = array(); - // The typeof attribute specifies the RDF type(s) of this resource. They - // are defined in the 'rdftype' property of the user RDF mapping. - if (!empty($rdf_mapping['rdftype'])) { - $attributes['typeof'] = $rdf_mapping['rdftype']; + // Add RDF type of user. + $mapping_manager = drupal_container()->get('rdf.mapping_manager'); + $bundle_mapping = $mapping_manager->getBundleMapping('user', 'user'); + if (!empty($bundle_mapping['types'])) { + $attributes['typeof'] = $bundle_mapping['types']; } // Annotate the username in RDFa. A property attribute is used with an empty // datatype attribute to ensure the username is parsed as a plain literal // in RDFa 1.0 and 1.1. - if (!empty($rdf_mapping['name'])) { - $attributes['property'] = $rdf_mapping['name']['predicates']; + $name_mapping = $mapping_manager->getFieldMapping('user', 'user', 'name'); + if (!empty($name_mapping)) { + $attributes['property'] = $name_mapping['properties']; $attributes['datatype'] = ''; } // Add the homepage RDFa markup if present. - if (!empty($variables['homepage']) && !empty($rdf_mapping['homepage'])) { - $attributes['rel'] = $rdf_mapping['homepage']['predicates']; + $homepage_mapping = $mapping_manager->getFieldMapping('user', 'user', 'homepage'); + if (!empty($variables['homepage']) && !empty($homepage_mapping)) { + $attributes['rel'] = $homepage_mapping['properties']; } // The remaining attributes can have multiple values listed, with whitespace // separating the values in the RDFa attributes @@ -705,18 +485,22 @@ function rdf_preprocess_username(&$variables) { */ function rdf_preprocess_comment(&$variables) { $comment = $variables['comment']; - if (!empty($comment->rdf_mapping['rdftype'])) { + $mapping_manager = drupal_container()->get('rdf.mapping_manager'); + $bundle_mapping = $mapping_manager->getBundleMapping('comment', $comment->bundle()); + + if (!empty($bundle_mapping['types'])) { // Adds RDFa markup to the comment container. The about attribute specifies // the URI of the resource described within the HTML element, while the // typeof attribute indicates its RDF type (e.g., sioc:Post, foaf:Document, // and so on.) $uri = $comment->uri(); $variables['attributes']['about'] = url($uri['path'], $uri['options']); - $variables['attributes']['typeof'] = $comment->rdf_mapping['rdftype']; + $variables['attributes']['typeof'] = $bundle_mapping['types']; } // Adds RDFa markup for the date of the comment. - if (!empty($comment->rdf_mapping['created'])) { + $created_mapping = $mapping_manager->getFieldMapping('comment', $comment->bundle(), 'created'); + if (!empty($created_mapping)) { // The comment date is precomputed as part of the rdf_data so that it can be // cached as part of the entity. $date_attributes = $comment->rdf_data['date']; @@ -724,24 +508,27 @@ function rdf_preprocess_comment(&$variables) { $variables['rdf_template_variable_attributes']['submitted'] = $date_attributes; } // Adds RDFa markup for the relation between the comment and its author. - if (!empty($comment->rdf_mapping['uid'])) { - $variables['rdf_template_variable_attributes']['author']['rel'] = $comment->rdf_mapping['uid']['predicates']; - $variables['rdf_template_variable_attributes']['submitted']['rel'] = $comment->rdf_mapping['uid']['predicates']; + $uid_mapping = $mapping_manager->getFieldMapping('comment', $comment->bundle(), 'uid'); + if (!empty($uid_mapping)) { + $variables['rdf_template_variable_attributes']['author']['rel'] = $uid_mapping['properties']; + $variables['rdf_template_variable_attributes']['submitted']['rel'] = $uid_mapping['properties']; } - if (!empty($comment->rdf_mapping['title'])) { + $title_mapping = $mapping_manager->getFieldMapping('comment', $comment->bundle(), 'title'); + if (!empty($title_mapping)) { // Adds RDFa markup to the subject of the comment. Because the RDFa markup // is added to an

tag which might contain HTML code, we specify an // empty datatype to ensure the value of the title read by the RDFa parsers // is a literal. - $variables['title_attributes']['property'] = $comment->rdf_mapping['title']['predicates']; + $variables['title_attributes']['property'] = $title_mapping['properties']; $variables['title_attributes']['datatype'] = ''; } // Annotates the parent relationship between the current comment and the node // it belongs to. If available, the parent comment is also annotated. - if (!empty($comment->rdf_mapping['pid'])) { + $pid_mapping = $mapping_manager->getFieldMapping('comment', $comment->bundle(), 'pid'); + if (!empty($pid_mapping)) { // Adds the relation to the parent node. - $parent_node_attributes['rel'] = $comment->rdf_mapping['pid']['predicates']; + $parent_node_attributes['rel'] = $pid_mapping['properties']; // The parent node URI is precomputed as part of the rdf_data so that it can // be cached as part of the entity. $parent_node_attributes['resource'] = $comment->rdf_data['nid_uri']; @@ -749,7 +536,7 @@ function rdf_preprocess_comment(&$variables) { // Adds the relation to parent comment, if it exists. if ($comment->pid->target_id != 0) { - $parent_comment_attributes['rel'] = $comment->rdf_mapping['pid']['predicates']; + $parent_comment_attributes['rel'] = $pid_mapping['properties']; // The parent comment URI is precomputed as part of the rdf_data so that // it can be cached as part of the entity. $parent_comment_attributes['resource'] = $comment->rdf_data['pid_uri']; @@ -762,14 +549,17 @@ function rdf_preprocess_comment(&$variables) { * Implements hook_preprocess_HOOK() for taxonomy-term.tpl.php. */ function rdf_preprocess_taxonomy_term(&$variables) { - // Adds the RDF type of the term and the term name in a tag. $term = $variables['term']; + $mapping_manager = drupal_container()->get('rdf.mapping_manager'); + $bundle_mapping = $mapping_manager->getBundleMapping('taxonomy_term', $term->vid); + $name_field_mapping = $mapping_manager->getFieldMapping('taxonomy_term', $term->vid, 'name'); + // Adds the RDF type of the term and the term name in a tag. $term_label_meta = array( '#tag' => 'meta', '#attributes' => array( 'about' => url('taxonomy/term/' . $term->id()), - 'typeof' => $term->rdf_mapping['rdftype'], - 'property' => $term->rdf_mapping['name']['predicates'], + 'typeof' => $bundle_mapping['types'], + 'property' => $name_field_mapping['properties'], 'content' => $term->label(), ), ); @@ -790,13 +580,16 @@ function rdf_field_attach_view_alter(&$output, $context) { // yet and thus have no $item['entity']. if (isset($item['entity'])) { $term = $item['entity']; - if (!empty($term->rdf_mapping['rdftype'])) { - $element[$delta]['#options']['attributes']['typeof'] = $term->rdf_mapping['rdftype']; + $mapping_manager = drupal_container()->get('rdf.mapping_manager'); + $bundle_mapping = $mapping_manager->getBundleMapping('taxonomy_term', $term->bundle()); + if (!empty($bundle_mapping['types'])) { + $element[$delta]['#options']['attributes']['typeof'] = $bundle_mapping['types']; } - if (!empty($term->rdf_mapping['name']['predicates'])) { + $name_field_mapping = $mapping_manager->getFieldMapping('taxonomy_term', $term->bundle(), 'name'); + if (!empty($name_field_mapping['properties'])) { // A property attribute is used with an empty datatype attribute so // the term name is parsed as a plain literal in RDFa 1.0 and 1.1. - $element[$delta]['#options']['attributes']['property'] = $term->rdf_mapping['name']['predicates']; + $element[$delta]['#options']['attributes']['property'] = $name_field_mapping['properties']; $element[$delta]['#options']['attributes']['datatype'] = ''; } } @@ -911,3 +704,139 @@ function theme_rdf_metadata($variables) { } return $output; } + +function rdf_save_standard_profile_mappings() { + // Save the Node RDF mapping configuration. + $rdf_mapping_manager = drupal_container()->get('rdf.mapping_manager'); + $node_shared_field_mappings = array( + 'title' => array( + 'properties' => array('dc:title'), + ), + 'created' => array( + 'properties' => array('dc:date', 'dc:created'), + 'datatype' => 'xsd:dateTime', + 'datatype_callback' => 'date_iso8601', + ), + 'changed' => array( + 'properties' => array('dc:modified'), + 'datatype' => 'xsd:dateTime', + 'datatype_callback' => 'date_iso8601', + ), + 'body' => array( + 'properties' => array('content:encoded'), + ), + 'uid' => array( + 'properties' => array('sioc:has_creator'), + 'mapping_type' => 'rel', + ), + 'name' => array( + 'properties' => array('foaf:name'), + ), + 'comment_count' => array( + 'properties' => array('sioc:num_replies'), + 'datatype' => 'xsd:integer', + ), + 'last_activity' => array( + 'properties' => array('sioc:last_activity_date'), + 'datatype' => 'xsd:dateTime', + 'datatype_callback' => 'date_iso8601', + ), + ); + // Save the bundle mapping and shared field mappings for both node bundles. + foreach (array('article', 'page') as $bundle) { + // Save bundle mapping config. + $config = $rdf_mapping_manager->getBundleMappingConfig('node', $bundle); + $bundle_mapping = array_merge($config->get(), array('types' => array('foaf:Document', 'sioc:Item'))); + $config->setData($bundle_mapping)->save(); + // Iterate over shared field mappings and save. + foreach ($node_shared_field_mappings as $field_name => $field_mapping) { + $config = $rdf_mapping_manager->getFieldMappingConfig('node', $bundle, $field_name); + $field_mapping = array_merge($config->get(), $field_mapping); + $config->setData($field_mapping)->save(); + } + } + + // Save the RDF mappings for fields which are unique to articles. + $field_image_mapping = array( + 'properties' => array('og:image', 'rdfs:seeAlso'), + 'mapping_type' => 'rel', + ); + $field_tags_mapping = array( + 'properties' => array('dc:subject'), + 'mapping_type' => 'rel', + ); + // Configure RDF mapping for field_image on Article bundle. + $config = $rdf_mapping_manager->getFieldMappingConfig('node', 'article', 'field_image'); + $field_mapping = array_merge($config->get(), $field_image_mapping); + $config->setData($field_mapping)->save(); + // Configure RDF mapping for field_tags on Article bundle. + $config = $rdf_mapping_manager->getFieldMappingConfig('node', 'article', 'field_tags'); + $field_mapping = array_merge($config->get(), $field_tags_mapping); + $config->setData($field_mapping)->save(); + + // Save comment RDF mapping configuration for article. + $rdf_mapping_manager = drupal_container()->get('rdf.mapping_manager'); + // Save bundle mapping config. + $config = $rdf_mapping_manager->getBundleMappingConfig('comment', 'comment_node_article'); + $bundle_mapping = array_merge($config->get(), array('types' => array('sioc:Post', 'sioct:Comment'))); + $config->setData($bundle_mapping)->save(); + $comment_shared_field_mappings = array( + 'title' => array( + 'properties' => array('dc:title'), + ), + 'created' => array( + 'properties' => array('dc:date', 'dc:created'), + 'datatype' => 'xsd:dateTime', + 'datatype_callback' => 'date_iso8601', + ), + 'changed' => array( + 'properties' => array('dc:modified'), + 'datatype' => 'xsd:dateTime', + 'datatype_callback' => 'date_iso8601', + ), + 'comment_body' => array( + 'properties' => array('content:encoded'), + ), + 'pid' => array( + 'properties' => array('sioc:reply_of'), + 'mapping_type' => 'rel', + ), + 'uid' => array( + 'properties' => array('sioc:has_creator'), + 'mapping_type' => 'rel', + ), + 'name' => array( + 'properties' => array('foaf:name'), + ), + ); + // Iterate over shared field mappings and save. + foreach ($comment_shared_field_mappings as $field_name => $field_mapping) { + $config = $rdf_mapping_manager->getFieldMappingConfig('comment', 'comment_node_article', $field_name); + $field_mapping = array_merge($config->get(), $field_mapping); + $config->setData($field_mapping)->save(); + } + + // Configure RDF mapping for User bundle. + $config = $rdf_mapping_manager->getBundleMappingConfig('user', 'user'); + $bundle_mapping = array_merge($config->get(), array('types' => array('sioc:UserAccount'))); + $config->setData($bundle_mapping)->save(); + // @todo Change to reflect field names once User uses real fields. + // Configure RDF mapping for name field on User bundle. + $config = $rdf_mapping_manager->getFieldMappingConfig('user', 'user', 'name'); + $field_mapping = array_merge($config->get(), array('properties' => array('foaf:name'))); + $config->setData($field_mapping)->save(); + // Configure RDF mapping for homepage field on User bundle. + $config = $rdf_mapping_manager->getFieldMappingConfig('user', 'user', 'homepage'); + $field_mapping = array_merge($config->get(), array('properties' => array('foaf:page'), 'mapping_type' => 'rel')); + $config->setData($field_mapping)->save(); + + // Configure RDF mapping for Tags bundle. + $config = $rdf_mapping_manager->getBundleMappingConfig('taxonomy_term', 'tags'); + $bundle_mapping = array_merge($config->get(), array('types' => array('skos:Concept'))); + $config->setData($bundle_mapping)->save(); + // Configure RDF mapping for name field on Tags bundle. + $config = $rdf_mapping_manager->getFieldMappingConfig('taxonomy_term', 'tags', 'name'); + $field_mapping = array_merge($config->get(), array('properties' => array('rdfs:label', 'skos:prefLabel'))); + $config->setData($field_mapping)->save(); +} + diff --git a/core/modules/rdf/rdf.services.yml b/core/modules/rdf/rdf.services.yml index 9777720..0719597 100644 --- a/core/modules/rdf/rdf.services.yml +++ b/core/modules/rdf/rdf.services.yml @@ -3,7 +3,7 @@ services: class: Drupal\rdf\SiteSchema\SiteSchemaManager arguments: ['@cache.cache'] rdf.mapping_manager: - class: Drupal\rdf\RdfMappingManager + class: Drupal\rdf\Mapping\RdfMappingManager arguments: ['@event_dispatcher', '@rdf.site_schema_manager'] rdf.mapping: class: Drupal\rdf\EventSubscriber\MappingSubscriber diff --git a/core/modules/rdf/tests/rdf_test.install b/core/modules/rdf/tests/rdf_test.install index 91a3392..9a4d76a 100644 --- a/core/modules/rdf/tests/rdf_test.install +++ b/core/modules/rdf/tests/rdf_test.install @@ -9,17 +9,108 @@ * Implements hook_install(). */ function rdf_test_install() { - $rdf_mappings = array( - array( - 'type' => 'node', - 'bundle' => 'test_bundle_hook_install', - 'mapping' => array( - 'rdftype' => array('foo:mapping_install1', 'bar:mapping_install2'), - ), + // Set bundle mapping config. + $rdf_mapping_manager = drupal_container()->get('rdf.mapping_manager'); + $config = $rdf_mapping_manager->getBundleMappingConfig('node', 'article'); + $bundle_mapping = array_merge($config->get(), array('types' => array('foaf:Document', 'sioc:Item'))); + $config->setData($bundle_mapping)->save(); + + // Set fields mapping config. + $node_shared_field_mappings = array( + 'title' => array( + 'properties' => array('dc:title'), + ), + 'created' => array( + 'properties' => array('dc:date', 'dc:created'), + 'datatype' => 'xsd:dateTime', + 'datatype_callback' => 'date_iso8601', + ), + 'changed' => array( + 'properties' => array('dc:modified'), + 'datatype' => 'xsd:dateTime', + 'datatype_callback' => 'date_iso8601', + ), + 'body' => array( + 'properties' => array('content:encoded'), + ), + 'uid' => array( + 'properties' => array('sioc:has_creator'), + 'mapping_type' => 'rel', + ), + 'name' => array( + 'properties' => array('foaf:name'), + ), + 'comment_count' => array( + 'properties' => array('sioc:num_replies'), + 'datatype' => 'xsd:integer', + ), + 'last_activity' => array( + 'properties' => array('sioc:last_activity_date'), + 'datatype' => 'xsd:dateTime', + 'datatype_callback' => 'date_iso8601', ), ); + // Iterate over field mappings and save. + foreach ($node_shared_field_mappings as $field_name => $field_mapping) { + $config = $rdf_mapping_manager->getFieldMappingConfig('node', 'article', $field_name); + $field_mapping = array_merge($config->get(), $field_mapping); + $config->setData($field_mapping)->save(); + } - foreach ($rdf_mappings as $rdf_mapping) { - rdf_mapping_save($rdf_mapping); + // Save comment RDF mapping configuration for article. + $rdf_mapping_manager = drupal_container()->get('rdf.mapping_manager'); + // Save bundle mapping config. + $config = $rdf_mapping_manager->getBundleMappingConfig('comment', 'comment_node_article'); + $bundle_mapping = array_merge($config->get(), array('types' => array('sioc:Post', 'sioct:Comment'))); + $config->setData($bundle_mapping)->save(); + $comment_shared_field_mappings = array( + 'title' => array( + 'properties' => array('dc:title'), + ), + 'created' => array( + 'properties' => array('dc:date', 'dc:created'), + 'datatype' => 'xsd:dateTime', + 'datatype_callback' => 'date_iso8601', + ), + 'changed' => array( + 'properties' => array('dc:modified'), + 'datatype' => 'xsd:dateTime', + 'datatype_callback' => 'date_iso8601', + ), + 'comment_body' => array( + 'properties' => array('content:encoded'), + ), + 'pid' => array( + 'properties' => array('sioc:reply_of'), + 'mapping_type' => 'rel', + ), + 'uid' => array( + 'properties' => array('sioc:has_creator'), + 'mapping_type' => 'rel', + ), + 'name' => array( + 'properties' => array('foaf:name'), + ), + ); + // Iterate over shared field mappings and save. + foreach ($comment_shared_field_mappings as $field_name => $field_mapping) { + $config = $rdf_mapping_manager->getFieldMappingConfig('comment', 'comment_node_article', $field_name); + $field_mapping = array_merge($config->get(), $field_mapping); + $config->setData($field_mapping)->save(); } + + // Configure RDF mapping for User bundle. + $config = $rdf_mapping_manager->getBundleMappingConfig('user', 'user'); + $bundle_mapping = array_merge($config->get(), array('types' => array('sioc:UserAccount'))); + $config->setData($bundle_mapping)->save(); + // @todo Change to reflect field names once User uses real fields. + // Configure RDF mapping for name field on User bundle. + $config = $rdf_mapping_manager->getFieldMappingConfig('user', 'user', 'name'); + $field_mapping = array_merge($config->get(), array('properties' => array('foaf:name'))); + $config->setData($field_mapping)->save(); + // Configure RDF mapping for homepage field on User bundle. + $config = $rdf_mapping_manager->getFieldMappingConfig('user', 'user', 'homepage'); + $field_mapping = array_merge($config->get(), array('properties' => array('foaf:page'), 'mapping_type' => 'rel')); + $config->setData($field_mapping)->save(); + } diff --git a/core/modules/rdf/tests/rdf_test.module b/core/modules/rdf/tests/rdf_test.module index 4d90472..b3d9bbc 100644 --- a/core/modules/rdf/tests/rdf_test.module +++ b/core/modules/rdf/tests/rdf_test.module @@ -1,59 +1 @@ 'test_entity', - 'bundle' => 'test_bundle', - 'mapping' => array( - 'rdftype' => array('sioc:Post'), - 'title' => array( - 'predicates' => array('dc:title'), - ), - 'created' => array( - 'predicates' => array('dc:created'), - 'datatype' => 'xsd:dateTime', - 'callback' => 'date_iso8601', - ), - 'uid' => array( - 'predicates' => array('sioc:has_creator', 'dc:creator'), - 'type' => 'rel', - ), - 'foobar' => array( - 'predicates' => array('foo:bar'), - ), - 'foobar1' => array( - 'datatype' => 'foo:bar1type', - 'predicates' => array('foo:bar1'), - ), - 'foobar_objproperty1' => array( - 'predicates' => array('sioc:has_creator', 'dc:creator'), - 'type' => 'rel', - ), - 'foobar_objproperty2' => array( - 'predicates' => array('sioc:reply_of'), - 'type' => 'rev', - ), - ), - ), - ); -} - -/** - * Implements hook_rdf_namespaces(). - */ -function rdf_test_rdf_namespaces() { - return array( - 'dc' => 'http://purl.org/conflicting/namespace', - 'foaf' => 'http://xmlns.com/foaf/0.1/', - 'foaf1' => 'http://xmlns.com/foaf/0.1/', - ); -} diff --git a/core/modules/rdf/tests/rdf_test_mapping_event/lib/Drupal/rdf_test_mapping_event/EventSubscriber/TestMappingSubscriber.php b/core/modules/rdf/tests/rdf_test_mapping_event/lib/Drupal/rdf_test_mapping_event/EventSubscriber/TestMappingSubscriber.php new file mode 100644 index 0000000..0a6aaf6 --- /dev/null +++ b/core/modules/rdf/tests/rdf_test_mapping_event/lib/Drupal/rdf_test_mapping_event/EventSubscriber/TestMappingSubscriber.php @@ -0,0 +1,95 @@ +getInputUris(); + $site_schema_types = $event->getSiteSchemaTypes(); + + // This mapping between an external type and a site schema type would be + // managed by something in the implementing module, such as a database + // table. For the test, manually map a fake external URI to the site schema + // URI for the test entity. + $schema = new SiteSchema(SiteSchema::CONTENT_DEPLOYMENT); + $bundle_schema = $schema->bundle('entity_test', 'entity_test'); + $site_schema_type = $bundle_schema->getUri(); + $mapping = array( + self::STAGING_SITE_TYPE_URI => $site_schema_type, + ); + + foreach ($input_uris as $input_uri) { + // If the incoming URI is mapped in the mapping array, and the value of + // that mapping is found in the cache of site schema types, then set the + // site schema URI. + if (isset($mapping[$input_uri]) && isset($site_schema_types[$mapping[$input_uri]])) { + $event->setSiteSchemaUri($mapping[$input_uri]); + } + } + } + + /** + * Demonstrate mapping bundle to external type for publishing. + * + * Generally, one should set mappings for output in the RDF mapping config + * instead of this event. + * + * @param \Drupal\rdf\Mapping\MapBundleForOutputEvent $event + * The mapping event. + */ + public function mapBundleForOutput($event) { + $term_schema = $event->getTermSchema(); + $ids = $term_schema->getTermIds(); + if ($ids['entity_type'] == 'entity_test' && $ids['bundle'] == 'entity_test') { + $event->addTypes(array(self::STAGING_SITE_TYPE_URI)); + } + } + + /** + * Demonstrate mapping field to external property for publishing. + * + * Generally, one should set mappings for output in the RDF mapping config + * instead of this event. + * + * @param \Drupal\rdf\Mapping\MapFieldForOutputEvent $event + * The mapping event. + */ + public function mapFieldForOutput($event) { + $term_schema = $event->getTermSchema(); + $ids = $term_schema->getTermIds(); + if ($ids['entity_type'] == 'entity_test' && $ids['bundle'] == 'entity_test' && $ids['field_name'] == 'user_id') { + $event->addPredicates(array(self::STAGING_SITE_PROPERTY_URI)); + } + } + + /** + * Implements EventSubscriberInterface::getSubscribedEvents(). + */ + static function getSubscribedEvents() { + $events[RdfMappingEvents::MAP_TYPES_FROM_INPUT] = 'mapTypesFromInput'; + $events[RdfMappingEvents::MAP_BUNDLE_FOR_OUTPUT] = 'mapBundleForOutput'; + $events[RdfMappingEvents::MAP_FIELD_FOR_OUTPUT] = 'mapFieldForOutput'; + return $events; + } + +} diff --git a/core/modules/rdf/tests/rdf_test_mapping_event/lib/Drupal/rdf_test_mapping_event/RdfTestMappingEventBundle.php b/core/modules/rdf/tests/rdf_test_mapping_event/lib/Drupal/rdf_test_mapping_event/RdfTestMappingEventBundle.php new file mode 100644 index 0000000..0bd0c4e --- /dev/null +++ b/core/modules/rdf/tests/rdf_test_mapping_event/lib/Drupal/rdf_test_mapping_event/RdfTestMappingEventBundle.php @@ -0,0 +1,27 @@ +register('rdf_test_mapping_event.mapping', 'Drupal\rdf_test_mapping_event\EventSubscriber\TestMappingSubscriber') + ->addTag('event_subscriber'); + } + +} diff --git a/core/modules/rdf/tests/rdf_test_mapping_event/rdf_test_mapping_event.info b/core/modules/rdf/tests/rdf_test_mapping_event/rdf_test_mapping_event.info new file mode 100644 index 0000000..3b34e19 --- /dev/null +++ b/core/modules/rdf/tests/rdf_test_mapping_event/rdf_test_mapping_event.info @@ -0,0 +1,6 @@ +name = "RDF module mapping event test" +description = "Test mapping subscriber for RDF mapping tests." +package = Testing +core = 8.x +hidden = TRUE +dependencies[] = rdf diff --git a/core/modules/rdf/tests/rdf_test_mapping_event/rdf_test_mapping_event.module b/core/modules/rdf/tests/rdf_test_mapping_event/rdf_test_mapping_event.module new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/core/modules/rdf/tests/rdf_test_mapping_event/rdf_test_mapping_event.module @@ -0,0 +1 @@ + 'http://purl.org/conflicting/namespace', + 'foaf' => 'http://xmlns.com/foaf/0.1/', + 'foaf1' => 'http://xmlns.com/foaf/0.1/', + ); +}