From: fago --- includes/form.inc | 11 + modules/comment/comment.entity.inc | 13 + modules/comment/comment.module | 3 modules/field/field.info.inc | 22 ++- modules/field/field.module | 2 modules/node/node.entity.inc | 6 + modules/node/node.module | 7 + modules/node/node.tpl.php | 9 + modules/rdfa/rdfa.info | 7 + modules/rdfa/rdfa.module | 295 ++++++++++++++++++++++++++++++++++ modules/rdfa/rdfa.test | 153 ++++++++++++++++++ modules/system/page.tpl.php | 6 - modules/system/system.api.php | 17 ++ modules/system/system.module | 2 modules/taxonomy/taxonomy.entity.inc | 9 + modules/user/user.entity.inc | 3 themes/garland/node.tpl.php | 2 themes/garland/page.tpl.php | 4 themes/seven/page.tpl.php | 4 19 files changed, 559 insertions(+), 16 deletions(-) create mode 100644 modules/rdfa/rdfa.info create mode 100644 modules/rdfa/rdfa.module create mode 100644 modules/rdfa/rdfa.test diff --git includes/form.inc includes/form.inc index efa21c1..abb256e 100644 --- includes/form.inc +++ includes/form.inc @@ -2545,7 +2545,7 @@ function theme_textarea($element) { * * @param $element * An associative array containing the properties of the element. - * Properties used: #markup, #children. + * Properties used: #markup, #attributes, #children. * @return * A themed HTML string representing the HTML markup. * @@ -2553,7 +2553,14 @@ function theme_textarea($element) { */ function theme_markup($element) { - return (!empty($element['#markup']) ? $element['#markup'] : '') . drupal_render_children($element); + $output = (!empty($element['#markup']) ? $element['#markup'] : '') . drupal_render_children($element); + if (!empty($element['#attributes'])) { + // We cannot use a span as we might wrap block level elements. + $element['#attributes'] += array('style' => ''); + $element['#attributes']['style'] .= 'display: inline;'; + $output = '' . $output . ''; + } + return $output; } /** diff --git modules/comment/comment.entity.inc modules/comment/comment.entity.inc index afd8812..0fa631a 100644 --- modules/comment/comment.entity.inc +++ modules/comment/comment.entity.inc @@ -21,6 +21,8 @@ function comment_entity_info() { 'id' => 'cid', 'bundle' => 'node_type', ), + 'path' => 'comment', + 'rdf-type' => array('sioc:Post'), 'bundle keys' => array( 'bundle' => 'type', ), @@ -85,12 +87,14 @@ function comment_entity_info() { 'label' => t("Title"), 'description' => t("The title of the comment."), 'getter callback' => 'comment_get_properties', + 'rdf-property' => array('dc:title'), ); $properties['body'] = array( 'label' => t("Content"), 'description' => t("The formatted content of the comment itself."), 'getter callback' => 'comment_get_properties', 'setter callback' => 'drupal_property_verbatim_set', + 'rdf-property' => array('content:encoded'), ); $properties['url'] = array( 'label' => t("URL"), @@ -107,24 +111,33 @@ function comment_entity_info() { 'description' => t("The date the comment was posted."), 'type' => 'date', 'setter callback' => 'drupal_property_verbatim_set', + 'rdf-property' => array('dc:created'), ); $properties['parent'] = array( 'label' => t("Parent"), 'description' => t("The comment's parent, if comment threading is active."), 'type' => 'comment', 'getter callback' => 'comment_get_properties', + 'rdf-property' => array('sioc:reply_of'), ); $properties['node'] = array( 'label' => t("Node"), 'description' => t("The node the comment was posted to."), 'type' => 'node', 'getter callback' => 'comment_get_properties', + 'rdf-property' => array('sioc:reply_of'), ); $properties['author'] = array( 'label' => t("Author"), 'description' => t("The author of the comment, if they were logged in."), 'type' => 'user', 'getter callback' => 'comment_get_properties', + 'rdf-property' => array('sioc:has_creator'), + ); + $properties['rdf-uri'] = array( + 'label' => t("RDF URI"), + 'description' => t("The RDF URI of this entity."), + 'getter callback' => 'comment_get_properties', ); return $return; } diff --git modules/comment/comment.module modules/comment/comment.module index 22d50ca..c36c452 100644 --- modules/comment/comment.module +++ modules/comment/comment.module @@ -2428,6 +2428,9 @@ function comment_get_properties($comment, array $options, $name) { case 'url': return url('comment/' . $comment->cid, array('fragment' => 'comment-' . $comment->cid) + $options); + case 'rdf-uri': + return url('comment/' . $comment->cid, array('fragment' => 'comment-' . $comment->cid, 'absolute' => TRUE)); + case 'edit-url': return url('comment/edit/' . $comment->cid, $options); diff --git modules/field/field.info.inc modules/field/field.info.inc index f840886..e34247d 100644 --- modules/field/field.info.inc +++ modules/field/field.info.inc @@ -608,7 +608,7 @@ function field_info_formatter_settings($type) { /** * Implement hook_entity_info_alter(). * - * Add metadata about all properties provided by fields. + * Add metadata about all properties provided by fields and generate RDF URIs. */ function field_entity_info_alter(&$entity_info) { // Loop over all field instance and add them as property. @@ -627,6 +627,26 @@ function field_entity_info_alter(&$entity_info) { } } } + // Auto-generate RDF URIs for fieldable entities with a specified path. + foreach ($entity_info as $type => &$info) { + if (!empty($info['object keys']) && !empty($info['path']) && empty($info['properties']['rdf-uri'])) { + $info['properties']['rdf-uri'] = array( + 'label' => t("RDF URI"), + 'description' => t("The RDF URI of this entity."), + 'getter callback' => 'field_object_get_rdf_uri', + ); + } + } +} + +/** + * Callback to get the RDF URI of fieldable objects. + * @see field_entity_info_alter(). + */ +function field_object_get_rdf_uri($object, array $options = array(), $name = 'rdf-uri', $object_type) { + $info = entity_get_info($object_type); + list($id, $vid, $bundle) = field_extract_ids($object_type, $object); + return url($info['path'] . '/' . $id , array('absolute' => TRUE, 'fragment' => 'this')); } /** diff --git modules/field/field.module modules/field/field.module index 7f30f2a..a3433f7 100644 --- modules/field/field.module +++ modules/field/field.module @@ -727,6 +727,8 @@ function template_preprocess_field(&$variables) { $additions = array( 'object' => $element['#object'], + 'object_type' => $element['#object_type'], + 'bundle' => $bundle, 'field' => $field, 'instance' => $instance, 'build_mode' => $element['#build_mode'], diff --git modules/node/node.entity.inc modules/node/node.entity.inc index b34d68f..520121b 100644 --- modules/node/node.entity.inc +++ modules/node/node.entity.inc @@ -23,6 +23,8 @@ function node_entity_info() { 'revision' => 'vid', 'bundle' => 'type', ), + 'path' => 'node', + 'rdf-type' => array('sioc:Item', 'foaf:Document'), // Node.module handles its own caching. // 'cacheable' => FALSE, 'bundles' => array(), @@ -77,6 +79,7 @@ function node_entity_info() { 'label' => t("Title"), 'description' => t("The title of the node."), 'setter callback' => 'drupal_property_verbatim_set', + 'rdf-property' => array('dc:title'), ); $properties['language'] = array( 'label' => t("Language"), @@ -98,11 +101,13 @@ function node_entity_info() { 'type' => 'date', 'description' => t("The date the node was posted."), 'setter callback' => 'drupal_property_verbatim_set', + 'rdf-property' => array('dc:created'), ); $properties['changed'] = array( 'label' => t("Date changed"), 'type' => 'date', 'description' => t("The date the node was most recently updated."), + 'rdf-property' => array('dc:modified'), ); $properties['author-name'] = array( 'label' => t("Author name"), @@ -114,6 +119,7 @@ function node_entity_info() { 'type' => 'user', 'description' => t("The author of the node."), 'getter callback' => 'node_get_properties', + 'rdf-property' => array('sioc:has_creator'), ); return $return; diff --git modules/node/node.module modules/node/node.module index 411477a..0bb0400 100644 --- modules/node/node.module +++ modules/node/node.module @@ -524,6 +524,9 @@ function node_configure_fields($type) { 'type' => 'text_summary_or_trimmed', ), ), + 'property info' => array( + 'rdf-property' => array('content:encoded'), + ), ); field_create_instance($instance); } @@ -1108,8 +1111,8 @@ function template_preprocess_node(&$variables) { $variables['node'] = $variables['elements']['#node']; $node = $variables['node']; - $variables['date'] = format_date($node->created); - $variables['name'] = theme('username', $node); + $variables['date'] = array('#markup' => format_date($node->created)); + $variables['name'] = array('#markup' => theme('username', $node)); $variables['node_url'] = url('node/' . $node->nid); $variables['title'] = check_plain($node->title); $variables['page'] = (bool)menu_get_object(); diff --git modules/node/node.tpl.php modules/node/node.tpl.php index af2b0c5..8a7ea3e 100644 --- modules/node/node.tpl.php +++ modules/node/node.tpl.php @@ -12,9 +12,10 @@ * hide($content['field_example']) to temporarily suppress the printing of a * 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()). - * - $name: Themed username of node author output from theme_username(). + * - $date: An array containing the formatted creation date (use $created to + * reformat with format_date()). Use render($date) to print it. + * - $name: An array containing themed username of node author output from + * theme_username(). Use render($name) to print it. * - $node_url: Direct url of the current node. * - $terms: the themed list of taxonomy term links output from theme_links(). * - $display_submitted: whether submission information should be displayed. @@ -84,7 +85,7 @@ diff --git modules/rdfa/rdfa.info modules/rdfa/rdfa.info new file mode 100644 index 0000000..25d0045 --- /dev/null +++ modules/rdfa/rdfa.info @@ -0,0 +1,7 @@ +; $Id$ +name = RDFa +description = Enables RDFa output on your site. +package = Core +core = 7.x +files[] = rdfa.module +files[] = rdfa.test diff --git modules/rdfa/rdfa.module modules/rdfa/rdfa.module new file mode 100644 index 0000000..ecfb56c --- /dev/null +++ modules/rdfa/rdfa.module @@ -0,0 +1,295 @@ + t("Current page"), + 'rdf-type' => array('foaf:Document'), + ); + $properties = &$types['page']['properties']; + + $properties['title'] = array( + 'rdf-property' => array('dc:title'), + 'getter callback' => 'drupal_get_title', + ); + $properties['rdf-uri'] = array( + 'label' => t("RDF URI"), + 'description' => t("The RDF URI of the current page."), + 'getter callback' => 'rdfa_page_get_uri', + ); + return $types; +} + +/** + * Allows altering the RDF mapping of the currently served page. + * + * @param $properties + * An array keyed by the property name and containing an array of RDF related + * property info, which is to be used for the current page. + * @param $rdfa_type + * (optional) If set, the rdf-type of the page is set to the given value. + */ +function rdfa_alter_page_mapping(array $properties = array(), $rdfa_type = NULL) { + // To alter the mapping we change the entity info from a page by altering the + // static entity info variable. We can savely do so, as there can't be + // multiple instances of a page at the same time. + + // Make sure entity info is initialized and get a reference on it. + entity_get_info(); + $entity_info = &drupal_static('entity_get_info', array()); + $page = &$entity_info['page']; + + // Add in only rdf-specific info of the properties. + $rdfa_keys = array_fill_keys(array('rdf-property', 'rdf-value', 'rdf-datatype'), TRUE); + + foreach ($properties as $name => $info) { + $rdfa_info = array_intersect_key($info, $rdfa_keys); + $page['properties'][$name] = $rdfa_info + $page['properties'][$name]; + } + // Add in the rdf-type if given. + if (isset($rdfa_type)) { + $page['rdf-type'] = $rdfa_type; + } +} + +/** + * Returns RDFa attributes to render. + * + * @param $wrapper + * A DrupalEntityPropertyWrapper of the rendered entity. + * @param $property + * The name of the property to get attributes for. If set to NULL, the 'about' + * option defaults to TRUE, so RDFa about the entity is added in. + * @param $delta + * If the property has multiple values, an optional delta to return attributes + * for a single value with the given delta. + * @param $options + * (optional) An array of options, supported are: + * - 'about': If set to TRUE, add in RDFa about the entity. + * - 'resource': When refering to another entity, the resource attribute is + * added by default. To avoid that, set this to FALSE. + * @return array + * An array containing RDFa attributes as needed by drupal_attributes(). + */ +function rdfa_attributes(DrupalEntityPropertyWrapper $wrapper, $property = NULL, $delta = NULL, array $options = array()) { + $attributes = array(); + $options += array('about' => !isset($property), 'resource' => TRUE); + + if ($options['about'] && isset($wrapper->{'rdf-uri'})) { + // Generate attributes for the whole entity. + $attributes['about'] = (string)$wrapper->{'rdf-uri'}; + $info = $wrapper->getInfo() + array('rdf-type' => array()); + $attributes['typeof'] = (array)$info['rdf-type']; + + // Add in mappings defined for associated entity tags. + foreach ($wrapper->getTags() as $tag) { + if (!empty($info['tags'][$tag]['rdf-type'])) { + $attributes['typeof'] = array_merge((array)$info['tags'][$tag]['rdf-type'], $attributes['typeof']); + } + } + $attributes = array_filter($attributes); + } + + try { + // Add in property specific attributes. + if (isset($property) && isset($wrapper->$property) && ($info = $wrapper->getPropertyInfo($property))) { + $valueWrapper = $wrapper->$property; + // Handle the delta value, if it has been specified. + if (isset($delta)) { + $valueWrapper = isset($valueWrapper[$delta]) ? $valueWrapper[$delta] : NULL; + $info = isset($info['item']) ? $info['item'] : array(); + } + + if (isset($info['rdf-property']) && isset($valueWrapper)) { + $entity_info = entity_get_info(); + // When referring to an entity, we have to use the 'rel' attribute. + $name = isset($entity_info[$info['type']]) ? 'rel' : 'property'; + + if ($name == 'rel' && $options['resource'] && isset($valueWrapper->{'rdf-uri'})) { + $attributes['resource'] = (string)$valueWrapper->{'rdf-uri'}; + } + if (!empty($info['rdf-value']) && isset($valueWrapper->{$info['rdf-value']})) { + $attributes['content'] = (string)$valueWrapper->{$info['rdf-value']}; + } + if (isset($info['rdf-datatype'])) { + $attributes['datatype'] = $info['rdf-datatype']; + } + $attributes[$name] = $info['rdf-property']; + } + } + } + catch (DrupalPropertyWrapperException $e) { + // A needed value isn't available, so ignore it. + } + return $attributes; +} + +/** + * Implement hook_datatype_info(). + */ +function rdfa_datatype_info() { + // Add the iso8601 date format and use it for RDF. + $types['date'] = array(); + $types['date']['rdf-value'] = 'iso8601'; + $types['date']['rdf-datatype'] = 'xsd:dateTime'; + $types['date']['formats']['iso8601'] = array( + 'label' => t("ISO8601 formatted"), + 'description' => t("A date in ISO8601 format. (%date)", array('%date' => rdfa_date_iso8601(REQUEST_TIME))), + 'callback' => 'rdfa_date_iso8601', + ); + return $types; +} + +/** + * Returns an ISO8601 formatted date based on the given date. + * + * Can be used as a callback for mappings. + * + * @param $timestamp + * A date formatted as timestamp. + * @return string + * An ISO8601 formatted date. + */ +function rdfa_date_iso8601($timestamp) { + return date(DATE_ISO8601, $timestamp); +} + +/** + * Implement hook_node_view(). + */ +function rdfa_node_view($node, $build_mode) { + if ($build_mode == 'full' && menu_get_object() == $node && arg(0) == 'node') { + // Full page view, so use some node-properties for the page. + $node_info = entity_get_info('node'); + rdfa_alter_page_mapping(array('title' => $node_info['properties']['title']), $node_info['rdf-type']); + } +} + +/** + * Implement MODULE_preprocess_HOOK() for the node template. + */ +function rdfa_preprocess_node(&$variables) { + $node = $variables['node']; + $wrapper = drupal_get_property_wrapper('node', $node, array('sanitize' => TRUE)); + $variables['node_uri'] = $wrapper->{'rdf-uri'}; + + // Add about and typeof attributes. + $variables['attributes_array'] += rdfa_attributes($wrapper); + + $variables['title_attributes_array'] += rdfa_attributes($wrapper, 'title'); + $variables['date'] += array('#attributes' => array()); + $variables['date']['#attributes'] += rdfa_attributes($wrapper, 'created'); + $variables['name'] += array('#attributes' => array()); + $variables['name']['#attributes'] += rdfa_attributes($wrapper, 'author', NULL, array('resource' => FALSE)); + + // Support terms. + if (!empty($node->taxonomy)) { + $links = &$variables['content']['links']['terms']['#links']; + // Add the node -> term relation. + $variables['content']['links']['terms']['#attributes'] += rdfa_attributes($wrapper, 'taxonomy', 0, array('resource' => FALSE)); + foreach (array_values($node->taxonomy) as $key => $term) { + // Add RDFa for the term itself. + $links['taxonomy_term_' . $term->tid]['attributes'] += rdfa_attributes($wrapper->taxonomy[$key], 'name', NULL, array('about' => TRUE)); + } + } +} + +/** + * Implement MODULE_preprocess_HOOK() for the comment template. + */ +function rdfa_preprocess_comment(&$variables) { + $wrapper = drupal_get_property_wrapper('comment', $variables['comment'], array('sanitize' => TRUE)); + $variables['attributes_array'] += rdfa_attributes($wrapper); + $variables['title_attributes_array'] += rdfa_attributes($wrapper, 'title'); + $variables['content']['comment_body'] += array('#attributes' => array()); + $variables['content']['comment_body']['#attributes'] += rdfa_attributes($wrapper, 'body'); + // Add the relation to the node and the parent comment, if any. + $variables['content']['node'] = array('#markup' => rdfa_reference(rdfa_attributes($wrapper, 'node'))); + $variables['content']['parent'] = array('#markup' => rdfa_reference(rdfa_attributes($wrapper, 'parent'))); +} + +/** + * Returns an empty link just serving as RDFa reference. + * + * @param $attributes + * The RDFa attributes to add, as returned from rdfa_attributes(). + */ +function rdfa_reference(array $attributes = array()) { + if (isset($attributes['resource'])) { + // Rename the resource attribute to 'href'. + $attributes['href'] = $attributes['resource']; + unset($attributes['resource']); + } + return ''; +} + +/** + * Implement hook_page_build(). + */ +function rdfa_page_build(&$page) { + // Set the default rdf-uri for the current page. + $page['#uri'] = '#this'; +} + +/** + * Callback to get the RDF URI of a page. + * @see rdfa_entity_info(). + */ +function rdfa_page_get_uri(array $page) { + return $page['#uri']; +} + +/** + * Implement MODULE_preprocess_HOOK(). + */ +function rdfa_preprocess_page(&$variables) { + $wrapper = drupal_get_property_wrapper('page', $variables['page'], array('sanitize' => TRUE)); + + // HTML attributes which define what resource a page is about and optionally + // its RDF type. + $variables['attributes_array'] += rdfa_attributes($wrapper); + // Set the property for the title of the page. + $variables['title_attributes_array'] += rdfa_attributes($wrapper, 'title'); +} + +/** + * Adds RDFa attributes to the template variables. + */ +function rdfa_preprocess_field(&$variables) { + $wrapper = drupal_get_property_wrapper($variables['object_type'], $variables['object'], array('sanitize' => TRUE)); + $variables['attributes_array'] += rdfa_attributes($wrapper, $variables['field_name']); + + foreach ($variables['items'] as $delta => $item) { + $variables['item_attributes_array'][$delta] = rdfa_attributes($wrapper, $variables['field_name'], $delta); + } +} + +/** + * Implement hook_user_view(). + */ +function rdfa_user_view($account) { + if (menu_get_object('user_uid_optional') == $account && arg(0) == 'user') { + // We are viewing the account, so use the account-properties for the page. + $user_info = entity_get_info('user'); + rdfa_alter_page_mapping(array('title' => $user_info['properties']['name']), $user_info['rdf-type']); + } +} + +/** + * Implements MODULE_preprocess_HOOK(). + */ +function rdfa_preprocess_username(&$variables) { + $wrapper = drupal_get_property_wrapper('user', $variables['object']->account, array('sanitize' => TRUE)); + $variables['object']->attributes += rdfa_attributes($wrapper, 'name', NULL, array('about' => TRUE)); +} diff --git modules/rdfa/rdfa.test modules/rdfa/rdfa.test new file mode 100644 index 0000000..aba5016 --- /dev/null +++ modules/rdfa/rdfa.test @@ -0,0 +1,153 @@ + t('RDFa markup'), + 'description' => t('Test RDF markup generation.'), + 'group' => t('RDF'), + ); + } + + function setUp() { + // Enable RDF in the test environment. + parent::setUp('rdfa'); + } + + /** + * Test rdfa_attributes(). + */ + function testDrupalRdfaAtributes() { + + $node = $this->drupalCreateNode(); + $wrapper = drupal_get_property_wrapper('node', $node); + $attributes = rdfa_attributes($wrapper, 'created'); + + // Test getting attributes for the node created date. + $this->assertEqual('xsd:dateTime', $attributes['datatype'], 'Datatype returned.'); + $this->assertTrue(in_array('dc:created', $attributes['property']), 'Property returned.'); + $this->assertEqual(rdfa_date_iso8601($node->created), $attributes['content'], 'Content returned.'); + + // Test getting attributes for the node title. + $attributes = rdfa_attributes($wrapper, 'title'); + $this->assertEqual(array('property' => array('dc:title')), $attributes, 'Property returned.'); + } +} + +class RDFaTestCase extends DrupalWebTestCase { + + public static function getInfo() { + return array( + 'name' => t('Test RDFa output'), + 'description' => t('Ensure that RDFa is output on pages.'), + 'group' => t('RDF'), + ); + } + + function setUp() { + parent::setUp('rdfa'); + } + + function testUserRdfa() { + // Create a user with access to view user profiles. + $user = $this->drupalCreateUser(array('access user profiles')); + // Create a user profile to view. + $account = $this->drupalCreateUser(); + + $this->drupalLogin($user); + $this->drupalGet('user/' . $account->uid); + + $result = $this->xpath('//*[contains(@about, "#this") and contains(@typeof, "sioc:User")]'); + $this->assertFalse(empty($result), t('Found a typeof attribute including sioc:User')); + + $result = $this->xpath('//h2[contains(@property, "foaf:name")]'); + $this->assertFalse(empty($result), t('Found a h2 tag with property foaf:name')); + } + + function testNodeRdfa() { + $user = $this->drupalCreateUser(array('access content')); + // Create a node to view. + $node = $this->drupalCreateNode(); + + $this->drupalLogin($user); + $this->drupalGet('node/' . $node->nid); + + $result = $this->xpath('//*[contains(@about, "#this") and contains(@typeof, "foaf:Document")]'); + $this->assertFalse(empty($result), t('Found a typeof attribute including foaf:Document')); + + $result = $this->xpath('//h2[contains(@property, "dc:title")]'); + $this->assertFalse(empty($result), t('Found a h2 tag with property dc:title')); + } + + +} + + + +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', 'rdfa'); + + $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(), + ) + ), + 'property info' => array( + 'item' => array( + 'rdf-property' => array('dc:created'), + ), + ), + ); + 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' => strtotime($date))); + $entity->{$this->field_name}[$langcode] = $values; + + $wrapper = drupal_get_property_wrapper($entity_type, $entity); + + $this->content = drupal_render(field_attach_view($entity_type, $entity)); + + $this->assertPattern('/property="dc:created"/'); + $this->assertPattern('/datatype="xsd:dateTime"/'); + $date_iso8601 = preg_quote(rdfa_date_iso8601(strtotime($date))); + $this->assertPattern("/content=\"$date_iso8601\"/"); + } + +} diff --git modules/system/page.tpl.php modules/system/page.tpl.php index beb0e7a..41081b1 100644 --- modules/system/page.tpl.php +++ modules/system/page.tpl.php @@ -35,7 +35,9 @@ * administration interface. * * Page content (in order of occurrence in the default page.tpl.php): + * - $attributes: String of attributes referring to the whole page. * - $title: The page title, for use in the actual HTML content. + * - $title_attributes: String of attributes referring to the page title. * - $messages: HTML for status and error messages. Should be displayed prominently. * - $tabs: Tabs linking to any sub-pages beneath the current page (e.g., the view * and edit tabs when displaying a node). @@ -108,9 +110,9 @@
-
+
>
-

+

>

diff --git modules/system/system.api.php modules/system/system.api.php index 23cc1dc..7862dd8 100644 --- modules/system/system.api.php +++ modules/system/system.api.php @@ -28,6 +28,12 @@ * modules are supposed to build upon this hook to add further entity or entity * property related information. * + * To define the RDF mappings of an entity specify the attribute 'rdf-type' + * accordingly for an entity, bundle or entity tag. Furthermore each entity with + * ab RDF mapping needs a property 'rdf-uri', which is used to get its URI. + * However this property and the URI are automatically generated for entities + * with 'object keys' and 'path' specified. + * * @see entity_load() * @see hook_entity_info_alter() * @see drupal_get_property_wrapper() @@ -63,6 +69,9 @@ * the object (e.g. what nodes call "content type"). * This element can be omitted if this type has no bundles (all objects * have the same fields). + * - path: Optionally, the path that is used to view a object without the + * trailing id. The id of the viewed object is automatically appended. + * - rdf-type: An optional array describing a mapping to RDF classes. * - bundle keys: An array describing how the Field API can extract the * information it needs from the bundle objects for this type (e.g * $vocabulary objects for terms; not applicable for nodes). @@ -93,6 +102,7 @@ * - access arguments: As in hook_menu(). * - 'default tags': (optional) An array of entity tags that are usually * attached to that bundle. + * - rdf-type: An optional array describing a mapping to RDF classes. * - properties: An array describing the properties specific to this bundle * supporting the same keys as usual entity properties below. * - properties: An array describing the properties of an entity keyed by @@ -119,12 +129,19 @@ * to specify the bundle of the referenced entity beforehand. * - tags: If this property references another entity, this can be used to * specify an array of tags of the referenced entity beforehand. + * - rdf-property: The RDF properties to be used for generating RDF. May be + * a single string value or an array of values. + * - rdf-datatype: (optional) If given, the RDF datatype of the RDF value. + * - rdf-value: (optional) If given, the name of a format used to format the + * RDF value of the property. If not set, the default format (or display + * in case of RDFa) of the property determines the value. * - 'default tags': (optional) An array of entity tags that are usually * attached to this entity. * - tags: An array describing all entity tags for this entity type. Keys are * the actual tag names. * - label: The human-readable name of the entity tag. * - description: A human-readable description of the entity tag. + * - rdf-type: An optional array describing a mapping to RDF classes. * - 'name property': Specifies the property used to get the name of an * entity. If there is a property 'name', it's used by default. */ diff --git modules/system/system.module modules/system/system.module index 658ed3b..2a43931 100644 --- modules/system/system.module +++ modules/system/system.module @@ -257,7 +257,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', ); } diff --git modules/taxonomy/taxonomy.entity.inc modules/taxonomy/taxonomy.entity.inc index fa07547..1daf0b8 100644 --- modules/taxonomy/taxonomy.entity.inc +++ modules/taxonomy/taxonomy.entity.inc @@ -20,6 +20,8 @@ function taxonomy_entity_info() { 'id' => 'tid', 'bundle' => 'vocabulary_machine_name', ), + 'path' => 'taxonomy/term', + 'rdf-type' => array('skos:concept'), 'bundle keys' => array( 'bundle' => 'machine_name', ), @@ -55,12 +57,14 @@ function taxonomy_entity_info() { 'label' => t("Name"), 'description' => t("The name of the taxonomy term."), 'setter callback' => 'drupal_property_verbatim_set', + 'rdf-property' => array('skos:prefLabel'), ); $properties['description'] = array( 'label' => t("Description"), 'description' => t("The optional description of the taxonomy term."), 'sanitize' => 'filter_xss', 'setter callback' => 'drupal_property_verbatim_set', + 'rdf-property' => array('skos:note'), ); $properties['node-count'] = array( 'label' => t("Node count"), @@ -83,6 +87,7 @@ function taxonomy_entity_info() { 'description' => t("The parent term of the taxonomy term, if one exists."), 'getter callback' => 'taxonomy_term_get_properties', 'type' => 'taxonomy_term', + 'rdf-property' => array('skos:broader'), ); $return['taxonomy_vocabulary'] = array( @@ -142,5 +147,9 @@ function taxonomy_entity_info_alter(&$entity_info) { 'description' => t("The taxonomy terms associated with this node."), 'getter callback' => 'drupal_property_get_list', 'type' => 'list', + 'item' => array( + 'rdf-property' => array('sioc:topic'), + ), ); } + diff --git modules/user/user.entity.inc modules/user/user.entity.inc index f4d10e7..8d89161 100644 --- modules/user/user.entity.inc +++ modules/user/user.entity.inc @@ -19,6 +19,8 @@ function user_entity_info() { 'object keys' => array( 'id' => 'uid', ), + 'path' => 'user', + 'rdf-type' => array('sioc:User'), 'bundles' => array( 'user' => array( 'label' => t('User'), @@ -43,6 +45,7 @@ function user_entity_info() { 'description' => t("The login name of the user account."), 'getter callback' => 'user_get_properties', 'setter callback' => 'drupal_property_verbatim_set', + 'rdf-property' => array('foaf:name'), ); $properties['mail'] = array( 'label' => t("Email"), diff --git themes/garland/node.tpl.php themes/garland/node.tpl.php index 1939a66..2fc39b9 100644 --- themes/garland/node.tpl.php +++ themes/garland/node.tpl.php @@ -10,7 +10,7 @@ - +
diff --git themes/garland/page.tpl.php themes/garland/page.tpl.php index 0b3e16e..49148a7 100644 --- themes/garland/page.tpl.php +++ themes/garland/page.tpl.php @@ -4,7 +4,7 @@
-
+
>