diff options
author | Angie Byron <webchick@24967.no-reply.drupal.org> | 2009-10-20 17:33:43 +0000 |
---|---|---|
committer | Angie Byron <webchick@24967.no-reply.drupal.org> | 2009-10-20 17:33:43 +0000 |
commit | 8f21cc40259db9406e00a083b4b6ca2ff9ebbb57 (patch) | |
tree | 0e420408ae7173fd9d03ec48ef7aa064adbb6f3c /modules/rdf | |
parent | 0fa300a696e42483d6ca3a94ba8ce74b7cbea2be (diff) | |
download | brdo-8f21cc40259db9406e00a083b4b6ca2ff9ebbb57.tar.gz brdo-8f21cc40259db9406e00a083b4b6ca2ff9ebbb57.tar.bz2 |
#493030 follow-up by sun: Clean-ups to RDF module.
Diffstat (limited to 'modules/rdf')
-rw-r--r-- | modules/rdf/rdf.api.php | 59 | ||||
-rw-r--r-- | modules/rdf/rdf.install | 4 | ||||
-rw-r--r-- | modules/rdf/rdf.module | 683 | ||||
-rw-r--r-- | modules/rdf/rdf.test | 64 | ||||
-rw-r--r-- | modules/rdf/tests/rdf_test.install | 2 | ||||
-rw-r--r-- | modules/rdf/tests/rdf_test.module | 11 |
6 files changed, 400 insertions, 423 deletions
diff --git a/modules/rdf/rdf.api.php b/modules/rdf/rdf.api.php index 0635b7faf..a4fc070a2 100644 --- a/modules/rdf/rdf.api.php +++ b/modules/rdf/rdf.api.php @@ -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'), ), - ) + ), ), ); } diff --git a/modules/rdf/rdf.install b/modules/rdf/rdf.install index 0467842a6..ec553fcda 100644 --- a/modules/rdf/rdf.install +++ b/modules/rdf/rdf.install @@ -43,8 +43,8 @@ function rdf_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. + // 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); } diff --git a/modules/rdf/rdf.module b/modules/rdf/rdf.module index 3fbb5d3b7..62b1a8c84 100644 --- a/modules/rdf/rdf.module +++ b/modules/rdf/rdf.module @@ -4,8 +4,14 @@ /** * @file * Enables semantically enriched output for Drupal sites. + */ + +/** + * @defgroup rdf RDFa API + * @{ + * Functions to describe entities and bundles for RDFa. * - * This module introduces RDFa to Drupal, which provides a set of XHTML + * RDF 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/ * @@ -15,206 +21,118 @@ * 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(). - */ -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. - * - * 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'] ? "<span$attributes>$output</span>" : "<div$attributes>$output</div>"; +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. - * - * 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. + * Returns the default RDF mapping for a given entity type. * - * @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 .= '<span' . drupal_attributes($attributes) . ' />'; - } - 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. + * Reads an RDF mapping record directly from the database. * * @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,28 +145,105 @@ function rdf_get_mapping($type, $bundle = RDF_DEFAULT_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']); - - 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; - } + $mapping['mapping'] += _rdf_get_default_mapping($mapping['type']); + + $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; } /** + * Deletes the mapping for the given pair of type and bundle from the database. + * + * @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); +} + +/** + * Builds an array of RDFa attributes for a given mapping. + * + * @param $mapping + * An array containing a mandatory 'predicates' key and optional 'datatype', + * '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 + * An array containing RDFa attributes suitable for drupal_attributes(). + */ +function rdf_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 expresses 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; + } + + return $attributes; +} + +/** + * @} End of "defgroup rdf". + */ + +/** * Implements hook_modules_installed(). * * Checks if the installed modules have any RDF mapping definitions to declare @@ -267,12 +262,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); } } } @@ -283,7 +278,7 @@ function rdf_modules_installed($modules) { * Implements hook_modules_uninstalled(). */ function rdf_modules_uninstalled($modules) { -// @todo remove the RDF mappings. + // @todo Remove RDF mappings of uninstalled modules. } /** @@ -296,7 +291,7 @@ function rdf_entity_info_alter(&$entity_info) { 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,56 +305,6 @@ function rdf_entity_info_alter(&$entity_info) { } /** - * 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) { @@ -371,16 +316,66 @@ function rdf_entity_load($entities, $type) { } /** + * 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()), + ), + ); +} + +/** + * 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) { + // Handles attributes needed for content not covered by title, content, + // and field items. Does 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)); + } + } + // Handles 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(). */ function rdf_preprocess_node(&$variables) { - // Add RDFa markup to the node container. The about attribute specifies the + // Adds 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 + // Adds 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']; @@ -395,9 +390,9 @@ function rdf_preprocess_node(&$variables) { drupal_add_html_head('<meta' . drupal_attributes($title_attributes) . ' />'); } - // Add RDFa markup for the date. + // Adds 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_rdfa_attributes($variables['rdf_mapping']['created'], $variables['created']); $variables['rdf_template_variable_attributes_array']['date'] = $date_attributes_array; } } @@ -414,13 +409,12 @@ function rdf_preprocess_field(&$variables) { 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_rdfa_attributes($mapping[$field_name], $item['#item']); } } } } - /** * Implements MODULE_preprocess_HOOK(). */ @@ -457,7 +451,7 @@ function rdf_preprocess_username(&$variables) { // 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 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. @@ -490,20 +484,20 @@ function rdf_preprocess_username(&$variables) { 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 + // Adds 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. + // Adds 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_rdfa_attributes($comment->rdf_mapping['created'], $comment->created); $variables['rdf_template_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 + // Adds 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. @@ -514,7 +508,7 @@ function rdf_preprocess_comment(&$variables) { // 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 + // @todo Use fields instead, see http://drupal.org/node/538164 $variables['content_attributes_array']['property'] = $comment->rdf_mapping['body']['predicates']; } @@ -549,122 +543,103 @@ function rdf_preprocess_field_formatter_taxonomy_term_link(&$variables) { } /** - * 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. + * + * @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. * - * RDF CRUD API, handling RDF mapping creation and deletion. + * @return + * A string containing the wrapped content. The template receives the for its + * variable instead of the original content. * - * @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. + * 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 * - * RDF CRUD API, handling RDF mapping creation and deletion. + * @see rdf_process() * - * @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. + * @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'] ? "<span$attributes>$output</span>" : "<div$attributes>$output</div>"; } - - 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 .= '<span' . drupal_attributes($attributes) . ' />'; + } + return $output; } + diff --git a/modules/rdf/rdf.test b/modules/rdf/rdf.test index ce4652551..978b6c582 100644 --- a/modules/rdf/rdf.test +++ b/modules/rdf/rdf.test @@ -1,6 +1,11 @@ <?php // $Id$ +/** + * @file + * Tests for RDF functionality. + */ + class RdfMappingHookTestCase extends DrupalWebTestCase { public static function getInfo() { return array( @@ -52,7 +57,7 @@ class RdfMarkupTestCase extends DrupalWebTestCase { } /** - * Test drupal_rdfa_attributes(). + * Test rdf_rdfa_attributes(). */ function testDrupalRdfaAtributes() { $date = 1252750327; @@ -63,7 +68,7 @@ class RdfMarkupTestCase extends DrupalWebTestCase { $expected_value = $isoDate; $mapping = rdf_get_mapping('test_entity', 'test_bundle'); - $attributes = drupal_rdfa_attributes($mapping['created'], $date); + $attributes = rdf_rdfa_attributes($mapping['created'], $date); $this->assertEqual($expected_type, $attributes['datatype']); $this->assertEqual($expected_property, $attributes['property']); @@ -85,42 +90,36 @@ class RdfCrudTestCase extends DrupalWebTestCase { 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'] - ); + // Verify loading of a default mapping. + $this->assertFalse(count(rdf_mapping_load('test_entity', 'test_bundle')), t('Default mapping was found.')); - 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 saving a mapping. + $mapping = (array) $test_mapping; + rdf_mapping_save($mapping[0]); + $this->assertEqual($mapping[0]['mapping'], $test_mapping[0]['mapping'], t('Saved mapping equals default mapping.')); + $this->assertTrue(rdf_mapping_save($mapping[1]) === SAVED_NEW, t('Second mapping was inserted.')); + $this->assertEqual($mapping[1]['mapping'], rdf_mapping_load($test_mapping[1]['type'], $test_mapping[1]['bundle']), t('Second mapping equals default mapping.')); - function testSaveMapping() { - $test_mapping = rdf_test_rdf_mapping(); - rdf_save_mapping($test_mapping[0]); + // Verify loading of saved mapping. + $this->assertEqual($mapping[0]['mapping'], rdf_mapping_load($test_mapping[0]['type'], $test_mapping[0]['bundle']), t('Saved mapping equals loaded default mapping.')); - $this->assertEqual($test_mapping[0]['mapping'], - rdf_read_mapping('test_entity', 'test_bundle')); - } + // Verify updating of mapping. + $mapping[0]['mapping']['boofar'] = array( + 'predicates' => array('foo:bar'), + ); + $this->assertTrue(rdf_mapping_save($mapping[0]) === SAVED_UPDATED, t('Mapping was updated.')); + $this->assertEqual($mapping[0]['mapping'], rdf_mapping_load($test_mapping[0]['type'], $test_mapping[0]['bundle']), t('Updated and loaded mapping are equal.')); + // Verify deleting of mapping. + $this->assertTrue(rdf_mapping_delete($test_mapping[0]['type'], $test_mapping[0]['bundle']), t('Mapping was deleted.')); + $this->assertFalse(rdf_mapping_load($test_mapping[0]['type'], $test_mapping[0]['bundle']), t('Deleted mapping is no longer found.')); + } } class RdfMappingDefinitionTestCase extends DrupalWebTestCase { @@ -156,7 +155,6 @@ class RdfMappingDefinitionTestCase extends DrupalWebTestCase { // 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.install b/modules/rdf/tests/rdf_test.install index ba7086ddd..b5253f7f7 100644 --- a/modules/rdf/tests/rdf_test.install +++ b/modules/rdf/tests/rdf_test.install @@ -21,6 +21,6 @@ function rdf_test_install() { ); foreach ($rdf_mappings as $rdf_mapping) { - rdf_save_mapping($rdf_mapping); + rdf_mapping_save($rdf_mapping); } } diff --git a/modules/rdf/tests/rdf_test.module b/modules/rdf/tests/rdf_test.module index 72959705f..46144951b 100644 --- a/modules/rdf/tests/rdf_test.module +++ b/modules/rdf/tests/rdf_test.module @@ -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(). + * Implements 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'), - ) + ), ), ); } |