diff --git a/modules/blog/blog.module b/modules/blog/blog.module
index 600a8d33f4198682715161c1ad6edbe477b3ab2f..ac5627017783af311a6c5f524297b9bf58e411df 100644
--- a/modules/blog/blog.module
+++ b/modules/blog/blog.module
@@ -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'),
+      )
+    ),
+  );
+} 
diff --git a/modules/comment/comment.module b/modules/comment/comment.module
index ff88f6635a445d12489577c6f3cb17ab0894fa45..718a41921d531e4416134f18d24e45dc1eaceb82 100644
--- a/modules/comment/comment.module
+++ b/modules/comment/comment.module
@@ -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'),
+        ),
+      ),
+    ),
+  );
+}
diff --git a/modules/comment/comment.test b/modules/comment/comment.test
index e6d30d04c40eb06cc40b30be7123bdd75bb1f315..7987db6226782f29b135c404ae4834056c265bee 100644
--- a/modules/comment/comment.test
+++ b/modules/comment/comment.test
@@ -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);
+  }
+
+}
diff --git a/modules/forum/forum.module b/modules/forum/forum.module
index da83c86f6a5d4dde3232ce0b8da3f2a81fba763a..e22a1c6ec9d1450372fd5f639d6b09b071d453f9 100644
--- a/modules/forum/forum.module
+++ b/modules/forum/forum.module
@@ -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'),
+      ),
+    ),
+  );
+}
diff --git a/modules/node/node.module b/modules/node/node.module
index de59e22f7b33850e24a8880075fa2f5ba67d2890..b0301e854a5b8ff93b307188807e438a5b475cbc 100644
--- a/modules/node/node.module
+++ b/modules/node/node.module
@@ -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.
  *
diff --git a/modules/node/node.tpl.php b/modules/node/node.tpl.php
index ae03d400d7800cbe83a6c659ec95b222cbf0b18b..3b50ebcd48a3166ec9e790b8ee43d00004e4c22b 100644
--- a/modules/node/node.tpl.php
+++ b/modules/node/node.tpl.php
@@ -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; ?>
diff --git a/modules/rdf/rdf.api.php b/modules/rdf/rdf.api.php
new file mode 100644
index 0000000000000000000000000000000000000000..0635b7faff5236e6d759757913d8568b76170290
--- /dev/null
+++ b/modules/rdf/rdf.api.php
@@ -0,0 +1,72 @@
+<?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".
+ */
diff --git a/modules/rdf/rdf.info b/modules/rdf/rdf.info
new file mode 100644
index 0000000000000000000000000000000000000000..aab433471083eab5880a0c8ea31b2c88752a9b5c
--- /dev/null
+++ b/modules/rdf/rdf.info
@@ -0,0 +1,9 @@
+; $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
diff --git a/modules/rdf/rdf.install b/modules/rdf/rdf.install
new file mode 100644
index 0000000000000000000000000000000000000000..0467842a6eefa462bc90d9c27ceb410b661d1453
--- /dev/null
+++ b/modules/rdf/rdf.install
@@ -0,0 +1,50 @@
+<?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);
+}
diff --git a/modules/rdf/rdf.module b/modules/rdf/rdf.module
new file mode 100644
index 0000000000000000000000000000000000000000..a50d645db616cf6434b8d62afe4ed0773014617f
--- /dev/null
+++ b/modules/rdf/rdf.module
@@ -0,0 +1,590 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Enables semantically enriched output for Drupal sites.
+ *
+ * This module introduces RDFa to Drupal, which provides a set of XHTML
+ * attributes to augment visual data with machine-readable hints.
+ * @see http://www.w3.org/TR/xhtml-rdfa-primer/
+ *
+ * Modules can provide mappings of their bundles' data and metadata to RDFa
+ * properties using the appropriate vocabularies. This module takes care of
+ * injecting that data into variables available to themers in the .tpl files.
+ * Drupal core themes ship with RDFa output enabled.
+ *
+ * Example mapping from node.module:
+ *   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',
+ *      ),
+ *      'body'    => array(
+ *        'predicates' => array('content:encoded'),
+ *      ),
+ *      'uid'     => array(
+ *        'predicates' => array('sioc:has_creator'),
+ *      ),
+ *      'name'    => array(
+ *        'predicates' => array('foaf:name'),
+ *      ),
+ *    ),
+ *  );
+ */
+
+/**
+ * Defines the empty string as the name of the bundle to store default
+ * RDF mappings of a type's properties (fields, et. al.).
+ */
+define('RDF_DEFAULT_BUNDLE', '');
+
+/**
+ * Implements hook_theme().
+ */
+function rdf_theme() {
+  return array(
+    'rdf_template_variable_wrapper' => array(
+      'arguments' => array('content' => NULL, 'attributes' => array(), 'context' => array(), 'inline' => TRUE),
+    ),
+    'rdf_metadata' => array(
+      'arguments' => array('metadata' => array()),
+    ),
+  );
+}
+
+ /**
+ * Wraps a template variable in an HTML element with the desired attributes.
+ *
+ * @ingroup themeable
+ */
+function theme_rdf_template_variable_wrapper($variables) {
+  $output = $variables['content'];
+  if (!empty($output) && !empty($variables['attributes'])) {
+    $attributes = drupal_attributes($variables['attributes']);
+    $output = $variables['inline'] ? "<span$attributes>$output</span>" : "<div$attributes>$output</div>";
+  }
+  return $output;
+}
+
+ /**
+ * Outputs a series of empty spans for exporting RDF metadata in RDFa.
+ *
+ * Sometimes it is useful to export data which is not semantically present in
+ * the HTML output. For example, a hierarchy of comments is visible for a human
+ * but not for machines because this hiearchy is not present in the DOM tree.
+ * We can express it in RDFa via empty span tags. These won't be visible and
+ * will give machines extra information about the content and its structure.
+ *
+ * @ingroup themeable
+ */
+function theme_rdf_metadata($variables) {
+  $output = '';
+  foreach ($variables['metadata'] as $attributes) {
+    $output .= '<span' . drupal_attributes($attributes) . ' />';
+  }
+  return $output;
+}
+
+ /**
+ * Process function for wrapping some content with an extra tag.
+ */
+function rdf_process(&$variables, $hook) {
+  if (!empty($variables['rdf_variable_attributes_array'])) {
+    foreach ($variables['rdf_variable_attributes_array'] as $variable_name => $attributes) {
+      $context = array('hook' => $hook, 'variable_name' => $variable_name, 'variables' => $variables);
+      $variables[$variable_name] = theme('rdf_template_variable_wrapper', array('content' => $variables[$variable_name], 'attributes' => $attributes, 'context' => $context));
+    }
+  }
+
+  if (!empty($variables['metadata_attributes_array'])) {
+    if (!isset($variables['content']['#prefix'])) {
+      $variables['content']['#prefix'] = '';
+    }
+    $variables['content']['#prefix'] = theme('rdf_metadata', array('metadata' => $variables['metadata_attributes_array'])) . $variables['content']['#prefix'];
+  }
+
+
+}
+
+/**
+ * Returns the mapping for the attributes of the given type, bundle pair.
+ *
+ * @param $type
+ *   An entity type.
+ * @param $bundle
+ *   A bundle name.
+ * @return array
+ *   The mapping corresponding to the requested type, bundle pair or an empty
+ *   array.
+ */
+function rdf_get_mapping($type, $bundle = RDF_DEFAULT_BUNDLE) {
+  // Retrieve the 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'];
+  }
+  else {
+    return _rdf_get_default_mapping($type);
+  }
+}
+
+/**
+ * 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
+ * type, bundle pair. If available, adds default values for non-existent
+ * mapping keys.
+ *
+ * @param $mapping
+ *   The RDF mapping to save, as an array.
+ * @return
+ *   Status flag indicating the outcome of the operation.
+ */
+function rdf_save_mapping($mapping) {
+  // Adds default values for non-existent keys.
+  $new_mapping = $mapping['mapping'] + _rdf_get_default_mapping($mapping['type']);
+  $exists = (bool)rdf_read_mapping($mapping['type'], $mapping['bundle']);
+
+  if ($exists) {
+    rdf_update_mapping($mapping['type'], $mapping['bundle'], $new_mapping);
+    return SAVED_UPDATED;
+  }
+  else {
+    rdf_create_mapping($mapping['type'], $mapping['bundle'], $new_mapping);
+    return SAVED_NEW;
+  }
+
+  cache_clear_all('entity_info', 'cache');
+  drupal_static_reset('entity_get_info');
+}
+
+/**
+ * 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(), we do not want to save the default entity
+ * mappings in the database because users are not expected to alter these.
+ * Instead they should alter specific bundle mappings which are stored in the
+ * database so that they can be altered via the RDF CRUD mapping API.
+ */
+function rdf_modules_installed($modules) {
+  // We need to clear the caches of entity_info as this is not done right
+  // during the tests. see http://drupal.org/node/594234
+  cache_clear_all('entity_info', 'cache');
+  drupal_static_reset('entity_get_info');
+
+  foreach ($modules as $module) {
+    if (function_exists($module . '_rdf_mapping')) {
+      $mapping_array = call_user_func($module . '_rdf_mapping');
+      foreach ($mapping_array as $mapping) {
+        // Only the bundle mappings are saved in the database.
+        if ($mapping['bundle'] != RDF_DEFAULT_BUNDLE) {
+          rdf_save_mapping($mapping);
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_modules_uninstalled().
+ */
+function rdf_modules_uninstalled($modules) {
+// @todo remove the RDF mappings.
+}
+
+/**
+ * Implements hook_entity_info_alter().
+ *
+ * Adds the proper RDF mapping to each entity type, bundle pair.
+ */
+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 (isset($entity_type_info['bundles'])) {
+      foreach ($entity_type_info['bundles'] as $bundle => $bundle_info) {
+        if ($mapping = rdf_read_mapping($entity_type, $bundle)) {
+          $entity_info[$entity_type]['bundles'][$bundle]['rdf_mapping'] = $mapping;
+        }
+        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);
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Returns ready to render RDFa attributes for the given mapping.
+ *
+ * @param $mapping
+ *   An array containing a mandatory predicates key and optional datatype,
+ *   callback and type keys.
+ *   Example:
+ *   array(
+ *     'predicates' => 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) {
+  // The type of mapping defaults to 'property'.
+  $type = isset($mapping['type']) ? $mapping['type'] : 'property';
+
+  switch ($type) {
+  // The mapping expresses the relationship between two resources.
+  case 'rel':
+  case 'rev':
+    $attributes[$type] = $mapping['predicates'];
+    break;
+
+  // The mapping expressed the relationship between a resource and some
+  // literal text.
+  case 'property':
+    $attributes['property'] = $mapping['predicates'];
+
+    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'];
+      }
+    }
+    break;
+  }
+
+  return $attributes;
+}
+
+
+/**
+ * Implements hook_entity_load().
+ */
+function rdf_entity_load($entities, $type) {
+  foreach ($entities as $entity) {
+    // Extracts the bundle of the entity being loaded.
+    list($id, $vid, $bundle) = field_extract_ids($type, $entity);
+    $entity->rdf_mapping = rdf_get_mapping($type, $bundle);
+  }
+}
+
+/**
+ * Implements 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'] = empty($variables['node_url']) ? NULL: $variables['node_url'];
+  $variables['attributes_array']['typeof'] = empty($variables['node']->rdf_mapping['rdftype']) ? NULL : $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'] = empty($variables['node']->rdf_mapping['title']['predicates']) ? NULL : $variables['node']->rdf_mapping['title']['predicates'];
+  $variables['title_attributes_array']['datatype'] = '';
+
+  // In full node mode, the title is not displayed by node.tpl.php so it is
+  // added in the head tag of the HTML page.
+  if ($variables['page']) {
+    $title_attributes['property'] = empty($variables['node']->rdf_mapping['title']['predicates']) ? NULL : $variables['node']->rdf_mapping['title']['predicates'];
+    $title_attributes['content'] = $variables['node_title'];
+    $title_attributes['about'] = $variables['node_url'];
+    drupal_add_html_head('<meta' . drupal_attributes($title_attributes) . ' />');
+  }
+
+  // Add RDFa markup for the date.
+  if (!empty($variables['rdf_mapping']['created'])) {
+    $date_attributes_array = drupal_rdfa_attributes($variables['rdf_mapping']['created'], $variables['created']);
+    $variables['rdf_variable_attributes_array']['date'] = $date_attributes_array;
+  }
+}
+
+/**
+ * Implements MODULE_preprocess_HOOK().
+ */
+function rdf_preprocess_field(&$variables) {
+  $entity_type = $variables['element']['#object_type'];
+  $instance = $variables['instance'];
+  $mapping = rdf_get_mapping($entity_type, $instance['bundle']);
+  $field_name = $instance['field_name'];
+
+  if (!empty($mapping) && !empty($mapping[$field_name])) {
+    foreach ($variables['items'] as $delta => $item) {
+      if (!empty($item['#item'])) {
+        $variables['item_attributes_array'][$delta] = drupal_rdfa_attributes($mapping[$field_name], $item['#item']);
+      }
+    }
+  }
+}
+
+
+/**
+ * Implements MODULE_preprocess_HOOK().
+ */
+function rdf_preprocess_user_profile(&$variables) {
+  // Adds RDFa markup to the user profile page. Fields displayed in this page
+  // will automatically describe the user.
+  // @todo move to user.module
+  $account = user_load($variables['user']->uid);
+  if (!empty($account->rdf_mapping['rdftype'])) {
+    $variables['attributes_array']['typeof'] = $account->rdf_mapping['rdftype'];
+    $variables['attributes_array']['about'] = url('user/' . $account->uid);
+  }
+}
+
+/**
+ * Implements MODULE_preprocess_HOOK().
+ */
+function rdf_preprocess_username(&$variables) {
+  $account = $variables['account'];
+  if (!empty($account->rdf_mapping['name'])) {
+    if ($account->uid != 0) {
+      // The following RDFa construct allows to fit all the needed information
+      // into the a tag and avoids having to wrap it with an extra span.
+
+      // An RDF resource for the user is created with the 'about' attribute and
+      // the profile URI is used to identify this resource. Even if the user
+      // profile is not accessible, we generate its URI regardless in order to
+      // be able to identify the user in RDF.
+      $variables['attributes_array']['about'] = url('user/' . $account->uid);
+      // The 'typeof' attribute specifies the RDF type(s) of this resource. They
+      // are defined in the 'rdftype' property of the user object RDF mapping.
+      // Since the full user object is not available in $variables, it needs to
+      // be loaded. This is due to the collision between the node and user
+      // when they are merged into $account and some properties are overridden.
+      $variables['attributes_array']['typeof'] = user_load($account->uid)->rdf_mapping['rdftype'];
+
+      // This first thing we are describing is the relation between the user and
+      // the parent resource (e.g. a node). Because the set of predicate link
+      // the parent to the user, we must use the 'rev' RDFa attribute to specify
+      // that the relationship is reverse.
+      if (!empty($account->rdf_mapping['uid']['predicates'])) {
+        $variables['attributes_array']['rev'] = $account->rdf_mapping['uid']['predicates'];
+        // We indicate the parent identifier in the 'resource' attribute,
+        // typically this is the entity URI. This is the object in RDF.
+        $parent_uri = '';
+        if (!empty($account->path['source'])) {
+          $parent_uri = url($account->path['source']);
+        }
+        elseif (!empty($account->cid)) {
+          $parent_uri = url('comment/' . $account->cid, array('fragment' => 'comment-' . $account->cid));
+        }
+        $variables['attributes_array']['resource'] = $parent_uri;
+      }
+
+      // The second information we annotate is the name of the user with the
+      // 'property' attribute. We do not need to specify the RDF object here
+      // because it's the value inside the a tag which will be used
+      // automatically according to the RDFa parsing rules.
+      $variables['attributes_array']['property'] = $account->rdf_mapping['name']['predicates'];
+    }
+  }
+}
+
+/**
+ * Implements MODULE_preprocess_HOOK().
+ */
+function rdf_preprocess_comment(&$variables) {
+  $comment = $variables['comment'];
+  if (!empty($comment->rdf_mapping['rdftype'])) {
+    // Add RDFa markup to the comment container. The about attribute specifies
+    // the URI of the resource described within the HTML element, while the
+    // typeof attribute indicates its RDF type (e.g. sioc:Post, etc.).
+    $variables['attributes_array']['about'] = url('comment/' . $comment->cid, array('fragment' => 'comment-' . $comment->cid));
+    $variables['attributes_array']['typeof'] = $comment->rdf_mapping['rdftype'];
+  }
+
+  // RDFa markup for the date of the comment.
+  if (!empty($comment->rdf_mapping['created'])) {
+    $date_attributes_array = drupal_rdfa_attributes($comment->rdf_mapping['created'], $comment->created);
+    $variables['rdf_variable_attributes_array']['created'] = $date_attributes_array;
+  }
+  if (!empty($comment->rdf_mapping['title'])) {
+    // Add RDFa markup to the subject of the comment. Because the RDFa markup is
+    // added to an h3 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'] = $comment->rdf_mapping['title']['predicates'];
+    $variables['title_attributes_array']['datatype'] = '';
+  }
+  if (!empty($comment->rdf_mapping['body'])) {
+    // We need a special case here since the comment body is not a field. Note
+    // that for that reason, fields attached to comment will be ignored by RDFa
+    // parsers since we set the property attribute here.
+    // @todo use fields instead, see http://drupal.org/node/538164
+    $variables['content_attributes_array']['property'] = $comment->rdf_mapping['body']['predicates'];
+  }
+
+  // Annotates the parent relationship between the current comment and the node
+  // it belongs to. If available, the parent comment is also annotated.
+  if (!empty($comment->rdf_mapping['pid'])) {
+    // Relation to parent node.
+    $parent_node_attributes['rel'] = $comment->rdf_mapping['pid']['predicates'];
+    $parent_node_attributes['resource'] = url('node/' . $comment->nid);
+    $variables['metadata_attributes_array'][] = $parent_node_attributes;
+
+    // Relation to parent comment if it exists.
+    if ($comment->pid != 0) {
+      $parent_comment_attributes['rel'] = $comment->rdf_mapping['pid']['predicates'];
+      $parent_comment_attributes['resource'] = url('comment/' . $comment->pid, array('fragment' => 'comment-' . $comment->pid));
+      $variables['metadata_attributes_array'][] = $parent_comment_attributes;
+    }
+  }
+}
+
+/**
+ * Implements MODULE_preprocess_HOOK().
+ */
+function rdf_preprocess_field_formatter_taxonomy_term_link(&$variables) {
+  $term = $variables['element']['#item']['taxonomy_term'];
+  if (!empty($term->rdf_mapping['rdftype'])) {
+    $variables['link_options']['attributes']['typeof'] = $term->rdf_mapping['rdftype'];
+  }
+  if (!empty($term->rdf_mapping['name']['predicates'])) {
+    $variables['link_options']['attributes']['property'] = $term->rdf_mapping['name']['predicates'];
+  }
+}
+
+/**
+ * Returns the default RDF mapping for the given entity type.
+ *
+ * @param $type
+ *   An entity type.
+ * @return array
+ *   The RDF mapping or an empty array.
+ */
+function _rdf_get_default_mapping($type) {
+  $default_mappings = &drupal_static(__FUNCTION__, array());
+
+  if (empty($default_mappings)) {
+    // Get all modules implementing 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'];
+        }
+      }
+    }
+  }
+
+  return empty($default_mappings[$type]) ? array() : $default_mappings[$type];
+}
+
+/**
+ * Create an RDF mapping binded to a bundle and an entity type.
+ *
+ * RDF CRUD API, handling RDF mapping creation and deletion.
+ *
+ * @param $type
+ *   The entity type the mapping refers to (node, user, comment, term, etc.).
+ * @param $bundle
+ *   The bundle the mapping refers to.
+ * @param $mapping
+ *   An associative array represeting an RDF mapping structure.
+ * @return array
+ *   The stored mapping.
+ */
+function rdf_create_mapping($type, $bundle, $mapping) {
+  $fields = array(
+    'type' => $type,
+    'bundle' => $bundle,
+    'mapping' => serialize($mapping)
+  );
+
+  db_insert('rdf_mapping')->fields($fields)->execute();
+
+  return $mapping;
+}
+
+/**
+ * Read an RDF mapping record directly from the database.
+ *
+ * RDF CRUD API, handling RDF mapping creation and deletion.
+ *
+ * @param $type
+ *   The entity type the mapping refers to.
+ * @param $bundle
+ *   The bundle the mapping refers to.
+ * @return array
+ *   An RDF mapping structure or FALSE if the mapping could not be found.
+ */
+function rdf_read_mapping($type, $bundle) {
+  $query = db_select('rdf_mapping')->fields(NULL, array('mapping'))
+    ->condition('type', $type)->condition('bundle', $bundle)->execute();
+
+  $mapping = unserialize($query->fetchField());
+
+  if (!is_array($mapping)) {
+    $mapping = array();
+  }
+
+  return $mapping;
+}
+
+/**
+ * Update an RDF mapping binded to a bundle and an entity type.
+ *
+ * RDF CRUD API, handling RDF mapping creation and deletion.
+ *
+ * @param $type
+ *   The entity type the mapping refers to.
+ * @param $bundle
+ *   The bundle the mapping refers to.
+ * @param $mapping
+ *   An associative array representing an RDF mapping structure.
+ * @return bool
+ *   Return boolean TRUE if mapping updated, FALSE if not.
+ */
+function rdf_update_mapping($type, $bundle, $mapping) {
+  $fields = array('mapping' => serialize($mapping));
+  $num_rows = db_update('rdf_mapping')->fields($fields)
+    ->condition('type', $type)->condition('bundle', $bundle)->execute();
+
+  return (bool) ($num_rows > 0);
+}
+
+/**
+ * Delete the mapping for the given pair of type and bundle from the database.
+ *
+ * RDF CRUD API, handling RDF mapping creation and deletion.
+ *
+ * @param $type
+ *   The entity type the mapping refers to.
+ * @param $bundle
+ *   The bundle the mapping refers to.
+ * @return bool
+ *   Return boolean TRUE if mapping deleted, FALSE if not.
+ */
+function rdf_delete_mapping($type, $bundle) {
+  $num_rows = db_delete('rdf_mapping')->condition('type', $type)
+    ->condition('bundle', $bundle)->execute();
+
+  return (bool) ($num_rows > 0);
+}
diff --git a/modules/rdf/rdf.test b/modules/rdf/rdf.test
new file mode 100644
index 0000000000000000000000000000000000000000..ce4652551143e461108fcba7959d12f014e45a48
--- /dev/null
+++ b/modules/rdf/rdf.test
@@ -0,0 +1,193 @@
+<?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"');
+  }
+}
diff --git a/modules/rdf/tests/rdf_test.info b/modules/rdf/tests/rdf_test.info
new file mode 100644
index 0000000000000000000000000000000000000000..72f6518ec8918cb470838c8c31fc27852dcb8be5
--- /dev/null
+++ b/modules/rdf/tests/rdf_test.info
@@ -0,0 +1,9 @@
+; $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
diff --git a/modules/rdf/tests/rdf_test.install b/modules/rdf/tests/rdf_test.install
new file mode 100644
index 0000000000000000000000000000000000000000..ba7086ddd900387dab55e5f0225110e4c559d673
--- /dev/null
+++ b/modules/rdf/tests/rdf_test.install
@@ -0,0 +1,26 @@
+<?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);
+  }
+}
diff --git a/modules/rdf/tests/rdf_test.module b/modules/rdf/tests/rdf_test.module
new file mode 100644
index 0000000000000000000000000000000000000000..72959705fcf117b949bfddd813ee7441452d5dc9
--- /dev/null
+++ b/modules/rdf/tests/rdf_test.module
@@ -0,0 +1,44 @@
+<?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'),
+        ),
+      ),
+    ),
+    1 => array(
+      'type' => 'node',
+      'bundle' => 'blog',
+      'mapping' => array(
+        'rdftype' => array('sioct:Weblog'),
+      )
+    ),
+  );
+}
diff --git a/modules/system/html.tpl.php b/modules/system/html.tpl.php
index c84fcbea9ae68623fa9bf7adcb4feb1567d0fbe5..846a1ec70aaecc0f92e5085a9ed10ce65840c5cf 100644
--- a/modules/system/html.tpl.php
+++ b/modules/system/html.tpl.php
@@ -35,7 +35,7 @@
  */
 ?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN"
   "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php print $language->language; ?>" dir="<?php print $language->dir; ?>"
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php print $language->language; ?>" version="XHTML+RDFa 1.0" dir="<?php print $language->dir; ?>"
   <?php print $rdf_namespaces; ?>>
 
 <head profile="<?php print $grddl_profile; ?>">
diff --git a/modules/system/system.module b/modules/system/system.module
index 80d22d26be6559198a252c41579d9b6c3910e57b..5fb04db43e4460f28ebf1bce7e2d1d7a24ad1987 100644
--- a/modules/system/system.module
+++ b/modules/system/system.module
@@ -265,15 +265,18 @@ function system_rdf_namespaces() {
   return array(
     'admin'    => 'http://webns.net/mvcb/',
     'content'  => 'http://purl.org/rss/1.0/modules/content/',
-    'dc'       => 'http://purl.org/dc/elements/1.1/',
-    'dcterms'  => 'http://purl.org/dc/terms/',
+    'dc'       => 'http://purl.org/dc/terms/',
     'foaf'     => 'http://xmlns.com/foaf/0.1/',
     'owl'      => 'http://www.w3.org/2002/07/owl#',
     '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',
+    'sioct'    => 'http://rdfs.org/sioc/types#',
+    'ctag'     => 'http://commontag.org/ns#',
+    'skos'     => 'http://www.w3.org/2004/02/skos/core#',
+    'xsd'      => 'http://www.w3.org/2001/XMLSchema#',
   );
 }
 
diff --git a/modules/taxonomy/taxonomy.module b/modules/taxonomy/taxonomy.module
index 69025f6bb36e98515e2a48acddc2999d423c0d77..8a4bbd11c8a73ff8a4c6b219fe5a61d1466bb764 100644
--- a/modules/taxonomy/taxonomy.module
+++ b/modules/taxonomy/taxonomy.module
@@ -1102,7 +1102,8 @@ function taxonomy_field_formatter_info() {
  */
 function theme_field_formatter_taxonomy_term_link($variables) {
   $term = $variables['element']['#item']['taxonomy_term'];
-  return l($term->name, taxonomy_term_path($term));
+  $attributes = empty($variables['link_options']) ? array() : $variables['link_options'];
+  return l($term->name, taxonomy_term_path($term), $attributes);
 }
 
 /**
@@ -1405,6 +1406,47 @@ function taxonomy_field_settings_form($field, $instance, $has_data) {
   return $form;
 }
 
+/**
+ * Implement hook_rdf_mapping().
+ *
+ * @return array
+ *   The rdf mapping for vocabularies and terms.
+ */
+function taxonomy_rdf_mapping() {
+  return array(
+    array(
+      'type' => 'taxonomy_term',
+      'bundle' => RDF_DEFAULT_BUNDLE,
+      'mapping' => array(
+        'rdftype' => array('skos:Concept'),
+        'name'   => array(
+          'predicates' => array('skos:prefLabel'),
+        ),
+        'description'   => array(
+          'predicates' => array('skos:definition'),
+        ),
+        // The vocabulary this term belongs to.
+        'vid'   => array(
+          'predicates' => array('skos:member'),
+        ),
+      ),
+    ),
+    array(
+      'type' => 'taxonomy_vocabulary',
+      'bundle' => RDF_DEFAULT_BUNDLE,
+      'mapping' => array(
+        'rdftype' => array('skos:Collection'),
+        'name'   => array(
+          'predicates' => array('rdfs:label'),
+        ),
+        'description'   => array(
+          'predicates' => array('rdfs:comment'),
+        ),
+      ),
+    ),
+  );
+}
+
 /**
  * @defgroup taxonomy indexing Taxonomy functions maintaining {taxonomy_index}.
  *
@@ -1495,4 +1537,3 @@ function taxonomy_taxonomy_term_delete($term) {
 /**
  * @} End of "defgroup taxonomy indexing"
  */
-
diff --git a/modules/user/user.module b/modules/user/user.module
index cf10cfe3456d0059573c77934eb2d17199056b67..424916ef4b3eb053bf4f2072229656a3a6d15534 100644
--- a/modules/user/user.module
+++ b/modules/user/user.module
@@ -3255,3 +3255,21 @@ function user_cookie_save(array $values, array $fields = array('name', 'mail', '
     }
   }
 }
+
+/**
+ * Implementation of hook_rdf_mapping().
+ */
+function user_rdf_mapping() {
+  return array(
+    array(
+      'type' => 'user',
+      'bundle' => RDF_DEFAULT_BUNDLE,
+      'mapping' => array(
+        'rdftype' => array('sioc:User'),
+        'name' => array(
+          'predicates' => array('foaf:name'),
+        ),
+      ),
+    ),
+  );
+}
diff --git a/profiles/default/default.info b/profiles/default/default.info
index 7e205eba5bb3e4fc9292d26b1dc34ea27666d89a..ccec0c991adf3a19e7ab693ed333245554bee39b 100644
--- a/profiles/default/default.info
+++ b/profiles/default/default.info
@@ -18,4 +18,5 @@ dependencies[] = shortcut
 dependencies[] = toolbar
 dependencies[] = field_ui
 dependencies[] = file
+dependencies[] = rdf
 files[] = default.profile
diff --git a/profiles/default/default.install b/profiles/default/default.install
index 60338f582f36209c818910d6796440992a81ef67..8174a43bf12344eb6c5e2eacd1b1031e443ec1d2 100644
--- a/profiles/default/default.install
+++ b/profiles/default/default.install
@@ -166,6 +166,28 @@ function default_install() {
     node_type_save($type);
   }
 
+  // Insert default user-defined RDF mapping into the database.
+  $rdf_mappings = array(
+    array(
+      'type' => 'node',
+      'bundle' => 'page',
+      'mapping' => array(
+        'rdftype' => array('foaf:Document'),
+      ),
+    ),
+    array(
+      'type' => 'node',
+      'bundle' => 'article',
+      'mapping' => array(
+        'rdftype' => array('sioc:Item', 'foaf:Document'),
+      ),
+    ),
+  );
+
+  foreach ($rdf_mappings as $rdf_mapping) {
+    rdf_save_mapping($rdf_mapping);
+  }
+
   // Default page to not be promoted and have comments disabled.
   variable_set('node_options_page', array('status'));
   variable_set('comment_page', COMMENT_NODE_HIDDEN);