=== added directory 'modules/rdf'
=== added file 'modules/rdf/rdf.api.php'
--- modules/rdf/rdf.api.php 1970-01-01 00:00:00 +0000
+++ modules/rdf/rdf.api.php 2009-09-12 09:37:16 +0000
@@ -0,0 +1,69 @@
+ array(
+ 'rdftype' => array('sioc:Post'),
+ 'title' => array('dc:title'),
+ 'created' => array(
+ 'property' => array('dc:date', 'dc:created'),
+ 'datatype' => 'xsd:dateTime',
+ 'callback' => 'date_iso8601',
+ ),
+ 'name' => array('foaf:name'),
+ 'uid' => array('sioc:has_creator', 'foaf:maker'),
+ )
+ );
+}
+
+/**
+ * Allow modules to override existing mappings.
+ *
+ * @param &$mappings
+ * An associative array of mappings
+ */
+function hook_rdf_mapping_alter(&$mapping) {
+ return array(
+ 'blog' => array(
+ 'rdftype' => array('sioc:Post','myvoc:BlogPost'),
+ 'title' => array('dc:title', 'myvoc:title'),
+ 'created' => array(
+ 'property' => array('dc:date', 'dc:created'),
+ 'datatype' => 'xsd:dateTime',
+ 'callback' => 'date_iso8601',
+ ),
+ 'name' => array('foaf:name'),
+ 'uid' => array('sioc:has_creator', 'foaf:maker'),
+ )
+ );
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */
=== added file 'modules/rdf/rdf.info'
--- modules/rdf/rdf.info 1970-01-01 00:00:00 +0000
+++ modules/rdf/rdf.info 2009-09-11 11:23:02 +0000
@@ -0,0 +1,7 @@
+; $Id$
+name = RDF
+description = Supports RDF mappings.
+package = Core
+core = 7.x
+files[] = rdf.module
+files[] = rdf.test
=== added file 'modules/rdf/rdf.module'
--- modules/rdf/rdf.module 1970-01-01 00:00:00 +0000
+++ modules/rdf/rdf.module 2009-09-12 10:29:13 +0000
@@ -0,0 +1,217 @@
+ 'foaf:Document',
+ 'title' => array(
+ 'property' => array('dc:title'),
+ )
+ );
+ $stored_mapping = &drupal_static(__FUNCTION__, $default_mapping);
+
+ if (isset($mapping)) {
+ $stored_mapping = $mapping;
+ }
+
+ return $stored_mapping;
+}
+
+/**
+ * Returns the page level RDF mapping from the dictionary.
+ *
+ * @return array
+ * The page level RDF mapping.
+ */
+function drupal_get_rdf_page_mapping() {
+ return drupal_set_rdf_page_mapping();
+}
+
+/**
+ * Returns RDFa attributes to render.
+ *
+ * @param $mapping
+ * An array containing a mandatory property and optional datatype and
+ * callback.
+ * Example:
+ * array(
+ * 'property' => array('dc:created'),
+ * 'datatype' => 'xsd:dateTime',
+ * 'callback' => 'date_iso8601',
+ * )
+ * @param $data
+ * A value that needs to be converted by the provided callback function.
+ * @return array
+ * An array containing RDFa attributes ready for rendering.
+ */
+function drupal_rdfa_attributes($mapping, $data = NULL) {
+ $attributes['property'] = $mapping['property'];
+
+ if (isset($mapping['callback']) && isset($data)) {
+ $callback = $mapping['callback'];
+
+ if (function_exists($callback)) {
+ $attributes['content'] = call_user_func($callback, $data);
+ }
+ if (isset($mapping['datatype'])) {
+ $attributes['datatype'] = $mapping['datatype'];
+ }
+ }
+
+ return $attributes;
+}
+
+/**
+ * Implementation of hook_node_load().
+ */
+function rdf_node_load($nodes, $types) {
+ foreach ($nodes as $node) {
+ $node->rdf_mapping = rdf_get_mapping($node->type);
+
+ // The node_load function does not reuse user_load(), so we need to regenerate
+ // the URI of the author of the node.
+ $node->rdf['user'] = array('uri' => _rdf_generate_user_uri($node->uid));
+ }
+}
+
+/**
+ * Implement hook_user_load().
+ */
+function rdf_user_load($users) {
+ // Generate an RDF resource URI for each user account.
+ foreach ($users as $user) {
+ $user->rdf = array('uri' => _rdf_generate_user_uri($user->uid));
+ }
+}
+
+/**
+ * Implements hook_comment_view().
+ *
+ * @param $comment
+ * A comment object.
+ */
+function rdf_comment_view($comment){
+ $comment->rdf_mapping = rdf_get_mapping('comment');
+}
+
+/**
+ * Helper function to create a URI for the user object.
+ *
+ * @param $uid
+ * The user id the URI should be generated from.
+ * @return
+ * A URI including the base path of the site, e.g. '/subdomain/user/1#user'.
+ */
+function _rdf_generate_user_uri($uid) {
+ return base_path() . 'user/' . $uid . '#user';
+}
+
+/**
+ * Returns an ISO8601 formatted date based on the given date.
+ *
+ * Can be used as a callback for mappings.
+ *
+ * @param $date
+ * A date in a format parseable by strtotime.
+ * @return string
+ * An ISO8601 formatted date.
+ */
+function date_iso8601($date) {
+ return date(DATE_ISO8601, $date);
+}
+
+/**
+ * Implement MODULE_preprocess_HOOK().
+ */
+function rdf_preprocess_node(&$variables) {
+ // Add RDFa markup to the node container. The about attribute specifies the
+ // URI of the resource described within the HTML element, while the typeof
+ // attribute indicates its RDF type (foaf:Document, or sioc:User, etc.).
+ $variables['attributes_array']['about'] = $variables['node_url'];
+ $variables['attributes_array']['typeof'] = $variables['node']->rdf_mapping['rdftype'];
+
+ // Add RDFa markup to the title of the node. Because the RDFa markup is added
+ // to the h2 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_array']['property'] = $variables['node']->rdf_mapping['title']['property'];
+ $variables['title_attributes_array']['datatype'] = '';
+
+ // Add RDFa markup for the date.
+ // TODO use a theme_timestamp() function instead? see http://drupal.org/node/574954
+ $date_attributes_array = drupal_rdfa_attributes($variables['rdf_mapping']['created'], $variables['created']);
+ $variables['date'] = '' . $variables['date'] . '';
+
+ // Add RDFa markup describing the relation between the node and its author.
+ $author_attributes_array = array('rel' => $variables['rdf_mapping']['uid']['property']);
+ $variables['name'] = '' . $variables['name'] . '';
+
+}
+
+/**
+ * Adds RDFa attributes to the template variables.
+ *
+ * @param $variables
+ */
+function rdf_preprocess_field(&$variables) {
+ $instance = $variables['instance'];
+ $value = $variables['items'][0]['#item']['value'];
+
+ if (isset($instance['rdf_mapping'])) {
+ $variables['#attributes'] = drupal_rdfa_attributes($instance['rdf_mapping'], $value);
+ }
+}
+
+/**
+ * Implements MODULE_preprocess_HOOK().
+ */
+function rdf_preprocess_username(&$variables) {
+ if (!empty($variables['object']->account->rdf_mapping['name'])) {
+ $variables['object']->attributes['property'] = $variables['object']->account->rdf_mapping['name']['property'];
+ }
+}
=== added file 'modules/rdf/rdf.test'
--- modules/rdf/rdf.test 1970-01-01 00:00:00 +0000
+++ modules/rdf/rdf.test 2009-09-11 11:23:02 +0000
@@ -0,0 +1,218 @@
+ t('RDF mappings'),
+ 'description' => t('Test RDF mapping hooks.'),
+ 'group' => t('RDF'),
+ );
+ }
+
+ function setUp() {
+ // Enable RDF and RDF test in the test environment.
+ parent::setUp('rdf', 'rdf_test');
+ }
+
+ /**
+ * Test that the mappings are correctly returned and processed by the hooks.
+ */
+ function testMappings() {
+ // Test that the mapping is returned correctly by the hook.
+ $mapping = rdf_get_mapping('rdf_test_container');
+ $this->assertIdentical($mapping['rdftype'], array('sioc:Post'), t('Mapping for rdftype is sioc:Post.'));
+ $this->assertIdentical($mapping['title'], array('property' => array('dc:title')), t('Mapping for title is dc:title.'));
+ $this->assertIdentical($mapping['created'], array('property' => array('dc:created')), t('Mapping for created is dc:created.'));
+ $this->assertTrue(in_array('sioc:has-creator', $mapping['uid']['property']), t('Mapping for uid contains sioc:has-creator'));
+ $this->assertTrue(in_array('dc:creator', $mapping['uid']['property']), t('Mapping for uid contains dc:creator'));
+
+ // Test that an empty array is returned when a container has no mappings.
+ $mapping = rdf_get_mapping('rdf_test_nonexistent_container');
+ $this->assertTrue(is_array($mapping) && empty($mapping), t('Empty array returned for container with no mappings'));
+
+ // Test that the mappings are altered correctly.
+ $mapping = rdf_get_mapping('rdf_test_another');
+ $this->assertTrue(in_array('dc:title', $mapping['title']['property']), t('Mapping for title contains dc:title'));
+ $this->assertTrue(in_array('sioc:title', $mapping['title']['property']), t('Mapping for title contains sioc:title'));
+ $this->assertTrue(in_array('sioc:has-creator', $mapping['uid']['property']), t('Mapping for uid contains sioc:has-creator'));
+ $this->assertTrue(in_array('foo:bar', $mapping['uid']['property']), t('Mapping for uid contains foo:bar'));
+ $this->assertFalse(in_array('dc:creator', $mapping['uid']['property']), t('Mapping for uid does not contain dc:creator'));
+ $this->assertTrue(in_array('foaf:name', $mapping['name']['property']), t('Mapping for name contains foaf:name'));
+ $this->assertFalse(in_array('dc:creator', $mapping['name']['property']), t('Mapping for name does not contain dc:creator'));
+ $this->assertIdentical($mapping['rdftype'], array('foaf:Person'), t('Mapping for rdftype was changed'));
+ $this->assertFalse(isset($mapping['foobar']), t('Mapping for foobar was removed.'));
+ }
+
+ /**
+ * Test manually setting and getting the page mapping.
+ */
+ function testPageMapping() {
+ // Default value should be returned if nothing was explicitly set.
+ $this->assertEqual(drupal_get_rdf_page_mapping(), array('title' => array('property' => array('dc:title')), 'rdftype' => 'foaf:Document'), t('Default mapping is set correctly.'));
+
+ // If we manually set a mapping, that should replace the default.
+ drupal_set_rdf_page_mapping(array('foobar' => array('property' => array('foo:bar'))));
+ $this->assertEqual(drupal_get_rdf_page_mapping(), array('foobar' => array('property' => array('foo:bar'))), t('Custom mapping overrides default mapping.'));
+ }
+}
+
+/**
+ * RDFa markup generation tests.
+ */
+class RdfaMarkupTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => t('RDFa markup'),
+ 'description' => t('Test RDF markup generation.'),
+ 'group' => t('RDF'),
+ );
+ }
+
+ function setUp() {
+ // Enable RDF and RDF test in the test environment.
+ parent::setUp('rdf', 'rdf_test');
+ }
+
+ /**
+ * Test drupal_rdfa_attributes().
+ */
+ function testDrupalRdfaAtributes() {
+ $date = '2009-05-14';
+ $isoDate = date(DATE_ISO8601, strtotime($date));
+
+ $expected_type = 'xsd:dateTime';
+ $expected_property = array('dc:created');
+ $expected_value = $isoDate;
+ $mapping = rdf_get_mapping('rdf_complex_test');
+ $attributes = drupal_rdfa_attributes($mapping['date'], $date);
+
+ $this->assertEqual($expected_type, $attributes['datatype']);
+ $this->assertEqual($expected_property, $attributes['property']);
+ $this->assertEqual($expected_value, $attributes['content']);
+
+ $expected_property = array('dc:title');
+ $mapping = rdf_get_mapping('rdf_test_container');
+ $attributes = drupal_rdfa_attributes($mapping['title']);
+
+ $this->assertEqual($expected_property, $attributes['property']);
+ }
+
+}
+
+class RdfaFieldTestCase extends DrupalWebTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => t('RDFa field markup'),
+ 'description' => t('Test RDFa markup in fields.'),
+ 'group' => t('RDF'),
+ );
+ }
+
+ function setUp() {
+ parent::setUp('field_test', 'rdf');
+
+ $this->field_name = drupal_strtolower($this->randomName() . '_field_name');
+ $this->field = array('field_name' => $this->field_name, 'type' => 'test_field', 'cardinality' => 4);
+ $this->field = field_create_field($this->field);
+ $this->field_id = $this->field['id'];
+ $this->instance = array(
+ 'field_name' => $this->field_name,
+ 'bundle' => 'test_bundle',
+ 'label' => $this->randomName() . '_label',
+ 'description' => $this->randomName() . '_description',
+ 'weight' => mt_rand(0, 127),
+ 'settings' => array(
+ 'test_instance_setting' => $this->randomName(),
+ ),
+ 'widget' => array(
+ 'type' => 'test_field_widget',
+ 'label' => 'Test Field',
+ 'settings' => array(
+ 'test_widget_setting' => $this->randomName(),
+ )
+ ),
+ 'rdf_mapping' => array(
+ 'property' => array('dc:created'),
+ 'datatype' => 'xsd:dateTime',
+ 'callback' => 'date_iso8601',
+ )
+ );
+ field_create_instance($this->instance);
+ }
+
+ function testAttributesInMarkup() {
+ $entity_type = 'test_entity';
+ $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+ $langcode = FIELD_LANGUAGE_NONE;
+
+ // Populate values to be displayed.
+ $date = '2009-09-05';
+ $values = array(0 => array('value' => $date));
+ $entity->{$this->field_name}[$langcode] = $values;
+
+ // Simple formatter, label displayed.
+ $formatter_setting = $this->randomName();
+ $this->field['display'] = array(
+ 'full' => array(
+ 'label' => 'above',
+ 'type' => 'field_test_default',
+ 'settings' => array(
+ 'test_formatter_setting' => $formatter_setting,
+ )
+ ),
+ );
+
+ field_update_instance($this->instance);
+ $this->content = drupal_render(field_attach_view($entity_type, $entity));
+ $this->assertPattern('/property="dc:created"/');
+ $this->assertPattern('/datatype="xsd:dateTime"/');
+ $date_iso8601 = preg_quote(date(DATE_ISO8601, strtotime($date)));
+ $this->assertPattern("/content=\"$date_iso8601\"/");
+ }
+
+}
+
+/**
+ * Test the generation of the user RDF URI.
+ */
+class RdfUserLoadUnitTest extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'User RDF URI generation',
+ 'description' => 'Test the loading of a user and a node and make sure the user RDF URI is added to the respective object.',
+ 'group' => 'RDF',
+ );
+ }
+
+ function setUp() {
+ // Enable RDF in the test environment.
+ parent::setUp('rdf');
+ $web_user = $this->drupalCreateUser(array('create article content'));
+ $this->drupalLogin($web_user);
+ }
+
+ /**
+ * Create a user and ensure it has an RDF URI.
+ */
+ function testUserLoad() {
+ // Create a user.
+ $account = $this->drupalCreateUser(array());
+ // Load real user object.
+ $account = user_load($account->uid, TRUE);
+ $this->assertIdentical($account->rdf, array('uri' => base_path() . 'user/' . $account->uid . '#user'), t('The user RDF URI is present in the user object.'));
+ }
+
+ /**
+ * Create a node and ensure it has an RDF URI for the author of the node.
+ */
+ function testNodeLoad() {
+ $node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1));
+ $node = node_load($node->nid);
+ $this->assertIdentical($node->rdf['user'], array('uri' => base_path() . 'user/' . $node->uid . '#user'), t('The user RDF URI is present in the node object.'));
+ }
+}
=== added directory 'modules/rdf/tests'
=== added file 'modules/rdf/tests/rdf_test.info'
--- modules/rdf/tests/rdf_test.info 1970-01-01 00:00:00 +0000
+++ modules/rdf/tests/rdf_test.info 2009-09-11 11:23:02 +0000
@@ -0,0 +1,8 @@
+; $Id$
+name = "RDF module tests"
+description = "Support module for RDF module testing."
+package = Testing
+version = VERSION
+core = 7.x
+files[] = rdf_test.module
+hidden = TRUE
=== added file 'modules/rdf/tests/rdf_test.module'
--- modules/rdf/tests/rdf_test.module 1970-01-01 00:00:00 +0000
+++ modules/rdf/tests/rdf_test.module 2009-09-11 11:23:02 +0000
@@ -0,0 +1,99 @@
+ array(
+ 'rdftype' => array('sioc:Post'),
+ 'title' => array(
+ 'property' => array(
+ 'dc:title'
+ ),
+ ),
+ 'created' => array('property' => array('dc:created')),
+ 'uid' => array(
+ 'property' => array(
+ 'sioc:has-creator',
+ 'dc:creator',
+ ),
+ ),
+ 'foobar' => array(
+ 'property' => array(
+ 'foo:bar'
+ ),
+ ),
+ ),
+ 'rdf_test_another' => array(
+ 'rdftype' => array('sioc:User'),
+ 'title' => array(
+ 'property' => array('dc:title'),
+ ),
+ 'name' => array(
+ 'property' => array('dc:creator'),
+ ),
+ 'uid' => array(
+ 'property' => array(
+ 'sioc:has-creator',
+ 'dc:creator',
+ ),
+ ),
+ 'foobar' => array(
+ 'property' => array('foo:bar'),
+ ),
+ ),
+ 'rdf_complex_test' => array(
+ 'date' => array(
+ 'property' => array('dc:created'),
+ 'datatype' => 'xsd:dateTime',
+ 'callback' => 'date_iso8601',
+ ),
+ )
+ );
+}
+
+/**
+ * Implementation of hook_rdf_mapping_alter().
+ */
+function rdf_test_rdf_mapping_alter(&$mapping) {
+ // Add a single mapping without overriding anything.
+ $mapping['rdf_test_another']['name']['property'][] = 'foaf:name';
+
+ // Add a multi-value mapping without overwriting anything.
+ $mapping_addition = array(
+ 'rdf_test_another' => array(
+ 'title' => array(
+ 'property' => array('sioc:title'),
+ ),
+ ),
+ );
+ $mapping = array_merge_recursive($mapping, $mapping_addition);
+
+ // @TODO - in both above examples, what happens if we declare the same
+ // property more than once-- so that 'title' equals for instance
+ // array('foaf:Person', 'dc:title', 'foaf:Person')
+ // should/could we force values to unique at some point?
+
+ // Override (replace) a single property.
+ if (isset($mapping['rdf_test_another']['uid'])) {
+ $mapping['rdf_test_another']['uid']['property'] = array_diff($mapping['rdf_test_another']['uid']['property'], array('dc:creator'));
+ $mapping['rdf_test_another']['uid']['property'][] = 'foo:bar';
+ }
+
+ // Delete a specific property.
+ $mapping['rdf_test_another']['name']['property'] = array_diff($mapping['rdf_test_another']['name']['property'], array('dc:creator'));
+
+ // Delete all mappings for an attribute.
+ unset($mapping['rdf_test_another']['foobar']);
+
+ // Replace a mapping.
+ $mapping['rdf_test_another']['rdftype'] = array('foaf:Person');
+}
=== modified file 'modules/system/system.module'
--- modules/system/system.module 2009-09-11 02:14:20 +0000
+++ modules/system/system.module 2009-09-12 09:26:24 +0000
@@ -257,8 +257,10 @@ function system_rdf_namespaces() {
'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#',
'rss' => 'http://purl.org/rss/1.0/',
+ 'tags' => 'http://www.holygoat.co.uk/owl/redwood/0.1/tags/',
'sioc' => 'http://rdfs.org/sioc/ns#',
- 'xsd' => 'http://www.w3.org/2001/XMLSchema',
+ 'skos' => 'http://www.w3.org/2004/02/skos/core#',
+ 'xsd' => 'http://www.w3.org/2001/XMLSchema#',
);
}
=== modified file 'profiles/default/default.info'
--- profiles/default/default.info 2009-08-30 06:02:08 +0000
+++ profiles/default/default.info 2009-09-12 10:08:19 +0000
@@ -16,4 +16,5 @@ dependencies[] = search
dependencies[] = toolbar
dependencies[] = field_ui
dependencies[] = file
+dependencies[] = rdf
files[] = default.profile