This issue is part of a series of patches which came out of the RDF code sprint. See the list of all RDF patches for Drupal code. Please read State of the RDF in Drupal core after the code sprint which explains the overall approach of the RDF effort in core.

To try this patch and see some RDFa output, make sure to (1) patch and enable the RDF module in core and (2) switch to the Stark theme.

This patch stores the RDF mappings of each content type in a new column rdf_mapping in the node_type table, this is because the content types cannot be known upfront, and their mappings must be stored in the database for greater flexibility. We also define some default mappings for node types which don't define their mappings. The patch also pushes the RDF model through the theme layer in template_preprocess_node() and node.tpl.php.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

scor’s picture

FileSize
8.85 KB

time to reroll...

Anonymous’s picture

I fear that people will not like any of the changes to make $title_rendered. We should get some feedback on it ASAP.

scor’s picture

Status: Needs work » Needs review
FileSize
8.69 KB

removing the rdf_mapping attachment to the node object (moved to the RDF module).

Status: Needs review » Needs work

The last submitted patch failed testing.

scor’s picture

FileSize
7.34 KB

reroll for the version 10 of the RDF patches:
* moved the code for generation of the RDFa attributes for nodes in teaser mode from theme.inc to rdf_preprocess_node() in the RDF module
* remove hardcoded RDFa in node.tpl.php by wrapping the title value in a span tag.

clemens.tolboom’s picture

After applying #493030: RDF #1: core RDF module and then #493086: RDF #2: node module then running update.php I got an error

Notice: Undefined index: current_set in _batch_next_set() (line 375 of /home/clemens/htdocs/d7/d7/includes/batch.inc).
that is running
node_update_7099 - Add a column to the node_type table to store RDF mappings.

Reinstalling and then enabling I get (_edit_) on node/1/edit <===

# Notice: Undefined index: property in rdf_preprocess_page() (line 194 of /home/clemens/htdocs/d7/d7/modules/rdf/rdf.module).
# Warning: implode(): Invalid arguments passed in rdf_preprocess_page() (line 194 of /home/clemens/htdocs/d7/d7/modules/rdf/rdf.module).

Note that I only applied above named patches ...

clemens.tolboom’s picture

The notice only happens when navigating from node/1 to node/1/edit

Refresh/click node/1/edit does not display the notice

clemens.tolboom’s picture

Yeah right ... the notice comes from the theme layer ... so it's from the previous page :)

scor’s picture

FileSize
6.84 KB

rerolling patch for the node module. many changes have happened since the patch. The major changes are:
- moved a lot of code from node_show() to the RDF module
- altered node.tpl.php to allow datetime to contain HTML

scor’s picture

Title: Add RDF to the node module » RDF #2: node module

somehow I can't get the RDFa tests working:

  function setUp() {
    parent::setUp('rdf');
    variable_set('theme_default', 'stark');
  }

  function testNodeRdfa() {
    $node = $this->drupalCreateNode(array('type' => 'article'));

    $this->drupalGet('node/' . $node->nid);

    $result = $this->xpath('//div[contains(@class, "node") and contains(@typeof, "sioc:Item")]');
    $this->assertFalse(empty($result), t('Found a typeof attribute including sioc:Item'));
  }

This basic test does not work, though a fresh install with the RDF module enabled displays the typeof attribute fine. I outputted the HTML generated during the simpletest and it does not include any of the attributes added by rdf_preprocess_node(). It seems none of the preprocess functions is triggered at all...

John Morahan’s picture

Now that the main content area is a normal block region, variable_set('theme_default', 'stark') seems no longer to work for switching the theme at the start of the tests. But, since (as scor pointed out to me in IRC) the RDFa markup should now be present in all themes - that's no longer required anyway. Just removing the variable_set and letting the tests run on Garland seems to solve this problem.

scor’s picture

FileSize
8.31 KB

- fix tests so we don't use the stark theme anymore since all core themes are now supporting RDFa out of the box (see #11 above).
- in full node mode, the title is not displayed the usual way using node.tpl.php so we are missing the RDFa markup. The title is now added in the head tag of the HTML as a hidden meta element:

<meta property="dc:title" content="title of the node" />

One alternative would be to add an attribute to page.tpl.php or wrap the page title with a span tag, but I think this is nicer.
- add test for RDFa node output in teaser mode, so we now test the same node in teaser and full node mode.
- fix node.module and include the RDF mapping definitions of hook_rdf_mapping() in _node_types_build() (used during the installation of Drupal for example).
- remove the Implementation of hook_rdf_mapping() in node module which is a special case and is handled separately in rdf_get_mapping().

yched’s picture

+++ modules/node/node.module	2009-09-15 18:06:33 +0000
@@ -694,6 +709,29 @@ function node_type_set_defaults($info = 
+    $type->rdf_mapping = array(
+      'rdftype' => array('sioc:Item', 'foaf:Document'),
+      'title'   => array(
+        'property' => array('dc:title'),
+      ),
+      'created' => array(
+        'property' => array('dc:date', 'dc:created'),
+        'datatype' => 'xsd:dateTime',
+        'callback' => 'date_iso8601',
+      ),
+      'changed' => array(
+        'property' => array('dc:modified'),
+      ),
+      'body'    => array(
+        'property' => array('content:encoded'),
+      ),
+      'uid'     => array(
+        'property' => array('sioc:has_creator', 'foaf:maker'),
+      ),
+      'name'    => array(
+        'property' => array('foaf:name'),
+      ),
+    );

Is it OK that the mapping info for 'body' is in there while 'body' is now a regular field ?

This review is powered by Dreditor.

scor’s picture

@yched: Modules implementing their own node types via hook_node_info() and their own fields programmatically are also encouraged to define a default mapping for their "data model". Although the node types and the fields are implemented in 2 different ways (hook_node_info vs. Field API), we decided to centralize the definition of the RDF mapping, and this is why you can find these 2 aspects mixed in the same array (along with some built in fields like 'created'). So technically you could have much more field RDF mapping in there. The default mapping defined in the module can be altered via hook_rdf_mapping_alter(). This was just a reminder to make sure you get the general approach of the mappings. Now, on to your question. The mapping you pasted above is the default mapping for a node (unless there is a specific mapping defined for a given bundle via hook_rdf_mapping). As you point out, given that body is optional, I'm not sure it makes sense to include its mapping in the defaults, though it will do no harm as it will be stored in the node_type table but will never be used/outputed unless there is an actual body field. We could also test if the body field is present or not before including it.

Does that answer your question?

scor’s picture

FileSize
7.51 KB

- the same logic was used in both node_type_save() and _node_types_build() to invoke hook_rdf_mapping and override the node default mapping. This logic has been centralized in node_type_set_defaults().
- add a test for checking that a mapping for a node bundle defined via hook_rdf_mapping() overrides the node_type default mapping.

scor’s picture

yched sent me his comments offline. I'm replying below with his approval.

I have to say I got fairly confused with where RDF mappings are specified, between hook_node_info(), node_type_set_defaults(), $instance['rdf_mapping'], hook_rdf_mapping() and hook_rdf_mapping_alter().

in fact bjaspan agreed it would make sense to define the fields in hook_node_info() (like it is for title and body in D6) when I asked him in Washington DC. I didn't end up working on this and nobody did, but we reused this idea for the RDF mappings: a centralized place of definition giving an overview of what the data model means in RDF. Do you think this is confusing?

So looking at the RDF patches #1 2 and 3, it looks like rdf_get_mapping() is the central reference, and is populated in this order :
a) hook_rdf_mapping() - that's when field modules sends its $instance['rdf_mapping'] info

field modules = any module which define its own note type, bundle and/or fields, yes. They can also say what type (in RDF) a node type or bundle is (rdftype key in the mapping).

b) hook_node_info() / {node_type}.rdf_mapping comes after that and overwrites this

yes, except we don't use any info from hook_node_info since it does not hold any RDF mapping definition, but only the rdf mapping stored in the node_type table. It's the same way the data contained in node_type table might differ from hook_node_info if the user has changed the structure/info of some node types. The data in the node_type table overwrites the data returned by module-defined hook_node_info() and we follow the same idea here for hook_rdf_mapping.

c) hook_rdf_mapping_alter() lets everyone have their say

correct.

(BTW, It seems that this code:
$rdf_mapping = module_invoke_all('rdf_mapping');
$types = node_type_get_types();
foreach ($types as $key => $type) {
$rdf_mapping[$key] = isset($type->rdf_mapping) ? $type->rdf_mapping : NULL;
}
*removes* any RDF info on nodes provided by hook_rdf_mapping, including field RDF info. It seems the code needs to merge mappings instead of taking one array or the other ?)

true. this code looks like the opposite of what it intended to do. Either way last night I worked on a patch which centralizes all this logic in node_type_set_defaults(), see #15 above.
however this logic might be better to live in its own function we could call right after node_type_set_defaults(), what do you think?

Still sounds really confusing to me. Why does node_type info gets special treatment ? Couldn't it do its job in hook_rdf_mapping() ?

In fact that's how it was originally! everything was in hook_rdf_mapping and node.module was defining its own set of mapping in node_rdf_mapping, but because node.module might offer non default (i.e. overriden) mappings then we changed that to keep node.module out of the module-defined mapping so we give it special treatment. we first need to collect all the module-defined RDF mapping via hook_rdf_mapping and then override them with the mapping stored in the node_type table. It's the same reason why node module does not implement hook_node_info().

And I'd still vote to keep 'field' RDF info only in $instance['rdf_mapping'] and out of hook_node_info() / {node_type}.rdf_mapping.

fair enough, I'm happy to change that as long as the Field API allows to store this data easily in its tables. we'll look into that.

+ node_type_set_defaults() doesn't look right: default RDF settings are merged in globally and not individually. If I want to add specific mappings for property 'foo' in my hook_node_info(), I need to redefine the whole array of mappings for title, created, etc...

I'm glad you asked for it! It's something I intended to do. It should be in its own function rdf_mapping_set_defaults()... the only issue here is that each object type (node, user, term, comment) should get a different set of default mapping, and it should be up to the respective module to define these default mapping. So how about the node module calling rdf_mapping_set_defaults($default_mapping, $info)? and each module can call this and give it the default mapping it should have. rdf_mapping_set_defaults() can live in the rdf module and handle the per field overriding.

Last thing: regarding bundle names, we agreed with Barry Jaspan that the approach in Namespacing for bundle names is the way to go.
A bundle name itself cannot be forced unique across all entity types. Bundle names can only be unique within a given entity type.
That means you need an (entity type, bundle name) pair to unambiguously identify a bundle.
The Field API patch will be a drag to reroll, so you might want to *not*make the same mistake as we did and take that into account for the RDF patch :-/.

thanks for pointing this out. it is not too late to change that in the RDF module.

Overall I'm happy to discuss this and willing to make any change in the way we handle/store the mapping. thoughts?

scor’s picture

The only reason why we store the mapping in the node_type table at the first place is to allow custom content types to be mapped to RDF. One example is the default profile: the article and page content types are created by default.profile which saves them to the database via node_type_save(). So these content types are not governed by any module and therefore we cannot program these mappings anywhere. One solution would be to store them in the rdf module, however I strongly believe that each module (or custom content type) should define its own mapping to RDF, this should not be the role of the RDF module (catch and fago agreed while discussing this on IRC). The RDF module should provide the means for these mapping to be used and exposed (in RDFa for example) but the RDF module should not care about the actual mappings. When a developer writes a module which contains some content types and fields definitions and choose a human friendly name for it, why couldn't she also define an RDF friendly mapping for it? (this also applies to custom content type generated via the core UI, it is where the mapping could be specified also like it is the case in contrib with RDF and RDF CCK).

The problem with the node_type table is that this is only node type specific, so any other bundle or entity type cannot have its mapping stored in the database. I've started to think of a more generic solution for storing these, and discussed it with catch, fago and pwolanin on IRC. The RDF module could have its own table of mappings which would have a basic schema [ entity | bundle | rdf_mapping ]. Here we are inline with the last point of yched on #16: (entity type, bundle name) pair to unambiguously identify a bundle. From talking to catch, the content of this table would be cached thanks to hook_entity_info() and would therefore have a reduced overhead.

fago’s picture

As mentioned I agree it makes much sense that the data-structure providing module also provides the mapping to RDF, so node module should provide the RDF mapping. However, that doesn't say that we have to store it automatically - if we just provide one default mapping for all nodes, we would be fine without any node module storage.

Thus if a module / profile wants to provide a content type with a associated mapping, it'd have to use hook_node_info + provide the mapping hook and lock the content type, so it can't be renamed. Well that's not 100% ideal, but I think it suffices as when types get renamed, the meaning would change too in most cases.

That way the module implementing the UI could just provide the storage of the mapping too.

@hook_node_info() mappings:
I'm not a fan of overwriting the default mappings per node type. The node default mapping should be valid for any node on the site, thus it's mapping shouldn't be overwritten. Though a module can still override the mapping using alter(), but I don't think we should overwrite it in core.
Thus bundle specific mappings should be *additional* mappings, being more specialized but don't overwrite the defaults.

scor’s picture

Status: Needs work » Closed (fixed)

this was integrated in the main RDF patch #493030: RDF #1: core RDF module.