Commit 24669bfb authored by Dries's avatar Dries
Browse files

- Patch #493030 by scor, Stefan Freudenberg, pwolanin, fago, Benjamin...

- Patch #493030 by scor, Stefan Freudenberg, pwolanin, fago, Benjamin Melançon, kriskras, dmitrig01, sun: added RDFa support to Drupal core. Oh my, oh my.
parent 0b750209
......@@ -184,3 +184,17 @@ function blog_block_view($delta = '') {
}
}
/**
* Implementation of hook_rdf_mapping().
*/
function blog_rdf_mapping() {
return array(
array(
'type' => 'node',
'bundle' => 'blog',
'mapping' => array(
'rdftype' => array('sioct:Weblog'),
)
),
);
}
......@@ -2494,3 +2494,38 @@ function comment_filter_format_delete($format, $fallback) {
->condition('format', $format->format)
->execute();
}
/**
* Implementation of hook_rdf_mapping().
*/
function comment_rdf_mapping() {
return array(
array(
'type' => 'comment',
'bundle' => RDF_DEFAULT_BUNDLE,
'mapping' => array(
'rdftype' => array('sioct:Post'),
'title' => array(
'predicates' => array('dc:title'),
),
'created' => array(
'predicates' => array('dc:date', 'dc:created'),
'datatype' => 'xsd:dateTime',
'callback' => 'date_iso8601',
),
'body' => array(
'predicates' => array('content:encoded'),
),
'pid' => array(
'predicates' => array('sioc:reply_of'),
),
'uid' => array(
'predicates' => array('sioc:has_creator'),
),
'name' => array(
'predicates' => array('foaf:name'),
),
),
),
);
}
......@@ -864,3 +864,52 @@ class CommentRSSUnitTest extends CommentHelperCase {
$this->assertNoRaw($raw, t('Hidden comments is not a part of RSS feed.'));
}
}
class RdfaCommentTestCase extends CommentHelperCase {
public static function getInfo() {
return array(
'name' => 'RDFa comment markup',
'description' => 'Test RDFa markup in comments.',
'group' => 'RDF',
);
}
function setUp() {
parent::setUp('comment', 'rdf');
$this->admin_user = $this->drupalCreateUser(array('administer content types', 'administer comments', 'administer permissions', 'administer blocks'));
$this->web_user = $this->drupalCreateUser(array('access comments', 'post comments', 'create article content'));
$this->drupalLogin($this->web_user);
$this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1));
$this->drupalLogout();
}
function testAttributesInMarkup() {
// Set comments to not have subject.
$this->drupalLogin($this->admin_user);
$this->setCommentPreview(FALSE);
$this->setCommentForm(TRUE);
$this->setCommentSubject(TRUE);
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, t('Comment paging changed.'));
$this->drupalLogout();
// Post comment.
$this->drupalLogin($this->web_user);
$subject_text = 'foo';
$comment_text = 'bar';
$comment = $this->postComment($this->node, $comment_text, $subject_text, FALSE);
$this->drupalGet('node/' . $this->node->nid);
$comment_container = $this->xpath("//div[contains(@class, 'comment') and @typeof='sioct:Post']");
$this->assertFalse(empty($comment_container));
$comment_title = $this->xpath("//h3[@property='dc:title']");
$this->assertEqual((string)$comment_title[0]->a, 'foo');
$comment_date = $this->xpath("//div[@typeof='sioct:Post']//*[contains(@property, 'dc:date') and contains(@property, 'dc:created')]");
$this->assertFalse(empty($comment_date));
$comment_author = $this->xpath("//div[@typeof='sioct:Post']//*[contains(@property, 'foaf:name')]");
$this->assertEqual((string)$comment_author[0], $this->web_user->name);
}
}
......@@ -1132,3 +1132,29 @@ function _forum_update_forum_index($nid) {
->execute();
}
}
/**
* Implementation of hook_rdf_mapping().
*/
function forum_rdf_mapping() {
return array(
array(
'type' => 'node',
'bundle' => 'forum',
'mapping' => array(
'rdftype' => array('sioct:Post', 'sioct:ForumTopic'),
'taxonomy_forums' => array(
'predicates' => array('sioc:has_container'),
'type' => 'rel',
),
),
),
array(
'type' => 'taxonomy_term',
'bundle' => 'forums',
'mapping' => array(
'rdftype' => array('sioct:Container'),
),
),
);
}
......@@ -753,6 +753,47 @@ function node_type_set_defaults($info = array()) {
return $new_type;
}
/**
* Define the default RDF mapping for the node entity type.
*
* These default mapping properties are used by rdf_save_mapping() to populate
* non-existing properties before they are saved to the database.
*
* @return
* A list of default mapping properties for the node entity type.
*/
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'),
),
'body' => array(
'predicates' => array('content:encoded'),
),
'uid' => array(
'predicates' => array('sioc:has_creator'),
),
'name' => array(
'predicates' => array('foaf:name'),
),
),
),
);
}
/**
* Determine whether a node hook exists.
*
......
......@@ -13,7 +13,7 @@
* given element.
* - $user_picture: The node author's picture from user-picture.tpl.php.
* - $date: Formatted creation date (use $created to reformat with
* format_date()).
* format_date()). This data is excepted to be sanitized beforehand.
* - $name: Themed username of node author output from theme_username().
* - $node_url: Direct url of the current node.
* - $terms: the themed list of taxonomy term links output from theme_links().
......@@ -88,8 +88,8 @@
<?php if ($display_submitted): ?>
<span class="submitted">
<?php
print t('Submitted by !username on @datetime',
array('!username' => $name, '@datetime' => $date));
print t('Submitted by !username on !datetime',
array('!username' => $name, '!datetime' => $date));
?>
</span>
<?php endif; ?>
......
<?php
// $Id$
/**
* @file
* Hooks provided by the RDF module.
*/
/**
* @addtogroup hooks
* @{
*/
/**
* Allow modules to define RDF mappings for bundles.
*
* Modules defining their own bundles can specify which RDF semantics should be
* used to annotate these bundles. These mappings are then used for automatic
* RDFa output in the HTML code.
*
* @return
* An array of mapping structures. Each mapping has three mandatory keys:
* - type: The name of an entity type.
* - bundle: The name of a bundle.
* - mapping: The mapping structure which applies to the entity type, bundle
* pair. A mapping structure is an array with keys corresponding to
* existing field instances in the bundle. Each field is then described in
* terms of RDF mapping. 'predicates' is an array of RDF predicates which
* describe the relation between the bundle (subject in RDF) and the value of
* the field (object in RDF), this value being either some text, another
* bundle or a URL in general. 'datatype' and 'callback' are used in RDFa to
* format data so that it's readable by machine: a typical example is a date
* which can be written in many different formats but should be translated
* into a uniform format for machine comsumption. 'type' is a string used to
* determine the type of RDFa markup which will be used in the final HTML
* output, depending on whether the RDF object is a literal text or another
* RDF resource. The 'rdftype' key is a special case which is used to define
* the type of the instance, its value shoud be an array of RDF classes.
*/
function hook_rdf_mapping() {
return array(
array(
'type' => 'node',
'bundle' => 'blog',
'mapping' => array(
'rdftype' => array('sioct:Weblog'),
'title' => array(
'predicates' => array('dc:title'),
),
'created' => array(
'predicates' => array('dc:date', 'dc:created'),
'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'),
),
)
),
);
}
/**
* @} End of "addtogroup hooks".
*/
; $Id$
name = RDF
description = Allows to map the site data structure to RDF and export it in RDFa.
package = Core
version = VERSION
core = 7.x
files[] = rdf.install
files[] = rdf.module
files[] = rdf.test
<?php
// $Id$
/**
* @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' => 'text',
'not null' => FALSE,
'size' => 'big',
'serialize' => TRUE,
),
),
'primary key' => array('type', 'bundle'),
);
return $schema;
}
/**
* Implements hook_install().
*/
function rdf_install() {
// The installer does not trigger hook_modules_installed() so it needs to
// triggered programmatically on the modules which defined RDF mappings.
$modules = module_implements('rdf_mapping');
rdf_modules_installed($modules);
}
This diff is collapsed.
<?php
// $Id$
class RdfMappingHookTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'RDF mapping hook',
'description' => 'Test hook_rdf_mapping().',
'group' => 'RDF',
);
}
function setUp() {
parent::setUp('rdf', 'rdf_test', 'field_test');
// We need to trigger rdf_modules_installed() because
// hook_modules_installed() is not automatically invoked during testing.
rdf_modules_installed(array('rdf_test'));
}
/**
* Test that hook_rdf_mapping() correctly returns and processes mapping.
*/
function testMapping() {
// Test that the mapping is returned correctly by the hook.
$mapping = rdf_get_mapping('test_entity', 'test_bundle');
$this->assertIdentical($mapping['rdftype'], array('sioc:Post'), t('Mapping for rdftype is sioc:Post.'));
$this->assertIdentical($mapping['title'], array('predicates' => array('dc:title')), t('Mapping for title is dc:title.'));
$this->assertIdentical($mapping['created'], array(
'predicates' => array('dc:created'),
'datatype' => 'xsd:dateTime',
'callback' => 'date_iso8601',
), t('Mapping for created is dc:created with datatype xsd:dateTime and callback date_iso8601.'));
$this->assertIdentical($mapping['uid'], array('predicates' => array('sioc:has_creator', 'dc:creator')), t('Mapping for uid is sioc:has_creator and dc:creator.'));
$mapping = rdf_get_mapping('test_entity', 'test_bundle_no_mapping');
$this->assertEqual($mapping, array(), t('Empty array returned when an entity type, bundle pair has no mapping.'));
}
}
class RdfMarkupTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'RDFa markup',
'description' => 'Test RDFa markup generation.',
'group' => 'RDF',
);
}
function setUp() {
parent::setUp('rdf', 'field_test', 'rdf_test');
rdf_modules_installed(array('field_test', 'rdf_test'));
}
/**
* Test drupal_rdfa_attributes().
*/
function testDrupalRdfaAtributes() {
$date = 1252750327;
$isoDate = date('c', $date);
$expected_type = 'xsd:dateTime';
$expected_property = array('dc:created');
$expected_value = $isoDate;
$mapping = rdf_get_mapping('test_entity', 'test_bundle');
$attributes = drupal_rdfa_attributes($mapping['created'], $date);
$this->assertEqual($expected_type, $attributes['datatype']);
$this->assertEqual($expected_property, $attributes['property']);
$this->assertEqual($expected_value, $attributes['content']);
}
}
class RdfCrudTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'RDF mapping CRUD functions',
'description' => 'Test the RDF mapping CRUD functions.',
'group' => 'RDF',
);
}
function setUp() {
parent::setUp('rdf', 'rdf_test');
}
function testCreateReadUpdateWrite() {
$test_mapping = rdf_test_rdf_mapping();
$this->assertTrue(is_array(rdf_read_mapping('test_entity', 'test_bundle')));
$this->assertEqual(count(rdf_read_mapping('test_entity', 'test_bundle')), 0);
$this->assertEqual(
rdf_create_mapping('test_entity', 'test_bundle', $test_mapping[0]['mapping']),
$test_mapping[0]['mapping']
);
try {
rdf_create_mapping('test_entity', 'test_bundle', $test_mapping[0]['mapping']);
$this->fail('No Exception thrown when attempting to insert the same mapping another time.');
}
catch (Exception $e) {
$this->pass('Exception thrown when attempting to insert the same mapping another time.');
}
$this->assertEqual($test_mapping[0]['mapping'],
rdf_read_mapping('test_entity', 'test_bundle'));
$this->assertTrue(rdf_update_mapping('test_entity', 'test_bundle',
$test_mapping[1]['mapping']));
$this->assertEqual($test_mapping[1]['mapping'],
rdf_read_mapping('test_entity', 'test_bundle'));
$this->assertTrue(rdf_delete_mapping('test_entity', 'test_bundle'));
$this->assertFalse(rdf_read_mapping('test_entity', 'test_bundle'));
}
function testSaveMapping() {
$test_mapping = rdf_test_rdf_mapping();
rdf_save_mapping($test_mapping[0]);
$this->assertEqual($test_mapping[0]['mapping'],
rdf_read_mapping('test_entity', 'test_bundle'));
}
}
class RdfMappingDefinitionTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'RDF mapping definition functionality',
'description' => 'Test the different types of RDF mappings and ensure the proper RDFa markup in included in node pages.',
'group' => 'RDF',
);
}
function setUp() {
parent::setUp('rdf', 'rdf_test', 'blog');
// We need to trigger rdf_modules_installed() because
// hook_modules_installed() is not automatically invoked during testing.
rdf_modules_installed(array('rdf_test', 'node'));
// entity_info caches must be cleared during testing. This is done
// automatically during the manual installation.
cache_clear_all('entity_info', 'cache');
drupal_static_reset('entity_get_info');
}
/**
* Create a node of type blog and test whether the RDF mapping defined for
* this node type in rdf_test.module is used in the node page.
*/
function testAttributesInMarkup1() {
$node = $this->drupalCreateNode(array('type' => 'blog'));
$this->drupalGet('node/' . $node->nid);
$this->assertRaw('typeof="sioct:Weblog"');
// Ensure the default bundle mapping for node is used. These attributes come
// from the node default bundle definition.
$this->assertRaw('property="dc:title"');
$this->assertRaw('property="dc:date dc:created"');
}
/**
* Create a content type and a node of type test_bundle_hook_install and test
* whether the RDF mapping defined in rdf_test.install is used.
*/
function testAttributesInMarkup2() {
$type = $this->drupalCreateContentType(array('type' => 'test_bundle_hook_install'));
$node = $this->drupalCreateNode(array('type' => 'test_bundle_hook_install'));
$this->drupalGet('node/' . $node->nid);
$this->assertRaw('typeof="foo:mapping_install1 bar:mapping_install2"');
// Ensure the default bundle mapping for node is used. These attributes come
// from the node default bundle definition.
$this->assertRaw('property="dc:title"');
$this->assertRaw('property="dc:date dc:created"');
}
/**
* Create a random content type and node and ensure the default mapping for
* node is used.
*/
function testAttributesInMarkup3() {
$type = $this->drupalCreateContentType();
$node = $this->drupalCreateNode(array('type' => $type->type));
$this->drupalGet('node/' . $node->nid);
$this->assertRaw('typeof="sioc:Item foaf:Document"');
// Ensure the default bundle mapping for node is used. These attributes come
// from the node default bundle definition.
$this->assertRaw('property="dc:title"');
$this->assertRaw('property="dc:date dc:created"');
}
}
; $Id$
name = "RDF module tests"
description = "Support module for RDF module testing."
package = Testing
version = VERSION
core = 7.x
files[] = rdf_test.install
files[] = rdf_test.module
hidden = TRUE
<?php
// $Id$
/**
* @file
* Install, update and uninstall functions for the rdf module.
*/
/**
* Implement 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'),
),
),
);
foreach ($rdf_mappings as $rdf_mapping) {
rdf_save_mapping($rdf_mapping);
}
}
<?php
// $Id$
/**
* @file
* Dummy module implementing RDF related hooks to test API interaction with
* the RDF module.
*/
/**
* Implementation of hook_rdf_mapping().
*/
function rdf_test_rdf_mapping() {
return array(
0 => array(
'type' => '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'),
),
'foobar' => array(
'predicates' => array('foo:bar'),
),
),
),