Index: modules/blog/blog.module =================================================================== RCS file: /cvs/drupal/drupal/modules/blog/blog.module,v retrieving revision 1.337 diff -u -p -r1.337 blog.module --- modules/blog/blog.module 19 Oct 2009 18:28:15 -0000 1.337 +++ modules/blog/blog.module 19 Oct 2009 19:03:53 -0000 @@ -185,7 +185,7 @@ function blog_block_view($delta = '') { } /** - * Implementation of hook_rdf_mapping(). + * Implement hook_rdf_mapping(). */ function blog_rdf_mapping() { return array( @@ -194,7 +194,7 @@ function blog_rdf_mapping() { 'bundle' => 'blog', 'mapping' => array( 'rdftype' => array('sioct:Weblog'), - ) + ), ), ); -} +} Index: modules/comment/comment.module =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v retrieving revision 1.794 diff -u -p -r1.794 comment.module --- modules/comment/comment.module 19 Oct 2009 18:28:15 -0000 1.794 +++ modules/comment/comment.module 19 Oct 2009 19:04:34 -0000 @@ -2496,7 +2496,7 @@ function comment_filter_format_delete($f } /** - * Implementation of hook_rdf_mapping(). + * Implement hook_rdf_mapping(). */ function comment_rdf_mapping() { return array( @@ -2505,7 +2505,7 @@ function comment_rdf_mapping() { 'bundle' => RDF_DEFAULT_BUNDLE, 'mapping' => array( 'rdftype' => array('sioct:Post'), - 'title' => array( + 'title' => array( 'predicates' => array('dc:title'), ), 'created' => array( @@ -2513,16 +2513,16 @@ function comment_rdf_mapping() { 'datatype' => 'xsd:dateTime', 'callback' => 'date_iso8601', ), - 'body' => array( + 'body' => array( 'predicates' => array('content:encoded'), ), - 'pid' => array( + 'pid' => array( 'predicates' => array('sioc:reply_of'), ), - 'uid' => array( + 'uid' => array( 'predicates' => array('sioc:has_creator'), ), - 'name' => array( + 'name' => array( 'predicates' => array('foaf:name'), ), ), Index: modules/comment/comment.test =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.test,v retrieving revision 1.51 diff -u -p -r1.51 comment.test --- modules/comment/comment.test 19 Oct 2009 18:28:15 -0000 1.51 +++ modules/comment/comment.test 19 Oct 2009 19:05:19 -0000 @@ -865,8 +865,10 @@ class CommentRSSUnitTest extends Comment } } -class RdfaCommentTestCase extends CommentHelperCase { - +/** + * Test RDFa markup for comments. + */ +class CommentRdfaTestCase extends CommentHelperCase { public static function getInfo() { return array( 'name' => 'RDFa comment markup', @@ -911,5 +913,4 @@ class RdfaCommentTestCase extends Commen $comment_author = $this->xpath("//div[@typeof='sioct:Post']//*[contains(@property, 'foaf:name')]"); $this->assertEqual((string)$comment_author[0], $this->web_user->name); } - } Index: modules/forum/forum.module =================================================================== RCS file: /cvs/drupal/drupal/modules/forum/forum.module,v retrieving revision 1.525 diff -u -p -r1.525 forum.module --- modules/forum/forum.module 19 Oct 2009 18:28:15 -0000 1.525 +++ modules/forum/forum.module 19 Oct 2009 19:06:26 -0000 @@ -412,7 +412,7 @@ function forum_comment_update($comment) } /** - * Implement forum_comment_unpublish() { + * Implement forum_comment_unpublish(). */ function forum_comment_unpublish($comment) { _forum_update_forum_index($comment->nid); @@ -1134,7 +1134,7 @@ function _forum_update_forum_index($nid) } /** - * Implementation of hook_rdf_mapping(). + * Implement hook_rdf_mapping(). */ function forum_rdf_mapping() { return array( @@ -1143,7 +1143,7 @@ function forum_rdf_mapping() { 'bundle' => 'forum', 'mapping' => array( 'rdftype' => array('sioct:Post', 'sioct:ForumTopic'), - 'taxonomy_forums' => array( + 'taxonomy_forums' => array( 'predicates' => array('sioc:has_container'), 'type' => 'rel', ), Index: modules/node/node.module =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.module,v retrieving revision 1.1151 diff -u -p -r1.1151 node.module --- modules/node/node.module 19 Oct 2009 18:28:15 -0000 1.1151 +++ modules/node/node.module 19 Oct 2009 19:09:27 -0000 @@ -754,13 +754,7 @@ function node_type_set_defaults($info = } /** - * 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. + * Implement hook_rdf_mapping(). */ function node_rdf_mapping() { return array( @@ -769,7 +763,7 @@ function node_rdf_mapping() { 'bundle' => RDF_DEFAULT_BUNDLE, 'mapping' => array( 'rdftype' => array('sioc:Item', 'foaf:Document'), - 'title' => array( + 'title' => array( 'predicates' => array('dc:title'), ), 'created' => array( @@ -780,13 +774,13 @@ function node_rdf_mapping() { 'changed' => array( 'predicates' => array('dc:modified'), ), - 'body' => array( + 'body' => array( 'predicates' => array('content:encoded'), ), - 'uid' => array( + 'uid' => array( 'predicates' => array('sioc:has_creator'), ), - 'name' => array( + 'name' => array( 'predicates' => array('foaf:name'), ), ), Index: modules/rdf/rdf.api.php =================================================================== RCS file: /cvs/drupal/drupal/modules/rdf/rdf.api.php,v retrieving revision 1.1 diff -u -p -r1.1 rdf.api.php --- modules/rdf/rdf.api.php 19 Oct 2009 18:28:15 -0000 1.1 +++ modules/rdf/rdf.api.php 19 Oct 2009 19:21:12 -0000 @@ -12,30 +12,35 @@ */ /** - * Allow modules to define RDF mappings for bundles. + * Allow modules to define RDF mappings for field 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. + * Modules defining their own field 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. + * A list of mapping structures, where each mapping is an associative array: + * - type: The name of an entity type, e.g. 'node' or 'comment'. + * - bundle: The name of the bundle, e.g. 'blog', or RDF_DEFAULT_BUNDLE for + * default mappings. + * - mapping: The mapping structure which applies to the entity type and + * bundle. 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: An array of RDF predicates which describe the relation + * between the bundle (RDF subject) and the value of the field (RDF + * object). This value is either some text, another bundle or a URL in + * general. + * - datatype: Is used along with 'callback' to format data so that it is + * 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. + * - callback: A function name to invoke for 'datatype'. + * - type: 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. + * - rdftype: A special property used to define the type of the instance. + * Its value shoud be an array of RDF classes. */ function hook_rdf_mapping() { return array( @@ -44,7 +49,7 @@ function hook_rdf_mapping() { 'bundle' => 'blog', 'mapping' => array( 'rdftype' => array('sioct:Weblog'), - 'title' => array( + 'title' => array( 'predicates' => array('dc:title'), ), 'created' => array( @@ -52,17 +57,17 @@ function hook_rdf_mapping() { 'datatype' => 'xsd:dateTime', 'callback' => 'date_iso8601', ), - 'body' => array( + 'body' => array( 'predicates' => array('content:encoded'), ), - 'uid' => array( + 'uid' => array( 'predicates' => array('sioc:has_creator'), - 'type' => 'rel', + 'type' => 'rel', ), - 'name' => array( + 'name' => array( 'predicates' => array('foaf:name'), ), - ) + ), ), ); } Index: modules/rdf/rdf.install =================================================================== RCS file: /cvs/drupal/drupal/modules/rdf/rdf.install,v retrieving revision 1.1 diff -u -p -r1.1 rdf.install --- modules/rdf/rdf.install 19 Oct 2009 18:28:15 -0000 1.1 +++ modules/rdf/rdf.install 19 Oct 2009 19:22:34 -0000 @@ -7,7 +7,7 @@ */ /** - * Implements hook_schema(). + * Implement hook_schema(). */ function rdf_schema() { $schema['rdf_mapping'] = array( @@ -40,11 +40,11 @@ function rdf_schema() { } /** - * Implements hook_install(). + * Implement 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. + // The installer does not trigger hook_modules_installed(), so it needs to be + // triggered manually for modules defining RDF mappings. $modules = module_implements('rdf_mapping'); rdf_modules_installed($modules); } Index: modules/rdf/rdf.module =================================================================== RCS file: /cvs/drupal/drupal/modules/rdf/rdf.module,v retrieving revision 1.2 diff -u -p -r1.2 rdf.module --- modules/rdf/rdf.module 19 Oct 2009 20:33:21 -0000 1.2 +++ modules/rdf/rdf.module 19 Oct 2009 21:25:34 -0000 @@ -15,40 +15,44 @@ * Drupal core themes ship with RDFa output enabled. * * Example mapping from node.module: + * @code * 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'), - * ), - * ), - * ); + * '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'), + * ), + * ), + * ); + * @endcode */ /** - * Defines the empty string as the name of the bundle to store default - * RDF mappings of a type's properties (fields, et. al.). + * RDF bundle flag: Default bundle. + * + * Defines an empty string as the name of the bundle to store default + * RDF mappings of a type's properties (fields, etc.). */ define('RDF_DEFAULT_BUNDLE', ''); /** - * Implements hook_theme(). + * Implement hook_theme(). */ function rdf_theme() { return array( @@ -62,159 +66,83 @@ function rdf_theme() { } /** - * Wraps a template variable in an HTML element with the desired attributes. - * - * This is called by rdf_process() shortly before the theme system renders - * a template file. It is called once for each template variable for which - * additional attributes are needed. While template files are responsible for - * rendering the attributes for the template's primary object (via the - * $attributes variable), title (via the $title_attributes variable), and - * content (via the $content_attributes variable), additional template variables - * that need containing attributes are routed through this function, allowing - * the template file to receive properly wrapped variables. + * Returns the mapping for the attributes of the given type, bundle pair. * - * @param $variables - * An associative array containing: - * - content: A string of content to be wrapped with attributes. - * - attributes: An array of attributes desired on the wrapping element. - * - context: An array of context information about the content to be wrapped: - * - 'hook': The theme hook that will use the wrapped content. This - * corresponds to the key within the theme registry for this template. - * For example, if this content is about to be used in node.tpl.php or - * node-TYPE.tpl.php, then the 'hook' is 'node'. - * - 'variable_name': The name of the variable, by which the template will - * refer to this content. Each template file has documentation about - * the variables it uses. For example, if this function is called in - * preparing the $author variable for comment.tpl.php, then the - * 'variable_name' is 'author'. - * - 'variables': The full array of variables about to be passed to the - * template. - * - inline: TRUE if the content contains only inline HTML elements and - * therefore can be validly wrapped by a 'span' tag. FALSE if the content - * might contain block level HTML elements and therefore cannot be validly - * wrapped by a 'span' tag. Modules implementing preprocess functions that - * set 'rdf_template_variable_attributes_array' for a particular template - * variable that might contain block level HTML must also implement - * hook_preprocess_rdf_template_variable_wrapper() and set 'inline' to FALSE - * for that context. Themes that render normally inline content with block - * level HTML must similarly implement - * hook_preprocess_rdf_template_variable_wrapper() and set 'inline' - * accordingly. + * @param $type + * An entity type. + * @param $bundle + * (optional) A bundle name. * * @return - * A string containing the wrapped content. The template receives the for its - * variable instead of the original content. - * - * Tip for themers: if you're already outputting a wrapper element around a - * particular template variable in your template file and if you don't want - * an extra wrapper element, you can override this function to not wrap that - * variable and instead print: - * @code - * drupal_attributes($rdf_template_variable_attributes_array[$variable_name]) - * @endcode - * inside your template file. - * - * @see rdf_process() - * - * @ingroup themeable + * The mapping corresponding to the requested type/bundle pair or an empty + * array. */ -function theme_rdf_template_variable_wrapper($variables) { - $output = $variables['content']; - if (!empty($output) && !empty($variables['attributes'])) { - $attributes = drupal_attributes($variables['attributes']); - $output = $variables['inline'] ? "$output" : "$output"; +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); } - return $output; } /** - * Outputs a series of empty spans for exporting RDF metadata in RDFa. + * Returns the default RDF mapping for a given entity type. * - * 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. - * - * @param $variables - * An associative array containing: - * - metadata: An array of attribute arrays. Each item in the array - * corresponds to its own set of attributes, and therefore, needs its own - * element. + * @param $type + * An entity type, e.g. 'node' or 'comment'. * * @return - * A string of HTML containing markup that can be understood by an RDF parser. - * - * Tip for themers: while this default implementation results in valid markup - * for the XHTML+RDFa doctype, you may need to override this in your theme to be - * valid for doctypes that don't support empty spans. Or, if empty spans create - * visual problems in your theme, you may want to override this to set a - * class on them, and apply a CSS rule of display:none for that class. - * - * @see rdf_process() - * - * @ingroup themeable + * The RDF mapping or an empty array. */ -function theme_rdf_metadata($variables) { - $output = ''; - foreach ($variables['metadata'] as $attributes) { - $output .= ''; - } - return $output; -} +function _rdf_get_default_mapping($type) { + $default_mappings = &drupal_static(__FUNCTION__); -/** - * Template process function for adding extra tags to hold RDFa attributes. - * - * Since template files already have built-in support for $attributes, - * $title_attributes, and $content_attributes, and field templates have support - * for $item_attributes, we try to leverage those as much as possible. However, - * in some cases additional attributes are needed not covered by these. We deal - * with those here. - */ -function rdf_process(&$variables, $hook) { - // Handle attributes needed for content not covered by title, content, - // and field items. Do this by adjusting the variable sent to the template - // so that the template doesn't have to worry about it. - // @see theme_rdf_template_variable_wrapper() - if (!empty($variables['rdf_template_variable_attributes_array'])) { - foreach ($variables['rdf_template_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)); - } - } - // Handle additional attributes about a template entity that for RDF parsing - // reasons, can't be placed into that template's $attributes variable. This - // is "meta" information that is related to particular content, so render it - // close to that content. - if (!empty($variables['rdf_metadata_attributes_array'])) { - if (!isset($variables['content']['#prefix'])) { - $variables['content']['#prefix'] = ''; + if (!isset($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']; + } + } } - $variables['content']['#prefix'] = theme('rdf_metadata', array('metadata' => $variables['rdf_metadata_attributes_array'])) . $variables['content']['#prefix']; } + + return isset($default_mappings[$type]) ? $default_mappings[$type] : array(); } /** - * Returns the mapping for the attributes of the given type, bundle pair. + * Read an RDF mapping record directly from the database. + * + * RDF CRUD API, handling RDF mapping creation and deletion. * * @param $type - * An entity type. + * The entity type the mapping refers to. * @param $bundle - * A bundle name. - * @return array - * The mapping corresponding to the requested type, bundle pair or an empty - * array. + * The bundle the mapping refers to. + * + * @return + * An RDF mapping structure or FALSE if the mapping could not be found. */ -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); +function rdf_mapping_load($type, $bundle) { + $mapping = db_select('rdf_mapping') + ->fields(NULL, array('mapping')) + ->condition('type', $type) + ->condition('bundle', $bundle) + ->execute() + ->fetchField(); + + if (!$mapping) { + return array(); } + return unserialize($mapping); } /** @@ -227,29 +155,54 @@ function rdf_get_mapping($type, $bundle * * @param $mapping * The RDF mapping to save, as an array. + * * @return * Status flag indicating the outcome of the operation. */ -function rdf_save_mapping($mapping) { +function rdf_mapping_save(&$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']); + $mapping['mapping'] += _rdf_get_default_mapping($mapping['type']); - 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; - } + $status = db_merge('rdf_mapping') + ->key(array( + 'type' => $mapping['type'], + 'bundle' => $mapping['bundle'], + )) + ->fields(array( + 'mapping' => serialize($mapping['mapping']), + )) + ->execute(); cache_clear_all('entity_info', 'cache'); drupal_static_reset('entity_get_info'); + + return $status; } /** - * Implements hook_modules_installed(). + * 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 + * Return boolean TRUE if mapping deleted, FALSE if not. + */ +function rdf_mapping_delete($type, $bundle) { + $num_rows = db_delete('rdf_mapping') + ->condition('type', $type) + ->condition('bundle', $bundle) + ->execute(); + + return (bool) ($num_rows > 0); +} + +/** + * Implement hook_modules_installed(). * * Checks if the installed modules have any RDF mapping definitions to declare * and stores them in the rdf_mapping table. @@ -267,12 +220,12 @@ function rdf_modules_installed($modules) 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) { + $function = $module . '_rdf_mapping'; + if (function_exists($function)) { + foreach ($function() as $mapping) { // Only the bundle mappings are saved in the database. - if ($mapping['bundle'] != RDF_DEFAULT_BUNDLE) { - rdf_save_mapping($mapping); + if ($mapping['bundle'] !== RDF_DEFAULT_BUNDLE) { + rdf_mapping_save($mapping); } } } @@ -280,14 +233,14 @@ function rdf_modules_installed($modules) } /** - * Implements hook_modules_uninstalled(). + * Implement hook_modules_uninstalled(). */ function rdf_modules_uninstalled($modules) { -// @todo remove the RDF mappings. + // @todo Remove RDF mappings of uninstalled modules. } /** - * Implements hook_entity_info_alter(). + * Implement hook_entity_info_alter(). * * Adds the proper RDF mapping to each entity type, bundle pair. */ @@ -296,7 +249,7 @@ function rdf_entity_info_alter(&$entity_ 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)) { + if ($mapping = rdf_mapping_load($entity_type, $bundle)) { $entity_info[$entity_type]['bundles'][$bundle]['rdf_mapping'] = $mapping; } else { @@ -310,68 +263,100 @@ function rdf_entity_info_alter(&$entity_ } /** + * Implement 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); + } +} + +/** * 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', - * ) + * callback and type keys. For example: + * @code + * array( + * 'predicates' => array('dc:created'), + * 'datatype' => 'xsd:dateTime', + * 'callback' => 'date_iso8601', + * ), + * ); + * @endcode * @param $data * A value that needs to be converted by the provided callback function. - * @return array + * + * @return * An array containing RDFa attributes ready for rendering. */ -function drupal_rdfa_attributes($mapping, $data = NULL) { +function rdf_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']; + // 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'] = $callback($data); + } + if (isset($mapping['datatype'])) { + $attributes['datatype'] = $mapping['datatype']; + } } - } - break; + break; } return $attributes; } - /** - * Implements hook_entity_load(). + * Template process function for adding extra tags to hold RDFa attributes. + * + * Since template files already have built-in support for $attributes, + * $title_attributes, and $content_attributes, and field templates have support + * for $item_attributes, we try to leverage those as much as possible. However, + * in some cases additional attributes are needed not covered by these. We deal + * with those here. */ -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); +function rdf_process(&$variables, $hook) { + // Handle attributes needed for content not covered by title, content, + // and field items. Do this by adjusting the variable sent to the template + // so that the template doesn't have to worry about it. + // @see theme_rdf_template_variable_wrapper() + if (!empty($variables['rdf_template_variable_attributes_array'])) { + foreach ($variables['rdf_template_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)); + } + } + // Handle additional attributes about a template entity that for RDF parsing + // reasons, can't be placed into that template's $attributes variable. This + // is "meta" information that is related to particular content, so render it + // close to that content. + if (!empty($variables['rdf_metadata_attributes_array'])) { + if (!isset($variables['content']['#prefix'])) { + $variables['content']['#prefix'] = ''; + } + $variables['content']['#prefix'] = theme('rdf_metadata', array('metadata' => $variables['rdf_metadata_attributes_array'])) . $variables['content']['#prefix']; } } /** - * Implements MODULE_preprocess_HOOK(). + * Implement MODULE_preprocess_HOOK(). */ function rdf_preprocess_node(&$variables) { // Add RDFa markup to the node container. The about attribute specifies the @@ -397,13 +382,13 @@ function rdf_preprocess_node(&$variables // Add RDFa markup for the date. if (!empty($variables['rdf_mapping']['created'])) { - $date_attributes_array = drupal_rdfa_attributes($variables['rdf_mapping']['created'], $variables['created']); + $date_attributes_array = rdf_attributes($variables['rdf_mapping']['created'], $variables['created']); $variables['rdf_template_variable_attributes_array']['date'] = $date_attributes_array; } } /** - * Implements MODULE_preprocess_HOOK(). + * Implement MODULE_preprocess_HOOK(). */ function rdf_preprocess_field(&$variables) { $entity_type = $variables['element']['#object_type']; @@ -414,15 +399,14 @@ function rdf_preprocess_field(&$variable 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']); + $variables['item_attributes_array'][$delta] = rdf_attributes($mapping[$field_name], $item['#item']); } } } } - /** - * Implements MODULE_preprocess_HOOK(). + * Implement MODULE_preprocess_HOOK(). */ function rdf_preprocess_user_profile(&$variables) { // Adds RDFa markup to the user profile page. Fields displayed in this page @@ -436,7 +420,7 @@ function rdf_preprocess_user_profile(&$v } /** - * Implements MODULE_preprocess_HOOK(). + * Implement MODULE_preprocess_HOOK(). */ function rdf_preprocess_username(&$variables) { $account = $variables['account']; @@ -485,7 +469,7 @@ function rdf_preprocess_username(&$varia } /** - * Implements MODULE_preprocess_HOOK(). + * Implement MODULE_preprocess_HOOK(). */ function rdf_preprocess_comment(&$variables) { $comment = $variables['comment']; @@ -499,7 +483,7 @@ function rdf_preprocess_comment(&$variab // 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); + $date_attributes_array = rdf_attributes($comment->rdf_mapping['created'], $comment->created); $variables['rdf_template_variable_attributes_array']['created'] = $date_attributes_array; } if (!empty($comment->rdf_mapping['title'])) { @@ -536,7 +520,7 @@ function rdf_preprocess_comment(&$variab } /** - * Implements MODULE_preprocess_HOOK(). + * Implement MODULE_preprocess_HOOK(). */ function rdf_preprocess_field_formatter_taxonomy_term_link(&$variables) { $term = $variables['element']['#item']['taxonomy_term']; @@ -549,122 +533,103 @@ function rdf_preprocess_field_formatter_ } /** - * Returns the default RDF mapping for the given entity type. + * Wraps a template variable in an HTML element with the desired attributes. * - * @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. + * This is called by rdf_process() shortly before the theme system renders + * a template file. It is called once for each template variable for which + * additional attributes are needed. While template files are responsible for + * rendering the attributes for the template's primary object (via the + * $attributes variable), title (via the $title_attributes variable), and + * content (via the $content_attributes variable), additional template variables + * that need containing attributes are routed through this function, allowing + * the template file to receive properly wrapped variables. * - * RDF CRUD API, handling RDF mapping creation and deletion. + * @param $variables + * An associative array containing: + * - content: A string of content to be wrapped with attributes. + * - attributes: An array of attributes desired on the wrapping element. + * - context: An array of context information about the content to be wrapped: + * - hook: The theme hook that will use the wrapped content. This + * corresponds to the key within the theme registry for this template. + * For example, if this content is about to be used in node.tpl.php or + * node-TYPE.tpl.php, then the 'hook' is 'node'. + * - variable_name: The name of the variable, by which the template will + * refer to this content. Each template file has documentation about + * the variables it uses. For example, if this function is called in + * preparing the $author variable for comment.tpl.php, then the + * 'variable_name' is 'author'. + * - variables: The full array of variables about to be passed to the + * template. + * - inline: TRUE if the content contains only inline HTML elements and + * therefore can be validly wrapped by a 'span' tag. FALSE if the content + * might contain block level HTML elements and therefore cannot be validly + * wrapped by a 'span' tag. Modules implementing preprocess functions that + * set 'rdf_template_variable_attributes_array' for a particular template + * variable that might contain block level HTML must also implement + * hook_preprocess_rdf_template_variable_wrapper() and set 'inline' to FALSE + * for that context. Themes that render normally inline content with block + * level HTML must similarly implement + * hook_preprocess_rdf_template_variable_wrapper() and set 'inline' + * accordingly. * - * @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. + * @return + * A string containing the wrapped content. The template receives the for its + * variable instead of the original content. * - * RDF CRUD API, handling RDF mapping creation and deletion. + * Tip for themers: if you're already outputting a wrapper element around a + * particular template variable in your template file and if you don't want + * an extra wrapper element, you can override this function to not wrap that + * variable and instead print the following inside your template file: + * @code + * drupal_attributes($rdf_template_variable_attributes_array[$variable_name]) + * @endcode * - * @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. + * @see rdf_process() + * + * @ingroup themeable */ -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(); +function theme_rdf_template_variable_wrapper($variables) { + $output = $variables['content']; + if (!empty($output) && !empty($variables['attributes'])) { + $attributes = drupal_attributes($variables['attributes']); + $output = $variables['inline'] ? "$output" : "$output"; } - - return $mapping; + return $output; } /** - * Update an RDF mapping binded to a bundle and an entity type. + * Outputs a series of empty spans for exporting RDF metadata in RDFa. * - * RDF CRUD API, handling RDF mapping creation and deletion. + * 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. * - * @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. + * @param $variables + * An associative array containing: + * - metadata: An array of attribute arrays. Each item in the array + * corresponds to its own set of attributes, and therefore, needs its own + * element. * - * RDF CRUD API, handling RDF mapping creation and deletion. + * @return + * A string of HTML containing markup that can be understood by an RDF parser. * - * @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. + * Tip for themers: while this default implementation results in valid markup + * for the XHTML+RDFa doctype, you may need to override this in your theme to be + * valid for doctypes that don't support empty spans. Or, if empty spans create + * visual problems in your theme, you may want to override this to set a + * class on them, and apply a CSS rule of display:none for that class. + * + * @see rdf_process() + * + * @ingroup themeable */ -function rdf_delete_mapping($type, $bundle) { - $num_rows = db_delete('rdf_mapping')->condition('type', $type) - ->condition('bundle', $bundle)->execute(); - - return (bool) ($num_rows > 0); +function theme_rdf_metadata($variables) { + $output = ''; + foreach ($variables['metadata'] as $attributes) { + $output .= ''; + } + return $output; } + Index: modules/rdf/rdf.test =================================================================== RCS file: /cvs/drupal/drupal/modules/rdf/rdf.test,v retrieving revision 1.1 diff -u -p -r1.1 rdf.test --- modules/rdf/rdf.test 19 Oct 2009 18:28:15 -0000 1.1 +++ modules/rdf/rdf.test 19 Oct 2009 21:15:22 -0000 @@ -1,6 +1,11 @@ assertEqual($expected_type, $attributes['datatype']); $this->assertEqual($expected_property, $attributes['property']); @@ -85,40 +90,36 @@ class RdfCrudTestCase extends DrupalWebT parent::setUp('rdf', 'rdf_test'); } - function testCreateReadUpdateWrite() { + /** + * Test inserting, loading, updating, and deleting RDF mappings. + */ + function testCRUD() { $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')); + // Verify loading of a non-existing mapping. + $this->assertTrue(is_array(rdf_mapping_load('test_entity', 'test_bundle'))); + $this->assertFalse(count(rdf_mapping_load('test_entity', 'test_bundle'))); + + // Verify saving a mapping. + $this->assertEqual(rdf_mapping_save('test_entity', 'test_bundle', $test_mapping[0]['mapping']), $test_mapping[0]['mapping']); + + // Verify loading of saved mapping. + $this->assertEqual($test_mapping[0]['mapping'], rdf_mapping_load('test_entity', 'test_bundle')); + + // Verify updating of mapping. + $this->assertTrue(rdf_mapping_save('test_entity', 'test_bundle', $test_mapping[1]['mapping'])); + $this->assertEqual($test_mapping[1]['mapping'], rdf_mapping_load('test_entity', 'test_bundle')); + + // Verify deleting of mapping. + $this->assertTrue(rdf_mapping_delete('test_entity', 'test_bundle')); + $this->assertFalse(rdf_mapping_load('test_entity', 'test_bundle')); } function testSaveMapping() { $test_mapping = rdf_test_rdf_mapping(); - rdf_save_mapping($test_mapping[0]); + rdf_mapping_save($test_mapping[0]); - $this->assertEqual($test_mapping[0]['mapping'], - rdf_read_mapping('test_entity', 'test_bundle')); + $this->assertEqual($test_mapping[0]['mapping'], rdf_mapping_load('test_entity', 'test_bundle')); } } @@ -156,7 +157,6 @@ class RdfMappingDefinitionTestCase exten // from the node default bundle definition. $this->assertRaw('property="dc:title"'); $this->assertRaw('property="dc:date dc:created"'); - } /** Index: modules/rdf/tests/rdf_test.install =================================================================== RCS file: /cvs/drupal/drupal/modules/rdf/tests/rdf_test.install,v retrieving revision 1.1 diff -u -p -r1.1 rdf_test.install --- modules/rdf/tests/rdf_test.install 19 Oct 2009 18:28:15 -0000 1.1 +++ modules/rdf/tests/rdf_test.install 19 Oct 2009 20:22:25 -0000 @@ -21,6 +21,6 @@ function rdf_test_install() { ); foreach ($rdf_mappings as $rdf_mapping) { - rdf_save_mapping($rdf_mapping); + rdf_mapping_save($rdf_mapping); } } Index: modules/rdf/tests/rdf_test.module =================================================================== RCS file: /cvs/drupal/drupal/modules/rdf/tests/rdf_test.module,v retrieving revision 1.1 diff -u -p -r1.1 rdf_test.module --- modules/rdf/tests/rdf_test.module 19 Oct 2009 18:28:15 -0000 1.1 +++ modules/rdf/tests/rdf_test.module 19 Oct 2009 20:23:40 -0000 @@ -3,16 +3,15 @@ /** * @file - * Dummy module implementing RDF related hooks to test API interaction with - * the RDF module. + * Test API interaction with the RDF module. */ /** - * Implementation of hook_rdf_mapping(). + * Implement hook_rdf_mapping(). */ function rdf_test_rdf_mapping() { return array( - 0 => array( + array( 'type' => 'test_entity', 'bundle' => 'test_bundle', 'mapping' => array( @@ -33,12 +32,12 @@ function rdf_test_rdf_mapping() { ), ), ), - 1 => array( + array( 'type' => 'node', 'bundle' => 'blog', 'mapping' => array( 'rdftype' => array('sioct:Weblog'), - ) + ), ), ); } Index: profiles/default/default.install =================================================================== RCS file: /cvs/drupal/drupal/profiles/default/default.install,v retrieving revision 1.15 diff -u -p -r1.15 default.install --- profiles/default/default.install 19 Oct 2009 18:28:16 -0000 1.15 +++ profiles/default/default.install 19 Oct 2009 20:24:11 -0000 @@ -183,9 +183,8 @@ function default_install() { ), ), ); - foreach ($rdf_mappings as $rdf_mapping) { - rdf_save_mapping($rdf_mapping); + rdf_mapping_save($rdf_mapping); } // Default page to not be promoted and have comments disabled.