In our FavePersonal theme we integrated the excellent Social plugin from MailChimp and included an option to turn it on or off. Most importantly we did it in a way that still allows the Social plugin to be installed and updated independently. We made sure that we implemented this in a manner that:
- respected users’ expectations
- allowed users to manage the plugin independently of the theme
I thought it might be useful to outline the approach we used in a little detail in case others might want to do something similar.
At Crowd Favorite we believe strongly in the principle of least astonishment. WordPress users expect to be able to download and install plugins whenever they see something that looks interesting. We wanted to make sure that we didn’t impede this process. So even though FavePersonal includes the Social plugin, we wanted to make sure we accounted for situations where Social was already installed on a site before FavePersonal was activated; as well as handling a situation where someone installs and activates the Social plugin once FavePersonal is already installed.1
Foundation
When including a plugin in a theme, the main thing you want to avoid is a situation where the same PHP code is processed twice. This will trigger a fatal error (often times resulting in the white screen of death). In other words, don’t just include()
the plugin in your theme. If someone already has that plugin activated on their site, you’d have the “loaded twice” issue (and your site would crash).
To avoid this situation, it’s useful to look at the WordPress load process (in wp-settings.php, referencing WP 3.4.1). The first thing WordPress does is load in its core functionality:
// Load most of WordPress.
require( ABSPATH . WPINC . '/class-wp-walker.php' );
require( ABSPATH . WPINC . '/class-wp-ajax-response.php' );
require( ABSPATH . WPINC . '/formatting.php' );
require( ABSPATH . WPINC . '/capabilities.php' );
require( ABSPATH . WPINC . '/query.php' );
require( ABSPATH . WPINC . '/theme.php' );
[...]
This is followed by the loading of MU (Must Use) plugins; these are plugins that are placed into the wp-content/mu-plugins directory and cannot be managed from the Plugins screen.
// Load must-use plugins.
foreach ( wp_get_mu_plugins() as $mu_plugin ) {
include_once( $mu_plugin );
}
unset( $mu_plugin );
You’ll see the standard plugins loaded on line 196:
// Load active plugins.
foreach ( wp_get_active_and_valid_plugins() as $plugin )
include_once( $plugin );
unset( $plugin );
and on line 282 you’ll see the theme’s functions.php
file loaded, along with any parent theme (if your theme is a child theme):
// Load the functions for the active theme, for both parent and child theme if applicable.
if ( ! defined( 'WP_INSTALLING' ) || 'wp-activate.php' === $pagenow ) {
if ( TEMPLATEPATH !== STYLESHEETPATH && file_exists( STYLESHEETPATH . '/functions.php' ) )
include( STYLESHEETPATH . '/functions.php' );
if ( file_exists( TEMPLATEPATH . '/functions.php' ) )
include( TEMPLATEPATH . '/functions.php' );
}
Right after that, you’ll see the following action:
do_action( 'after_setup_theme' );
Ok, now we have all the bits of the process we need to understand to properly conditionally include a plugin from a theme. So let’s do it.
Implementation
Since the theme is loading in last, we can use that to our advantage. If the plugin has been installed separately as a plugin (and not as part of the theme), we will be able to see that it is active. If it is already active, we don’t want to load it again a second time.
In our FavePersonal theme we added an additional check in the if
statement to see if the setting to activate the Social plugin was turned on. This allows us to avoid loading the plugin if the site owner has decided to disable this functionality.
There you have it – very simple, the plugin will only be loaded by the theme if it hasn’t been activated separately as a plugin, and the plugin is properly in place before init
or wp_loaded
(where plugins commonly do set-up work).
- Or perhaps a situation where someone: activates the FavePersonal theme, finds that they like the features of Social, changes to a new theme and installs/activates the Social plugin, then switches back to the FavePersonal theme (while the Social plugin is still active). ↩
This post is part of the following projects: Personal Theme, Social. View the project timelines for more context on this post.
This post is part of the following projects: Personal Theme, Social. View the project timelines for more context on this post.
@alexkingorg I’d argue more that’s the safest way to do something you shouldn’t be doing in the first place …
@EricMann Why do you think it’s a bad idea to create discrete pieces of functionality and be able to use them together?
@alexkingorg Using them together isn’t the problem. It’s *distributing* them as a bundle that I have issue with.
@EricMann @alexkingorg I’d prefer to say that the best way is to use the TGM activation class: https://t.co/Re0gHau5
@pippinsplugins @EricMann A ~2000 line code lib + ~25 lines of custom set-up code vs. a single if statement and standard WordPress APIs? (That’s before considering potential lib version dependency issues, etc.)
@alexkingorg absolutely. The biggest reason is that you don’t include the plugin files in the theme, which has huge benefits
@alexkingorg @EricMann As soon as you place a plugin in your theme, you lose the ability to auto-update it
@pippinsplugins @alexkingorg Exactly. Users need to either install it standalone or you need to release simultaneous theme updates.
@alexkingorg @pippinsplugins I’m not really a fan of the TGM activation class. Seems a bit overkill.
@EricMann @alexkingorg I can agree that it’s overkill, but I’d definitely say it’s better than dropping a plugin in a theme’s folder
@alexkingorg @pippinsplugins If I absolutely *had* to include a plugin with a theme, I’d use the method described in the blog post.
Thanks Alex, this is very helpful. I’ve been using functionality from several plugins in my theme, and I find myself using function_exists every time I call functions from my included plugins. I like this approach better – where you make sure that the plugin is active in the first place.
What do you do if they haven’t installed that plugin (or deleted it)? Is there a good place to put up a warning in the dashboard?
I would recommend creating your theme in such a way that it works elegantly with or without the plugin(s) active (that is what we did). If the functionality is required, include it in the theme and don’t provide an option to disable it (but do use the mechanism above to allow your users to install a newer version as a plugin directly) if they choose to.
The Right Way to Include a Plugin in a WordPress Theme http://t.co/TzQUXLee via @sharethis
@alexkingorg It might be worth mentioning that a plugin included from your theme can’t replace Pluggable functions.
[…] The Right Way to Include a Plugin in a WordPress Theme : alexking.org. […]
Great article today from @alexkingorg about including @WordPress plugins in your theme. http://t.co/tDfTpEqN
What if a theme has a newer version of a plugin bundled, but the site has an older version active (a version lacking a method used in the theme)?
It’s easy enough to check version or method, but should the theme deactivate the the plugin so it can load newer version? Is there another option?
This doesn’t try to solve that problem. You might consider looking for a feature and throwing an admin notice if you can’t find it.
Personally I’m a fan of progressive enhancement, i.e. bundling nothing but the absolutely-won’t-work-without-them plugins. Then you can just recommend that plugins be installed (even providing a one-click link to do it) without actually having to bundle plugins with your theme.
Then if the plugin is available, you use it to do fancy stuff and if not then you don’t. Much like how WordPress uses Javascript in the admin area.
I agree with Viper, I think themes need to work well without any other dependencies and if there is extra goodies your theme can do if a certain plugin is active, write about it, use is_plugin_active and get_plugin_data (inside is_admin), but don’t ship the plugin with your theme.
That’s just brilliant! Thanks for this super useful piece of information!
I’d go with TGM instead of this route.
https://github.com/thomasgriffin/TGM-Plugin-Activation
[…] In our FavePersonal theme we integrated the excellent Social plugin from MailChimp and included an option to turn it on or off. Most importantly we… Read More […]
[…] recently came across an article by Alex King regarding the “right way” to include a plugin into a theme. In it, Alex integrates a plugin called Social, available […]
The Right Way to Include a Plugin in a WordPress Theme | @alexkingorg http://t.co/v1u09on9
I’m going to add the +1 for Thomas’ TGM Plugin Activation… after hearing him talk at a WordCamp about it I don’t have any doubts that it’s the best way to do this.
@jthomasgriffin you’re getting some love here… but they don’t seem convinced! http://t.co/SXkTY8uh
“@wpengine: The Right Way to Include a Plugin in a WordPress Theme | @alexkingorg http://t.co/e3ZNELhI” @carlospaulinor
The Right Way to Include a Plugin in a WordPress Theme http://t.co/IAAEhGMb via @alexkingorg
[…] In our FavePersonal theme we integrated the excellent Social plugin from MailChimp and included an option to turn it on or off. Most importantly we… Read More […]
[…] the Wikipedia page. Kudos to Alex King for making me aware of this principle. Share this:TwitterFacebookLike this:LikeOne blogger likes […]