diff --git includes/theme.inc includes/theme.inc index 21175e3..d631d15 100644 --- includes/theme.inc +++ includes/theme.inc @@ -2003,7 +2003,10 @@ function template_preprocess_username(&$variables) { $variables['link_path'] = 'user/' . $variables['uid']; } elseif (!empty($account->homepage)) { - $variables['link_attributes'] = array('rel' => 'nofollow'); + // Like the 'class' attribute, the 'rel' attribute can hold a + // space-separated set of values, so initialize it as an array to make it + // easier for other preprocess functions to append to it. + $variables['link_attributes'] = array('rel' => array('nofollow')); $variables['link_path'] = $account->homepage; $variables['homepage'] = $account->homepage; } @@ -2023,7 +2026,14 @@ function template_process_username(&$variables) { // This is done in the process phase so that attributes may be added by // modules or the theme during the preprocess phase. if (isset($variables['link_path'])) { - $variables['link_options']['attributes'] = $variables['link_attributes'] + $variables['attributes_array']; + // $variables['attributes_array'] contains attributes that should be applied + // regardless of whether a link is being rendered or not. + // $variables['link_attributes'] contains attributes that should only be + // applied if a link is being rendered. Preprocess functions are encouraged + // to use the former unless they want to add attributes on the link only. + // If a link is being rendered, these need to be merged. Some attributes are + // themselves arrays, so the merging needs to be recursive. + $variables['link_options']['attributes'] = array_merge_recursive($variables['link_attributes'], $variables['attributes_array']); } } diff --git modules/comment/comment.test modules/comment/comment.test index 4d41c45..808432c 100644 --- modules/comment/comment.test +++ modules/comment/comment.test @@ -149,7 +149,7 @@ class CommentHelperCase extends DrupalWebTestCase { } /** - * Set comment form setting. + * Set comment form location setting. * * @param boolean $enabled * Form value. @@ -866,7 +866,7 @@ class CommentRSSUnitTest extends CommentHelperCase { } /** - * Test RDFa markup for comments. + * Tests RDFa markup for comments. */ class CommentRdfaTestCase extends CommentHelperCase { public static function getInfo() { @@ -881,36 +881,101 @@ class CommentRdfaTestCase extends CommentHelperCase { parent::setUp('comment', 'rdf'); $this->admin_user = $this->drupalCreateUser(array('administer content types', 'administer comments', 'administer permissions', 'administer blocks')); - $this->web_user = $this->drupalCreateUser(array('access comments', 'post comments', 'create article content')); - - $this->drupalLogin($this->web_user); - $this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1)); - $this->drupalLogout(); - } + $this->web_user = $this->drupalCreateUser(array('access comments', 'post comments', 'create article content', 'access user profiles')); - function testAttributesInMarkup() { - // Set comments to not have subject. - $this->drupalLogin($this->admin_user); - $this->setCommentPreview(FALSE); + // Enables anonymous user comments. + user_role_change_permissions(DRUPAL_ANONYMOUS_RID, array( + 'access comments' => TRUE, + 'post comments' => TRUE, + 'post comments without approval' => TRUE, + )); + // Allows anonymous to leave their contact information. + $this->setCommentAnonymous(COMMENT_ANONYMOUS_MAY_CONTACT); + $this->setCommentPreview(DRUPAL_OPTIONAL); $this->setCommentForm(TRUE); $this->setCommentSubject(TRUE); $this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, t('Comment paging changed.')); + + // Creates the nodes on which the test comments will be posted. + $this->drupalLogin($this->web_user); + $this->node1 = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1)); + $this->node2 = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1)); $this->drupalLogout(); + } + + /** + * Tests the presence of the RDFa markup for the title, date and author and + * homepage on registered users and anonymous comments. + */ + function testAttributesInRdfaMarkup() { - // Post comment. + // Posts comment #1 as a registered user. $this->drupalLogin($this->web_user); - $subject_text = 'foo'; - $comment_text = 'bar'; - $comment = $this->postComment($this->node, $comment_text, $subject_text, FALSE); - $this->drupalGet('node/' . $this->node->nid); + $comment1_subject = $this->randomName(); + $comment1_body = $this->randomName(); + $comment1 = $this->postComment($this->node1, $comment1_body, $comment1_subject); + + // Tests comment #1 with access to the user profile. + $this->drupalGet('node/' . $this->node1->nid); + $this->_testBasicCommentRdfaMarkup($comment1); + + // Tests comment #1 with no access to the user profile (as anonymous user). + $this->drupalLogout(); + $this->drupalGet('node/' . $this->node1->nid); + $this->_testBasicCommentRdfaMarkup($comment1); + + + // Posts comment #2 as anonymous user. + $comment2_subject = $this->randomName(); + $comment2_body = $this->randomName(); + $anonymous_user = array(); + $anonymous_user['name'] = $this->randomName(); + $anonymous_user['mail'] = 'tester@simpletest.org'; + $anonymous_user['homepage'] = 'http://example.org/'; + $comment2 = $this->postComment($this->node2, $comment2_body, $comment2_subject, $anonymous_user); + $this->drupalGet('node/' . $this->node2->nid); + + // Tests comment #2 as anonymous user. + $this->_testBasicCommentRdfaMarkup($comment2, $anonymous_user); + // Tests the RDFa markup for the homepage (specific to anonymous comments). + $comment_homepage = $this->xpath("//div[@typeof='sioct:Post']//span[@rel='sioc:has_creator']/a[contains(@class, 'username') and @typeof='sioc:User' and @property='foaf:name' and @href='http://example.org/' and contains(@rel, 'foaf:page')]"); + $this->assertTrue(!empty($comment_homepage), t('RDFa markup for the homepage of anonymous user found.')); + // There should be no about attribute on anonymous comments. + $comment_homepage = $this->xpath("//div[@typeof='sioct:Post']//span[@rel='sioc:has_creator']/a[@about]"); + $this->assertTrue(empty($comment_homepage), t('No about attribute is present on anonymous user comment.')); + + // Tests comment #2 as logged in user. + $this->drupalLogin($this->web_user); + $this->drupalGet('node/' . $this->node2->nid); + $this->_testBasicCommentRdfaMarkup($comment2, $anonymous_user); + // Tests the RDFa markup for the homepage (specific to anonymous comments). + $comment_homepage = $this->xpath("//div[@typeof='sioct:Post']//span[@rel='sioc:has_creator']/a[contains(@class, 'username') and @typeof='sioc:User' and @property='foaf:name' and @href='http://example.org/' and contains(@rel, 'foaf:page')]"); + $this->assertTrue(!empty($comment_homepage), t('RDFa markup for the homepage of anonymous user found.')); + // There should be no about attribute on anonymous comments. + $comment_homepage = $this->xpath("//div[@typeof='sioct:Post']//span[@rel='sioc:has_creator']/a[@about]"); + $this->assertTrue(empty($comment_homepage), t('No about attribute is present on anonymous user comment.')); + } + /** + * Helper function for testAttributesInRdfaMarkup(). + * + * Tests the current page for basic comment RDFa markup. + * + * @param $comment + * Comment object. + * @param $acount + * An array containing information about an anonymous user. + */ + function _testBasicCommentRdfaMarkup($comment, $account = array()) { $comment_container = $this->xpath("//div[contains(@class, 'comment') and @typeof='sioct:Post']"); - $this->assertFalse(empty($comment_container)); - $comment_title = $this->xpath("//h3[@property='dc:title']"); - $this->assertEqual((string)$comment_title[0]->a, 'foo'); + $this->assertTrue(!empty($comment_container)); + $comment_title = $this->xpath("//div[@typeof='sioct:Post']//h3[@property='dc:title']"); + $this->assertEqual((string)$comment_title[0]->a, $comment->subject); $comment_date = $this->xpath("//div[@typeof='sioct:Post']//*[contains(@property, 'dc:date') and contains(@property, 'dc:created')]"); - $this->assertFalse(empty($comment_date)); - $comment_author = $this->xpath("//div[@typeof='sioct:Post']//*[contains(@property, 'foaf:name')]"); - $this->assertEqual((string)$comment_author[0], $this->web_user->name); + $this->assertTrue(!empty($comment_date)); + // The author tag can be either a or span + $comment_author = $this->xpath("//div[@typeof='sioct:Post']//span[@rel='sioc:has_creator']/*[contains(@class, 'username') and @typeof='sioc:User' and @property='foaf:name']"); + $name = empty($account['name']) ? $this->web_user->name : $account['name'] . ' (not verified)'; + $this->assertEqual((string)$comment_author[0], $name); } } diff --git modules/rdf/rdf.module modules/rdf/rdf.module index 28035da..14ed0e0 100644 --- modules/rdf/rdf.module +++ modules/rdf/rdf.module @@ -402,6 +402,10 @@ function rdf_preprocess_node(&$variables) { $date_attributes_array = rdf_rdfa_attributes($variables['rdf_mapping']['created'], $variables['created']); $variables['rdf_template_variable_attributes_array']['date'] = $date_attributes_array; } + // Adds RDFa markup for the relation between the node and its author. + if (!empty($variables['rdf_mapping']['uid'])) { + $variables['rdf_template_variable_attributes_array']['name']['rel'] = $variables['rdf_mapping']['uid']['predicates']; + } } /** @@ -440,48 +444,46 @@ function rdf_preprocess_user_profile(&$variables) { * Implements MODULE_preprocess_HOOK(). */ function rdf_preprocess_username(&$variables) { - $account = $variables['account']; - if (!empty($account->rdf_mapping['name'])) { - if ($account->uid != 0) { - // The following RDFa construct allows to fit all the needed information - // into the a tag and avoids having to wrap it with an extra span. - - // An RDF resource for the user is created with the 'about' attribute and - // the profile URI is used to identify this resource. Even if the user - // profile is not accessible, we generate its URI regardless in order to - // be able to identify the user in RDF. + // $variables['account'] is a pseudo account object, and as such, does not + // contain the rdf mappings for the user; in the case of nodes and comments, + // it contains the mappings for the node or comment object instead. Therefore, + // the real account object for the user is needed. The real account object is + // needed for this function only; it should not replace $variables['account']. + if ($account = user_load($variables['uid'])) { + // An RDF resource for the user is created with the 'about' attribute and + // the profile URI is used to identify this resource. Even if the user + // profile is not accessible, we generate its URI regardless in order to + // be able to identify the user in RDF. We do not use this attribute for + // the anonymous user because we do not have a user profile URI for it (only + // a homepage which cannot be used as user profile in RDF). + if ($account->uid > 0) { $variables['attributes_array']['about'] = url('user/' . $account->uid); - // The 'typeof' attribute specifies the RDF type(s) of this resource. They - // are defined in the 'rdftype' property of the user object RDF mapping. - // Since the full user object is not available in $variables, it needs to - // be loaded. This is due to the collision between the node and user - // when they are merged into $account and some properties are overridden. - $variables['attributes_array']['typeof'] = user_load($account->uid)->rdf_mapping['rdftype']; - - // 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. - if (!empty($account->rdf_mapping['uid']['predicates'])) { - $variables['attributes_array']['rev'] = $account->rdf_mapping['uid']['predicates']; - // We indicate the parent identifier in the 'resource' attribute, - // typically this is the entity URI. This is the object in RDF. - $parent_uri = ''; - if (!empty($account->path['source'])) { - $parent_uri = url($account->path['source']); - } - elseif (!empty($account->cid)) { - $parent_uri = url('comment/' . $account->cid, array('fragment' => 'comment-' . $account->cid)); - } - $variables['attributes_array']['resource'] = $parent_uri; - } + } + + // The remaining attributes are defined by RDFa as lists + // (http://www.w3.org/TR/rdfa-syntax/#rdfa-attributes). Therefore, merge + // rather than override, so as not to clobber values set by earlier + // preprocess functions. + $attributes = array(); - // The second information we annotate is the name of the user with the - // 'property' attribute. We do not need to specify the RDF object here - // because it's the value inside the a tag which will be used - // automatically according to the RDFa parsing rules. - $variables['attributes_array']['property'] = $account->rdf_mapping['name']['predicates']; + // The 'typeof' attribute specifies the RDF type(s) of this resource. They + // are defined in the 'rdftype' property of the user object RDF mapping. + if (!empty($account->rdf_mapping['rdftype'])) { + $attributes['typeof'] = $account->rdf_mapping['rdftype']; } + + // Annotate the user name in RDFa. The attribute 'property' is used here + // because the user name is a literal. + if (!empty($account->rdf_mapping['name'])) { + $attributes['property'] = $account->rdf_mapping['name']['predicates']; + } + + // Add the homepage RDFa markup if present. + if (!empty($variables['homepage']) && !empty($account->rdf_mapping['homepage'])) { + $attributes['rel'] = $account->rdf_mapping['homepage']['predicates']; + } + + $variables['attributes_array'] = array_merge_recursive($variables['attributes_array'], $attributes); } } @@ -503,6 +505,10 @@ function rdf_preprocess_comment(&$variables) { $date_attributes_array = rdf_rdfa_attributes($comment->rdf_mapping['created'], $comment->created); $variables['rdf_template_variable_attributes_array']['created'] = $date_attributes_array; } + // Adds RDFa markup for the relation between the comment and its author. + if (!empty($comment->rdf_mapping['uid'])) { + $variables['rdf_template_variable_attributes_array']['author']['rel'] = $comment->rdf_mapping['uid']['predicates']; + } if (!empty($comment->rdf_mapping['title'])) { // 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 diff --git modules/user/user.module modules/user/user.module index b747813..eaa524d 100644 --- modules/user/user.module +++ modules/user/user.module @@ -3275,6 +3275,10 @@ function user_rdf_mapping() { 'name' => array( 'predicates' => array('foaf:name'), ), + 'homepage' => array( + 'predicates' => array('foaf:page'), + 'type' => 'rel', + ), ), ), );