Category Archives: WordPress

Quick Tip: WP Cron on HTTP Basic Protected Site

Today I ran into a situation where I needed the default WP Cron processing to work while a site was protected behind HTTP Basic authentication. If you’re not familiar with the way WP Cron works by default, it spawns an HTTP request to example.com/wp-cron.php on page load (given a certain set of conditions) and allows that request to sit and process whatever jobs have been registered.

When HTTP Basic authentication is active on the site root, though, the request never gets through to wp-cron.php because it gets intercepted by the web server and blocked (because the request isn’t authenticated). To get around that, add the following code into your wp-config.php file:

if(!defined('WP_CRON_CUSTOM_HTTP_BASIC_USERNAME')) {
    define('WP_CRON_CUSTOM_HTTP_BASIC_USERNAME', 'YOUR_USERNAME_HERE');
}

if(!defined('WP_CRON_CUSTOM_HTTP_BASIC_PASSWORD')) {
    define('WP_CRON_CUSTOM_HTTP_BASIC_PASSWORD', 'YOUR_PASSWORD_HERE');
}

Then, download this gist and put it inside of the mu-plugins directory.

That’s it!

Better Links & Better Links Pro

I haven’t been writing as much lately because I’ve been hard at work building new products for the customers over at BoostWP. In the last two months we’ve released two awesome new products and I wanted to take the opportunity to talk a little bit about one of them today, including benefits it brings our customers, the ways in which it compares to a similar product, and the technical challenges I faced during implementation.

Better Links – a feature complete, free plugin for everyone

There are a variety of link shortening plugins on the market, with the 800 pound gorilla in the room being Pretty Link and its paid add-on, Pretty Link Pro. It was obvious there was demand for a high-quality product in the space and I saw some pretty immediate deficiencies in Pretty Link, so Better Links was born.

Better Links – what is it and why is it valuable?

Better Links, which you can download for free from the WordPress plugin repository, has all the standard features you’d expect from a link shortening plugin. The most important, of course, is the ability to define a redirect to a specified URL from a specific suffix on your WordPress installation’s site URL.

What does this mean? Well, essentially you can tell Better Links that any time someone visits mysite.com/best-black-library-book/ they should be redirected to www.amazon.com/Eisenhorn-Warhammer-40-000-Omnibus/dp/1844161560/ref=sr_1_1?ie=UTF8&qid=1404223645&sr=8-1&keywords=eisenhorn immediately and it just works. Here’s how that looks in the plugin:

Better Links Core - Edit Link

It looks so easy, right? I guess the next question is, why in the world would you want to do this? There are a couple of important use cases for Better Links (or other link shorteners).

First, you can change the destination URL for this particular link at any point in the future, even after you’ve used it in post or page (or other, I suppose) content. If I change my mind in the future about what the best Black Library book is (perhaps to something like the Salamanders Omnibus), then I can update the destination once and all my links automatically point to the right place.

Second, you can make your URLs cleaner for your site’s visitors. Some URLs (like the Amazon one I showed above) are fairly messy, containing a lot of extraneous information and query arguments. This can scare some visitors to your site away from clicking on links; this is especially true for affiliate or other links where a myriad of tracking information is added to an otherwise clean URL. By showing the user that they’ll be visiting mysite.com/pretty-slug/ instead of something with 15 query variables, they’ll be more likely to click on it.

Third, you can easily specify a link once and then use it in your site’s content without having to remember some long, complicated URL each time. Better Links makes it easy to insert links as long as you have some idea what you’re looking for. How? Well, let me show you:

That’s it! You can also search for your links by name or slug once you have more than can be shown on one page. You can insert your links using a shortcode (which guarantees appropriate linking and tracking) or you can insert “raw” where a simple a tag is inserted into the appropriate place in your content.

With the free Better Links plugin you also get some rudimentary click tracking and the ability to import your links from Pretty Link if you decide to switch over (which I wholeheartedly recommend).

Better Links – how is it better than Pretty Link?

There’s a couple of big ways that Better Links differentiates itself from Pretty Link. First, it does things the WordPress way. In Better Links, we don’t create separate database tables to hold data about your links, link categories, and clicks like Pretty Link does. We use the standard data storage structure of WordPress thoughtfully to save information the way WordPress is designed to do.

Second, (and this may be a bit of hubris, but forgive me for a moment) the interface for Better Links in all areas of operation is superior to that of Pretty Link. Compare the two side by side and think about which one you’d like to use:

Comparison of Link Adding Interfaces

Finally, Pretty Link doesn’t even offer you the ability to insert your links into your post or page content with any ease unless you upgrade to Pretty Link Pro. Better Links gives you the easy insert and editing features in the base version. I understand the reasoning behind that decision, but if I’m going to release something as FOSS, I’m not going to pull core features just to prompt someone to pay me money.

Better Links Pro – more features for the advanced user

If you’re an entrepreneur and serious about using a link shortener as a tool for your business, then you’ll probably want to take a look at Better Links Pro. Better Links Pro is an add on to the Better Links plugin that adds some features that can be useful for those looking to take their link strategies to the next level. Here’s what the insert link screen looks if you have Better Links Pro activated:

better-links-add-pro

As you can probably tell, there’s a bit more there than with the base Better Links plugin – these new features are specifically targeted at use cases that our customers have identified as important to them.

Destination types

First, you’ll see there are three different ways to redirect URLs with Better Links Pro:

  1. Single destination – the same as Better Links
  2. Multiple destinations – redirects for this link (for different visitors) are split evenly between multiple URLs
  3. Geographic destinations – redirects for this link are split based on the visitor’s country (or sent to a default location if one isn’t specified for a particular country)

We already talked about the first option, but why might you want to use one of the others?

Multiple destinations

Let’s say you have multiple variations of a landing or sales page at myshop.com/landing-1, myshop.com/landing-2, and myshop.com/landing-3. You want to drive traffic to each of them and evaluate the conversions (lead capture, sale, etc) that are realized for each variation. Obviously, you need to try to drive roughly equal traffic to each page so that you can fairly compare the results.

That’s where the multiple destinations destination type comes in. In this example, you’d specify your link as mysite.com/sales-page and Better Links Pro would automatically rotate new visitors through each of the variations. One important thing to note: if a user has already clicked the link and been redirected to a particular URL, they will always be redirected to that URL so a single individual doesn’t see more than one variation on accident.

Given this functionality, you’d be able to drive traffic through a single gate (mysite.com/sales-page) and then evaluate the results for each variation appropriately without any extra work on your end. Pretty great, right?

Geographic destinations

This is where things get really interesting! As you can see in the above screenshot, you can specify where a user should go based on their detected country. There are multiple reasons why this could be valuable. Off the top of my head, here’s two important ones:

  1. Several affiliate marketing companies allow you to specify different URLs for different countries so you can get appropriate commissions (but won’t pay commissions if the visitor goes to the wrong country affiliated site)
  2. You need to block or otherwise restrict citizens of particular countries from directly accessing content you provide (this may be because of terrorism, morality, or other statutes in those countries)

Country based redirection has long been one of the best features of EasyAzon Pro, so I was excited to use some of the same implementation techniques here.

Keyword replacement

This feature has been hugely important to many of our customers since we released Better Links Pro. Why? Because it lets our customers automatically derive new revenue from existing content on their websites. They put in a trivial amount of new work and can benefit from everything they’ve done in the past. How does that work?

Say you write a lot about hosting and you often mention various hosting companies by name in your posts: HostGator, BlueHost, WP Engine, SiteGround, etc. With Better Links Pro, each of those names can automatically be assigned a Better Link without you having to manually add links around the hosting companies’ names. That’s pretty great, right?

Better Links Pro is even smart enough to exclude links from headers if you want it to! That means you won’t have a big honking blue link right in the middle of h2 or h3 in your post content.

This is just another example of my philosophy of plugin development. Make things easier for end users by making technology do the hard work.

Advanced tracking – leveraging Google Analytics for fun and profit

The final piece of the puzzle is really where you can make a lot of money and leverage your insights to better serve your site’s visitors. Better Links Pro includes integration with Google Analytics that tracks a variety of things:

  • When is a Better Link shown on the frontend of the site?
  • Where is that Better Link located (single page / post content)?
  • How often is that Better Link clicked in that location?

Using this data, you can see which links are performing the best and where, leading to potential optimizations in terms of making link text more attractive, reusing well-performing link contexts, and more. Better Links Pro gives you the ability to dig deep and really dial in your link performance with very little extra effort on your part.

Oh, and did I mention that all this data is displayed right on your Better Link and Post edit screens? That’s right! You don’t have to head over to Google Analytics and decipher event tracking streams to get the information you want because it is all there for you to use, right in your WordPress backend.

Go buy Better Links Pro

I honestly believe that Better Links Pro is worth every penny (although I may be a tad bit biased) so I recommend you go buy it now, either as a one-time purchase or, to your benefit, as a part of a BoostWP subscription.

Better Links and Better Links Pro – some implementation challenges

On to the geeky stuff!

Building Better Links and Better Links Pro wasn’t as simple and straightforward as I had first imagined as there were a lot of edge cases that I had to capture and work around.

First, and most importantly, I had to make sure people weren’t creating and activating links that were going to conflict with existing links on their site. This meant detecting the slug that a user specified, comparing it against the existing active link slugs in the database, and saving / displaying an error to the user while disallowing activation of that link. Here’s what a user sees if they try to save a link that conflicts with another:

link-conflict-in-post

link-conflict-dashboard

A similar error message is displayed if the user tries to specify an invalid destination URL (or empty destination URL) when saving a link.

Second, working with keyword replacement is tricky when you’re modifying a potentially invalid DOM and dealing with markup that is wholly unknown. I used the native PHP DOM handling functionality with a minor facade added on top to properly insert link nodes without invalidating the remainder of the structure. Doing some clever XPath detection, I was able to detect when keyword text was inside of a header tag and restrict placement there if appropriate.

Finally, working with the Google Analytics API was not the enjoyable experience I thought it would be. API responses come back as a set of dimensions and metrics “table rows” which then have to be transformed into something that is actually useful. It definitely wasn’t the hardest API integration I’ve ever done, but it was certainly an interesting one.

The future of Better Links and Better Links Pro

Right now I have some bug fixes and feature upgrades prepared and being tested for both the core Better Links plugin (again, free on the WordPress.org plugin repository) and Better Links Pro. I’m excited about taking the next steps with the plugin and continuing to improve it for users. If you’ve used either the base or pro version, I’d love to hear from you in the comments!

Multiple Featured Images on the WordPress Repository

In September of last year I spent some time building a small utility plugin that allows for the registration and use of arbitrary image pickers for any WordPress content type. Since then, the plugin has been sitting on Github because I’ve been too lazy to get it into the repository.

That changed today! I submitted a request for inclusion and just deployed MFI Reloaded to the WP.org repository. I’m hoping that more people will use the plugin now that it is in a place that more WordPress developers go to look for things. It is really handy for custom site builds, especially for image heavy sites.

Simple Dribbble Shot Embed Plugin

I was sitting around today thinking about some parts of WordPress that I hadn’t got a chance to play with and I realized that I’d never touched the embedding framework built into core. Most people think of the embed framework in terms of the ability to immediately include oEmbed enabled resources into WordPress. oEmbed enabled resources include things like YouTube videos and Instagram media. If you’ve never taken a look at it, the oEmbed framework is neatly tucked into its own class which is really easy to read through.

Unfortunately, the resource I wanted to embed doesn’t conform to the oEmbed spec, so I was left with the manual method. Again, WordPress has a special class for dealing with these types of scenarios. Once I read through that class, I knew exactly how to do what I wanted.

I’m a big fan of the Dribbble API. It covers all of the main functionality of the website (at least in terms of data exposure) and is ridiculously clean in terms of resource representation. I thought it would be neat if a user could embed a Dribbble shot into a post or page (or any piece of content, really) just by copying and pasting the URL to that shot. I can think of several scenarios where this might be useful, including project profiles, discussions of WIP, or examination of design trends (colors, shapes, textures, etc.).

So that’s what I built. All told, it took me about twenty minutes to find the hooks I needed in WordPress and build the very small plugin that provided the functionality I desired. The plugin includes some basic caching so that the Dribbble API isn’t hit every time content is viewed. You can see the finished Dribbble Shot Embed plugin on GitHub.

If you’re interested in me developing this idea further, I’d love to hear from you. Alternatively, let me know if you use this as a starting point to develop your own WordPress embed system plugin. I think it is a good base and should be easy to build upon.

Multiple Rich Editors in the Plugin Repository

The WordPress plugin that I built last week has been added to the official repository.

I’m fairly happy with this plugin. It solves a small problem in a way that makes sense. It is well documented (for the most part) and should prove useful to other developers. It serves a need that I know exists (because someone asked a question) and shouldn’t need to change much in the future.

If you decide to use Multiple Rich Editors, please let me know. I’m interested in the various uses that people come up with.

Recent Open Source Work

I recently did some work on two open source plugins that I’d like to talk a little bit about. They’re very different, both in function and purpose, and I think that disparity gives a good glimpse into the variety of work I do in my professional life.

WP ConvertKit

If you haven’t heard of Nathan Barry by now, you should probably click on his name and go read a little bit about him. Nathan seems to really have it together, starting and growing a product business based on the concept that teaching sells. I spent a few days last year reading through his blog and bought one of his books, Authority, because I thought it was a great product.

Anyways, Nathan runs a SaaS called ConvertKit. It’s an interesting product because it recommends and guides you through a particular workflow that Nathan believes (and has somewhat proven) helps you sell digital products. I saw that there was an existing WordPress plugin for the service, but it was lacking in a few ways. Implementation details aside, I felt like there was a better solution and more “WordPress”-y way to solve the problems the plugin presented. Also, I felt like I learned so much from Nathan’s blog, it would probably be a good thing to give a little back. I took the opportunity to write a new plugin for ConvertKit, put the code on GitHub, and emailed Nathan about it.

A few months later (he’s a busy guy, so I didn’t mind) I got an email asking if the plugin I wrote could be the canonical plugin for ConvertKit and wondering if I’d add a few features. I added them and finalized the plugin a few weeks ago. I’ve heard from Nathan that it has worked pretty well so I’m happy about that. It is always nice to know that people are using the things you built.

Anyways, the final version of the plugin lives here and you can go grab it if you want to either take a look at the code or use ConvertKit and want to integrate it more easily into your WordPress site.

Multiple Rich Editors

Erik Ford, who seems to inspire open source work and blog posts on my part, posted a question on Twitter regarding the best way to add multiple editors to a custom post type:

I hate the thought of bundling or requiring a third-party plugin with your theme, especially if it includes a lot of functionality that you probably don’t need. Advanced Custom Fields is a great plugin for a lot of people, but if you’re just looking to add some WYSIWYG editors, there’s no reason to use it. I told Erik I’d look into it and get him some sample code.

The next day I worked up about 25 lines of code that you could throw into a file and you’d add an editor and save its contents for a custom post type. It was short and sweet, but it didn’t really fit with the style that I tend to build things in. Taking a cue from a previous plugin that I developed and blogged about here, I decided to make use of the theme support API that WordPress provides to allow quick and easy implementation of multiple content editors on any registered post type.

The result was a plugin I call Multiple Rich Editors. It allows a developer to specify and multiple editors per post type that should appear on the edit screen. It handles saving the data for each editor and provides template tags and guidance for outputting the information the user has entered. On top of all that, it is simple enough that Erik can drop it into his theme and save the effort of building something himself.

There are a lot of different use cases for a plugin like this. Perhaps a content type requires different sections of rich content (intro, body, conclusion, perhaps?) or you have a complex layout where a user of your theme needs a fine-grained level of control over what appears where. Regardless, if you stumble upon this and end up using the plugin I’d love to hear what you like and what you don’t and how I can make it better.

How to Get the Attachment ID of the WordPress Header Image

Skip to the answer

I saw a question on Twitter today about getting the attachment ID for the header image set through the WordPress theme customization screen. I haven’t worked with that part of the WordPress API before, so I wasn’t exactly sure how to do it. I decided to find out so I could help another developer and, maybe, save myself some leg work down the road.

I started by activating the 2013 theme on my local WordPress install. I knew this theme uses the header image functionality, and figured it was probably a good place to start looking. After activating the theme, I uploaded a custom header image and inspected the frontend of the site with Chrome’s web inspector to see what kind of markup and styles are used to place the image in the header.

I was able to see the background image was set on an element with the class site-header. I did a text search on my local WordPress install and found a file inside the 2013 theme called custom-header.php (an obviously promising name). I perused that file and found a call to the function get_header_image. At this point, I knew I was getting close.

There were a couple of layers of abstraction, but I eventually found the get_theme_mods functionality. I threw the following line into my functions.php file so I could see what was returned from the function.

error_log(print_r(get_theme_mods(), true));

As a side note, I find this to be a simple and effective method of exploring the WordPress API. I noticed the data returned contained a key called header_image_data. The value for that key contained the information I needed, so it was just time to walk back up the hierarchy and figure out the best way to get the data. get_theme_mod delegates to get_theme_mods so I knew I had my answer.

All you have to do to get the attachment ID of the uploaded header image is call get_theme_mod('header_image_data'), check the result is an array, and that the attachment_id key is set. If it is, that’s your answer.

$data = get_theme_mod('header_image_data');

$attachment_id = is_array($data) && isset($data['attachment_id']) ? $data['attachment_id'] : false;

if($attachment_id) {
	// Do something here
}

Multiple Featured Images Now Uses Theme Support

I recently posted about my approach to building a WordPress plugin that would provide multiple featured images on any content type. Today, I was looking over the plugin again and realized that I could have done a better job implementing the public API in terms of registering new image pickers. As a reminder, this is how I said you should do that:

<?php

add_action('init', function() {
	if(function_exists('mfi_reloaded_add_image_picker')) {
		mfi_reloaded_add_image_picker('hero-image', array(
			'post_types' => array('post'),
			'labels' => array(
				'name' => __('Hero Image'),
				'set' => __('Set hero image'),
				'remove' => __('Remove hero image'),
				'popup_title' => __('Set Hero Image'),
				'popup_select' => __('Set hero image'),
			),
		));

		mfi_reloaded_add_image_picker('sidekick-image', array(
			'post_types' => array('post'),
			'labels' => array(
				'name' => __('Sidekick Image'),
				'set' => __('Set sidekick image'),
				'remove' => __('Remove sidekick image'),
				'popup_title' => __('Set Sidekick Image'),
				'popup_select' => __('Set sidekick image'),
			),
		));
	}
});

There’s a couple of things I see wrong with this after looking at it again. First, hooking on init is probably too late in some cases. That hook should probably be after_setup_theme. Second, there is already a function that allows for registering support in a theme for a certain piece of functionality: add_theme_support. Finally, I hate the function_exists call there. It is aesthetically unpleasant in my mind.

As such, I’ve changed the plugin to use add_theme_support so there is a new way to use the functionality included:

function register_custom_image_pickers() {
	add_theme_support('mfi-reloaded', array(
		'hero-image' => array(
			'post_types' => array('post'),
			'labels' => array(
				'name' => __('Hero Image'),
				'set' => __('Set hero image'),
				'remove' => __('Remove hero image'),
				'popup_title' => __('Set Hero Image'),
				'popup_select' => __('Set hero image'),
			),
		),
		'sidekick-image' => array(
			'post_types' => array('post'),
			'labels' => array(
				'name' => __('Sidekick Image'),
				'set' => __('Set sidekick image'),
				'remove' => __('Remove sidekick image'),
				'popup_title' => __('Set Sidekick Image'),
				'popup_select' => __('Set sidekick image'),
			),
		),
	));
}
add_action('after_setup_theme', 'register_custom_image_pickers');

You can see the changes in the GitHub repository. Let me know if you have any questions!

WordPress Plugin Skeleton Updated – Added Settings Functionality

Today, I updated my WordPress plugin skeleton to implement basic settings functionality. This update includes some code that adds a settings page, registers the settings on that page, outputs some common controls, and sanitizes the options after saving. I think this code could be useful to anyone looking to build a WordPress plugin with a settings page. As a quick preview, here’s the settings panel that the plugin skeleton now provides:

settings-page

You can find the WordPress plugin skeleton repository on GitHub or download it and take a look locally. If you don’t want the settings page functionality, you can just grab the bare bones version.

Building a WordPress Plugin: Multiple Featured Images

I worked on a WordPress site recently where the client needed to associate two different images with each piece of content. I couldn’t use a single image with a custom size because the images’ aspect ratios were so different.

I knew that I could use WordPress’s Featured Image functionality for the first image, but I wasn’t quite sure what to do for the second. I needed it to be as easy as possible to use because my client was non-technical. I looked for an existing solution by searching the WordPress plugin repository. I found a plugin that seemed promising, but Multiple Featured Images hasn’t been updated since May 2012 and didn’t support the media picker interface introduced in WordPress 3.5.

Because I knew this was something I would use on future projects, I decided to build my own plugin.

Building the Plugin

The plugin provides an interface that a developer can use to add multiple named image pickers to any type of content and access the images within the site’s theme. It uses the new media picker interface introduced in 3.5.

Laying the Foundation

Every WordPress plugin I build starts from the same basic plugin skeleton. If you’re interested in using my skeleton, feel free to download it from GitHub.

My plugin skeleton is ridiculously simple – it is a class with exclusively static methods. The class acts as a pseudo namespace that encapsulates the functionality of my plugin and ensures there are no function naming collisions. There are a million different ways to structure a WordPress plugin, but my skeleton has worked well for me over the past few years.

After forking my skeleton for this project, I renamed the plugin class to something appropriate (MFI_Reloaded) and changed all other strings as necessary.

Defining the Public Interface

Since this plugin is going to be used by other developers, the first thing I wanted to do is define a well thought out interface. The following is what I came up with:

function mfi_reloaded_add_image_picker($name, $args = array());

function mfi_reloaded_has_image($name, $post_id = null);

function mfi_reloaded_get_image_id($name, $post_id = null);

function mfi_reloaded_get_image($name, $size = 'thumbnail', $post_id = null, $attributes = array());
function mfi_reloaded_the_image($name, $size = 'thumbnail', $post_id = null, $attributes = array());

This interface accomplishes both goals I had for the plugin. Developers can register new named image pickers using mfi_reloaded_add_image_picker and access saved information with the rest.

With this interface, I consciously tried to stay close to the analogous functions provided by the WordPress post thumbnail template tags.

Implementing the Public Interface

Now that there is an interface for telling the plugin what image pickers to display, I need a way to store that data in the plugin. For the sake of simplicity, I decided to use an associative array which I added as a private static field on our plugin class.

At the top of my plugin class I added the following:

private static $image_pickers = array();

Now, I needed to actually make the template tags defined earlier do something. When I build template tags, I tend to make all “getters” and “setters” simply delegate to public static methods on the class. That’s what I did here.

At the bottom of my plugin class I added the following:

public static function add_image_picker($name, $args) {
	return false;
}

public static function get_image_id($name, $post_id) {
	return false;
}

public static function get_image($name, $size, $post_id, $attributes) {
	return false;
}

I started here by implementing add_image_picker. This could have been as easy as adding $args as a new item on the previously defined $image_pickers field with $name as the array key. In the interest of making this plugin more robust, however, I went a little bit further:

  1. Return false immediately if the image picker name is not a string or if an image picker with that name has been previously registered
  2. Normalize the data passed in the $args parameter given the defaults specified in the template tag documentation
  3. Set $image_pickers[$name] = $normalized_args

Here is what this method ended up looking like:

public static function add_image_picker($name, $args) {
	if(!is_string($name) || isset(self::$image_pickers[$name])) {
		return false;
	}

	self::$image_pickers[$name] = self::_normalize_args($args);
}

I needed to normalize the arguments so I created the helper method named _normalize_args to do so. I usually prefix helper methods like this with an underscore (as you can see). That method reads as follows:

private static function _normalize_args($args) {
	$normalized_args = array();

	if(!isset($args['post_types'])) {
		$normalized_args['post_types'] = array('post', 'page');
	} else if(!is_array($args['post_types'])) {
		$normalized_args['post_types'] = array($args['post_types']);
	} else {
		$normalized_args['post_types'] = $args['post_types'];
	}

	$default_labels = array(
		'name' => __('Featured Image'),
		'set' => __('Set featured image'),
		'remove' => __('Remove featured image'),
		'popup_title' => __('Set Featured Image'),
		'popup_select' => __('Set featured image'),
	);

	if(!isset($args['labels']) || !is_array($args['labels'])) {
		$normalized_args['labels'] = $default_labels;
	} else {
		$normalized_args['labels'] = shortcode_atts($default_labels, $args['labels']);
	}

	return $normalized_args;
}

For now, that’s all I could really implement. I wasn’t storing any data yet so there was no way to return anything meaningful.

Adding the Meta Boxes

Once the public API was defined, the next step was to show the user an interface they could use. It won’t be fully functional at first, but it will look like correct.

To register our meta boxes, we first add a callback to the add_meta_boxes action. In my add_actions method (inside the is_admin conditional), I added the following code:

add_action('add_meta_boxes', array(__CLASS__, 'add_image_picker_meta_boxes'));

Then, I added the following public static method inside of my class:

public static function add_image_picker_meta_boxes($post_type) {
	foreach(self::$image_pickers as $image_picker_name => $image_picker_args) {
		if(in_array($post_type, $image_picker_args['post_types'])) {
			add_meta_box(
				'mfi-reloaded-' . sanitize_title_with_dashes($image_picker_name),
				$image_picker_args['labels']['name'],
				array(__CLASS__, 'display_image_picker_meta_box'),
				$post_type,
				'side',
				'default',
				compact('image_picker_name', 'image_picker_args')
			);
		}
	}
}

This method iterates over each registered image picker and checks to see if it is registered for the post type currently being edited. If it is, it adds a meta box by calling add_meta_box with the appropriate arguments.

In order to actually display the meta box, we need to implement the display_image_picker_meta_box method. In my plugin class, I added another public static method with that name that displays some output:

public static function display_image_picker_meta_box($post, $meta_box) {
	$image_picker_args = $meta_box['args']['image_picker_args'];
	$image_picker_name = $meta_box['args']['image_picker_name'];

	$image_id = mfi_reloaded_get_image_id($image_picker_name, $post->ID);
	$image = mfi_reloaded_get_image($image_picker_name, 'full', $post->ID);

	include('views/meta-boxes/image-picker.php');
}

We’re using the unimplemented public interface that we stubbed out earlier to grab the full size image for the picker in question (if it exists). We’ll be using this later. When printing HTML, I prefer to include a separate file (as you can see in the method above). The entirety of that file is as follows:

<div class="mfi-reloaded-image-picker"
		data-mfi-reloaded-image-id="<?php printf('%d', $image_id); ?>"
		data-mfi-reloaded-name="<?php esc_attr_e($image_picker_name); ?>"
		data-mfi-reloaded-select="<?php esc_attr_e($image_picker_args['labels']['popup_select']); ?>"
		data-mfi-reloaded-title="<?php esc_attr_e($image_picker_args['labels']['popup_title']); ?>">

	<div class="mfi-reloaded-image-picker-preview"></div>

	<a class="mfi-reloaded-image-picker-remove" href="#"><?php esc_html_e($image_picker_args['labels']['remove']); ?></a>
	<a class="mfi-reloaded-image-picker-set" href="#"><?php esc_html_e($image_picker_args['labels']['set']); ?></a>
</div>

This code outputs a container for the thumbnail preview and two links that allow the user to take action. Of course, nothing happens yet.

Adding the Image Picking Functionality

The media picker is controlled via JavaScript and styled with CSS so I needed to enqueue the appropriate scripts and styles in the administrative panel on the editing pages. To do so, I hooked into admin_enqueue_scripts by adding the following line inside of my add_actions method (in the is_admin conditional):

add_action('admin_enqueue_scripts', array(__CLASS__, 'enqueue_administrative_resources'));

The enqueue_administrative_resources callback is responsible for checking if the post editing screen is being viewed and, if so, enqueueing the appropriate scripts.

public static function enqueue_administrative_resources() {
	$screen = get_current_screen();

	if('post' === $screen->base) {
		wp_enqueue_media();

		wp_enqueue_script('mfi-reloaded', plugins_url('resources/backend/mfi-reloaded.js', __FILE__), array('jquery'), self::VERSION, true);
		wp_enqueue_style('mfi-reloaded', plugins_url('resources/backend/mfi-reloaded.css', __FILE__), array(), self::VERSION);
	}
}

The stylesheet is pretty simple as it just prevents the preview image from flowing outside the bounds of its container:

.mfi-reloaded-image-picker-preview img {
	height: auto;
	max-width: 100%;
}

The JavaScript file is a little more complex, but I’ve added comments liberally where I think there could be confusion. I encourage you to read the source if you’re interested in what is going on:

jQuery(document).ready(function($) {
	// Register a click event handler on the remove links
	$('.mfi-reloaded-image-picker-remove').click(function(event) {
		event.preventDefault();

		var $remove = $(this),
			$container = $remove.parents('.mfi-reloaded-image-picker'),
			$preview = $remove.siblings('.mfi-reloaded-image-picker-preview'),
			$set = $remove.siblings('.mfi-reloaded-image-picker-set'),
			_name = $container.data('mfi-reloaded-name');

		// Initiate an AJAX request to remove the image id for this image picker
		$.post(
			ajaxurl,
			{
				action: 'mfi_reloaded_set_image_id',
				image_id: 0,
				name: _name,
				post_id: $('#post_ID').val()
			},
			function(data, status) { }
		);

		// Hide the link that allows a user to remove the image
		$remove.hide();

		// Remove the preview thumbnail because it is no longer valid
		$preview.empty();

		// Show the link that allows a user to set the image
		$set.show();
	});

	// Register a click event handler in order to show the media picker
	$('.mfi-reloaded-image-picker-set').click(function(event) {
		event.preventDefault();

		var $set = $(this),
			$container = $set.parents('.mfi-reloaded-image-picker'),
			$preview = $set.siblings('.mfi-reloaded-image-picker-preview'),
			$remove = $set.siblings('.mfi-reloaded-image-picker-remove'),
			_name = $container.data('mfi-reloaded-name'),
			_select = $container.data('mfi-reloaded-select'),
			_title = $container.data('mfi-reloaded-title');

		// Set up the media picker frame
		var mfi_reloaded_frame = wp.media({
			// Open the media picker in select mode only
			frame: 'select',

			// Only allow a single image to be chosen
			multiple: false,

			// Set the popup title from the HTML markup we output for the active picker
			title: _title,

			// Only allow the user to choose form images
			library: { type: 'image' },

			button: {
				// Set the button text from the HTML markup we output for the active picker
				text:  _select
			}
		});

		mfi_reloaded_frame.on('select', function(){
			var media_attachment = mfi_reloaded_frame.state().get('selection').first().toJSON();

			// Initiate an AJAX request to set the image id for this image picker
			$.post(
				ajaxurl,
				{
					action: 'mfi_reloaded_set_image_id',
					image_id: media_attachment.id,
					name: _name,
					post_id: $('#post_ID').val()
				},
				function(data, status) { }
			);

			// Add the image to the preview container
			$preview.append($('<img />').attr('src', media_attachment.sizes.full.url).attr('alt', media_attachment.title));
		});

		// Show the remove link
		$remove.show();

		// Hide the set link
		$set.hide();

		mfi_reloaded_frame.open();
	});

	$('.mfi-reloaded-image-picker').each(function(index, element) {
		var $container = $(element),
			$preview = $container.children('.mfi-reloaded-image-picker-preview'),
			$remove = $container.children('.mfi-reloaded-image-picker-remove'),
			$set = $container.children('.mfi-reloaded-image-picker-set');

		if(0 === $preview.children().size()) {
			$remove.hide();
		} else {
			$set.hide();
		}
	});
});

Now that the JavaScript was in place and working, I need to make sure that the user’s choices were persisted.

Saving and Loading Data

When I’m saving data for a post, I generally like to create two small wrapper methods that get and set the data. That’s exactly what I did for this plugin:

private static function _get_meta($post_id, $meta_key = null) {
	$post_id = empty($post_id) && in_the_loop() ? get_the_ID() : $post_id;

	$meta = get_post_meta($post_id, 'rfi-reloaded-images', true);
	
	if(!is_array($meta)) {
		$meta = array();
	}

	return is_null($meta_key) ? $meta : (isset($meta[$meta_key]) ? $meta[$meta_key] : false);
}

private static function _set_meta($post_id, $meta) {
	$post_id = empty($post_id) && in_the_loop() ? get_the_ID() : $post_id;

	update_post_meta($post_id, 'rfi-reloaded-images', $meta);

	return $meta;
}

Now that these were in place, I could address the AJAX call that was currently being unhandled. To start, I added an action in my add_actions method for the ajax call:

add_action('wp_ajax_mfi_reloaded_set_image_id', array(__CLASS__, 'ajax_mfi_reloaded_set_image_id'));

Then, I created the callback and made sure it checked permissions for the post being modified and then persisted data appropriately.

public static function ajax_mfi_reloaded_set_image_id() {
	$data = stripslashes_deep($_REQUEST);

	$image_id = $data['image_id'];
	$name = $data['name'];
	$post_id = $data['post_id'];

	if($post_id && current_user_can('edit_post', $post_id)) {
		$images = self::_get_meta($post_id);

		if(empty($image_id)) {
			unset($images[$name]);
		} else {
			$images[$name] = $image_id;
		}

		self::_set_meta($post_id, $images);
	}

	exit;
}

Finishing the Public Interface

Now that we’ve started saving data, we can finally implement the rest of the public interface. I started with the get_image_id method in the plugin class.

public static function get_image_id($name, $post_id) {
	return self::_get_meta($post_id, $name);
}

I just used the wrapper function I created earlier to pull out the appropriate information from the serialized data and returned it.

Next, I implemented the get_image method, which was straightforward enough now that I had the ID.

public static function get_image($name, $size, $post_id, $attributes) {
	$image_id = self::get_image_id($name, $post_id);

	if($image_id) {
		return wp_get_attachment_image($image_id, $size, false, $attributes);
	}

	return false;
}

And that was it, everything was working like I wanted it to.

Using the Multiple Image Picker

Now that the plugin is done, using it is easy. For testing, I put the following code into a file in /wp-content/mu-plugins/

<?php

add_action('init', function() {
	if(function_exists('mfi_reloaded_add_image_picker')) {
		mfi_reloaded_add_image_picker('hero-image', array(
			'post_types' => array('post'),
			'labels' => array(
				'name' => __('Hero Image'),
				'set' => __('Set hero image'),
				'remove' => __('Remove hero image'),
				'popup_title' => __('Set Hero Image'),
				'popup_select' => __('Set hero image'),
			),
		));

		mfi_reloaded_add_image_picker('sidekick-image', array(
			'post_types' => array('post'),
			'labels' => array(
				'name' => __('Sidekick Image'),
				'set' => __('Set sidekick image'),
				'remove' => __('Remove sidekick image'),
				'popup_title' => __('Set Sidekick Image'),
				'popup_select' => __('Set sidekick image'),
			),
		));
	}
});

This registers two image pickers for use on posts only. I then used the mfi_reloaded_the_image to display the images for posts in the loop.

If you’re interested in the plugin I built, download it from GitHub. If you’ve got suggestions for improvements, I’d love to see a pull request for patch. Let me know how you plan on using it in the comments!