=== modified file 'includes/theme.inc' --- includes/theme.inc 2009-08-13 03:05:54 +0000 +++ includes/theme.inc 2009-08-17 10:47:55 +0000 @@ -1724,11 +1724,20 @@ function theme_username($object) { $name = $object->name; } + if (!empty($object->rdf_mapping['name'])) { + $name = '' . check_plain($name) . ''; + } + if (user_access('access user profiles')) { - $output = l($name, 'user/' . $object->uid, array('attributes' => array('title' => t('View user profile.')))); + if (!empty($object->rdf_mapping['uid']) && !empty($object->rdf['user']['uri'])) { + $output = '' . l($name, 'user/' . $object->uid, array('attributes' => array('title' => t('View user profile.'), 'about' => $object->rdf['user']['uri']), 'html' => TRUE)) . ''; + } + else { + $output = l($name, 'user/' . $object->uid, array('attributes' => array('title' => t('View user profile.')), 'html' => TRUE)); + } } else { - $output = check_plain($name); + $output = $name; } } elseif ($object->name) { @@ -1929,6 +1938,30 @@ function template_preprocess_page(&$vari $variables['site_slogan'] = (theme_get_setting('toggle_slogan') ? filter_xss_admin(variable_get('site_slogan', '')) : ''); $variables['tabs'] = theme('menu_local_tasks'); $variables['title'] = drupal_get_title(); + + // The variable rdfa_page_about contains the HTML attributes which define + // what resource a page is about and optionally its RDF type. + $variables['rdfa_page_about'] = ''; + // The variable rdfa_page_title contains the HTML attributes which define + // which property should be used for the title of a page. + $variables['rdfa_page_title'] = ''; + + if (module_exists('rdf')) { + // Contruct the RDFa markup. + $rdf_page_mapping = drupal_get_rdf_page_mapping(); + if (!empty($rdf_page_mapping['rdftype'])) { + $variables['rdfa_page_about'] = ' about="#this" typeof="' . implode(' ', (array)$rdf_page_mapping['rdftype']) . '"'; + } + else { + // Even if we don't have an RDF type for the resource described on the page + // we create a URI for it. + $variables['rdfa_page_about'] = ' about="#this"'; + } + if (!empty($rdf_page_mapping['title'])) { + $variables['rdfa_page_title'] = ' property="' . implode(' ', (array)$rdf_page_mapping['title']) . '"'; + } + } + // RDFa allows annotation of XHTML pages with RDF data, while GRDDL provides // mechanisms for extraction of this RDF content via XSLT transformation // using an associated GRDDL profile. === 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-08-14 19:00:38 +0000 @@ -0,0 +1,69 @@ + array( + 'rdftype' => array('sioc:Post'), + 'title' => array('dc:title'), + 'created' => array( + 'properties' => 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( + 'properties' => 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-08-14 19:00:39 +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-08-15 12:25:33 +0000 @@ -0,0 +1,137 @@ + 'foaf:Document', + 'title' => '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 for output in XHTML elements. + * + * @param $mapping + * An array containing either a simple mapping or a complex array with + * property, datatype and callback. + * @param $data + * @return string + * An XHTML string ready for insertion into a tag. + */ +function drupal_rdfa_attributes($mapping, $data = NULL) { + if (!isset($mapping['property'])) { + $attributes['property'] = implode(' ', $mapping); + } + else { + $attributes['property'] = implode(' ', $mapping['property']); + + if (isset($mapping['callback']) && isset($data)) { + $callback = $mapping['callback']; + + if (drupal_function_exists($callback)) { + $attributes['content'] = call_user_func($callback, $data); + } + if (isset($mapping['datatype'])) { + $attributes['datatype'] = $mapping['datatype']; + } + } + } + + return drupal_attributes($attributes); +} + +/** + * Implementation of hook_node_load(). + */ +function rdf_node_load($nodes, $types) { + // The node_load function does not reuse user_load(), so we need to regenerate + // the URI of the author of the node. + foreach ($nodes as $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)); + } +} + +/** + * 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'; +} === added file 'modules/rdf/rdf.test' --- modules/rdf/rdf.test 1970-01-01 00:00:00 +0000 +++ modules/rdf/rdf.test 2009-08-14 19:00:39 +0000 @@ -0,0 +1,144 @@ + 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('dc:title'), t('Mapping for title is dc:title.')); + $this->assertIdentical($mapping['created'], array('dc:created'), t('Mapping for created is dc:created.')); + $this->assertTrue(in_array('sioc:has-creator', $mapping['uid']), t('Mapping for uid contains sioc:has-creator')); + $this->assertTrue(in_array('dc:creator', $mapping['uid']), 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']), t('Mapping for title contains dc:title')); + $this->assertTrue(in_array('sioc:title', $mapping['title']), t('Mapping for title contains sioc:title')); + $this->assertTrue(in_array('sioc:has-creator', $mapping['uid']), t('Mapping for uid contains sioc:has-creator')); + $this->assertTrue(in_array('foo:bar', $mapping['uid']), t('Mapping for uid contains foo:bar')); + $this->assertFalse(in_array('dc:creator', $mapping['uid']), t('Mapping for uid does not contain dc:creator')); + $this->assertTrue(in_array('foaf:Person', $mapping['name']), t('Mapping for name contains foaf:Person')); + $this->assertFalse(in_array('dc:creator', $mapping['name']), 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' => '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' => 'foo:bar')); + $this->assertEqual(drupal_get_rdf_page_mapping(), array('foobar' => '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)); + + $expectedType = 'datatype="xsd:dateTime"'; + $expectedProperty = 'property="dc:created"'; + $expectedValue = "content=\"$isoDate\""; + $mapping = rdf_get_mapping('rdf_complex_test'); + $attributes = drupal_rdfa_attributes($mapping['date'], $date); + + $this->assertNotEqual(FALSE, strpos($attributes, $expectedType)); + $this->assertNotEqual(FALSE, strpos($attributes, $expectedProperty)); + $this->assertNotEqual(FALSE, strpos($attributes, $expectedValue)); + + $expectedProperty = 'property="dc:title"'; + $mapping = rdf_get_mapping('rdf_test_container'); + $attributes = drupal_rdfa_attributes($mapping['title']); + + $this->assertNotEqual(FALSE, strpos($attributes, $expectedProperty)); + } +} + + +/** + * 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-05-12 16:33:28 +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-08-14 19:00:39 +0000 @@ -0,0 +1,83 @@ + array( + 'rdftype' => array('sioc:Post'), + 'title' => array('dc:title'), + 'created' => array('dc:created'), + 'uid' => array( + 'sioc:has-creator', + 'dc:creator', + ), + 'foobar' => array('foo:bar'), + ), + 'rdf_test_another' => array( + 'title' => array('dc:title'), + 'rdftype' => array('sioc:User'), + 'name' => array('dc:creator'), + 'uid' => array( + 'sioc:has-creator', + 'dc:creator', + ), + 'foobar' => array('foo:bar'), + ), + 'rdf_complex_test' => array( + 'date' => array( + 'datatype' => 'xsd:dateTime', + 'callback' => 'rdf_test_date', + 'property' => array('dc:created'), + ), + ) + ); +} + +/** + * 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'][] = 'foaf:Person'; + + // Add a multi-value mapping without overwriting anything. + $mapping_addition = array( + 'rdf_test_another' => array( + 'title' => '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'] = array_diff($mapping['rdf_test_another']['uid'], array('dc:creator')); + $mapping['rdf_test_another']['uid'][] = 'foo:bar'; + } + + // Delete a specific property. + $mapping['rdf_test_another']['name'] = array_diff((array)$mapping['rdf_test_another']['name'], 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'); +} + +function rdf_test_date($date) { + return date(DATE_ISO8601, strtotime($date)); +} === modified file 'modules/system/page.tpl.php' --- modules/system/page.tpl.php 2009-08-03 03:04:33 +0000 +++ modules/system/page.tpl.php 2009-08-17 10:45:23 +0000 @@ -166,9 +166,9 @@
-
+
>
-

+

>

=== modified file 'modules/system/system.module' --- modules/system/system.module 2009-08-17 07:12:15 +0000 +++ modules/system/system.module 2009-08-17 10:43:39 +0000 @@ -249,7 +249,9 @@ 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#', + 'skos' => 'http://www.w3.org/2004/02/skos/core#', 'xsd' => 'http://www.w3.org/2001/XMLSchema', ); }