Tag Archives: popularity

Enabling Popularity Contest for WordPress networkwide use

Alex King’s Popularity Contest is a pretty cool way to collect data about which posts on a WordPress site the most popular. The data collected is more sophisticated and customizable than simple analytics, because it distinguishes between page views and things like trackbacks, comments, and other kinds of hits. The plugin supports WordPress Multisite in the sense that it’s possible to activate network-wide; when you do so, the plugin keeps site-specific popularity stats. But what if you want popularity rankings across your entire network?

I recently modified Popularity Contest to do just that. The idea is simple: in order to keep network-wide stats, we need a network-wide table (instead of the default site-specific tables). How do you keep information about all networkwide posts on a single blog? I used Donncha O Caoimh’s Sitewide Tags as a bridge. All posts across the network are copied to the tags blog, and popularity data is indexed on the tags blog.

To make this work, several things are needed. I can’t just give you the files because I’ve altered them in other, irrelevant ways, but I will walk you through the process of setting it up. Also, keep in mind that we’ll be modifying the plugin code for both Popularity Contest and Sitewide Tags, modifications you’ll have to make each time you upgrade the plugins. Make sure you back up your work.

  1. Install Sitewide Tags which can be downloaded from its website. You have to activate a few things in order to turn it on – read the readme carefully. Be sure to take note of the tags blog id number, as we’ll need that in a later step.
  2. In sitewide-tags.php, look for the function sitewide_tags_post(). Near the end of the function is a line that says restore_current_blog();. Immediately after that line, enter the following code:
    [code language=”php”]update_post_meta( $post->ID, ‘tags_post_id’, $p );[/code]
    That line makes sure that every time a post is aggregated on the tags blog, the original post gets a piece of metadata noting the post id of the corresponding tags blog post. We’ll use that information in a later step.
  3. Download Popularity Contest from its website. I don’t recommend that you activate it yet, especially not networkwide, because it will create a lot of tables that you don’t really need.
  4. The next few steps will require mading some modifications in the main Popularity Contest plugin file, popularity-contest.php. The first modification is to change all references to $wpdb->posts (which, when activated networkwide, will refer to the posts table for the individual blogs) and change them to point to the tags blog post table. A search and replace that replaces $wpdb->posts with wp_posts (or wp_x_posts, if your tags blog is not site number 1 but is instead site x.
  5. The next modification involves the function akpc_init(), near the end of the plugin file. That’s where the table names for the Popularity Contest custom tables are found. We need to make sure that they point to the tags blog. Replace the existing function with this:
    [code language=”php”]
    function akpc_init() {
    global $wpdb, $akpc;

    $wpdb->ak_popularity = ‘wp_ak_popularity’;
    $wpdb->ak_popularity_options = ‘wp_ak_popularity_options’;

    $akpc = new ak_popularity_contest;
    $akpc->get_settings();
    }
    [/code]
    If your tags blog is something other than blog 1, you could change these table names to match (e.g. wp_15_ak_popularity) but it isn’t really necessary.

  6. Now we have to make Popularity Contest aware of the identity relationships between the tags posts and the original posts. Two snippets of code should do it in most places. First, find the function record_feedback, which starts around line 700. Right before the switch($type) line, insert the following:
    [code language=”php”]
    if ( $tags_post_id = get_post_meta( $comment_post_ID, ‘tags_post_id’, true ) )
    $comment_post_ID = $tags_post_id;
    [/code]
    Next, find the function akpc_api_record_view(), which starts around 2550. Right after array_unique($ids); (around line 2555), insert the following:
    [code language=”php”]
    $tags_ids = array();
    foreach ( $ids as $id ) {
    $tags_ids[] = get_post_meta( $id, ‘tags_post_id’, true );
    }
    $ids = $tags_ids;
    [/code]
    These two modifications make sure that Popularity Contest knows which post on the tags blog corresponds to the post being visited/commented on on the child blogs.
  7. At this point, you can activate the popularity plugin networkwide. Here’s what happens, very roughly:
    • The plugin creates the necessarily popularity tables – just one set for the whole installation.
    • When you publish a new post on any site, it gets copied to the tags blog. Our modification from step 2 makes sure that the copied post ID (let’s say 36) is saved to the original post.
    • When someone visits the original blog post, Popularity Contest fires (because it’s been activated network wide). Our modifications in steps 4 and 5 make sure that the plugin knows to record the activity to the tags blog index, and step 6 make sure that the plugin know which post the activity belongs to.
  8. You’ll need another modification to get the data out, since you’ll want to display it on your site somewhere. The default function for this is called show_top_ranked(). We need to modify so that it gets the requested data from the right place. Replace the stock function with this one:[code language=”php”]
    function show_top_ranked($limit, $before, $after) {
    switch_to_blog( 1 );
    if ($posts=$this->get_top_ranked_posts($limit)) {
    foreach ($posts as $post) {
    $ud = get_userdata( $post->post_author );
    print(
    $before. get_thumbnail( $post->post_author, 36 ) .’ID).'”>’
    .$post->post_title.’

    ‘. $ud->display_name . ‘‘ . $after
    );
    }
    }
    else {
    print($before.'(none)’.$after);
    }
    restore_current_blog();
    }
    [/code]
    Make sure you change the number in the switch_to_blog() call to the id of your tags blog.

I think I’ve remembered everything. Good luck!

Bonus!

For my project, I was moving from a single WordPress site to a multisite situation. The popularity plugin had been running on both setups for a while, so the data was totally messed up and needed to be combined (which meant finding the corresponding post data and adding it together – yeesh!). Here’s the script I used – be careful with it, and keep in mind that it was designed for a *very* specific use. Do not use this code if you don’t understand exactly what every line does!

[code language=”php”]
global $wpdb;

$query = “SELECT * FROM {$wpdb->blogs} WHERE site_id = ‘{$wpdb->siteid}’ “;
$blog_list = $wpdb->get_results( $query, ARRAY_A );

foreach( $blog_list as $blog ) {
//print_r($blog); continue;
if ( $blog[‘blog_id’] == 1 ) continue;
//if ( $blog[‘blog_id’] != 83 ) continue;

$tn = ‘wp_’ . $blog[‘blog_id’] . ‘_posts’;
$tnmeta = ‘wp_’ . $blog[‘blog_id’] . ‘_postmeta’;

$query = “SELECT ID FROM {$tn} WHERE post_type = ‘post’ AND post_status = ‘publish’ “;
$posts = $wpdb->get_results( $query, ARRAY_A );

foreach( $posts as $post ) {
$id = $post[‘ID’];

$query = “SELECT meta_value FROM {$tnmeta} WHERE post_id = ‘{$id}’ AND meta_key = ‘tags_post_id’ “;
$tags_post_id = $wpdb->get_results( $query, ARRAY_A );
$tpid = $tags_post_id[0][‘meta_value’];

$query = “SELECT * FROM wp_ak_popularity WHERE post_id = ‘{$id}'”;
$old_data = $wpdb->get_results( $query, ARRAY_A );
$old_data = $old_data[0];

$query = “SELECT * FROM wp_ak_popularity WHERE post_id = ‘{$tpid}'”;
$new_data = $wpdb->get_results( $query, ARRAY_A );
$new_data = $new_data[0];

if ( $old_data && $new_data ) {
$combined_data = array();

$combined_data[‘post_id’] = $new_data[‘post_id’];
$combined_data[‘last_modified’] = $new_data[‘last_modified’];

foreach( $old_data as $key => $d ) {
if ( $key == ‘post_id’ || $key == ‘last_modified’ )
continue;

$combined_data[$key] = (int)$d + (int)$new_data[$key];
}
}

$query = ‘UPDATE wp_ak_popularity SET ‘;
foreach( $combined_data as $key => $cd ) {
if ( $key == ‘post_id’ )
continue;
$query .= “{$key} = ‘{$cd}’, “;
}

$query = substr_replace( $query, ”, -2 );
$query .= ‘ ‘;

$query .= “WHERE post_id = ‘{$combined_data[‘post_id’]}'”;
$wpdb->query( $query );
print_r( $old_data ); echo “
“; print_r($new_data); echo “
“; print_r($combined_data); echo “
“; echo $query; echo “

“;

}

echo “

";
//		print_r($posts);
echo "

“;
}
[/code]