Tag Archives: WordPress

Getting started with WordPress plugin development

I got an email with the following question, and figured I may as well answer publicly. The question comes from someone who’s an experienced programmer, but has never worked with WordPress before.

[...] I wondered if you had some helpful advice on learning the basics of WP plugin development. I’m mostly hoping there are frameworks/libraries that the community is currently using.

I don’t know of a widely-used library of general WP plugin development tools. Partly this is because WP itself partially qualifies as a “library”, insofar as it provides ready-to-use functions for saving data to the database, URL routing, template loading, user management, etc. And partly it’s because the architecture of WP is so wide open that it’d be difficult to build a more generalized library that covered even a majority of the possible modifications you might make to stock WordPress.

That said, here are a few useful tools that I know of:

  • The official WP documentation on writing a plugin is good for learning about plugin manifests as well as a top-level overview of the aspects of plugin development.
  • The official Plugin API page is also a good starting point. It explains WP’s hook system, which is the foundation for plugin development.
  • Pippin Williamson has written a series called Plugin Development 101 that features a set of video tutorials on common plugin development techniques. Costs a couple bucks, but Pippin is a smart dude and I’m sure the videos are a really good way to get started.
  • There are a couple of tools that can be used to generate boilerplate and basic file setup for plugins. The ones I know of are https://github.com/tommcfarlin/WordPress-Plugin-Boilerplate and the wp scaffold plugin command in WP-CLI.
  • The book Professional WordPress Plugin Development is very solid, and a good investment for those who like to learn this kinda stuff through books.

My #1 piece of advice to people getting started with WP plugin development is to find a plugin that does something sorta like what they are trying to do and to reverse engineer it. For me, this is far faster and more practical than any hello-world tutorial or sequential set of lessons. YMMV.

If anyone has other suggestions, please feel free to add them in the comments.

Commit messages, tickets, and inline docs

As a Trained Academic™, I try to think about software documentation in terms of audience and purpose. Who is going to read technical docs? What will they be trying to learn by reading them? I use these questions as a lens for writing different kinds of documentation.

I like to think of commit messages as the canonical technical narrative of the project. These messages matter most for two groups of people: those following the ongoing development of the project, and those intrepid future souls who are combing through logs to track the lineage of given bit of code. The needs of the two groups are similar, which suggests certain content and formatting for commit messages. Both groups are scanning sizable streams of changesets, and thus need a quick way to disregard those that are irrelevant to them. That’s where the one-line summary is helpful, for git log and GUIs like this. Beyond the summary, they usually need a bit more context, a bird’s-eye summary of the problem the changeset is intended to fix. They need pointers to full discussions (ie, linked tickets). And because in projects like WordPress the changelog is also an attribution log, they need reference to the person responsible for the fix (“props”). It’s these kinds of needs I’m thinking about when I structure commit messages like this. (Side note: I’m a big fan of this post arguing for a similar commit message format.)

Tickets are bug notices or feature requests as they appear in the project’s issue tracker. The primary audience here is the developers, designers, and users who are currently involved in the ongoing development of the software. It’s a workspace, and it’s messy. Tickets may contain extended discussions, with numerous digressions and dead-end patches. It’s for this reason that issue tickets are not a replacement for good commit messages. Commit messages contain justification for changesets, while trackers contain the process that led to that justification.

Inline docs, primarily in the form of function/method/class docblocks, are intended first and foremost for developers who are currently trying to figure out how the software works. As a rule, these people don’t care about the history of the project, or the reasoning that led to a given piece of code. The documentation should answer questions like: where is this function used in the codebase? if I put x into the function, what will I get out of it? etc. While it’s often good to have pointers to the project history in certain cases – such as a link to a ticket that explains why an unintuitive bit of code works in the way it does – it probably does more harm than good to litter inline documentation with details about the project’s intellectual history. That’s what blame tools are for.

It goes without saying that there are gray areas. Commit messages, tickets, and inline docs all have the same “text” as their subject: the codebase. And the intended audiences for the three kinds of documentation are frequently overlapping. Still, I think it’s helpful to keep the distinctions in mind. When you write documentation, you’re writing the story of the project as it’ll be understand by future coder-historians. You want to make sure that the story makes sense.

Vim function for correcting whitespace to WP standards

When doing client work, especially in a team, I like to urge adherence to the WordPress coding standards. This is especially important with whitespace, in particular when other members of the team have their IDEs set up in certain ways that frustrate the standards. I wrote this quick little functions for your .vimrc that does some whitespace cleanup in the currently open buffer. (I’m sure there are a thousand better ways to do it, and you should feel free to let me know in the comments. This is just what I put together in 15 minutes.)

I’ve mapped it to F8. Modify as you’d like.

WordPress/BuddyPress registration and the Office 365 email filter

Just tore through the following problem on a client site (independently discovered by Martha Burtis here). WordPress/BuddyPress sites that allow for self-registration send out emails with activation links of the form: http://example.com/activate/?key=12345 (for BuddyPress) and http://example.com/wp-activate.php?key=12345 (for WordPress multisite). This format trips up the link filter that Microsoft’s Office 365 email service uses. After some experimentation, I figured out that the problem is the word ‘key’ in a URL parameter – once this term is removed from the URL, it passes right through the filter.

So, you can fix the problem by changing the URL parameter in the activation emails. That means (a) changing the text of the email, and (b) changing the server-side logic to expect something other than ‘key’. Here’s how to do it in BuddyPress:

function bbg_activation_email_content( $message ) {
	return str_replace( '?key=', '?activationk=', $message );
}
add_filter( 'bp_core_activation_signup_user_notification_message', 'bbg_activation_email_content' );

function bbg_screen_activation() {
	global $bp;

if ( !bp_is_current_component( 'activate' ) )
		return false;

// Check if an activation key has been passed
	if ( isset( $_GET['activationk'] ) ) {

// Activate the signup
		$user = apply_filters( 'bp_core_activate_account', bp_core_activate_signup( $_GET['activationk'] ) );

// If there were errors, add a message and redirect
		if ( !empty( $user->errors ) ) {
			bp_core_add_message( $user->get_error_message(), 'error' );
			bp_core_redirect( trailingslashit( bp_get_root_domain() . '/' . $bp->pages->activate->slug ) );
		}

// Check for an uploaded avatar and move that to the correct user folder
		if ( is_multisite() )
			$hashed_key = wp_hash( $_GET['activationk'] );
		else
			$hashed_key = wp_hash( $user );

// Check if the avatar folder exists. If it does, move rename it, move
		// it and delete the signup avatar dir
		if ( file_exists( bp_core_avatar_upload_path() . '/avatars/signups/' . $hashed_key ) )
			@rename( bp_core_avatar_upload_path() . '/avatars/signups/' . $hashed_key, bp_core_avatar_upload_path() . '/avatars/' . $user );

bp_core_add_message( __( 'Your account is now active!', 'buddypress' ) );

$bp->activation_complete = true;
	}

bp_core_load_template( apply_filters( 'bp_core_template_activate', array( 'activate', 'registration/activate' ) ) );
}
remove_action( 'bp_screens', 'bp_core_screen_activation' );
add_action( 'bp_screens', 'bbg_screen_activation' );

You’d have to do something in the same spirit when not using BuddyPress. For the email, filter ‘wpmu_signup_user_notification_email’. Catching the request and overriding ‘key’ will be trickier. I haven’t experimented with it, but maybe you can hook to ‘activate_header’, detect the presence of $_GET['activationk'], and then redirect to the ‘key=’ URL that wp-activate.php expects.

Hopefully this is enough to help if you’re having the problem.

A recursive sorta-version of wp_parse_args()

WordPress trick: wp_parse_args() is a helpful function for taking configuration arrays passed to a function and combining them intelligently with the default arguments for the function. I needed a recursive version, for use with multi-dimensional arrays of arbitrary depth, so I wrote the little ditty below. As I note in the docs, there are fundamental ambiguities in the process of rectifying multi-d arrays, and the technique I’ve chosen is specific to my implementation. Use at your own risk.

Get your Boone fix

Mostly for my own records, here are a few recent Boone-related videos and interviews from around the web:

  • Code Poet interview – April 18, 2013. In which I speak at length (ramble?) about BuddyPress, the university, and the value of free software
  • WordSesh panel – April 13, 2013. A livestreamed discussion about BuddyPress with John, Paul, Ray, and Tammie.
  • BuddyCamp Miami presentation – April 5, 2013. A brief talk where I talk about how to use BuddyPress’s Group Extension API to add BP features to a WordPress plugin. The slides are at blo.so/bcmia.
  • WPNYC presentation – January 15, 2013. A talk I gave on the big new features in BuddyPress 1.7

Using a hosts file for easy management of dev, staging, and production WordPress sites

This recent article at Smashing Magazine discussed trends and challenges in deploying WordPress sites. The biggest issue cited by the article and the lengthy comments that follow is the issue of the database: because WordPress stores strings in the database that contain the site domain (including configuration options and asset paths), it’s hard to migrate content and config between dev, staging, and production environments. A bunch of possible solutions were offered up, including interconnectit/Search-Replace-DB, which I use fairly often and really like.

I was surprised, however, that no one talked about my preferred strategy, which is, in a way, the simplest: Dev, staging, and production should all have the same domain names. When you remove the need to change the domain, you make it much easier to deploy specific pieces of content, spin up new instances, etc.

Since all versions of a site have the same domain name – say, booneisthebomb.com – I use my local hosts file (/etc/hosts on *nix systems) to switch between instances. So I may have the following lines in /etc/hosts:

# Local development
# 127.0.0.1      booneisthebomb.com

# Staging site
# 123.456.789.0  booneisthebomb.com

With these two lines commented out, going to booneisthebomb.com in a browser will use DNS for the lookup, which is to say it’ll go to the production site. Uncommenting one of the lines allows me to work on the local or staging site.

The biggest pitfall of this technique is that now there is no obvious way to tell your environments apart (and you definitely don’t want to mistake your production site for a dev site). My solution for this is to drop this file into wp-content/mu-plugins/. Then, in my environment file (which contains env-specific config, such as database connection info), I put a line like this:

define( 'ENV_TYPE', 'staging' );

Now, when I load up the staging site, it shows booneisthebomb.com in the URL bar, and the following box appears at the bottom of every page:

env-type-flag

WordPress developers: Write to the filesystem the right way

Many WordPress plugins and themes need to write to the filesystem, to cache data, create a debug log, download libraries that for one reason or another aren’t distributed with the main package, etc. And many of these plugins do it wrong, by writing (or attempting to write) to their own plugin/theme directories. This is a bad idea for a couple of reasons:

  • If you use version control to deploy/manage a site, you probably have configured your repo to ignore the content of dynamic directories like wp-content/uploads. Obviously, you don’t want to ignore plugin and theme directories. When Git etc detects your newly created files, it wreaks all sorts of havoc with workflow and, depending on the content of the files and the carelessness of the deployment manager, poses the risk of losing user content or endangering sensitive data.
  • Some people have their file permissions set very conservatively, so that the webserver user doesn’t have write access to wp-content/plugins or wp-content/themes. So plugins that attempt to write to those directories often break altogether.

The good news is that every properly-configured WordPress installation will have at least one location where the webserver can write, and which is highly likely to be ignored by all version control setups: the upload directory wp-content/uploads. The situation is more complicated on Multisite, where each site has its own subdirectory of wp-content/blogs.dir. Happily, there’s an easy way to concatenate an upload path that’ll work across installations:

$uploads = wp_upload_dir();
$my_upload_dir = $uploads['basedir'] . '/yourplugindir';

WordPress has a very slick filesystem class that’ll help you if you really do need to write to a plugin or theme directory. But 99% of the time, you don’t. Please keep your stuff out of the codebase.

Anthologize automated tests now run using WP’s unit test suite

Anthologize, I haven’t forgotten about you! I have some very cool stuff in the works, but for now, a quick update on the progress of the campaign-funded work.

Back in 2011, Patrick Murray-John added some unit tests to Anthologize, covering a number of public methods in the TeiApi class. A number of the major refactoring jobs I’m currently undertaking will require additional test coverage, but they are (unlike TeiApi) dependent on WordPress being initalized. So I’ve migrated Anthologize’s tests to use the WP test suite. I’ve used the scaffold provided by the dope and phatte wp-cli (incidentally, I hope that their scaffold becomes the de facto standard for WP plugin tests).

This change means that, in addition to requiring PHPUnit, you’ll also need to have the WP test suite installed. You can install it manually, but I recommend using wp-cli to get the job done in just a command or two. In brief:


$ wp core init-tests /path/to/wp-tests --dbname=wp_test --dbuser=root --dbpass=asd
$ mysql -u'root' -p'asd' -e 'CREATE DATABASE IF NOT EXISTS wp_test'

To run the tests,


$ cd /path/to/wp/wp-content/plugins/anthologize
$ WP_TESTS_DIR=~/path/to/wp-tests phpunit

You can define WP_TESTS_DIR in your .bashrc file for quicker use in the future.

This post is brought to you by Anthologize campaign supporter Demokratie & Dialog. D&D, a Major Sponsor of Anthologize (woo hoo!), is using WordPress and BuddyPress in amazing ways both to study the way that government policy affects youth and to get youth themselves involved in the development of said policy. I had a chance to get to know the very excellent Andreas Karsten of D&D at BuddyCamp Vancouver last year, and we have big plans to start a BuddyPress jazz band. Many thanks for your support of WP, BP, and Anthologize!

Selectively deprecating WordPress plugins from Dashboard > Plugins

On large WordPress MS installations where site admins are allowed to manage their own plugins, the list of plugins tends to get crowded over time. Sometimes you introduce a plugin to the network and admins start using it, but some time down the road – a year or two later, even – you decide that you want to deprecate that plugin (maybe to replace it with another one, etc). However, migrating users of one plugin to another plugin is a logistical and technical tangle, and sometimes the best medium-term strategy is to allow existing users of the plugin to keep using it, but to prevent admins from activating it in the future.

Here’s how we’re doing it on the CUNY Academic Commons. In the gist below, $disabled_plugins are the plugins that we don’t want people to activate in the future. In most cases, however, we do want people to be able to deactivate the plugins, so by default, we don’t filter plugins if they’re active. However, we also have an array of $undeactivatable_plugins, which cannot be activated or deactivated.

Props to dev team member Dominic Giglio for writing part of this.