Tell Your Users Where Your Plugin Puts Its Menu Pages

There’s one thing that always annoys me when installing new plugins: No matter how popular or obscure the plugin, the first few seconds (or minutes) are usually wasted on trying to figure out where it put its settings page.

We’ve all been there. Trawling the admin menu, looking for that elusive link that will actually let us use the awesome new plugin we just installed. Granted, it usually takes only a few moments to find it, but it’s still annoying and unnecessary. Why not just tell the user how to get to right admin page? (Some plugins do, but in my experience they’re the exception, not the rule.)

If you’re a plugin developer, you can make your users’ life a little easier, and the first-run experience a little smoother, by displaying a small notification that explains how to get to the plugin’s admin page. Example:

In this post, I’ll show you how you can add a notice like that to your plugin. First, I’ll give you the code, and then I’ll explain how it works.

The Code

<?php
function example_display_menu_notice() {
	//Replace these with your plugins' values.
	$pageSlug = 'example-settings-page';
	$parentSlug = 'options-general.php';
	$noticeOption = 'example_show_menu_notice';

	//Only show the notice to users who can access the menu page.
	//Replace 'manage_options' with the appropriate capability.
	if ( !current_user_can('manage_options') ) {
		return;
	}

	$showNotice = get_option($noticeOption, null);

	//Hide the notice when the user first visits the plugin page
	//or when they dismiss it manually.
	$isPluginPage = $GLOBALS['pagenow'] == $parentSlug && $GLOBALS['plugin_page'] == $pageSlug;
	$isNoticeDismissed = isset($_GET[$noticeOption]) && $_GET[$noticeOption] == 'hide';
	if ( ($isPluginPage || $isNoticeDismissed) && ($showNotice !== false) ) {
		$showNotice = false;
		update_option($noticeOption, false);
	}

	if ( $showNotice ) {
		$dismissUrl = add_query_arg($noticeOption, 'hide');
		$dismissUrl = remove_query_arg(
			array('message', 'settings-updated', 'activate', 'deactivate'), 
			$dismissUrl
		);

		printf(
			'<div class="updated">
				<p>
					Tip: Example Plugin\'s admin page is at
					<a href="%s">Settings -> Example Settings</a>.
				</p>
				<p><a href="%s">Dismiss</a></p>
			 </div>',
			//Replace this with your admin page URL.
			esc_attr(admin_url('options-general.php?page=example-settings-page')),
			esc_attr($dismissUrl)
		);
	}
}
add_action('admin_notices', 'example_display_menu_notice');

//Enable the notice when the plugin is activated.
function example_enable_menu_notice() {
	//Only enable it not already enabled/disabled.
	if ( get_option('example_show_menu_notice', null) === null ) {
		update_option('example_show_menu_notice', true);
	}
}
register_activation_hook(__FILE__, 'example_enable_menu_notice');

//Clean up the DB option upon uninstallation.
function example_delete_notice_flag() {
	delete_option('example_show_menu_notice');
}
register_uninstall_hook(__FILE__, 'example_delete_notice_flag');
?>

How It Works

Okay, now lets examine the code.

We’ll start with example_display_menu_notice(). This is the function that will display the notice. At the top of the function, we have a bunch of variable declarations:

//Replace these with your plugins' values.
$pageSlug = 'example-settings-page';
$parentSlug = 'options-general.php';
$noticeOption = 'example_show_menu_notice';

Here we just set up some variables that will be used later in the script.

  • $pageSlug – the slug you used when registering your menu page.
  • $parentSlug – the slug of the parent menu. For example, if your page is part of the the “Settings” menu, then the parent slug will “options-general.php”, and if it’s under “Posts” then the parent slug will be “edit.php”.
  • $noticeOption – the DB option we’ll use to store the notice state (visible or hidden).
if ( !current_user_can('manage_options') ) {
	return;
}

Naturally, we only want users who can actually access our menu page to see the notice. So we check if the current user has the necessary capability and bail out of the function if they don’t.

$showNotice = get_option($noticeOption, null);

//Hide the notice when the user first visits the plugin page
//or when they dismiss it manually.
$isPluginPage = $GLOBALS['pagenow'] == $parentSlug && $GLOBALS['plugin_page'] == $pageSlug;
$isNoticeDismissed = isset($_GET[$noticeOption]) && $_GET[$noticeOption] == 'hide';
if ( ($isPluginPage || $isNoticeDismissed) && ($showNotice !== false) ) {
	$showNotice = false;
	update_option($noticeOption, false);
}

This block of code determines whether we should show the notice or not. First, we check the DB option I mentioned later. It can either be true (= show the notice) or false (= don’t show the notice). If the option doesn’t exist yet, we’ll get a null instead. More on that later.

Next, we check if the user is currently viewing our menu page ($isPluginPage). If they are, then they obviously know where the page is and there’s no need to display the menu notice any more. To determine what the current page is, we check two global variables that are set by WordPress:

  • $pagenow – the current file. For example, this will be “options-general.php” if we’re on the “Settings -> General” page or any plugin page that’s part of the “Settings” menu.
  • $plugin_page – the slug of the currently open plugin page. If the current page was not created by a plugin, this will be null instead.

After that, we check if the user has clicked the “Dismiss” link. As we’ll see later, this link has a URL similar to “current-page-here?example_show_menu_notice=hide”. So we just check if the right query argument is there and is set to “hide”.

Finally, if either of the above conditions is met, we check if the notice isn’t already disabled ($showNotice !== false) and hide it by setting $showNotice and our DB option to false.

The second half of example_display_menu_notice() is what actually displays the notification message. After making sure that yes, we should show the notice, we build the URL for the “Dismiss” link:

$dismissUrl = add_query_arg($noticeOption, 'hide');
$dismissUrl = remove_query_arg(array('message', 'settings-updated', 'activate'), $dismissUrl);

As I mentioned before, the link is basically the current URL + an extra query argument that we set to “hide”.

We also need to remove a couple of arguments from the URL to avoid confusing the user. WordPress uses these arguments throughout the Dashboard to indicate the result of various operations like updating posts, saving settings, activating plugins and so on. If we leave them in and the user clicks the “Dismiss” link on a page that’s currently showing one of those messages, they’ll get the same message again, which can be misleading.

And finally, there’s this big printf() call:

printf(
	'<div class="updated">
		<p>
			Tip: Example Plugin\'s admin page is at
			<a href="%s">Settings -> Example Settings</a>.
		</p>
		<p><a href="%s">Dismiss</a></p>
	 </div>',
	//Replace this with your admin page URL.
	esc_attr(admin_url('options-general.php?page=example-settings-page')),
	esc_attr($dismissUrl)
);

This is what actually outputs the notice. Make sure to customize the message to suit your plugin, and replace options-general.php?page=example-settings-page with the URL of your menu page (relative to /wp-admin/).

To make the message show up in the right place, we’ll hook the display function to the admin_notices action. This action is run right before the main content of each Dashboard page, and that’s where WordPress displays most of its notifications and error messages.

add_action('admin_notices', 'example_display_menu_notice');

Next, we have two considerably simpler functions. The first, example_enable_menu_notice(), enables the notification when the plugin is activated:

function example_enable_menu_notice() {
	//Only enable it not already enabled/disabled.
	if ( get_option('example_show_menu_notice', null) === null ) {
		update_option('example_show_menu_notice', true);
	}
}
register_activation_hook(__FILE__, 'example_enable_menu_notice');

If the example_show_menu_notice option does not exist, we set it to true.

Now you might ask – why go to the trouble of explicitly enabling the notice upon activation, instead of just treating it as visible-by-default and hiding it when the user dismisses it or visits our page? The answer is this: we don’t want to annoy our existing users.

It’s a good idea to tell new users how to get to your plugin’s admin page(s). But if we do it to a user who just upgraded from an older version of the plugin, they’ll probably be annoyed because we’re telling them stuff they already know.

By enabling the notice on activation, we avoid this problem. The activation hook only runs when a plugin is installed or manually deactivated and re-activated. It does not run when you upgrade a plugin.

Finally, we want our plugin to be a good citizen and clean up after itself. The second short function is example_delete_notice_flag(), and it does just that by removing our DB option when the plugin is uninstalled.

function example_delete_notice_flag() {
	delete_option('example_show_menu_notice');
}
register_uninstall_hook(__FILE__, 'example_delete_notice_flag');
?>

This concludes today’s tutorial. If you have any questions or other feedback, leave a comment below.

Related posts :

Leave a Reply