Theme/Plugin Dependencies

In trying to figure out what to talk about at WordCamp Atlanta, I remembered a question put to me in WordCamp Birmingham. The question was how can a theme developer easily make a plugin-dependency in their theme?

I wrote some code to do this sort of thing, just as an example/test/demonstration, but then after looking over the schedule, I found that Thomas Griffin had beat me to it. After looking over his slides and having him walk me through his code, I realized that his solution was much more fully featured than mine, so I’m glad I didn’t present anything on this topic. (I ended up just doing an answer session where I tried to answer any question put to me, and frankly that was much more fun than having slides, so I’m probably just going to do that from now on.)

You can find his cool library here, BTW: http://tgmpluginactivation.com/

However, his solution is highly complex. The class he came up with is well done and fully-featured. He has capabilities for making notifications in the header space on the admin section, lightbox popups, bulk installs, forced activation, custom skinning, etc. It’s a big thing. While that’s great for a lot of people in terms of having code you can just drop-in and use, I thought that it doesn’t do much to teach how one can DIY it.

See, the code I wrote was tiny. It basically just provides some minor functionality to show a theme author how to detect installed plugins, how to detect when they’re active, how to build install and activate links, etc. It doesn’t do any pretty stuff. No custom skinning. No lightbox popups. All these things are possible, but if somebody hands you a hunk of library code to do them, then you know how to use that library, not how it works. I dislike using libraries for this reason.

So here’s the small class I wrote to do the same sort of thing, but in a very bare-bones style.

/* 

Simple class to let themes add dependencies on plugins in ways they might find useful

Example usage:

	$test = new Theme_Plugin_Dependency( 'simple-facebook-connect', 'http://ottopress.com/wordpress-plugins/simple-facebook-connect/' );
	if ( $test->check_active() ) 
		echo 'SFC is installed and activated!';
	else if ( $test->check() ) 
		echo 'SFC is installed, but not activated. <a href="'.$test->activate_link().'">Click here to activate the plugin.</a>';
	else if ( $install_link = $test->install_link() )
		echo 'SFC is not installed. <a href="'.$install_link.'">Click here to install the plugin.</a>';
	else 
		echo 'SFC is not installed and could not be found in the Plugin Directory. Please install this plugin manually.';

*/
if (!class_exists('Theme_Plugin_Dependency')) {
	class Theme_Plugin_Dependency {
		// input information from the theme
		var $slug;
		var $uri;

		// installed plugins and uris of them
		private $plugins; // holds the list of plugins and their info
		private $uris; // holds just the URIs for quick and easy searching

		// both slug and PluginURI are required for checking things
		function __construct( $slug, $uri ) {
			$this->slug = $slug;
			$this->uri = $uri;
			if ( empty( $this->plugins ) ) 
				$this->plugins = get_plugins();
			if ( empty( $this->uris ) ) 
				$this->uris = wp_list_pluck($this->plugins, 'PluginURI');
		}

		// return true if installed, false if not
		function check() {
			return in_array($this->uri, $this->uris);
		}

		// return true if installed and activated, false if not
		function check_active() {
			$plugin_file = $this->get_plugin_file();
			if ($plugin_file) return is_plugin_active($plugin_file);
			return false;
		}

		// gives a link to activate the plugin
		function activate_link() {
			$plugin_file = $this->get_plugin_file();
			if ($plugin_file) return wp_nonce_url(self_admin_url('plugins.php?action=activate&plugin='.$plugin_file), 'activate-plugin_'.$plugin_file);
			return false;
		}

		// return a nonced installation link for the plugin. checks wordpress.org to make sure it's there first.
		function install_link() {
			include_once ABSPATH . 'wp-admin/includes/plugin-install.php';

			$info = plugins_api('plugin_information', array('slug' => $this->slug ));

			if ( is_wp_error( $info ) ) 
				return false; // plugin not available from wordpress.org

			return wp_nonce_url(self_admin_url('update.php?action=install-plugin&plugin=' . $this->slug), 'install-plugin_' . $this->slug);
		}

		// return array key of plugin if installed, false if not, private because this isn't needed for themes, generally
		private function get_plugin_file() {
			return array_search($this->uri, $this->uris);
		}
	}
}

Obviously, for theme authors wanting to do something, they’re going to want to make much prettier means of displaying things and installing things. Thus, this code is meant as an example, to show the basics of how to detect such things.

So, use it directly if you like (it works), but more importantly, if you want to put plugin dependancies in your theme, then I suggest reading it and figuring out how it works instead. Then you can see how plugins can be detected and how to build simple install and activation links.

(BTW, note that I used the slug and the PluginURI for a reason. Plugins should be using a unique URL for the plugin in their code, and that URL is very likely to be the most unique thing about the plugin, and therefore the best way to check for a plugin already being there or not. Slugs can be duplicated by accident or design, but URLs are generally going to be unique and specific to a particular plugin.)