Including data in the page for your plugin’s JavaScript

Nip over to Otto’s blog and read his post entitled don’t include wp-load, please which I’m in complete and total agreement with. I’d like to add to the solutions with the following method of adding data to your page for use by your plugin’s JavaScript: wp_localize_script. Continue reading “Including data in the page for your plugin’s JavaScript”

Taking stock

I have a client with a lot lot lot lot of WordPress sites, and I’m helping them craft a strategy for dealing with WordPress throughout their business. One thing we wanted to do was to take stock of all the plugins they have installed on all their sites, work out how many are in common, understand if any have security issues and so forth. Logging into hundreds of WordPress sites, even if we did have their login details, isn’t my idea of fun so I hacked some of the WordPress plugin functions to output a report on any plugins it finds in a directory. Continue reading “Taking stock”

Eeek! The load-page-new.php and load-page.php actions have vanished!

So WordPress 3.0 is out, and some of the useful admin area load-* hooks that I use have vanished or changed. To refresh your memory, there are individual and specific hooks which run whenever WordPress loads an admin page. These hooks are named after the PHP file in the URL to the admin page, for example the URL to create a new post is post-new.php so the action you can hook when this page is loaded is “load-post-new.php“. All good. Very handy.

In WordPress 3.0 the URLs to create a post and edit a post are now all post-new.php and post.php, with the post type being passed in a GET parameter like so: post-new.php?post_type=page… i.e. load-page-new.php and load-page.php action hooks have both vanished! Never fear, there are two ways around this problem, read on… Continue reading “Eeek! The load-page-new.php and load-page.php actions have vanished!”

Live text streams for the National Care Service

We were approached by the Department of Health to further enhance their WordPress based website for the launch of theĀ National Care Service. The launch was to have a live web video stream and The Department of Health wanted live text updates to accompany the video. The text updates would allow late comers to catch up, mean the launch team could provide annotations, links and accompanying materials as the launch progressed, and would provide a text record of the event afterwards. The projected maximum audience for this event reached well into the thousands, so the solution we provided would have to scale appropriately, while not consuming so many resources that the webserver could not fulfil it’s other duties (like serving other pages and so forth).

Continue reading “Live text streams for the National Care Service”

Thickboxing in WordPress themes

The lovely Thickbox jQuery plugin appears to no longer be supported by it’s creator, Cody Lindley, but it’s included with WordPress (it provides the “popup” overlays in the post editor for uploading and inserting images and other media), very flexible and that’s good enough reason for me to use it for the time being.

I’ve always been a bit shy of using Thickbox in the front end of WP sites, as the close button and loading animation paths are hard-coded relative to the WP admin area… in short, they don’t work at the front without tinkering. Today I realised that the required tinkering was (relatively) trivial, so here’s my code for including thickbox at the front end, you can drop it into the functions.php of your theme or make it into a plugin.

function my_load_thickbox() {
	add_thickbox();
	?>
	<script type="text/javascript">
		/* <![CDATA[ */
		var tb_pathToImage = '<?php echo esc_js( includes_url( '/js/thickbox/loadingAnimation.gif' ) ); ?>';
		var tb_closeImage = '<?php echo esc_js( includes_url( '/js/thickbox/tb-close.png' ) ); ?>'; 
		/* ]]> */
	</script>
	<?php
}
add_action( 'wp_head', 'my_load_thickbox', 0 );

Taking this section by section:

The add_thickbox function is a wrapper for adding the styling and script for Thickbox; the style gets added to the HTML HEAD element, and the script is added to the footer.

The script block is where the Thickbox image paths are corrected, the technique works because the WP Thickbox script checks if these two paths are set a JS vars before it sets them and uses any pre-existing values. Because the vars in the function above are set before the Thickbox script is loaded (the script has been added to the footer, remember) we are able to override and set some paths which will work on the front end of a WP site. The include_urls function is a handy way to reference some code inside the wp_includes directory (which the two Thickbox images are). The esc_js escapes text for a JS context, it’s included to prevent something hairy and completely unexpected getting into the JS and making a security mess everywhere (it’s always a good idea to escape anywhere and everywhere you can).

(One gotcha: because the scripts are added to the footer, your theme must include a wp_footer action.)

Problem with a PHP fatal error when saving WP profile

This bug has been around for a little while now, and one of my clients has it bad as they cannot save user profiles without patches to WP core. (Unfortunately although WordPress Trac has tickets (#9640, #7540) the discussion over how the problem needs to be fixed has been going on for a couple of WP releases now.)

The symptoms are the following error:

Catchable fatal error: Object of class __PHP_Incomplete_Class could not be converted to string in /home/halfbets/public_html/wp-includes/wp-db.php on line 445

The solution is outlined in this WP support post, but does involve editing core WP files so does require both some knowledge and a brave heart (and the willingness to repeat the exercise if the problem isn’t fixed the next time you upgrade).

Fix users with no role – data migration plugin

So you’ve migrated a bunch of users over from another system and then, horror, you discover that they don’t have roles in WordPress. The WP Users admin screen doesn’t cope with this issue (why should it), so the only option would be to go through and manually select the roles for each user with no role… fine, right? Except I just migrated 8,000 ish users and there’s no way my meta carpels will take that abuse. This plugin finds any users on your system with no role and gives them the role of ‘subscriber’, it does this in batches of 1,000 users per page load and once it has finished it replaces your entire blog with a message saying it’s finished… so don’t leave it running unattended kids, eh? (I know that’s rude, but coding “echo ‘blah; exit;’ is SO much quicker and it’s only a dirty data migration tool.

You can view the source here:


/*
Plugin Name: Fix No Role Users
Plugin URI: http://simonwheatley.co.uk/wordpress/fox-no-role-users/
Description: Finds users with no role and makes them subscribers.
Version: 1
Author: Simon Wheatley
Author URI: http://www.simonwheatley.co.uk/wordpress/
*/

/*  Copyright 2009 Simon Wheatley

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/

/**
 *
 * @package default
 * @author Simon Wheatley
 **/
class FixNoRoleUsers
{
    protected $db;

    public function __construct()
    {
        global $wpdb;
        $this->db = & $wpdb;
        add_action( 'init', array( & $this, 'fix_roles' ) );
    }
    
    public function fix_roles()
    {
        // Get some more user IDs
        $user_ids = $this->no_role_user_ids();
        if ( ! count( $user_ids ) ) $this->finished();
        foreach ( $user_ids as $user_id ) {
            $user = new WP_User( $user_id );
            $user->add_role( 'subscriber' );
            error_log( "Given user $user->user_login the role of subscriber" );
            unset( $user );
        }
    }

    protected function no_role_user_ids()
    {
        $users = $this->db->users;
        $usermeta = $this->db->usermeta;
        // Pretty horrible query, but who cares as it's not for production usage
        // Grab groups of 1,000 for now
        $sql = " SELECT ID FROM $users WHERE ID NOT IN ( SELECT user_id FROM $usermeta WHERE meta_key = 'wp_capabilities' ) LIMIT 1000; "; 
        // No need to prepare as no user input
        return $this->db->get_col( $sql );
    }
    
    protected function finished()
    {
        // OK. Bung a rude message up and exit.
        echo "All users now have roles.";
        exit;
    }
}

/**
 * Instantiate the plugin
 *
 * @global
 **/

$fix_no_role_users = new FixNoRoleUsers();

?>

Migration to bbPress – fix topic slugs

So I’ve just had to migrate a whole load of data to bbPress, and unfortunately the fairly old migration tool I was using (while generally excellent) didn’t bring across the topic-slugs, meaning I couldn’t have pretty permalinks in bbPress. So I’ve written a quick plugin which creates the topic slugs, it does this by doing 1000 at a time every time the page loads, then it very rudely bangs up a big text notice telling you it’s done and you should remove the plugin… at this point bbPress won’t work until you remove the plugin. You have been warned. I couldn’t be bothered with polite messages as this is a one use migration tool. :)

Updated version, this one also create forum slugs:

<?php
/*
Plugin Name: Create Slugs
Description:  Creates topic and forum slugs where previously there weren't any. Outputs into error log, only works on plugin activation.
Plugin URI:  http://simonwheatley.co.uk/bbpress/create-slugs
Author: simonwheatley
Author URI: http://simonwheatley.co.uk
Version: 1.1
*/

class CreateSlugs
{
    protected $topics_have_slugs;
    protected $forums_have_slugs;
    
    function __construct()
    {
        global $bbdb;
        $this->db = & $bbdb;
        $this->topics_have_slugs = false;
        $this->forums_have_slugs = false;
    }
    
    public function create_slugs()
    {
        $this->create_topic_slugs();
        if ( ! $this->topics_have_slugs ) return;
        $this->create_forum_slugs();
        if ( ! $this->forums_have_slugs ) return;
        // Finished!
        echo "Everything now has slugs, you can disable the create topic slugs plugin by removing it from the plugins directory.";
        exit;
    }
    
    protected function maybe_finished()
    {
//        if ( ! $this->forums_have_slugs ) return;
    }
    
    protected function create_topic_slugs()
    {
        error_log( "TOPICS" );
        $topics = $this->slugless( 'topics' );
        if ( ! count( $topics ) ) $this->topics_have_slugs = true;
        foreach( $topics AS $topic ) {
            $new_slug = $this->generate_slug( 'topic', $topic->topic_id, $topic->topic_title );
            $this->update_topic_slug( $topic->topic_id, $new_slug );
            error_log( "$new_slug >> $topic->topic_title" );
        }
        error_log( "Last Query: " . $this->db->last_query );
    }
    
    protected function create_forum_slugs()
    {
        error_log( "FORUMS" );
        $forums = $this->slugless( 'forums' );
        if ( ! count( $forums ) ) $this->forums_have_slugs = true;
        foreach( $forums AS $forum ) {
            $new_slug = $this->generate_slug( 'forum', $forum->forum_id, $forum->forum_name );
            $this->update_forum_slug( $forum->forum_id, $new_slug );
            error_log( "$new_slug >> $forum->forum_name" );
        }
    }

    protected function slugless( $type )
    {
        // Just do 1,000 at a time for now
        if ( $type == 'topics' ) {
            $topics_table = $this->db->topics;
            $sql = " SELECT topic_id, topic_title FROM $topics_table WHERE topic_slug IS NULL OR topic_slug = '' LIMIT 1000 ";
        }
        if ( $type == 'forums' ) {
            $forums_table = $this->db->forums;
            $sql = " SELECT forum_id, forum_name FROM $forums_table WHERE forum_slug IS NULL OR forum_slug = '' LIMIT 1000 ";
        }
        // No need to prepared the SQL, no user input
        return $this->db->get_results( $sql );
    }
    
    protected function generate_slug( $type, $id, $title )
    {
        $slug = $_slug = bb_slug_sanitize( wp_specialchars_decode( $title, ENT_QUOTES ) );
        if ( strlen( $_slug ) < 1 ) $slug = $_slug = '0';

        // Ensure the slug is unique
        while ( is_numeric( $slug ) || $existing_slug = $this->existing_slug( $type, $id, $slug ) ) {
            $slug = bb_slug_increment( $_slug, $existing_slug );
        }
        return $slug;
    }
    
    protected function existing_slug( $type, $id, $slug )
    {
        if ( $type == 'topic' ) {
            $table = $this->db->topics;
            $sql = "SELECT topic_slug FROM $table WHERE topic_slug = %s AND topic_id != %d";
        }
        if ( $type == 'forum' ) {
            $table = $this->db->forums;
            $sql = "SELECT forum_slug FROM $table WHERE forum_slug = %s AND forum_id != %d";
        }
        $prepared_sql = $this->db->prepare( $sql, $slug, $id );
        return $this->db->get_var( $prepared_sql );
    }
    
    protected function update_topic_slug( $topic_id, $topic_slug )
    {
        $table = $this->db->topics;
        $data = array( 'topic_slug' => $topic_slug );
        $where = array( 'topic_id' => $topic_id );
        $this->db->update( $table, $data, $where );
    }
    
    protected function update_forum_slug( $forum_id, $forum_slug )
    {
        $table = $this->db->forums;
        $data = array( 'forum_slug' => $forum_slug );
        $where = array( 'forum_id' => $forum_id );
        $this->db->update( $table, $data, $where );
    }

}

$create_slugs = new CreateSlugs();

add_action( 'bb_init', array( & $create_slugs, 'create_slugs' ) );

?>

Installing ImageMagick on MacPorts and imagick on PECL

Over the last few hours I’ve installed ImageMagick on MacPorts (super easy), and imagick on PECL (not so much). Xentek‘s post on ImageMagick and imagick was a great help, and got me past the first brick wall I hit (passing in the ImageMagick prefix to prevent it complaining about Wand-config)… but then I couldn’t get the darned extension installed. Anyway, here’s the steps I went through, and if anyone has any suggestions for improvements I’m all ears. Continue reading “Installing ImageMagick on MacPorts and imagick on PECL”