Per Søderlind

I code for fun

  1. general
  2. WordPress plugin template

WordPress plugin template

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.


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:

[sourcecode language=”php” gutter=”false”]
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

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:

[sourcecode language=”php” gutter=”false”]
function {plugin_slug}_script() {
wp_enqueue_script(‘jquery’); // other scripts included with WordPress:
wp_enqueue_script(‘jquery-validate’, ‘’, 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:

[sourcecode language=”javascript” gutter=”false”]
<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."
/* ]]> */

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:

[sourcecode language=”php” gutter=”false”]
if (isset($_GET[‘{plugin_slug}_javascript’])) {

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

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


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:

[sourcecode language=”php” gutter=”false” highlight=”5″]
function {plugin_slug}_script() {
if (is_admin()){ // Only run when in wp-admin. Other conditional tags at
wp_enqueue_script(‘jquery’); // other scripts included with WordPress:
wp_enqueue_script(‘jquery-validate’, ‘’, 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.


When .LESS is more


radial navigation in html5


  1. Jack

    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. Eric

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


    } //End Class
    } //End if class exists statement

  3. Andrea


    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).


    • PerS

      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).

      • Andrea

        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);

    • PerS

      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!

    • PerS

      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.

    • PerS

      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.

  7. Another improvement: the shortcode template echoes the result, where it should return it. This results in the shortcode being rendered prior to the post, not in the place where it is put.

    Fix is:
    function shortcode($atts){
    // end of plugin code :
    $output_string = ob_get_contents();
    return $output_string;

    • PerS

      Shortcode template ? Where?

      Btw, I plan to update the generator and I’m (very) open for suggestions :)

  8. My bad, it was me who added the shortcode function. °-)

    but indeed, since you ask for suggestions, why not offer it as an option?

    also: shouldn’t all the add_action() calls be made in the init() function, instead of the construct?

    • PerS

      Agree, will do :)

      Btw, I always return values when I do shortcodes. Here’s an example taken from my lorem shortcode

      function pers_lorem_func($atts,$content=null) {
      extract(shortcode_atts(array( // default values
      ‘p’ => 5,
      ‘l’ => 3,
      ‘align’ => ‘right’
      ), $atts));

      if ($this->doparamcheck) { // do param testing
      $p = strval(intval($p));
      $l = strval(intval($l));
      $align = (strcasecmp($align, "left") == 0) ? ‘ style="float:left;"’ : ‘ style="float:right;"’ ; // align the embedded image

      // create lipsums
      $paragraphs = str_repeat(‘<p>%s</p>’, $p);
      $lipsums = preg_split(‘/([.?!])/’, $this->str_lipsum, -1, PREG_SPLIT_NO_EMPTY);
      $j = 0;
      for ($i = 0; $i < $p ; $i++) {
      if ($j + $l > count($lipsums)) $j=0;
      $lines[] = trim(implode(‘.’,array_slice($lipsums, $j, $l))) . ‘.’;
      $j = $j + $l;

      if ($content) { // support embedded shortcode
      $ret = ‘<div class="lorem-container">’;
      $ret .= sprintf(‘<div %s>’,$align);
      $ret .= do_shortcode($content);
      $ret .= ‘</div>’;
      $ret .= vsprintf($paragraphs,$lines);
      $ret .= ‘<div style="clear:both;overflow: auto"></div>’;
      $ret .= ‘</div>’;
      } else {
      $ret = ‘<div class="lorem-container">’;
      $ret .= vsprintf($paragraphs,$lines);
      $ret .= ‘</div>’;
      return $ret;

  9. dameer

    Great stuff, thanks a bunch!
    How to add plugin options page out of Settings in Dashboard? For example below Settings section?

  10. dameer

    I’ve tried something like (according to WP codex):
    add_menu_page(__(‘Test Toplevel’, $this->localizationDomain), __(‘Test Toplevel’, $this->localizationDomain), ‘manage_options’, ‘plugin-name-top-level-handle’, ‘admin_options_page’ );
    …but it ends up with WP error:

    Warning: call_user_func_array() expects parameter 1 to be a valid callback, function 'admin_menu_link' not found or invalid function name in /Applications/XAMPP/xamppfiles/htdocs/cudazi/wp-includes/plugin.php on line 403

    I didn’t change anything in “add_action”, constructor function!

  11. dameer

    Sorry PerS, I’ve figured out. It should be:
    add_menu_page(__(‘Test Toplevel’, $this->localizationDomain), __(‘Test Toplevel’, $this->localizationDomain), ‘manage_options’, ‘plugin-name-top-level-handle’, array(&$this,’admin_options_page’) );

    …and working just fine. Thanks for all your efforts anyway!

  12. Java

    Hi, there. I just don’t know pretty much about wordpress, codes o templates. I need a plugin for live audio, a plugin that allows me to take an external audio source (the output of our on air mixer) and stream it on my website?
    Would you help me, please

Leave a Reply

Powered by WordPress & Theme by Anders Norén