Displaying a Plugin Activation Message in WordPress

There have been times through out several projects where I’ve wanted to display a plugin activation message (or deactivation message).

WordPress makes it relatively easy to do this using two functions:

But here’s the the thing: These two functions may not work as you expect if you’re writing your plugins using object-oriented practices.

So I thought I’d share how I go about displaying a plugin activation message in hopes that it not only helps you in your future projects, but in hopes that you guys could offer up your own code review as well.

A Plugin Activation Message

In short, a WordPress plugin activation message is a notice that appears at the top of the dashboard whenever, y’know, the plugin is activated.

Ideally, it should look as if it’s part of the native WordPress dashboard theme, and should provide the user with any information they need to know (which shouldn’t be much) about the plugin.

For example:

Plugin Activation Message

Notice the “Advanced Google Analytics” message at the top of the screen.

And the behavior of something like this should happen as follows:

  • The plugin is activated
  • The message displays
  • The plugin is deactivated
  • If the plugin is activated again, the process repeats

Simple, right?

The Class Skeleton

To set this up, here’s a basic skeleton that I use when creating class-based plugins (note that I’ll actually be including this in the next version of the Plugin Boilerplate).

// Set the version of this plugin
if( ! defined( 'ADVANCED_GOOGLE_ANALYTICS' ) ) {
	define( 'ADVANCED_GOOGLE_ANALYTICS', '1.0' );
} // end if

/**
 * @version		1.0
 */
class Advanced_Google_Analytics {

	/--------------------------------------------------------
	 * Attributes
	 --------------------------------------------------------/

	 /** Static property to hold our singleton instance */
	 private static $instance = null;

	/--------------------------------------------------------
	 * Constructor
	 --------------------------------------------------------/

	/**
	 * Initializes the widget's classname, description, and JavaScripts.
	 */
	 public function get_instance() {

		 // Get an instance of the
		 if( null == self::$instance ) {
			 self::$instance = new self;
		 } // end if

		 return self::$instance;

	 } // end get_instance

	/**
	 * Initializes the plugin' textdomain, adminitration message, and more.
	 */
	 private function __construct() {

		// Load plugin textdomain
		add_action( 'init', array( $this, 'plugin_textdomain' ) );

		// Display the admin notification
		add_action( 'admin_notices', array( $this, 'plugin_activation' ) ) ;

	 } // end constructor

	/--------------------------------------------------------
	 * Functions
	 --------------------------------------------------------/

	 /**
	  * Defines the plugin textdomain.
	  */
	 public function plugin_textdomain() {

		$domain = 'advanced-google-analytics';
		$locale = apply_filters( 'advanced-google-analytics', get_locale(), $domain );

        load_textdomain( $domain, WP_LANG_DIR . '/' . $domain . '/' . $domain . '-' . $locale . '.mo' );
        load_plugin_textdomain( $domain, FALSE, dirname( plugin_basename( __FILE__ ) ) . '/lang/' );

	} // end plugin_textdomain

	/**
	 * Saves the version of the plugin to the database and displays an activation notice on where users
	 * can access the new options.
	 */
	public function plugin_activation() {

		if( ADVANCED_GOOGLE_ANALYTICS != get_option( 'advanced_google_analytics' ) ) {

			add_option( 'advanced_google_analytics', ADVANCED_GOOGLE_ANALYTICS );

			$html = '<div class="updated">';
				$html .= '<p>';
					$html .= __( 'The Advanced Google Analytics are available <a href="admin.php?page=theme_options&tab=lobal_options">on this page</a>.', 'advanced-google-analytics' );
				$html .= '</p>';
			$html .= '</div><!-- /.updated -->';

			echo $html;

		} // end if

	} // end plugin_activation

	/**
	 * Deletes the option from the database. Optionally displays an error message if there is a
	 * problem deleting the option.
	 */
	public static function plugin_deactivation() {

		// Display an error message if the option isn't properly deleted.
		if( false == delete_option( 'dvanced_google_analytics' ) ) {

			$html = '<div class="error">';
				$html .= '<p>';
					$html .= __( 'There was a problem deactivating the Google Analytics Plugin. Please try again.', 'advanced-google-analytics' );
				$html .= '</p>';
			$html .= '</div><!-- /.updated -->';

			echo $html;

		} // end if/else

	} // end plugin_deactivation

} // end class

In this class, I’m using an implementation of the singleton pattern and then I’m constructing an instance by using the plugins_loaded hook and by making a call to the get_instance static function.

The Plugin Action Message

Notice that in the constructor, I have a hook to admin_notices that calls to plugin_activation. It’s important to understand what’s happening in this particular function:

  • First, I compare the value of the current version of the plugin as defined at the top of the file matches the advanced_google_analytics option.
  • If it does not, I’ll save this option
  • Next, I’ll build a div element containing the message
  • Then I echo it to the screen

Nothing too complicated, but it’s important to note that version tracking using an option is how this is made possible especially when it comes to deactivation

Deactivating The Plugin

When deactivating the plugin:

  • The plugin’s version should be wiped from the database
  • If the deletion fails, then we need to notify the user

The thing is, using register_deactivation_hook in the context of the class doesn’t work. As such, we have to define a static method:

/**
 * Deletes the option from the database. Optionally displays an error message if there is a
 * problem deleting the option.
 */
public static function plugin_deactivation() {

	// Display an error message if the option isn't properly deleted.
	if( false == delete_option( 'dvanced_google_analytics' ) ) {

		$html = '<div class="error">';
			$html .= '<p>';
				$html .= __( 'There was a problem deactivating the Google Analytics Plugin. Please try again.', 'advanced-google-analytics' );
			$html .= '</p>';
		$html .= '</div><!-- /.updated -->';

		echo $html;

	} // end if/else

} // end plugin_deactivation

Finally, we add the following line to the end of the file:

register_deactivation_hook( __FILE__, array( 'Advanced_Google_Analytics', 'plugin_deactivation' ) );

Notice that this is outside of the context of the class, but it’s calling the static method defined within it.

A Few Words About Deactivation

Since we’re actually only displaying the plugin activation notice when the plugin is activated, and since we’re doing so by comparing the values of the plugin’s version to what’s saved in the database, we don’t actually have to delete anything.

The reason that I’m doing this is so that I can guarantee the user will see this message each time they activate the plugin. Ultimately, it’s about trying to make sure there’s a solid user experience.

If people forget where the new options have been added, then they’ll always be guided to them whenever the plugin is activated.

Is This How You’d Do It?

Obviously, I’ve simplified the code quite a bit for purposes of this post, but this is my implementation for a project that I’ve been working on.

It’s something that I felt worth sharing with the rest of you guys as I think it’s a relatively common need, but I’m also interested in feedback on the process.

So let’s hear it.

20 Comments

Useful code.

Something I find useful, and some might not agree with it, is to automatically redirect the user to the settings page if it’s the only plugin being activated. Makes life easier on the user. Of course, if they’re activating multiple plugins at once, you wouldn’t want to do it.
Example redirect if the only plugin being activated:
http://pastebin.com/EwTRLu62

Thank you, very useful. Love the boilerplate repos as well.

I think you can call register_activation_hook inside of the class’s __construct method like so:

register_deactivation_hook( __FILE__, array( __CLASS__, 'plugin_deactivation' ) );

This is how I’ve started doing it with my plugins, and it works fine.

It’s slightly off-topic, but I would be quite interested in hearing your motivation for choosing to only really start “initializing” your plugin on the plugins_loaded action and not on loading the file.

First off, this entire comment does not apply if the following conclusion of my reasoning is incorrect: Your plugin starts its main functionality (adding callbacks to hooks (e.g. a callback for the init hook for registering a post type) and such) on plugins_loaded.

In my opinion, the proper way to approach actually starting to handle parts of your plugin such as adding a callback to the init action to register post type should be done when the plugin file is loaded. Your plugin instance, in this case, could be considered as “your plugin”, as all main functionality resides in it and it is merely a holder for all your plugin functionality.

What you’re doing is hooking into all actions and filters on plugins_loaded. In my opinion (or understanding) the plugins_loaded hook should be used for functionality that should only be done once all plugins are loaded. In your case, however, you’re actually starting setup of your plugin when the action for “all plugins loaded!” has already been called.

There are two reasons for my opinion that this is not the right approach.

First, you’re forcing yourself to hook into anything before plugins_loaded outside of your main plugin instance. So even though all main plugin functionality resides in the plugin class, you are actually unable to hook into any actions or filters that are called before plugins_loaded — even though there might be few or none whatsoever.

Second, and this one is more important: you’re not providing any proper way for other plugins to remove your callbacks from hook. The normal approach a plugin would take to remove an action or filter hook you have implemented is by hooking into plugins_loaded and calling remove_action for the callback you added. However, as your plugin only starts setting up at plugins_loaded, unhooking is not properly possible (unless, of course, you add your own action hook after setting up your plugin, for example, via do_action('myplugin_loaded');.

I believe that the on doing the plugins_loaded action, all plugins should be fully set up. They should have added their action and filter hook callbacks and performed any functionality they need to on plugin instantiation. This prevents the problems I just described, caused by setting up your plugin on plugins_loaded.

I’m looking forward to hearing about your motivation for setting up your plugin on plugins_loaded!

    First off, you bring up some valid points – but I want to mention that not every use case is applicable in the generalized sense so I’ll try to address the core issues, that is – the hooks I’ve used in the example – and then my stance on when to / when not to use certain things.

    According to The Codex, plugins_loaded fires after all of the after plugins are loaded and init actually comes after that.

    First, you’re forcing yourself to hook into anything before plugins_loaded outside of your main plugin instance. So even though all main plugin functionality resides in the plugin class, you are actually unable to hook into any actions or filters that are called before plugins_loaded — even though there might be few or none whatsoever.

    This is true for the case of this plugin, but if I were sharing generic code or I were sharing code for how to accomplish something else, then I’d follow the approach that you’ve outlined.

    I tend to be more of a pragmatist and specialist than a generalist, so sometimes you’re going to see code I share differ from other snippets I’ve shared. It’s all about the use case for me.

    I typically don’t believe in a “one size fits all” approach, because each approach carries with it its share of advantages and disadvantages and I’ll try to maximize as many advantages as I can for a given plugin, if possible.

    To your second point, this is what bleeds into the generalized sense:

    Second, and this one is more important: you’re not providing any proper way for other plugins to remove your callbacks from hook. The normal approach a plugin would take to remove an action or filter hook you have implemented is by hooking into plugins_loaded and calling remove_action for the callback you added. However, as your plugin only starts setting up at plugins_loaded, unhooking is not properly possible (unless, of course, you add your own action hook after setting up your plugin, for example, via do_action(‘myplugin_loaded’);.

    In this case, that’s correct – but I think it’s acceptable. The point of the post isn’t to display how to setup a plugin so others can add hooks or remove hooks from the plugin, but to show how to introduce an administrative message when the plugin is installed.

    Now, in some cases, this is fine. If I’m just setting up localization or other certain features of my plugin, then there’s no reason that I need to worry about others removing hooks or things from my plugin because there aren’t any that they need to remove / change or that I want them to change.

    But, to your points, there are times where this should be a consideration (or a requirement) based on the features of the plugin.

    When it comes to the source code that I share on this blog, it’s often taken from client projects that I’m working on where the requirements will be calling for a specific behavior.

    But the thing I respect and like about your comment is that you bring a general point to the surface which is an important consideration in plugin development for all of us, so thanks for that!

      Let me start by saying that I do really appreciate you taking your time to respond in-depth and take thought into writing your replies.

      This is true for the case of this plugin, but if I were sharing generic code or I were sharing code for how to accomplish something else, then I’d follow the approach that you’ve outlined.

      The reason I posted such a lengthy reply was that you talked about using the approach in the snippet you posted in the next version of the plugin boilerplate. From that, I assumed that the statement in the align directly after that, In this class, I’m using an implementation of the singleton pattern and then I’m constructing an instance by using the plugins_loaded hook and by making a call to the get_instance static function., would also apply to your plugin boilerplate project, meaning it would be in a generic plugin that you were sharing. That’s the main reason I replied: I was wondering about your motivation for using this approach for a generic piece of code.

      I do think there are no use cases where this method would be the proper approach, but I would gladly hear examples of approaches where it would — even from a pragmatists point of view. It’s quite interesting, as I do believe in a one-size-fits-all solution for this problem. I do believe that, in this case, there is a “way to go” that works for all plugins. This is such a small part of a bigger issue (plugin code structure — classes, static classes, etc.), for which I do believe that there is no one-size-fits-all solution.

      In my opinion, the proper approach to adding actions and filters is always to do so on loading the plugin. My view is that adding action and filter hooks is actually not really part of the plugins functionality: it just states when functionality should be called. I see very few advantages of adding actions and filters (that have no conditionals that can not be accessed on plugin loading) on plugins_loaded (or any other action hook for that matter), and ever the more disadvantages.

      However, I do get the feeling that our opinions might not differ that much on this subject, which is a good thing from my perspective. Again, thanks for your elaborations!

      And a quick note about the init and plugins_loaded hooks: it does indeed, it would be very strange, even unthinkable, otherwise — that would mean that plugins might be loaded after init, and that plugins could therefore not hook into init.

        The reason I posted such a lengthy reply was that you talked about using the approach in the snippet you posted in the next version of the plugin boilerplate.

        Gotcha! Yes, your point is spot on with that and I should’ve been clearer about that.

        All I meant was that I was looking to implement the singleton pattern in the next person of the boilerplate – I’m not committing to using the plugins_loaded hook (and hopefully that’s evident from our discussion :)).

        I do think there are no use cases where this method would be the proper approach, but I would gladly hear examples of approaches where it would — even from a pragmatists point of view.

        Fair enough: In the simplest case, I’d say that it’s when I have a plugin that has no pluggable actions or filters and is not concerned with manipulating any external plugins, either.

        But with that said, I’m also curious as to see how you’d stub out a plugin using the singleton pattern with hooks organized how you see things best fit (doing so here, in a gist, or in something you’ve already done). I mention this simply as a point of education – not as a point of debate.

        However, I do get the feeling that our opinions might not differ that much on this subject, which is a good thing from my perspective. Again, thanks for your elaborations!

        Likewise – I always enjoy good discussion on this stuff!

          I’m not a big fan of implementing plugins using a main class with the singleton (in just about all cases), but that’s an entirely different spectrum ;-).

          I would take the following approach, provided that it shouldn’t rely on PHP 5.3 to function, for which I might implement an extensible Singleton class. You can find the Gist here:
          https://gist.github.com/Jepps/5452624

          The files should speak for themselves, but I’d be happy to answer any questions you may have!

          My opinion on the matter of using singletons for plugins is rapidly changing… It might not be such as bad idea after all. All right, I’ll get back to this later, time for some more thinking.

Hi, I tried this approach but I have a small problem.

My main plugin file is a class, in the constructor I use the register_activation_hook(__FILE__, array( $this, 'bpam_install' )); that triggers the function bpam_install.

In that function I wish to do all the initial settings that involves to create a table and a call to the add_role() plus some capabilities assignments.

During all this operations I’d like to catch any error and display it as a notice.

In my first attempt I’ve added add_action('admin_notices', array( $this, 'bpam_install' )); and in the install function just echo a message (with no actual tests). It works but, it makes display a further notice saying that the plugin generated n unexpected characters during the activation. How do I prevent this?

Thanks

    Without seeing your code, it’s harder to diagnose (and I don’t expect an entire plugin to be shared in a comment ;)); however, the `register_activation_hook` has to be defined outside of the class.

    So, for example, you’ll need to declare a static method within your class – say it’s named Example_Class – and then do something like this:

    `register_activation_hook( __FILE__, ‘Example_Class::bpam_install’ );`

    Also, you may not need to prefix your functions with `bpam` since they’ll all be encapsulated within a class.

    Anyway, hope this helps!

      I’ll give it a try and get back to you. Thanks for the prompt answer btw ;)

      so, I tries… the static method works, outside the class I do

      register_activation_hook(__FILE__, 'Bpam::bpam_install');
      new Bpam();

      to activate trigger the install method when the plugin is activated and then I instantiate the class as in its constructor I have a couple of hooks defined:

      class Bpam {
      function __construct() {
      add_action( 'admin_menu', array( $this, 'admin_menu' ) );
      add_action( 'wp_ajax_get_moderators_list_hook', array( $this, 'get_moderators_list' ) );
      }

      The problem is that from the install method I can’t manage notices, I tried to add an action with no success. I even tried to set a static variable to echo from a function triggered by an add_action that listens admin_notices added to the constructor, the function is triggered but the variable is empty…

      any clue?

        Without knowing much more about the project, I’d recommend not trying to tie the `admin_notices` hook to your installation method – it needs to be within the constructor, like you’ve mentioned.

        As var as variables are concerned, which one is not being set?

Leave a Reply

Name and email address are required. Your email address will not be published.

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>