diff --git a/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityWrapper.php b/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityWrapper.php index a2e49e3..6ff98f4 100644 --- a/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityWrapper.php +++ b/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityWrapper.php @@ -85,7 +85,7 @@ public function getId() { /** * Get the type URI. * - * @todo Once RdfMappingManager has a mapOutputTypes event, use that instead + * @todo Once RdfMappingManager has a mapBundleForOutput event, use that instead * of simply returning the site schema URI. */ public function getTypeUri() { diff --git a/core/modules/jsonld/lib/Drupal/jsonld/Tests/RdfSchemaSerializationTest.php b/core/modules/jsonld/lib/Drupal/jsonld/Tests/RdfSchemaSerializationTest.php index c4422aa..3cbfae4 100644 --- a/core/modules/jsonld/lib/Drupal/jsonld/Tests/RdfSchemaSerializationTest.php +++ b/core/modules/jsonld/lib/Drupal/jsonld/Tests/RdfSchemaSerializationTest.php @@ -46,7 +46,7 @@ function testSchemaSerialization() { $parsed_term = $decoded[0]; $this->assertEqual($parsed_term->{'@id'}, $bundle_schema->getUri(), 'JSON-LD for schema term uses correct @id.'); - $this->assertEqual($parsed_term->{'@type'}, 'http://www.w3.org/2000/01/rdf-schema#class', 'JSON-LD for schema term uses correct @type.'); + $this->assertEqual($parsed_term->{'@type'}, 'http://www.w3.org/2000/01/rdf-schema#Class', 'JSON-LD for schema term uses correct @type.'); // The @id and @type should be placed in the beginning of the array. $array_keys = array_keys((array) $parsed_term); $this->assertEqual(array('@id', '@type'), array_slice($array_keys, 0, 2), 'JSON-LD keywords are placed before other properties.'); diff --git a/core/modules/node/node.module b/core/modules/node/node.module index a37679c..d53f10e 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -811,53 +811,6 @@ function node_type_set_defaults($info = array()) { } /** - * Implements hook_rdf_mapping(). - */ -function node_rdf_mapping() { - return array( - array( - 'type' => 'node', - 'bundle' => RDF_DEFAULT_BUNDLE, - 'mapping' => array( - 'rdftype' => array('sioc:Item', 'foaf:Document'), - 'title' => array( - 'predicates' => array('dc:title'), - ), - 'created' => array( - 'predicates' => array('dc:date', 'dc:created'), - 'datatype' => 'xsd:dateTime', - 'callback' => 'date_iso8601', - ), - 'changed' => array( - 'predicates' => array('dc:modified'), - 'datatype' => 'xsd:dateTime', - 'callback' => 'date_iso8601', - ), - 'body' => array( - 'predicates' => array('content:encoded'), - ), - 'uid' => array( - 'predicates' => array('sioc:has_creator'), - 'type' => 'rel', - ), - 'name' => array( - 'predicates' => array('foaf:name'), - ), - 'comment_count' => array( - 'predicates' => array('sioc:num_replies'), - 'datatype' => 'xsd:integer', - ), - 'last_activity' => array( - 'predicates' => array('sioc:last_activity_date'), - 'datatype' => 'xsd:dateTime', - 'callback' => 'date_iso8601', - ), - ), - ), - ); -} - -/** * Determines whether a node hook exists. * * @param string $type diff --git a/core/modules/rdf/lib/Drupal/rdf/BundleRdfMappingStorageController.php b/core/modules/rdf/lib/Drupal/rdf/BundleRdfMappingStorageController.php new file mode 100644 index 0000000..ac4ea0c --- /dev/null +++ b/core/modules/rdf/lib/Drupal/rdf/BundleRdfMappingStorageController.php @@ -0,0 +1,19 @@ +siteSchema->bundle($entity->mappedEntityType, $entity->mappedBundle); + } + +} diff --git a/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/MappingSubscriber.php b/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/MappingSubscriber.php index 764c2f9..7131efd 100644 --- a/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/MappingSubscriber.php +++ b/core/modules/rdf/lib/Drupal/rdf/EventSubscriber/MappingSubscriber.php @@ -35,11 +35,41 @@ public function mapTypesFromInput(\Drupal\rdf\MapTypesFromInputEvent $event) { } } + public function mapBundleForOutput(\Drupal\rdf\MapBundleForOutputEvent $event) { + $term_schema = $event->getTermSchema(); + // @todo When the $schema is SYNDICATION, allow sites to exclude the site + // schema URI from the mapping. + $site_schema_curie = $term_schema->getCurie(); + $event->addTypes(array($site_schema_curie)); + + $config = config('rdf.mapping.bundle.' . $term_schema->getMappingConfigId()); + $types = $config->get('types'); + if (!empty($types)) { + $event->addTypes($types); + } + } + + public function mapFieldForOutput(\Drupal\rdf\MapFieldForOutputEvent $event) { + $term_schema = $event->getTermSchema(); + // @todo When the $schema is SYNDICATION, allow sites to exclude the site + // schema URI from the mapping. + $site_schema_curie = $term_schema->getCurie(); + $event->addPredicates(array($site_schema_curie)); + + $config = config('rdf.mapping.field.' . $term_schema->getMappingConfigId()); + $curies = $config->get('properties'); + if (!empty($curies)) { + $event->addPredicates($curies); + } + } + /** * 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/FieldRdfMappingStorageController.php b/core/modules/rdf/lib/Drupal/rdf/FieldRdfMappingStorageController.php new file mode 100644 index 0000000..83119e3 --- /dev/null +++ b/core/modules/rdf/lib/Drupal/rdf/FieldRdfMappingStorageController.php @@ -0,0 +1,20 @@ +siteSchema->field($entity->mappedEntityType, $entity->mappedBundle, $entity->mappedField); + } + +} diff --git a/core/modules/rdf/lib/Drupal/rdf/MapBundleForOutputEvent.php b/core/modules/rdf/lib/Drupal/rdf/MapBundleForOutputEvent.php new file mode 100644 index 0000000..59bfce0 --- /dev/null +++ b/core/modules/rdf/lib/Drupal/rdf/MapBundleForOutputEvent.php @@ -0,0 +1,59 @@ +termSchema = $term_schema; + $this->curies = 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->curies = array_merge($this->curies, $curies); + } + + public function getCuries() { + return $this->curies; + } +} diff --git a/core/modules/rdf/lib/Drupal/rdf/MapFieldForOutputEvent.php b/core/modules/rdf/lib/Drupal/rdf/MapFieldForOutputEvent.php new file mode 100644 index 0000000..afa1d9a --- /dev/null +++ b/core/modules/rdf/lib/Drupal/rdf/MapFieldForOutputEvent.php @@ -0,0 +1,71 @@ +termSchema = $term_schema; + $this->predicates = array(); + } + + public function getTermSchema() { + return $this->termSchema; + } + + /** + * Add CURIEs to the predicates array. + * + * @param array $curies + * + * @todo Add namespace IDs. + */ + public function addPredicates($curies) { + if (!is_array($curies)) { + $curies = array($curies); + } + $this->predicates = array_merge($this->predicates, $curies); + } + + public function getPredicates() { + return $this->predicates; + } + + public function getDatatype() { + return $this->datatype; + } + + public function getDatatypeCallback() { + return $this->datatypeCallback; + } +} 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..3e8a0d0 --- /dev/null +++ b/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/BundleRdfMapping.php @@ -0,0 +1,67 @@ +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..502c1c2 --- /dev/null +++ b/core/modules/rdf/lib/Drupal/rdf/Plugin/Core/Entity/FieldRdfMapping.php @@ -0,0 +1,88 @@ +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/RdfMappingEvents.php b/core/modules/rdf/lib/Drupal/rdf/RdfMappingEvents.php index 0e3fdae..f74c791 100644 --- a/core/modules/rdf/lib/Drupal/rdf/RdfMappingEvents.php +++ b/core/modules/rdf/lib/Drupal/rdf/RdfMappingEvents.php @@ -26,4 +26,24 @@ */ const MAP_TYPES_FROM_INPUT = 'rdf.map_types_from_input'; + /** + * Maps a bundle to corresponding RDF URIs. + * + * In contrast to MAP_INPUT_TYPES, this event is triggered when RDF is being + * published. Modules can use this event to add RDF types to a mapping, which + * will then be published in the site's RDFa, JSON-LD, etc. However, a module + * does not have to use this event to add RDF types. RDF module manages an + * RDF mapping configuration file for each bundle which modules can also use + * to register mappings. + * + * @todo Add a note about this being cached once it is. + * + * @see \Drupal\rdf\RdfMappingManager + * + * @var string + */ + const MAP_BUNDLE_FOR_OUTPUT = 'rdf.map_bundle_for_output'; + + const MAP_FIELD_FOR_OUTPUT = 'rdf.map_field_for_output'; + } diff --git a/core/modules/rdf/lib/Drupal/rdf/RdfMappingManager.php b/core/modules/rdf/lib/Drupal/rdf/RdfMappingManager.php index 90588e9..2fc262a 100644 --- a/core/modules/rdf/lib/Drupal/rdf/RdfMappingManager.php +++ b/core/modules/rdf/lib/Drupal/rdf/RdfMappingManager.php @@ -10,6 +10,8 @@ use ReflectionClass; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\rdf\MapTypesFromInputEvent; +use Drupal\rdf\MapBundleForOutputEvent; +use Drupal\rdf\MapFieldForOutputEvent; use Drupal\rdf\RdfMappingEvents; use Drupal\rdf\SiteSchema\BundleSchema; use Drupal\rdf\SiteSchema\SiteSchema; @@ -76,6 +78,79 @@ public function getTypedDataIdsFromTypeUris($input_rdf_types) { } /** + * Convert Typed Data IDs to an array of RDF types. + * + * @param string $entity_type + * The entity type of the bundle which the types correspond to. + * @param string $bundle + * The name of the bundle which the types correspond to. + * @param string $schema_path + * The site schema which is being mapped to. + */ + public function getBundleMapping($entity_type, $bundle, $schema_path = SiteSchema::SYNDICATION) { + $site_schema = $this->siteSchemaManager->getSchema($schema_path); + $term_schema = $site_schema->bundle($entity_type, $bundle); + // Get the site schema URI which corresponds to the bundle. + $curies = $this->mapBundleForOutput($term_schema); + return $curies; + } + + public function getFieldMapping($entity_type, $bundle, $field_name, $schema_path = SiteSchema::SYNDICATION) { + $site_schema = $this->siteSchemaManager->getSchema($schema_path); + $term_schema = $site_schema->field($entity_type, $bundle, $field_name); + return $this->mapFieldForOutput($term_schema); + } + + public function saveBundleMappingConfig($mapping, $entity_type, $bundle) { + $keys = array( + 'mappedEntityType' => $entity_type, + 'mappedBundle' => $bundle, + ); + $bundle_mapping = entity_create('rdf_mapping_bundle', $keys); + if (isset($mapping['types'])) { + $bundle_mapping->types = $mapping['types']; + } + $bundle_mapping->save(); + } + + public function saveFieldMappingConfig($mapping, $entity_type, $bundle, $field_name) { + $keys = array( + 'mappedEntityType' => $entity_type, + 'mappedBundle' => $bundle, + 'mappedField' => $field_name, + ); + $field_mapping = entity_create('rdf_mapping_field', $keys); + if (isset($mapping['properties'])) { + $field_mapping->properties = $mapping['properties']; + } + if (isset($mapping['datatype'])) { + $field_mapping->datatype = $mapping['datatype']; + } + if (isset($mapping['datatype_callback'])) { + $field_mapping->datatypeCallback = $mapping['datatype_callback']; + } + $field_mapping->save(); + } + + public function deleteBundleMappingConfig($entity_type, $bundle) { + // Config can only be stored for the syndication schema. + $schema = $this->siteSchemaManager->getSchema(SiteSchema::SYNDICATION); + $bundle_schema = $schema->bundle($entity_type, $bundle); + $config_id = $bundle_schema->getMappingConfigId(); + $config = config("rdf.mapping.bundle.$config_id"); + $config->delete(); + } + + public function deleteFieldMappingConfig($entity_type, $bundle, $field_name) { + // Config can only be stored for the syndication schema. + $schema = $this->siteSchemaManager->getSchema(SiteSchema::SYNDICATION); + $field_schema = $schema->field($entity_type, $bundle, $field_name); + $config_id = $field_schema->getMappingConfigId(); + $config = config("rdf.mapping.field.$config_id"); + $config->delete(); + } + + /** * Map an array of incoming URIs to an internal site schema URI. * * @param array $input_rdf_types @@ -98,4 +173,30 @@ protected function mapTypesFromInput($input_rdf_types) { return $mapping_event->getSiteSchemaUri(); } + + protected function mapBundleForOutput($term_schema) { + $mapping = array(); + $typeMap = new MapBundleForOutputEvent($term_schema); + $this->dispatcher->dispatch(RdfMappingEvents::MAP_BUNDLE_FOR_OUTPUT, $typeMap); + $types = $typeMap->getCuries(); + if (!empty($types)) { + $mapping['types'] = $types; + } + return $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(); + } + return array_filter($mapping); + } + } diff --git a/core/modules/rdf/lib/Drupal/rdf/RdfMappingStorageControllerBase.php b/core/modules/rdf/lib/Drupal/rdf/RdfMappingStorageControllerBase.php new file mode 100644 index 0000000..afbe5d7 --- /dev/null +++ b/core/modules/rdf/lib/Drupal/rdf/RdfMappingStorageControllerBase.php @@ -0,0 +1,40 @@ +siteSchema = drupal_container()->get('rdf.site_schema_manager')->getSchema(SiteSchema::SYNDICATION); + } + + /** + * Overrides \Drupal\Core\Config\Entity\ConfigStorageController::save(). + */ + public function save(EntityInterface $entity) { + if (!isset($entity->mid)) { + $entity->mid = $this->getTermSchema($entity)->getMappingConfigId(); + } + parent::save($entity); + } + +} diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/BundleSchema.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/BundleSchema.php index 2c92696..79dcdd5 100644 --- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/BundleSchema.php +++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/BundleSchema.php @@ -45,11 +45,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); } /** diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/EntitySchema.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/EntitySchema.php index 48a69fc..e951e4b 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; } /** 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..ca0c426 --- /dev/null +++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/FieldSchema.php @@ -0,0 +1,94 @@ +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; + } + +} diff --git a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermBase.php b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermBase.php index d7afd1c..c7a3339 100644 --- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermBase.php +++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SchemaTermBase.php @@ -39,6 +39,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 +58,8 @@ public function getProperties() { ); } + public function getMappingConfigId() { + return str_replace('/', ':', $this->getPath()); + } + } 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..b808826 100644 --- a/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchema.php +++ b/core/modules/rdf/lib/Drupal/rdf/SiteSchema/SiteSchema.php @@ -29,6 +29,8 @@ class SiteSchema { */ protected $schemaPath; + protected $prefix; + /** * Constructor. * @@ -43,6 +45,7 @@ public function __construct($schema_path) { throw new \UnexpectedValueException(sprintf('%s is not a valid site schema path. Schema path must be one of %s.'), $schema_path, implode(', ', $valid_paths)); } $this->schemaPath = $schema_path; + $this->prefix = ($schema_path == self::CONTENT_DEPLOYMENT) ? 'site-cd' : 'site-syn'; } /** @@ -60,6 +63,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 @@ -76,6 +86,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/Tests/CrudTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/CrudTest.php index 3383f94..ee256e2 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/CrudTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/CrudTest.php @@ -19,7 +19,7 @@ class CrudTest extends WebTestBase { * * @var array */ - public static $modules = array('rdf', 'rdf_test'); + public static $modules = array('rdf', 'entity_test'); public static function getInfo() { return array( @@ -33,53 +33,55 @@ 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'; + $bundle_mapping_config_name = "rdf.mapping.bundle.site-schema:syndication:$entity_type:$bundle"; + $field_mapping_config_name = "rdf.mapping.field.site-schema: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', - ), - ), + // Create mapping arrays to save. + $bundle_mapping = array( + 'types' => array('sioc:Post'), + ); + $user_id_mapping = array( + 'properties' => array('sioc:has_creator', 'dc:creator'), ); - $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 mappings. + $mapping_manager = drupal_container()->get('rdf.mapping_manager'); + $mapping_manager->saveBundleMappingConfig($bundle_mapping, $entity_type, $bundle); + $mapping_manager->saveFieldMappingConfig($user_id_mapping, $entity_type, $bundle, 'user_id'); - // 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($field_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 was property saved. + $types = config($bundle_mapping_config_name)->get('types'); + $this->assertTrue(in_array('sioc:Post', $types), 'Bundle mapping config values saved correctly.'); + $properties = config($field_mapping_config_name)->get('properties'); + $this->assertTrue(in_array('sioc:has_creator', $properties) && in_array('dc:creator', $properties), 'Field mapping config values saved correctly.'); - // 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 loading of saved mapping. + $loaded_mapping = $mapping_manager->getBundleMapping($entity_type, $bundle); + $this->assertTrue(in_array('sioc:Post', $loaded_mapping['types']), 'Bundle mapping loaded.'); + $loaded_mapping = $mapping_manager->getFieldMapping($entity_type, $bundle, 'user_id'); + $this->assertTrue(in_array('sioc:has_creator', $loaded_mapping['properties']) && in_array('dc:creator', $loaded_mapping['properties']), 'Field mapping config loaded.'); - // 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. + $user_id_mapping = array( + 'properties' => array('schema:author'), + ); + $mapping_manager->saveFieldMappingConfig($user_id_mapping, $entity_type, $bundle, 'user_id'); + $loaded_mapping = $mapping_manager->getFieldMapping($entity_type, $bundle, 'user_id'); + $this->assert(in_array('schema:author', $loaded_mapping['properties']) && !in_array('sioc:has_creator', $loaded_mapping['properties']), '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. + $mapping_manager->deleteBundleMappingConfig($entity_type, $bundle); + $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.'); + $mapping_manager->deleteFieldMappingConfig($entity_type, $bundle, 'user_id'); + $field_mapping_configs = config_get_storage_names_with_prefix('rdf.mapping.field'); + $this->assertFalse(in_array($field_mapping_config_name, $field_mapping_configs), 'Field mapping config deleted.'); } } 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/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 264a090..2cf869c 100644 --- a/core/modules/rdf/rdf.module +++ b/core/modules/rdf/rdf.module @@ -128,100 +128,34 @@ function rdf_get_namespaces() { * @param $type * An entity type. * @param $bundle - * (optional) A bundle name. + * 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. - $entity_info = entity_get_info($type); - if (!empty($entity_info['bundles'][$bundle]['rdf_mapping'])) { - return $entity_info['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']; - } +function rdf_mapping_load($type, $bundle) { + $mapping_manager = drupal_container()->get('rdf.mapping_manager'); + $mapping = array( + 'rdftypes' => $mapping_manager->getBundleMapping($type, $bundle), + ); + $entity = entity_create($type, array('type' => $bundle)); + + $properties = $entity->getPropertyDefinitions(); + if (!empty($properties)) { + foreach ($properties as $field_name => $field_info) { + $field_mapping = $mapping_manager->getFieldMapping($type, $bundle, $field_name); + if (!empty($field_mapping)) { + $mapping[$field_name] = $field_mapping; } } } - - return isset($default_mappings[$type]) ? $default_mappings[$type] : array(); + return $mapping; } /** - * 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. + * @} End of "defgroup rdf". */ -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 @@ -229,61 +163,6 @@ function _rdf_mapping_load_multiple($type, array $bundles) { */ /** - * 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 @@ -345,98 +224,6 @@ 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_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_info_alter(&$entity_info) { - // Loop through each entity type and its bundles. - foreach ($entity_info as $entity_type => $entity_type_info) { - if (!empty($entity_type_info['bundles'])) { - $bundles = array_keys($entity_type_info['bundles']); - $mappings = _rdf_mapping_load_multiple($entity_type, $bundles); - - foreach ($bundles as $bundle) { - if (isset($mappings[$bundle])) { - $entity_info[$entity_type]['bundles'][$bundle]['rdf_mapping'] = $mappings[$bundle]; - } - else { - // If no mapping was found in the database, assign the default RDF - // mapping for this entity type. - $entity_info[$entity_type]['bundles'][$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(). - */ -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); - $comment->rdf_data['nid_uri'] = url('node/' . $comment->nid); - if ($comment->pid) { - $comment->rdf_data['pid_uri'] = url('comment/' . $comment->pid, array('fragment' => 'comment-' . $comment->pid)); - } - } -} - -/** * Implements hook_theme(). */ function rdf_theme() { @@ -511,14 +298,17 @@ 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_mapping = $mapping_manager->getBundleMapping('node', $variables['type']); $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['rdftype']) ? NULL : $bundle_mapping['rdftype']; // 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', $variables['type'], 'title'); + $variables['title_attributes']['property'] = empty($title_mapping['predicates']) ? NULL : $title_mapping['predicates']; $variables['title_attributes']['datatype'] = ''; // In full node mode, the title is not displayed by node.tpl.php so it is @@ -531,35 +321,38 @@ 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['predicates'])) { + $element['#attributes']['property'] = $title_mapping['predicates']; } 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', $variables['type'], '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', $variables['type'], 'uid'); + if (!empty($uid_mapping)) { + $variables['rdf_template_variable_attributes']['name']['rel'] = $uid_mapping['predicates']; 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['predicates']; } } // 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', $variables['type'], 'comment_count'); + if (isset($variables['node']->comment_count) && !empty($comment_count_mapping['predicates'])) { // 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['predicates']; $comment_count_attributes['content'] = $variables['node']->comment_count; - $comment_count_attributes['datatype'] = $variables['node']->rdf_mapping['comment_count']['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 @@ -576,9 +369,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['predicates'], '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'); diff --git a/core/profiles/standard/standard.install b/core/profiles/standard/standard.install index 48da6b1..d71de9c 100644 --- a/core/profiles/standard/standard.install +++ b/core/profiles/standard/standard.install @@ -219,17 +219,52 @@ function standard_install() { } // Insert default pre-defined RDF mapping into the database. + $base_rdf_field_mappings = array( + 'title' => array( + 'predicates' => array('dc:title'), + ), + 'created' => array( + 'predicates' => array('dc:date', 'dc:created'), + 'datatype' => 'xsd:dateTime', + 'callback' => 'date_iso8601', + ), + 'changed' => array( + 'predicates' => array('dc:modified'), + 'datatype' => 'xsd:dateTime', + 'callback' => 'date_iso8601', + ), + 'body' => array( + 'predicates' => array('content:encoded'), + ), + 'uid' => array( + 'predicates' => array('sioc:has_creator'), + 'type' => 'rel', + ), + 'name' => array( + 'predicates' => array('foaf:name'), + ), + 'comment_count' => array( + 'predicates' => array('sioc:num_replies'), + 'datatype' => 'xsd:integer', + ), + 'last_activity' => array( + 'predicates' => array('sioc:last_activity_date'), + 'datatype' => 'xsd:dateTime', + 'callback' => 'date_iso8601', + ), + ); $rdf_mappings = array( array( 'type' => 'node', 'bundle' => 'page', 'mapping' => array( - 'rdftype' => array('foaf:Document'), + 'rdftype' => array('foaf:Document', 'sioc:Item'), ), ), array( 'type' => 'node', 'bundle' => 'article', + 'rdftype' => array('foaf:Document', 'sioc:Item'), 'mapping' => array( 'field_image' => array( 'predicates' => array('og:image', 'rdfs:seeAlso'), @@ -243,6 +278,7 @@ function standard_install() { ), ); foreach ($rdf_mappings as $rdf_mapping) { + $rdf_mapping['mapping'] += $base_rdf_field_mappings; rdf_mapping_save($rdf_mapping); }