Monthly Archives: January 2010

Class blogrolls: No sweat

Last week, in the post where I announced my new WPMU plugin Shared Blogroll, I briefly mentioned the use case that I had in mind, which was of course education-focused. Yesterday and today I did some more work on the plugin itself (MORE AJAX 4EVA) and some of the supporting players. I thought I’d spell it out a little more so that others could implement what is probably the coolest system for shared blogrolls on earth, or at least on WPMU, which amounts to pretty much the same thing.

The plugins

  • Shared Blogroll. This creates a widget that allows bloggers to embed a link category from any other blog on the system on their own blog.
  • Add Users Sidebar Widget. This plugin is already pretty great, as well it should be seeing as it’s written by good-looking Canadians. I made some edits to the plugin to make it a bit more flexible.
    1. First I removed the RSS box from the Add User widget. For most of my users, it’ll confuse; for all of them, it’ll be redundant, since they’ll all be using Wordpress blogs and thus will have feeds that are very auto-discoverable.
    2. As the plugin stands in the repository, links are automatically put into the blog’s default link category (or, if they’re using FeedWordPress, the FWP link category). For most users, that’s something unhelpful like “Links” or “Uncategorized”. I altered the plugin so that the blog owner can choose which link category the added links will be put into. As I’ll discuss below, I think that this will add a lot of flexibility to the way that the plugins can be used together. You can download the text of my modified version of Add Users Sidebar Widget here: add-link-sidebar-widget.txt. Save as .php before uploading – and make sure that you uninstall previous versions of the plugin before uploading, or you might get whitescreened to death.
  • Optional step. The default WP Links widget is set up so that you can’t select to display an empty category of links. As you’ll see below, I want to have instructors create a new category for their blogrolls, and I figured it’d be pretty confusing if they couldn’t create a sidebar widget corresponding to a link category until at least one student had posted a link (thus populating the category). So I wrote a little plugin that replaces the default Links widget with one that allows you to pick an empty links category. Drop this into mu-plugins: link_widget_allow_empty_cat. Be warned, though, that this plugin deregisters the default Links widget, which might have the effect of removing every links widget on your installation of WPMU. (I haven’t tested it.) For me, it’s OK, because I don’t have any active users yet. As an alternative, you could edit the core of WP (making sure to back up, take note of where you made the edit for future upgrades, blah blah blah) in the following way. In wp-includes/default-widgets.php, line 139 reads
    $link_cats = get_terms( 'link_category');

    Replace it with

    $link_cats = get_terms( 'link_category', 'hide_empty=0');

    That should leave all of your pre-existing links widgets in place.

The process

The immediate goal of this setup is to allow a class blogroll to be shared effortlessly between members of a class. (Though I can imagine lots of other very cool uses.) Here are the directions I’m going to give to instructors and students on how to leverage these two plugins to make it happen.

Instructors

  • Create a new Link Category for your class blogroll – let’s say you call it “ENG 110 Blogroll”.
  • Add a Links widget to the sidebar, making sure to select “ENG 110 Blogroll” from the category dropdown.
  • Add an Add Links widget to the sidebar. Again, be sure to select “ENG 110″ blogroll as the target category for added links.
  • Give your blog’s ID number to your students and give them the following directions.

Students

  • After creating a blog, visit the professor’s blog and enter your site’s URL into the Add Link sidebar widget.
  • Add a Shared Blogroll widget to your own blog’s sidebar. Enter the professor’s blog ID number as the source blog, select “ENG 110 Blogroll” from the dropdown list, and Save.

I hope that the instructors and students get into this blogroll sharing thing. It distributes what would otherwise be a huge amount of work for the instructor, or *worse yet*, for me.

New WPMU plugin: Shared Blogroll

I just wrote a widget that lets a blog admin pull a link category from any blog on the same WPMU installation. It is pretty much the bomb, and you are going to love using it so much that you may have to take downers in order to get yourself under control.

Check it out.

Side note: I’m going to start creating separate pages (as opposed to posts) for each of my software releases. That way all comments/support regarding a given plugin will be in the same place, with a pretty URL. That’s why I’m turning off comments on this post.

Blog-specific email plugin for WPMU users

A quick WPMU hack that I think will help a lot of people using an installation of WPMU for multiple classes.

The plugin Email Users by Vincent Prat allows blog authors/admins to email users in two different ways: 1) by emailing a group of users (such as those corresponding a particular role on your blog), or 2) by emailing individual users. The problem, though, is that this second option brings up a list of every single user on the installation of WPMU. This can be a bit of a pain for the normal blog user, as teachers or students in a class would probably only want to see a list of those people who are in the class, or on the blog.

Here’s a hack that will make the Email Users plugin show list only the members of the current blog for everyone except for the site admin:

  1. In the main plugin file (email-users.php), find the function mailusers_get_users. It should start around line 404.
  2. Look for the lines of code (414-417 in my version) that define the variable $users in the first conditional clause:
    $users = $wpdb->get_results(
    			  "SELECT id, user_email, display_name "
    			. "FROM $wpdb->users "
    			. $additional_sql_filter );
    
  3. Replace that line with the following code:
    if ( is_site_admin() ) {
    			$users = $wpdb->get_results(
    			  "SELECT id, user_email, display_name "
    			. "FROM $wpdb->users "
    			. $additional_sql_filter );
    		} else {
    			$wp_user_search = new WP_User_Search('', '', '');
    			$user_list = $wp_user_search->get_results();
    			$user_array = join(',', $user_list);
    			$users = $wpdb->get_results(
    			  "SELECT id, user_email, display_name "
    			. "FROM $wpdb->users "
    			. "WHERE id IN ( $user_array ) " );	
    		}
    

Here’s the use case I envision. The instructor for a class places the Add User Sidebar Widget (by my boys at UBC’s OLT!) in the sidebar of his or her blog. As part of the first assignment of the semester, the instructor asks each student to register for an account, and click the “Add Me” button on the instructor’s blog. That will automatically populate the email list above.

File this tip under “who needs Blackboard?”.

True cross-platform comment syncing with Disqus and Wordpress

FeedWordPress works well if you want to syndicate content from various sources into a single Wordpress blog. Syndicating comments is, of course, more difficult. I’m finishing up a job for a client who wanted real-time synced comments, and suggested that Disqus might do the trick. I quickly discovered that Disqus is clearly not made to do what I wanted it to do. But, being the cool guy that I am, I hacked something together that is more or less functional.

Here were the requirements: Comments on a blog post needed to be synchronized between the source blogs and the hub blog. Readers had to be able to comment in both places and have the comments sync. While I’d be using Wordpress to create the hub blog, the source blogs would be hosted on various platforms: Tumblr, Typepad, Blogger, self-hosted Wordpress. (The distributed requirement is especially important. If the blogs were all on the same installation of WPMU, the job would be trivial and would not require a third-party solution like Disqus.) Because bloggers would be coming from different platforms, I not only had to be able to accomodate those platforms, but I also had to make sure that the system would work with the platforms’ stock configuration. That is, since I (and, generally speaking, the bloggers) don’t have access to the platform code, all custom modifications need to happen at the hub blog.

I don’t particularly recommend that anyone try to replicate what I’ve done here. But hopefully it will point the way toward what might be a viable third-party system for true comment syncing.

The details

Here’s my strategy with regard to Disqus. If all the source blogs were registered to the same Disqus Comments account (ie corresponding to a single shortname), then they’d all have the same forum_key, which is to say they’d be accessible by the same API request. Thus the strategy is to make Disqus unable to distinguish between API calls from the source blogs (which are, recall, making stock API calls to Disqus) and API calls from the corresponding posts on the hub blog.

I installed the Disqus Comment System plugin for the Wordpress hub blog and registered with the same credentials that would be given to the source blogs. When feeds starting syndicating to the hub blog, however, I found that the comment sections on the source post weren’t matching the comment section on the hub post. The URL for each comment thread’s RSS feed showed me why: Disqus indexes a forum’s comment thread based on some post information that it gets from the client platform, and each platform was formatting the information in a different way.

First problem: The Wordpress Disqus plugin uses a post variable called $thread_meta, which is set in disqus-comment-system/lib/api.php thus:

$thread_meta = $post->ID . ' ' . $post->guid;

Disqus would then create a comment thread based on this string. The problem is that $post->ID is the post ID number for the hub blog, and has nothing to do with the source blog (which, depending on platform, does not include post ids in its API request at all). So the source blog’s thread would be identified as test_post (for example) while the hub blog would be 34_test_post. I replaced the code above with

$thread_meta = $post->guid

which manages to stay pretty consistent across platforms. (NB: The same change has to be made on the source blog version of the Disqus plugin, if the source blog is running a self-hosted installation of Wordpress.)

Second problem: Getting a stable and unique identifier for each post thread is only the first step. You also need to make sure that the identifier is concatenated correctly when the actual API request is made. Disqus comment sections work by loading a piece of Javascript that is concatenated from an API request to disqus.com for the proper thread, then finds the comment section on the post page, and replaces the native comment code with the code returned from disqus.com. But I found (again, by looking at the URL for the RSS feeds) that each platform was making the request a little bit differently. At the end of disqus-comments-system/comments.php, the stock WP plugin reads

<script type="text/javascript" charset="utf-8" src="http://<?php echo strtolower(get_option('disqus_forum_url')); ?>.<?php echo DISQUS_DOMAIN; ?>/disqus.js?v=2.0&amp;slug=<?php echo $dsq_response['thread_slug']; ?>&amp;pver=<?php echo $dsq_version; ?>"></script>

Through a fair degree of trial and error, I replaced it with a big block of code that figures out (via some metadata created by FeedWordPress) which platform that particular post came from, and then modifies the javascript accordingly:

<?php $ok = get_post_meta($post->ID, 'syndication_permalink'); ?>
<?php $name = get_post_meta($post->ID, 'syndication_source'); ?>
<?php $name = str_replace(" ", "_", $name[0]); ?>
<?php $theslug = $dsq_response['thread_slug']; ?>
<?php $theslug = str_replace( '8211_', '', $theslug ); /* Removes em dash UGH */ ?> 
<?php if ( preg_match( "/_[0-9]{2}$/", $dsq_response['thread_slug'] ) ) {
		$thesluglen = strlen($dsq_response['thread_slug']);
		$theslug = substr( $dsq_response['thread_slug'], 0, $thesluglen-3 );
		}
	?>
<?php if ( strpos( $ok[0], 'typepad' ) ) : ?>
	<script type="text/javascript" charset="utf-8" src="http://<?php echo strtolower(get_option('disqus_forum_url')); ?>.<?php echo DISQUS_DOMAIN; ?>/disqus.js?v=2.0&amp;slug=<?php echo $dsq_response['thread_slug'],'_',strtolower($name); ?>&amp;pname=wordpress&amp;pver=<?php echo $dsq_version; ?>"></script>
<?php elseif ( strpos( $ok[0], 'tumblr' ) ) : ?>
	<script type="text/javascript" charset="utf-8" src="http://<?php echo strtolower(get_option('disqus_forum_url')); ?>.<?php echo DISQUS_DOMAIN; ?>/disqus.js?v=2.0&amp;slug=<?php echo strtolower($name),'_',$dsq_response['thread_slug']; ?>&amp;pname=wordpress&amp;pver=<?php echo $dsq_version; ?>"></script>
<?php else : ?>
	<script type="text/javascript" charset="utf-8" src="http://<?php echo strtolower(get_option('disqus_forum_url')); ?>.<?php echo DISQUS_DOMAIN; ?>/disqus.js?v=2.0&amp;slug=<?php echo $theslug; ?>&amp;pver=<?php echo $dsq_version; ?>"></script>
<?php endif; ?>

In the first few lines, I do a bit of string manipulation to standardize the post title (ie the unique post identifier from Disqus’s point of view). Then I do some very ugly stuff. Wordpress was converting the em-dash (which was all over the client’s blog) into an ASCII code, which was screwing up the post identifier, so I just str_replaced it out. The next part (with the preg_match) is a bit tricky: in some cases, when Disqus receives requests from two blog posts with the same title (as is the case with the source blog post and the hub blog post), it differentiates between the two by assigning an apparently random two digit number to the second request it gets. Since the syndicated Disqus request will generally be sent after the source blog’s Disqus request (in virtue of its being syndicated), and therefore will be the one to be appended with the two-digit number, I figured I could just look for ‘_xx’ (where xx is a two digit number) at the end of the post title and strip it off. Ugly ugly ugly, but it works. The rest of the code just rearranges the javascript according to which platform the original post comes from, which in some cases requires the addition of source blog name.

With all this in place, I’ve got the following: A blogger posts on, say, his Tumblr blog, where Disqus is enabled. The post is fetched by FeedWordPress on the hub blog, where Disqus is also enabled, with the same user credentials. Then the hacks listed above trick Disqus into thinking that the syndicated post is the very same as the source post, so that the very same comments section is sent to each post. Kind of like magic, when it actually works.

Clearly, though, it would be much, much easier with a system that is built to do what I’m trying to do. That means, in part, having a single system for identifying posts across platforms (some appropriate htmlization of the post name, I presume) and then a single, unified system for making API requests.