Google Analytics Internal 0.3.0

I have finally got around to updating my Google Analytics Internal plugin which triggers Analytics events in response to internal WordPress events (specifically when a new post is published or a comment is submitted or approved).

This version fixes an issue where the UA string was not set correctly when using Yoast’s Google Analytics for WordPress.

More information here or download the plugin on

Note to self: WordPress comments are loaded in comments_tempate() not in WP_Query

This had somehow escaped my notice, but recently I was experimenting with a DRYing out my WordPress base theme and wanted to create some custom display logic for comments so I removed the call to comments_template() from my single post template.

I had assumed that all it really did was load the comments.php template from the theme or a fallback when needed. What I didn’t realise is that the comments for a given post or page are not loaded during the main query, but are queried directly in the comments_template() function. (The only exception is for RSS comment feeds which are loaded as part of the main query.)

The function is also responsible for stuffing the comments into the $wp_query global and setting the cpage query variables correctly.  It is a little frustrating as it means the theme must either some comment.php template even if it’s empty or recreate the comment display logic from comments_template(). to migration – problems and pitfalls

Recently I had a client who needed to migrate his blog from to a self-hosted install. This was a first for me, so I made a few notes to help me in the future and hopefully for anyone else doing the same thing. This isn’t intended to be step-by-step migration guide, instead think of it as a series of signposts warning you of potential hazards along the path from to the world of self-hosting.

First a few definitions

The task
   To start, just so we’re clear, the aim was to re-create a site that was hosted on on a standard shared hosting package. The client wanted to keep everything as close as possible to how it was on the original site including the various plugins, widgets and the theme. ( for short):
  The hosted version of WordPress run by Automattic.
  AKA ‘self hosted’.  A standard install of the open source WordPress project on a third-party hosting platform.


The first thing to look at was the theme (in this case Notepad). In this case the theme is quite old and no longer supported on, but as it is open source and free it was still possible to download it and activate it on the new site. However there were some differences between the version on and the open source version. I assume that theme has been changed for a more consistent experience on the hosted platform, but these alterations haven’t been back-ported to the theme.

In this case the things that were not supported in the open source version were:

  • Custom header and background support

  • Support for custom navigation menus

  • Integration with Jetpack’s infinite scroll feature

Adding support for the navigation menus was fairly easy – just a bit of code and some tweaking of the CSS to fit the new selectors. As the client wasn’t intending to change them in the near future, I simply hard-coded the custom header and background images into a new child theme.

Whilst it would have been possible to add the infinite scroll to the theme we decided that the infinite scroll element wasn’t really essential to the functioning of the site so I left it as it was.

Summary: if you are planning to use the same theme as on, make sure any essential features are available in the open source version. Ideally, if the theme is outdated, then find a new theme to work with instead.


Having set up the theme on the new install, I needed to get the content out of the old site and into the new one. WordPress has a built-in export function which should do the job (located under ‘Tools’ in the admin area), but it has some limitations.


What is not exported

  • Active widgets and their settings
  • Active plugins and their settings ( doesn’t actually allow plugins, but as we’ll see there are some equivalents for the self-hosted install)
  • Site-wide settings (site title, site description, comment settings etc.)
  • Likes, ratings, polls and surveys

Personally, I think this is a pretty big failing of the exporter. These settings constitute a large part of what makes a site unique and having to manually copy and input all the settings is quite tedious. Most of these things can be moved across to the new site, but as they have a few pitfalls of their own, I will look at them individually.

Summary: double check what settings you want to keep when you migrate. It’s probably useful to do a side-by-side comparison in two browser tabs to check what has changed.


First of all, install the Jetpack plugin. If you haven’t used it before, Jetpack was created by Automattic to take some of the services that users get out-of-the-box and allow a standard WordPress install to use them.

Once it’s activated, you will need to connect it to your account. You should see your new install listed with your old site at and in your new install there will be a new menu item in the admin called ‘Jetpack’.

Jetpack is made up of a number of modules which need to be activated individually once the main plugin is set up. These include some of the widgets and sharing options that are found on along with the stats module.


The first thing to understand is that not all widgets on have an equivalent available for installs (even with Jetpack activated) so you may have to search the plugins directory for a replacement or simply live without the widget.

Also, even where there is an equivalent widget available, the configuration and output are not always the same so you may find things looks a bit different. As an example, the Jetpack Twitter feed widget requires you to create a custom widget on Twitter and insert that into the WordPress widget rather than just setting your user name. Also, the output uses the standard Twitter styling rather than custom markup so you may have to adjust some CSS to make sure it fits with your theme.

In my case there was only one widget that the client wanted which didn’t have a direct equivalent (the blog stats widget). As it was a simple one, I simply coded up a replacement using the stats API which did the job.

Reminder: your widgets, widget settings and sidebars will not be imported with the content so you will need to do some configuration to get things set up.


Now for some bad news. If you have been using the like button on your site, those ‘likes’ will not be migrated. They seem to be deeply embedded with the infrastructure and there is no automatic or manual migration option. See this support thread for more details.

Note that this does not apply to Facebook likes or shares or Twitter tweets counts. These are based on the URLs of each post so as long as that has stayed the same (ie. your permalink structure hasn’t changed) then any FB like / share or tweet counts should also stay the same. If your site is under a subdomain (eg. then you will need to buy the domain forwarding add-on to make sure that any URLs are redirected to you new site (http://mycoolblog.example).

Summary: likes cannot be migrated, but Facebook likes and tweet counts will come across automatically as long as the URLs for your site remain the same.

Migrating Followers

As is a network of sites, it provides a ‘follow’ button to allow users to subscribe to their favourite sites and get updates in the reader. You can migrate these followers to your new site so that they continue to receive updates from your new install.

To do so, simply visit your list of site at and find your new Jetpack connected site. There should be a link called ‘Manage’ which will take you to a page where you can choose which site you want to migrate the followers from. It can take a day or two for everything to come over, but you should get an email from the support team when it’s done.

In my case it was a bit more complicated as some of the followers had already been migrated in error to another Jetpack-enabled site, but a quick email to the support team got both followers merged and moved over to the new site.

Migrating and merging stats

One of the nicest features of and Jetpack is the simple stats plugin that keeps track of visitors to your site. Naturally, my client wanted to keep all that useful information and, importantly, access it in one place. There is no migration tool for stats, but an email to Jetpack support got the data moved over in a day or so and merged it with the stats for the new install.

Migrating ratings from PollDaddy allows visitors to rate posts using a simple 5 star widget. This is backed by PollDaddy which is a service run by Automattic which, as the name suggests, is mainly used for adding polls and surveys to sites.

To use the ratings on the new install, I had to do the following:

  1. Install the PollDaddy plugin
  2. Create a new PollDaddy account. You can use your user details to log in and then connect to new account.
  3. Set up ratings settings on the new site using the details of the new account.

Now, according to this support thread should be able to sync the existing ratings from your old site over to the new site by ticking a box in the settings page. In my case though, that didn’t seem to work so one final email to WordPress support was required. I did need to update one or two values in the wp_options table to make sure I was pulling the correct data for the new site, but once that was done it all seemed to work fine and the existing ratings all synced up with the posts on the new site.

Summary: Followers, stats and ratings can all be migrated, but you will need to contact support to complete the process.


As you can see there are quite a few things that can trip you up on the path to migration. Ideally there would be one comprehensive process that would pull all the essential data across to the new site without the hassle of fiddling with settings and sending support requests. Nonetheless, it is possible to get almost everything migrated with a bit of work and patience.

De-duplicating MySQL rows in the WordPress post meta table

I recently needed to prune some duplicate rows from the WordPress post meta table. For reasons I won’t go into, there were two copies of many post meta values for most of the posts in the database and I wanted to remove the extra copies whilst keeping rest.

After trying various solutions (on a backup of the database) involving sub selects and unique keys, the simplest and fastest solution seems to be to create a new table and copy over rows I wanted to keep.

LIKE wp_postmeta AS
SELECT MIN(meta_id) as meta_id, post_id, meta_key, meta_value
FROM wp_postmeta
GROUP BY post_id, meta_key, meta_value;

This creates a new table with the same specification as the wp_postmeta table and inserts only one copy of each row based on the post_id, key and value. Then it’s just a question of swapping the names of the tables and testing the result.

DROP TABLE wp_postmeta;
RENAME TABLE tmp_meta TO wp_postmeta;

Note that this will take the meta value with the lowest meta_id and drop the rest. As always, take a backup of the database before doing this on your actual site.

Check available PHP version in a WordPress Plugin

Currently, WordPress only requires PHP 5.2.4. There has been a lot of debate about this, and I personally think that the core team should bite the bullet and set a date for moving to PHP 5.3 and beyond for reasons of security, performance and the numerous new features it would make available.

However, it seems unlikely that’s going to happen any time soon. This presents a problem: I develop on 5.4 and almost every client site I build uses features that are only available in 5.3 and beyond. If any of my custom code unexpectedly lands on a server where 5.2 is the latest version it will go belly-up and leave me staring at a white screen of death.

Now, of course, I can go in and change the cPanel settings, htaccess or whatever is needed to get things working. But what if I want to ship a plugin? I don’t want someone else’s site to go down without any explanation, so I need to check the available version before including any code that will cause a fatal error.


To help manage this I built a little class that can be included with any plugin (or added to the mu-plugins folder) which does the checking for you. It does two things:

  • On activate_plugin it checks the available version against whatever you specify as the minimum and prevents activation if it’s too low.
  • If the plugin has already been activated, it checks for the PHP version and prevents any further plugin code execution if it is insufficient.

In both cases, a message is displayed to the user if they are logged in or added to the the system error log if they are not.

At the moment the code and instructions are available on GitHub. I am looking into adding it to so it can be installed easily via composer. In the meantime you can add the following to your composer.json:

"repositories": [
        "type": "git",
        "url": ""
"require": {
    "dbisso/wp-version-check": "1.*"

Then composer update to bring in the code.

Use it in you plugin’s main file (ie. the one with the WordPress plugin data headers):

Plugin Name: My plugin
Plugin URI:
Description: Does something
Version: 0.1
Author: Dan Bissonnet <[email protected]>
Author URI:

if ( ! class_exists( 'DBisso_Version_Check' ) ) {
    include_once 'vendor/dbisso/wp-version-check/DBisso_Version_Check.php';

// Check for PHP version and then kick things off
DBisso_Version_Check::bootstrap( '5.3.5', __FILE__, dirname( __FILE__ ) . '/plugin.php' );

DBisso_Version_Check::bootstrap() takes three arguments:

  • (string) Minimum PHP version.
  • (string) The root plugin file – usually __FILE__ will do the trick.
  • (string) The initialising plugin file. This is where your plugin’s main code lives.

Your plugin needs to be organised in so that the main plugin file (the one with the data comment at the top) calls DBisso_Version_Checker first which will in turn include the file with the actual code.

Hopefully it helps out some other developers with the same problem.