soderlind.no

I code for fun

WordPress plugin template

| 14 Comments

UPDATE: As of WordPress 3.3, you shouldn’t use the wp_print_styles hook, I’ve updated the Adding JavaScript section below to reflect this change.

You can create a personalized plugin template by using my WordPress Plugin Template Creator

When I  rewrote my WP-DenyHost plugin, I wanted to do it as fast as possible. Instead of reinventing the wheel (again), I googled after a plugin template and found a very good one at Pressography. It had most of what I needed in a plugin template.

Modifications to the Pressography plugin template

I’ve made some changes to the Pressography plugin template and I have explained them below. Before you continue to read this article, you should view the video at Pressography, it presents the idea behind the plugin template and how it works. You get the full source of my modified plugin template by using my WordPress Plugin Template Creator

Being able to use new functionality in WordPress, I’ve decided that my plugins will only support WordPress one minor version lower than the current i.e. the current is 2.9 and hence my plugins and this plugin template will only supports WordPress 2.8 and later.

plugins_url()

WordPress 2.8 extended the plugins_url() function which makes it easy to find the plugin location. In the plugin template constructor, I use plugins_url() to find the languages files and the plugin itself:

function __construct(){
	//Language Setup
	$locale = get_locale();
	$mo = plugin_dir_path(__FILE__) . 'languages/' . $this->localizationDomain . '-' . $locale . '.mo';
	load_textdomain($this->localizationDomain, $mo);

	//"Constants" setup
	$this->url = plugins_url(basename(__FILE__), __FILE__);
	$this->urlpath = plugins_url('', __FILE__);

	//Initialize the options
	$this->getOptions();

	//Actions
	add_action("admin_menu", array(&$this,"admin_menu_link"));
	add_action('admin_enqueue_scripts', array(&$this,'{plugin_slug}_script')); // or wp_enqueue_scripts or login_enqueue_scripts
	add_action("init", array(&$this,"{plugin_slug}_init"));
}

Adding JavaScript

I’ve done it and I still see plugin authors adding scripts using the wp_head function. There’s only one correct way of adding scripts, and that’s using wp_enqueue_script via one of the script hooks (admin_print_scripts, wp_print_scripts wp_enqueue_scripts, login_enqueue_scripts and admin_enqueue_scripts). wp_enqueue_script makes sure that the script loads in the correct order and that a script is only loaded once (e.g. If another plugin has already loaded jQuery, my plugin will not load jQuery, but use the script already loaded).

I’ve added the wp_print_scripts admin_enqueue_scripts hook to the plugin template (see the constructor above). Use the wp_enqueue_scripts or login_enqueue_scripts if you want to load the script on the front-end or login page. The wp_print_scripts admin_enqueue_scripts hook adds the  {plugin_slug}_script function in which  I enqueue the scripts:

function {plugin_slug}_script() {
	wp_enqueue_script('jquery'); // other scripts included with WordPress: http://tinyurl.com/y875age
	wp_enqueue_script('jquery-validate', 'http://ajax.microsoft.com/ajax/jquery.validate/1.6/jquery.validate.min.js', array('jquery'));
	wp_enqueue_script('{plugin_slug}_script', $this->url.'?{plugin_slug}_javascript', array('jquery-validate')); // load embedded javascript
	wp_localize_script( '{plugin_slug}_script', '{plugin_slug}_lang', array(
		'required' => __('Please enter a number.', $this->localizationDomain),
		'number'   => __('Please enter a number.', $this->localizationDomain),
		'min'	   => __('Please enter a value greater than or equal to 1.', $this->localizationDomain),
	));
}

wp_localize_script, in the code above, creates a JavaScript object that passes the language strings to the embedded JavaScript:

<script type='text/javascript'>
/* CDATA[ */
var {plugin_slug}_lang = {
	required: "Please enter a number.",
	number: "Please enter a number.",
	min: "Please enter a value greater than or equal to 1."
};
/* ]]> */
</script>

Embedding JavaScript

When I write my plugins I like to keep the code in one file, that’s why I also embed the JavaScript code:

if (isset($_GET['{plugin_slug}_javascript'])) {

	Header("content-type: application/x-javascript");
	echo<<<ENDJS
/**
* @desc {Full Plugin Name}
* @author {Author} - {URL}
*/

jQuery(document).ready(function(){
	jQuery("#{plugin_slug}_options").validate({
		rules: {
			{plugin_slug}_option1: {
				required: true,
				number: true,
				min: 1
			}
		},
		messages: {
			{plugin_slug}_option1: {
				// the {plugin_slug}_lang object is define using wp_localize_script() in function {plugin_slug}_script()
				required: {plugin_slug}_lang.required,
				number: {plugin_slug}_lang.number,
				min: {plugin_slug}_lang.min
			}
		}
	});
});

ENDJS;
}

Note: If you prefer to keep your JavaScript code in a separate file, you can add it by adding the following to {plugin_slug}_script:

function {plugin_slug}_script() {
	if (is_admin()){ // Only run when in wp-admin. Other conditional tags at http://codex.wordpress.org/Conditional_Tags
		wp_enqueue_script('jquery'); // other scripts included with WordPress: http://tinyurl.com/y875age
		wp_enqueue_script('jquery-validate', 'http://ajax.microsoft.com/ajax/jquery.validate/1.6/jquery.validate.min.js', array('jquery'));
		wp_enqueue_script('{plugin_slug}_script', $this->urlpath.'/myscript.js', array('jquery)); //load your script
		wp_localize_script( '{plugin_slug}_script', '{plugin_slug}_lang', array(
			'required' => __('Please enter a number.', $this->localizationDomain),
			'number'   => __('Please enter a number.', $this->localizationDomain),
			'min'	   => __('Please enter a value greater than or equal to 1.', $this->localizationDomain),
		));
	}
}

WordPress Plugin Template Creator

While writing this article, I decided to create a tool that will create a personalized plugin template. The WordPress Plugin Template Creator takes your input, creates the necessary slugs and returns a personalized plugin template you can build your own plugin with.

14 Comments

  1. Thank you thank you. This is just what I needed. The old Pressography one was giving some errors in the lastest version of WordPress on my local machine and this did the trick. So great that you upgraded a great template.

  2. In the plugin generator, In function admin_options_page, I believe the HTML is missing the closing after the closing “” element …

    multisite_moderation
    ….

    <?php
    }
    } //End Class
    } //End if class exists statement

  3. Eric,

    thank you for your tool!

    I was getting mad trying to retrieve the Pressography template and eventually I’ve realized that the side is gone :(

    I was wondering if you could also provide an empty template (Like Pressography used to do) that we can customize without the online tool

    Or instead, add the right places where to call, from the plugin, external action and filters (e.g.: WP, other theme’s or plugins hooks).

    Thanks.

    • I guess you mean PerS and not Eric ;)

      The code generated is more or less the same as the empty template you could get from Pressography (didn’t know the page is gone). You’ll find reference to where to call external actions etc (in __construct).

      • Oops… sorry PerS :)

        Yes, Pressography is gone and I wonder why: it was quite useful.

        About where to call external actions, I’ve found myself just a couple of minutes ago: thank you!

        Anyway I’ve ended up using the online tool and customized/commented for my needs.

        Thanks again for this tool!

  4. hi, and thanks!

    Slight improvement:

    $this->pluginPath   =  dirname(__FILE__);
    $this->pluginUrl   =  WP_PLUGIN_URL . '/'.basename($this->pluginPath);
    

    `

    • Nah .. this is what plugins_url() does, and it also finds the url path when a plugin is in WPMU_PLUGIN_URL.

      I’m a big fan of using core WordPress functions when possible :)

  5. Well, that bit of code generated by your script did not work for me and that is how i fixed it.

    Another issue comes up with the translation. I put my languages files in a “languages” folder inside the plugin’s folder.
    This is how i managed to have it work:

    $locale = get_locale();
    $mo= plugin_dir_path(__FILE__).’languages/’.$this->localizationDomain . “-”.$locale.”.mo”;
    load_textdomain($this->localizationDomain, $mo);

    so: not the plugin_url, but the plugin_dir_path.

    Thank you!
    a.

    • Interesting. Is your plugin in /wp-content/plugins/NN or do you use a custom path?

      Do you have the error log ? That is, where is my routine pointing compared to where the plugin really is ?

      I really like to know so I can understand why this bug happens :)

  6. Is your plugin in /wp-content/plugins/NN or do you use a custom path?
    > Yes.

    Do you have the error log ? That is, where is my routine pointing compared to where the plugin really is ?
    > Unfortunately not. It does not produce an error apart from the fact that the .mo file was not loaded.

    • Thank you Alexandre for pointing out this bug. I’ve updated the code generator and the article above with your suggestion:

      
      $mo = plugin_dir_path(__FILE__) . 'languages/' . $this->localizationDomain . '-' . $locale . '.mo';
      
      • Great, thanks a lot!

        Another idea: alongside the widget and the shortcode, maybe add the basic function that enables template tags ?

        Super useful work you’ve done.

Click on a tab to select how you'd like to leave your comment

Leave a Reply

Required fields are marked *.

*