Over 2 years ago I published an article on how to fix problems with WordPress themes which didn’t pass the Google Rich Snippets Testing Tool. Since then lots has changed — WordPress and most themes have better support, HTML5 and CSS3 have arrived, and Google, Yahoo and Bing got together and decided to focus on Microdata, via schema.org. At the time microformats seemed the easiest way to mark up pages, and the testing tool had the most comprehensive support for these. The move to Microdata and the changes to WordPress required a new article: this is it!
Instead of having lots of separate articles for different themes I’ve decided to try to provide all the information in 1 guide. This makes it easier to keep up to date, and means there is just 1 central point for all requests and discussion. It also makes more sense as several of the steps are the same regardless of what theme you are using. I’m not going to cover all the optional extras in this article, just the basics to get your site to validate with the tool. Once you’ve managed that you should find adding extra markup is relatively easy. So lets get started…
Add a link from your google profile to your site
You will see on my google+ profile that I have a public link to https://diymediahome.org — you will need the same for your own site. Ideally you will need all of your authors to have a google profile that is linked to your site in the same way as this.
- Log in to your google profile
- Scroll down until you see the links section somewhere towards the bottom right of the page
- Click on the edit link
- Under the contributor to section click Add custom link
- Make sure the box to the right of the text contributor to is set to public
- Enter your site name (e.g. DIY Media Home) and address (e.g. https://diymediahome.org)
- Click Save
Add a link from your site to your google profile
Now we need to add a link from your author page to your google profile. There are 2 alternative ways to do this, I highly recommend using the Yoast SEO plugin.
- Install the Yoast SEO plugin OR add the following code to the functions.php file of your theme
function update_contactmethods( $contactmethods ) { // Add Google Profiles $contactmethods['googleplus'] = 'Google+'; // Add Twitter $contactmethods['twitter'] = __( 'Twitter username (without @)', 'wordpress-seo' ); return $contactmethods; } add_filter( 'user_contactmethods', 'update_contactmethods', 10, 1);
- Go to your author profile in the wordpress admin interface (hover over Users in the left-hand menu, and click on Your Profile)
- In the Google+ field add a link to your google profile (e.g. https://plus.google.com/104657888470728381512/)
- Make sure either you, or your other site authors do the same thing, and each link to your own google profiles
- Now, if you’re using the Yoast plugin, go to the SEO menu and select Titles and Metas, click on the Home menu at the top of the page and in the Author highlighting pulldown box select the user you want to use as the author of the homepage
- If you’re insistent about not using the Yoast plugin you will need to manually add the following code to header.php (assuming you have one). Don’t forget to change my google profile url to yours. This code will use the post author’s google profile url if there is a valid one, and will use a default (yours) if there isn’t.
<link rel="author" href="<?php $gplus = 'https://plus.google.com/115369062315673853712/posts'; if ( is_singular() ) {global $post; $gplus = get_the_author_meta( 'googleplus', $post->post_author );} echo $gplus; ?>"/>
Fix errors of a missing “updated” field
Most wordpress themes include information about when the post was published in the post information area, but very few also include when it was last updated. This is a pity, because updated is required whereas published actually isn’t! Adding it is easy though, and again, there are 2 ways, the proper way and the quick hack way. I recommend the proper way of course, but if necessary you can resort to the quick hack. We’re also going to change from microformats to microdata at the same time.
The proper way
We’re going to add some additional information to post headers, both for search engines and for our visitors. We will need to do this for posts, pages, archives and the homepage.
- We need to find the file that contains the code which generates the published link. This will almost certainly use the wordpress function get_the_date(). You can search through your theme files, or refer to theme specific details below where I have provided details for lots of themes
- We are going to check if the published and updated dates are the same. If they are we will just add the extra dateUpdated itemprop but if they are different we will output some extra text to the visitor
- We’re going to add the following (and modify it as it explains)
<?php if(get_the_date()==get_the_modified_date()): ?> //we're going to use the original code just with the updated class added //put your theme's original code here //remove any class="" //add itemprop="datePublished dateCreated dateModified" to the HTML tag surrounding the get_the_date() function <?php else: ?> //we're going to use slightly code just with the updated date added //copy your theme's original code to here and then modify it as below //remove any class="" //add itemprop="datePublished dateCreated" to the HTML tag surrounding the get_the_date() function //add in a new html tag into the line where you want it to appear as below //Updated <?php echo get_the_modified_date() ?> <?php endif; ?>
- You’ll either have to figure out the original theme’s code and edit it, or refer to the details for specific themes below.
- You may have to repeat this edit for multiple files
The quick hack
We’re going to cheat and supply the published date as the updated date, even if the post has actually been updated more recently. If the post has been updated then we’ll actually be supplying incorrect information, but it will still validate. It really is better to do it properly if possible, but if you’re insistent on doing it this way read on. As above we will need to do this for posts, pages, archives and the homepage.
- As above we need to find the file that contains the code which generates the published link. This will almost certainly use the wordpress function get_the_date(). You can search through your theme files, or refer to the table below where I have provided details for lots of themes
- Find the HTML element that surrounds the date (often it will have
class="published"
). - Edit the element by removing the class and adding the following instead
itemprop="datePublished dateCreated dateUpdated"
- As above, you may need to repeat this edit in multiple files
Replacing the built-in microformats (because mixing rich snippet formats breaks things)
By default wordpress is still using microformat markup for comments, which sadly seems to break microdata markup of things like recipes. The best way to fix this is to remove the microformat markup and replace it with microdata markup. Unfortunately, the wordpress core functions that produce this markup don’t have a conveniently located filter or hook so this requires a bit of tinkering about.
Replacing vcard in the comments section
- What we’re going to do is use a comment callback to change the output of comments. Many themes already use a callback which we can modify, and for those that don’t we’ll add one based on the wordpress default
- Check if you theme already has a comment callback — search your theme files for the function
wp_list_comments
. If it includes the codecallback=
or'callback' =>
then it is using a callback which you should be able to find (probably in functions.php) by looking for the name of the callback function, which is the part after the = sign or arrow, e.g.wp_list_comments( array( 'callback' => 'twentyten_comment' ) );
orwp_list_comments('type=comment&callback=bones_comments');
. If it doesn’t then we will need to add one by addingcallback=diymh_comment
orarray( 'callback' => 'diymh_comment' )
to the line. - If you needed to add a comment callback then you will need to add the following code to functions.php
function diymh_comment($comment, $args, $depth) { $GLOBALS['comment'] = $comment; ?> <li <?php comment_class(); ?> id="li-comment-<?php comment_ID() ?>"> <div id="comment-<?php comment_ID(); ?>"> <div class="comment-author novcard"> <?php echo get_avatar($comment,$size='48',$default='<path_to_url>' ); ?> <?php printf(__('<cite class="fn">%s</cite> <span class="says">says:</span>'), get_comment_author_link()) ?> </div> <?php if ($comment->comment_approved == '0') : ?> <em><?php _e('Your comment is awaiting moderation.') ?></em> <br /> <?php endif; ?> <div class="comment-meta commentmetadata"><a href="<?php echo htmlspecialchars( get_comment_link( $comment->comment_ID ) ) ?>"><?php printf(__('%1$s at %2$s'), get_comment_date(), get_comment_time()) ?></a><?php edit_comment_link(__('(Edit)'),' ','') ?></div> <?php comment_text() ?> <div class="reply"> <?php comment_reply_link(array_merge( $args, array('depth' => $depth, 'max_depth' => $args['max_depth']))) ?> </div> </div> <?php }
- You will notice that this code has a
class="novcard"
instead of the originalclass="vcard".
- If your theme did have its own callback you need to find the callback function and replace any instances of
class="vcard"
withclass="novcard"
Replacing hfeed in the header
- Next, we need to also remove any instances of
class="hfeed"
, which are usually found in header.php. Replace all occurrences ofhfeed
withnohfeed
Replacing hentry and title rich snippets
- We can replace these elements with a couple of neat functions added to functions.php
function diymh_replace_hentry($classes){ if(($key = array_search('hentry', $classes)) !== false) $classes[$key]='nohentry'; return $classes; } add_filter('post_class','diymh_replace_hentry'); function diymh_microdata_title($title) { return '<span itemprop="name">'.$title.'</span>'; } add_filter('the_title', 'diymh_microdata_title', 10, 2);
Adding the information type (e.g. Article, Recipe, etc.) to the article header
- First, decide what type of work you are publishing — most likely an article. Have a look at the list at schema.org
- Find the HTML tag which contains the function
post_class()
- Add the code
itemscope itemtype="https://schema.org/Article"
(replace article if you picked a different type of work)
Finally, to avoid breaking any styles
- You will need to edit your theme’s style.css and do a search/replace operation to replace all instances of
.vcard
with.novcard
and replace all instances of.hfeed
with.nohfeed
so that our changes don’t mess up any CSS styling of our comments
Fixing any other errors
Any other changes necessary will be detailed on a per theme basis below. If you have a theme which you are unable to fix please post a comment. If its a free theme please let me know the theme name, and if it is a premium theme please let me know so I can provide you with an e‑mail address to send a zipped copy to.
Theme specific details
Before just carrying out these edits don’t forget to carry out the steps to link your google profile and your site to each other, and install Yoast SEO, or add the code provides above.
Twenty Ten 1.5
In functions.php replace this
function twentyten_posted_on() { printf( __( '<span class="%1$s">Posted on</span> %2$s <span class="meta-sep">by</span> %3$s', 'twentyten' ), 'meta-prep meta-prep-author', sprintf( '<a href="%1$s" title="%2$s" rel="bookmark"><span class="entry-date">%3$s</span></a>', get_permalink(), esc_attr( get_the_time() ), get_the_date() ), sprintf( '<span class="author vcard"><a class="url fn n" href="%1$s" title="%2$s">%3$s</a></span>', get_author_posts_url( get_the_author_meta( 'ID' ) ), esc_attr( sprintf( __( 'View all posts by %s', 'twentyten' ), get_the_author() ) ), get_the_author() ) ); }
With this
function twentyten_posted_on() { if(get_the_date()==get_the_modified_date()){ printf( __( '<span class="%1$s">Posted on</span> %2$s <span class="meta-sep">by</span> %3$s', 'twentyten' ), 'meta-prep meta-prep-author', sprintf( '<a href="%1$s" title="%2$s" rel="bookmark"><span itemprop="datePublished dateCreated dateModified">%3$s</span></a>', get_permalink(), esc_attr( get_the_time() ), get_the_date() ), sprintf( '<span class="author vcard"><a itemprop="author" href="%1$s" title="%2$s">%3$s</a></span>', get_author_posts_url( get_the_author_meta( 'ID' ) ), esc_attr( sprintf( __( 'View all posts by %s', 'twentyten' ), get_the_author() ) ), get_the_author() ) ); } else{ printf( __( '<span class="%1$s">Posted on</span> %2$s <span class="meta-sep">by</span> %3$s. Updated <span itemprop="dateModified">%4$s</span>.', 'twentyten' ), 'meta-prep meta-prep-author', sprintf( '<a href="%1$s" title="%2$s" rel="bookmark"><span itemprop="datePublished dateCreated">%3$s</span></a>', get_permalink(), esc_attr( get_the_time() ), get_the_date() ), sprintf( '<span class="author vcard"><a itemprop="author" href="%1$s" title="%2$s">%3$s</a></span>', get_author_posts_url( get_the_author_meta( 'ID' ) ), esc_attr( sprintf( __( 'View all posts by %s', 'twentyten' ), get_the_author() ) ), get_the_author() ), get_the_modified_date() ); } }
In functions.php replace this
<div class="comment-author vcard">
With this
<div class="comment-author novcard">
In header.php replace this
<div id="wrapper" class="hfeed">
With this
<div id="wrapper" class="nohfeed">
In loop.php (3 instances), loop-single.php, loop-page.php and loop-attachment.php replace this
<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
With this
<div id="post-<?php the_ID(); ?>" <?php post_class(); ?> itemscope itemtype="https://schema.org/Article">
Twenty Eleven 1.5
coming very soon…
Annotum Base 1.1.1
In 7 files (functions\features-articles.php, excerpt\type-article.php, excerpt\excerpt-default.php, content\type-article.php, content\page.php, content\content-default.php, content\article-pdf.php) replace
<article <?php post_class('article-full'); ?>>
with
<article <?php post_class('article-full'); ?> itemscope itemtype="https://schema.org/Article">
Thanks — working on it — eisenberg.co.za
Ottimo, grazie mille!