I guess July is WordPress plugin month here at movingtofreedom.org, because here comes another one. This one was instigated by another Scott.

Scott Rosenberg wrote last week about the “mutability of online content” and the challenges news organizations face in handling changes and corrections to stories, and concluded that versioning made sense in journalism as a way to build trust. Readers can see all of the changes since publication so that there’s no sense of something being swept under the rug. The Drupal CMS has modules for making revisions publicly viewable, and of course this is a key feature of Wikipedia.

Scott noted that WordPress already stores revisions, and suggested a plugin could make old versions accessible to visitors. Looking to demo the idea on his blog, he later asked if any WordPress developers were intrigued. At first I was only intrigued enough to dig up an existing plugin by D’Arcy Norman that did some of what he was looking for: “Post Revision Display,” but gradually became invested in the challenge of making this thing happen. And here it is!

(And here is Scott’s post about the plugin: “Change is good, but show your work.”)

What It Does

It’s pretty simple. Drop the post-revision-display.php file into your plugins dir and activate it, and you’ll see a list of post-publication revisions at the end of single post pages:

WordPress Plugin: Post Revision Display Example, Current Revision

Click on the link for a revision, and you’ll get a page showing that revision along with the “diffs” between the old revision and the current revision:

WordPress Plugin: Post Revision Display Example, Old Revision with Diffs

If you have the standard <?php wp_head() ?> hook in your theme’s header.php file, the plugin will add to the page header when an old revision is displayed:

<meta name="robots" content="noindex, nofollow" />

To prevent indexing by search engines. (This is what Wikipedia does on its revision pages and I think is a good idea here as well.)

That’s all you have to do in the default “automatic” mode to start using the plugin, but you’ll likely want to add some CSS to make the header note stand out and have the diffs look all spiffy as in the screenshot.

CSS

The diff styling is modeled after how the WordPress admin pages show comparisons. You may want to use similar styles as I did:

table.diff { width: 100%; }
table.diff th { text-align: left; }
table.diff .diff-deletedline { background-color:#fdd;
                               width: 50%; }
table.diff .diff-deletedline del { background-color:#f99;
                                   text-decoration: none; }
table.diff .diff-addedline { background-color:#dfd;
                             width: 50%; }
table.diff .diff-addedline ins { background-color:#9f9;
                                 text-decoration: none; }
table.diff .diff-context { display: none; }

This is entirely up to you, of course, but I’ll make a few comments about my choices:

  • th I like left-alignment for the headers here.
  • .diff-deletedline, .diff-addedline These are the WordPress colors. I added 50% as the width. They are used for the column headers also, showing previous revision datetime and current revision.
  • .diff-deletedline del WordPress color. Default text-decoration for del seems to be strikethrough (actually: “line-through”), which is probably better-suited for a print stylesheet.
  • .diff-addedline ins WordPress color. Default text-decoration for ins is underline, which again might lend itself better to print.
  • .diff-context The WordPress admin diff shows all content for each post. Here, I’ve hidden the lines where there are no changes. Google, for one, frowns on hiding text, but since revision pages are marked as “noindex”, it shouldn’t hurt anything, other than wasting some bandwidth in sending down text that isn’t shown.

Other CSS

The note at the top uses <div class="revision-header"><p>. For example, I styled mine with:

.revision-header {
	background-color: yellow;
	border: 1px solid #3a8b8c;
	padding: 10px; }

div.revision-header { padding: 0 10px; }

The revision list uses:

<div class="post-revisions">
<h4>Post Revisions:</h4>
<ul class="post-revisions">

And the changes section:

<div id="revision-diffs">
<h4>Changes:</h4>

It uses an id instead of a class to allow the anchor jump “See below for differences.”

(The h4 headers for the list and the diffs can be changed in “manual mode”. Keep reading…)

Diffs / Changes

The revision comparison shows the underlying HTML in a table, with columns for previous and current revisions. With things like URLs, it’s likely you’ll have long strings that won’t wrap nicely in the table. The text will overflow to the right when this happens. In my theme, this means things will be hidden. Depending on how long your non-wrapping strings are, you can lose the entire right column. CSS will allow for a scrollbar in this situation:

#revision-diffs { overflow: auto; }

But to me, that’s not an ideal solution by itself. It doesn’t give you a nice side-by-side look at things. So what the plugin will do is try to break things up with spaces, inserting them in text that is at least 24 non-space characters long and has slashes, dashes, underscores, etcetera as breakpoints. This allows for better wrapping and doesn’t really change the gist of the comparison, IMO. Previous and current revisions will tend to break at the same spots and not be highlighted. If a diff is somehow introduced, it should be clear that it’s nothing significant. The revision text shown as the content is still the original text.

It’s still a good idea to use overflow auto in case some long strings don’t have breakpoints. Maybe you have narrow columns and tend to use words like internationalization or supercalifragilisticexpialidocious.

(If this feature offends you, you can modify the plugin code, taking out the calls to the prd_break_up_lines function.)

Automatic vs Manual Mode

In “automatic mode,” once you activate the plugin, revision information will show up on single post views (e.g. single.php) as shown in the screenshots. There is no need to modify theme files. (Other than your CSS, as discussed above!)

With “manual mode,” you can specify where in your theme you want revision info to be displayed. Most commonly, you’ll make calls from single.php to:

<?php the_revision_note_prd() ?>
<?php the_revision_list_prd() ?>
<?php the_revision_diffs_prd() ?>

Which I think are pretty self-explanatory. One of these will have to come before the_content so that when filters are applied on post content, the plugin will know it is in manual mode and won’t do the automatic stuff. To me it makes the most sense to have the note at the top as a warning for when a previous revision is displayed, which will take care of this requirement. (Even on current revision posts where there is no note, it will still set the manual flag.)

If you insist on putting all the function calls after the post content, you can place <?php prd_set_manual_mode() ?> before the_content is called in your theme.

Manual mode allows you to show revision info on “pages” (likely: page.php) in addition to single post views. (Or, if you want to do this in automatic mode and you’re willing to make a code change to the plugin, you can look for this line in the prd_display_post_revisions function: if (!is_single()) {, and change to: if (!is_single() && !is_page()) {.)

You can also put revision info on pages with multiple posts (e.g. the main index.php page or archive pages), although in those cases it only makes sense to show the revision list. When calling the_revision_list_prd() from within “the loop,” you’ll need to force an update of the revision info for each post by using the first optional parameter, $refreshGlobals:

the_revision_list_prd(true)

(When you call the three _prd functions, the first call will load all the revision info into a set of global variables. Without forcing the update with $refreshGlobals=true, you’d see the revision list for the first post for every following post displayed on the page.)

In manual mode, you can also specify your own header text for the revision list and diff sections. This is the second optional parameter for the list function:

the_revision_list_prd($refreshGlobals=false,
                      $header=REV_LIST_HEADER)

Where the default REV_LIST_HEADER is <h4>Revision List:</h4>

The header is the first and only optional parameter for the function:

the_revision_diffs_prd($header=REV_DIFFS_HEADER)

Where the default REV_DIFFS_HEADER is <h4>Changes:</h4>

An example of calling these from single.php:

<?php the_revision_list_prd
          (false, '<h3>This is the revision list!</h3>') ?>
<?php the_revision_diffs_prd
          ('<p><b>These are the diffs!</b></p>') ?>

Download v0.7 (87K)

Also available from the official wordpress.org plugin page.

The plugin has been tested with WordPress 3.0 and is licensed with the GNU General Public License, version 2 or later.

Updates

23 August 2010: v0.7: Nicer diff styling using built-in wp_text_diff function. Requires CSS for the “nicer” part.

16 August 2010: v0.6: Manual mode lets you make function calls from your theme for control over where revision information appears.

2 August 2010: v0.5.2: Compares and reports on changes in post title.

1 August 2010: v0.5.1: Revision note at top of post changed to use div + p instead of just p.

Notes / Acknowledgments

  • I don’t plan on using this right now on my site, so you’ll have to go to Scott’s Wordyard blog to see it in action. (Maybe not until a few days after this post.) And for more examples, please leave a note in the comments if you decide to try it!
  • The default behavior of WordPress is to save all revisions, but can be modified to save only the most recent “N” revisions. If used for the “trust factor,” it would be best to save all revisions, don’t you think?
  • The markup generated by the plugin validates as HTML5, and very possibly will also pass as Transitional XHTML 1.0.
  • Let’s consider this to be experimental and proof-of-concepty, although so far it appears to be robust enough. I tried to take care with security checks so that only post-publication versions of public posts can be viewed. It seems that WordPress’s security mostly carries the day. If there are private posts, only those with access should be able to see revisions. However, caveat emptor. Please let me know if you find any problems, with security or otherwise.
  • How cool is free software? I find that no matter how much I work with “free as in freedom” stuff, I still get a kick out of the way this works. We should all be free to build on each other’s work. It’s a culture of cooperation. Without the boost from the original, I likely wouldn’t have done this. (Thanks, D’Arcy!)
  • I found a nice PHP implementation of the Unix “diff” utility written by Daniel Unterberger and Nils Knappmeier, PHPDiff, which I modified for use in earlier versions, but later started using the built-in WordPress function, wp_text_diff. (Still, more wonderful free software at work.)
  • Thanks to Scott R. for starting the ball rolling. I enjoyed working on this little project and doing my part to save the credibility of journalism in the digital age.