Advertisement
  1. Code
  2. Creative Coding

Developing Plugins With WordPress Boilerplates: Building a Plugin

Scroll to top
Read Time: 17 min
This post is part of a series called Developing Plugins With WordPress Boilerplates.
Developing Plugins With WordPress Boilerplates: Why Boilerplates Matter

In the first article of this series, we looked at how a boilerplate can improve your development efforts by providing a foundation off of which your project can be built.

Ideally, boilerplates should provide just enough of a framework to get started while letting you focus on the specific business logic, core need, or domain-specific code that you need to write.

Specifically, we took a look at the WordPress Widget Boilerplates and the WordPress Plugin Boilerplate. In this post, we're going to take advantage of the Plugin Boilerplate to write our own plugin in order to see how Boilerplates both lay the foundation for writing good code, and how we can use it as a starting place for our future work.


A Post Message Plugin

In this post, we're going to be building a post notification widget that allows the author to add a new post message before the content on their page. This will actually be based on a plugin that's already in the wild so you can have a point of reference once the project has been completed.

As with the rest of my tutorials, I like to plan out the project in advance so let's lay out everything that we're going to be doing:

  1. Download a copy of the WordPress Plugin Boilerplate
  2. Fill out the TODO's with the specific information for your own project
  3. Implement the code necessary to display and save information in a post meta box
  4. Check for the existence of post meta and then render it in the content
  5. Finish up the README and Localization

Overall, it's a relatively simple plugin but it should provide a solid example of how Boilerplates allow you to focus on your specific functionality all the while working within the context of WordPress best practices.


Building the Widget

1. Download the WordPress Plugin Boilerplate

In order to get started, you need to download a copy of the WordPress Plugin Boilerplate. You can do this by navigating to its GitHub page, then clicking on the 'zip' button that you see near the top of the page or by clicking here.

Next, extract the contents of the download into your plugins directory. It should initially write out a directory called plugin-boilerplate.

WordPress Plugin BoilerplateWordPress Plugin BoilerplateWordPress Plugin Boilerplate
Rename this to post-message. At this point, we're ready to begin working on the source code for the boilerplate.

2. Fill Out the TODO's in the Boilerplate

Next, open the post-message directory in your favorite IDE. The first thing that we want to do is open plugin.php and then locate all of the TODO's that exist in the code.

Out of the box, the code will look something like this:

1
2
<?php
3
/*

4
Plugin Name: TODO

5
Plugin URI: TODO

6
Description: TODO

7
Version: 1.0

8
Author: TODO

9
Author URI: TODO

10
Author Email: TODO

11
License:

12


13
  Copyright 2013 TODO (email@domain.com)

14


15
  This program is free software; you can redistribute it and/or modify

16
  it under the terms of the GNU General Public License, version 2, as

17
  published by the Free Software Foundation.

18


19
  This program is distributed in the hope that it will be useful,

20
  but WITHOUT ANY WARRANTY; without even the implied warranty of

21
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

22
  GNU General Public License for more details.

23


24
  You should have received a copy of the GNU General Public License

25
  along with this program; if not, write to the Free Software

26
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

27


28
*/
29
30
// TODO: rename this class to a proper name for your plugin

31
class PluginName {
32
33
	/*--------------------------------------------*

34
	 * Constructor

35
	 *--------------------------------------------*/
36
37
	/**

38
	 * Initializes the plugin by setting localization, filters, and administration functions.

39
	 */
40
	function __construct() {
41
42
		// Load plugin text domain

43
		add_action( 'init', array( $this, 'plugin_textdomain' ) );
44
45
		// Register admin styles and scripts

46
		add_action( 'admin_print_styles', array( $this, 'register_admin_styles' ) );
47
		add_action( 'admin_enqueue_scripts', array( $this, 'register_admin_scripts' ) );
48
49
		// Register site styles and scripts

50
		add_action( 'wp_enqueue_scripts', array( $this, 'register_plugin_styles' ) );
51
		add_action( 'wp_enqueue_scripts', array( $this, 'register_plugin_scripts' ) );
52
53
		// Register hooks that are fired when the plugin is activated, deactivated, and uninstalled, respectively.

54
		register_activation_hook( __FILE__, array( $this, 'activate' ) );
55
		register_deactivation_hook( __FILE__, array( $this, 'deactivate' ) );
56
		register_uninstall_hook( __FILE__, array( $this, 'uninstall' ) );
57
58
	    /*

59
	     * TODO:

60
	     * Define the custom functionality for your plugin. The first parameter of the

61
	     * add_action/add_filter calls are the hooks into which your code should fire.

62
	     *

63
	     * The second parameter is the function name located within this class. See the stubs

64
	     * later in the file.

65
	     *

66
	     * For more information:

67
	     * http://codex.wordpress.org/Plugin_API#Hooks.2C_Actions_and_Filters

68
	     */
69
	    add_action( 'TODO', array( $this, 'action_method_name' ) );
70
	    add_filter( 'TODO', array( $this, 'filter_method_name' ) );
71
72
	} // end constructor

73
74
	/**

75
	 * Fired when the plugin is activated.

76
	 *

77
	 * @param	boolean	$network_wide	True if WPMU superadmin uses "Network Activate" action, false if WPMU is disabled or plugin is activated on an individual blog

78
	 */
79
	public function activate( $network_wide ) {
80
		// TODO:	Define activation functionality here

81
	} // end activate

82
83
	/**

84
	 * Fired when the plugin is deactivated.

85
	 *

86
	 * @param	boolean	$network_wide	True if WPMU superadmin uses "Network Activate" action, false if WPMU is disabled or plugin is activated on an individual blog

87
	 */
88
	public function deactivate( $network_wide ) {
89
		// TODO:	Define deactivation functionality here

90
	} // end deactivate

91
92
	/**

93
	 * Fired when the plugin is uninstalled.

94
	 *

95
	 * @param	boolean	$network_wide	True if WPMU superadmin uses "Network Activate" action, false if WPMU is disabled or plugin is activated on an individual blog

96
	 */
97
	public function uninstall( $network_wide ) {
98
		// TODO:	Define uninstall functionality here

99
	} // end uninstall

100
101
	/**

102
	 * Loads the plugin text domain for translation

103
	 */
104
	public function plugin_textdomain() {
105
106
		// TODO: replace "plugin-name-locale" with a unique value for your plugin

107
		load_plugin_textdomain( 'plugin-name-locale', false, dirname( plugin_basename( __FILE__ ) ) . '/lang' );
108
109
	} // end plugin_textdomain

110
111
	/**

112
	 * Registers and enqueues admin-specific styles.

113
	 */
114
	public function register_admin_styles() {
115
116
		// TODO:	Change 'plugin-name' to the name of your plugin

117
		wp_enqueue_style( 'plugin-name-admin-styles', plugins_url( 'plugin-name/css/admin.css' ) );
118
119
	} // end register_admin_styles

120
121
	/**

122
	 * Registers and enqueues admin-specific JavaScript.

123
	 */
124
	public function register_admin_scripts() {
125
126
		// TODO:	Change 'plugin-name' to the name of your plugin

127
		wp_enqueue_script( 'plugin-name-admin-script', plugins_url( 'plugin-name/js/admin.js' ) );
128
129
	} // end register_admin_scripts

130
131
	/**

132
	 * Registers and enqueues plugin-specific styles.

133
	 */
134
	public function register_plugin_styles() {
135
136
		// TODO:	Change 'plugin-name' to the name of your plugin

137
		wp_enqueue_style( 'plugin-name-plugin-styles', plugins_url( 'plugin-name/css/display.css' ) );
138
139
	} // end register_plugin_styles

140
141
	/**

142
	 * Registers and enqueues plugin-specific scripts.

143
	 */
144
	public function register_plugin_scripts() {
145
146
		// TODO:	Change 'plugin-name' to the name of your plugin

147
		wp_enqueue_script( 'plugin-name-plugin-script', plugins_url( 'plugin-name/js/display.js' ) );
148
149
	} // end register_plugin_scripts

150
151
	/*--------------------------------------------*

152
	 * Core Functions

153
	 *---------------------------------------------*/
154
155
	/**

156
 	 * NOTE:  Actions are points in the execution of a page or process

157
	 *        lifecycle that WordPress fires.

158
	 *

159
	 *		  WordPress Actions: http://codex.wordpress.org/Plugin_API#Actions

160
	 *		  Action Reference:  http://codex.wordpress.org/Plugin_API/Action_Reference

161
	 *

162
	 */
163
	function action_method_name() {
164
    	// TODO:	Define your action method here

165
	} // end action_method_name

166
167
	/**

168
	 * NOTE:  Filters are points of execution in which WordPress modifies data

169
	 *        before saving it or sending it to the browser.

170
	 *

171
	 *		  WordPress Filters: http://codex.wordpress.org/Plugin_API#Filters

172
	 *		  Filter Reference:  http://codex.wordpress.org/Plugin_API/Filter_Reference

173
	 *

174
	 */
175
	function filter_method_name() {
176
	    // TODO:	Define your filter method here

177
	} // end filter_method_name

178
179
} // end class

180
181
// TODO:	Update the instantiation call of your plugin to the name given at the class definition

182
$plugin_name = new PluginName();

Next, we need to make each TODO plugin-specific so we'll place all instances where needed with a variation of the name of the plugin.

For example:

  • Replace the name of the plugin with Post Message
  • Replace the URL of the plugin with your URL of choice
  • Populate the name, email address, and all personal information with what works
  • Name the class Post_Message
  • Name any locale-related strings, classnames, and ID's post-message

Next, we can also remove all of the JavaScript and stylesheet calls except the admin.css styles. We'll be using this file later. Also be sure to remove the following files from the Boilerplate:

  • views/display.php
  • css/plugin.css
  • js/admin.js
  • js/plugin.js

This should leave you with the following directory structure:

WordPress Plugin Boilerplate Directory StructureWordPress Plugin Boilerplate Directory StructureWordPress Plugin Boilerplate Directory Structure

Note that this is one thing about a Boilerplate: if anything, you should want to remove something from it. If you have to add something to it that helps you get off the ground - or that's more foundational - then it may be an opportunity to improve it.

Anyway, though we're not done yet, and though we will be adding to the plugin, the file should look something like this at this point:

1
2
<?php
3
/*

4
Plugin Name: Tom McFarlin

5
Plugin URI: http://tommcfarlin.com/single-post-message/

6
Description: A simple way to add a message at the top of each of your posts.

7
Version: 1.0

8
Author: Tom McFarlin

9
Author URI: http://tommcfarlin.com/

10
Author Email: tom@tommcfarlin.com

11
License:

12


13
  Copyright 2013 Tom McFarlin (om@tommcfarlin.com)

14


15
  This program is free software; you can redistribute it and/or modify

16
  it under the terms of the GNU General Public License, version 2, as

17
  published by the Free Software Foundation.

18


19
  This program is distributed in the hope that it will be useful,

20
  but WITHOUT ANY WARRANTY; without even the implied warranty of

21
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

22
  GNU General Public License for more details.

23


24
  You should have received a copy of the GNU General Public License

25
  along with this program; if not, write to the Free Software

26
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

27


28
*/
29
30
class Post_Message {
31
32
	/*--------------------------------------------*

33
	 * Constructor

34
	 *--------------------------------------------*/
35
36
	/**

37
	 * Initializes the plugin by setting localization, filters, and administration functions.

38
	 */
39
	function __construct() {
40
41
		// Load plugin text domain

42
		add_action( 'init', array( $this, 'plugin_textdomain' ) );
43
44
		// Register admin styles and scripts

45
		add_action( 'admin_print_styles', array( $this, 'register_admin_styles' ) );
46
47
	} // end constructor

48
49
	/**

50
	 * Loads the plugin text domain for translation

51
	 */
52
	public function plugin_textdomain() {
53
		load_plugin_textdomain( 'post-message', false, dirname( plugin_basename( __FILE__ ) ) . '/lang' );
54
	} // end plugin_textdomain

55
56
	/**

57
	 * Registers and enqueues admin-specific styles.

58
	 */
59
	public function register_admin_styles() {
60
		wp_enqueue_style( 'post-message-admin', plugins_url( 'post-message/css/admin.css' ) );
61
	} // end register_admin_styles

62
63
} // end class

64
65
new Post_Message();

Next, we need to actually begin working on our core business logic.

3. Implement the Code Necessary to Display and Save Information in a Post Meta Box

First, we need to identify exactly how this is going to work:

  1. We'll display a post meta box directly under the content editor
  2. It will include a text area that allows users to provide their own content
  3. When the post is updated, it needs to save the content of the text area

So the first thing we need to do is introduce a hook and a function in order to capture the data in the post meta box. In the constructor, let's add the following line:

add_action( 'add_meta_boxes', array( $this, 'add_notice_metabox' ) );

Next, we need to define the function add_notice_metabox so that we can actually render the meta box, so let's provide that function now:

1
2
function add_notice_metabox() {
3
4
	add_meta_box(
5
		'post_message',
6
		__( 'Post Message', 'post-message' ),
7
		array( $this, 'post_message_display' ),
8
		'post',
9
		'normal',
10
		'high'
11
	);
12
13
} // end add_notice_metabox

After that, we need to write a function that's responsible for actually rendering the post content:

1
2
function post_message_display( $post ) {
3
4
	wp_nonce_field( plugin_basename( __FILE__ ), 'post_message_nonce' );
5
6
	// The textfield and preview area

7
	echo '<textarea id="post-message" name="post_message" placeholder="' . __( 'Enter your post message here. HTML accepted.', 'post-message' ) . '">' . esc_textarea( get_post_meta( $post->ID, 'post_message', true ) ) . '</textarea>';
8
9
} // end post_message_display

Above, notice that we've introduced a nonce field for security purposes. Notice also that we've given this textarea the ID of post-message so that we can easily style it using CSS, and we've given it the name post_message which will come in handy as we save the contents of the textarea to the specific post's meta data.

Finally, we've taken advantage of the esc_textarea function to make sure that we're properly encoding our data for rendering it in the textarea.

At this point, let's add some light styling using the admin.css file to give the post message a slightly different look and feel from the content:

1
2
#post-message { width: 100%; }

This should result in something like the following:

Plugin Message ExamplePlugin Message ExamplePlugin Message Example

Of course, we still aren't done. We need to actually save and retrieve the data when the user clicks on the "Publish" or the "Update" button. To do this, we need to setup a call back for saving the post data.

So the last thing that we need to do is to introduce a save_notice function. First, we'll register this in the constructor with the following code:

add_action( 'save_post', array( $this, 'save_notice' ) );

Then, we'll define the following function:

1
2
function save_notice( $post_id ) {
3
4
	if ( isset( $_POST['post_message_nonce'] ) && isset( $_POST['post_type'] ) ) {
5
6
		// Don't save if the user hasn't submitted the changes

7
		if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
8
			return;
9
		} // end if

10
11
		// Verify that the input is coming from the proper form

12
		if ( ! wp_verify_nonce( $_POST['post_message_nonce'], plugin_basename( __FILE__ ) ) ) {
13
			return;
14
		} // end if

15
16
		// Make sure the user has permissions to post

17
		if ( 'post' == $_POST['post_type'] ) {
18
			if ( ! current_user_can( 'edit_post', $post_id ) ) {
19
				return;
20
			} // end if

21
		} // end if/else

22
23
		// Read the post message

24
		$post_message = isset( $_POST['post_message'] ) ? $_POST['post_message'] : '';
25
26
		// If the value for the post message exists, delete it first. Don't want to write extra rows into the table.

27
		if ( 0 == count( get_post_meta( $post_id, 'post_message' ) ) ) {
28
			delete_post_meta( $post_id, 'post_message' );
29
		} // end if

30
31
		// Update it for this post.

32
		update_post_meta( $post_id, 'post_message', $post_message );
33
34
	} // end if

35
36
} // end save_notice

We've discussed saving the contents of post meta data at length in previous articles, so I don't want to belabor the point here, but suffice it to say that the function does the following:

  • Verifies that the user has permission to save this information
  • Deletes any existing post meta data
  • Saves the data to the associated post meta data

At this point, we're ready to test the plugin so fill out the textarea, save the data, and when the page refreshes, make sure that the data also shows up in the textarea.

If so, we're ready to move on; otherwise, make sure your code looks like the code above.

4. Render the Post Notice

Next up, we're ready to render the post message in the content. The process for doing this will be as follows:

  • Register a function with the the_content filter
  • Check for the existence of post meta data
  • Render it above the content if it's present

So let's do just that. First, let's register a function with the_content filter:

add_filter( 'the_content', array( $this, 'prepend_post_message' ) );

After that, let's setup the actual function:

1
2
function prepend_post_message( $content ) {
3
4
	// If there is a notice, prepend it to the content

5
	if ( '' != get_post_meta( get_the_ID(), 'post_message', true ) ) {
6
7
		$post_message = '<p class="post-message">';
8
		$post_message .= get_post_meta( get_the_ID(), 'post_message', true );
9
		$post_message .= '</p><!-- /.post-message -->';
10
11
		$content = $post_message . $content;
12
13
	} // end if

14
15
	return $content;
16
17
} // end prepend_post_message

Notice that the post message is contained within its own p element with its own class name so that you can easily style it however you'd like, should you want to do so.

At this point, we should have everything that we need in order to see the post message, so let's test it out. Create a post message, publish it, then view the post in your browser.

Next, remove the post content and then verify that nothing appears.

Easy enough, isn't it?

5. Finish Up the README and Localization

Finally, we need to make sure that we clean up the README and properly localize the plugin.

First, the README should contain the usual information. This is largely subjective; however, I've provided an example README below:

1
2
=== Post Message ===
3
Contributors: tommcfarlin
4
Donate link: http://tommcfarlin.com/single-post-message/
5
Tags: post
6
Requires at least: 3.4.1
7
Tested up to: 3.5
8
Stable tag: 1.0
9
License: GPLv2 or later
10
License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
12
Easily add short messages and announcements above posts. Displays in the RSS feed and on the blog.
13
14
== Description ==
15
16
Post Message is a plugin that allows you to add custom messages, announcements, and notices to individual posts. It's styled to grab the reader's attention and will render in both the browser *and* in RSS readers.
17
18
Post Message...
19
20
* Supports the use of HTML tags in the message content
21
* Is available directly under the post content editor
22
* Is fully localized and ready for translation
23
24
== Installation ==
25
26
= Using The WordPress Dashboard =
27
28
1. Navigate to the 'Add New' Plugin Dashboard
29
1. Select `post-message.zip` from your computer
30
1. Upload
31
1. Activate the plugin on the WordPress Plugin Dashboard
32
33
= Using FTP =
34
35
1. Extract `post-message.zip` to your computer
36
1. Upload the `post-messsage` directory to your `wp-content/plugins` directory
37
1. Activate the plugin on the WordPress Plugins dashboard
38
39
== Changelog ==
40
41
= 1.0 =
42
* Initial release

And finally, localization should be a cinch: Simply open the `plugin.po` file in the `lang` directory in your IDE, make sure that you change up the name of the plugin and the author information, then open it in POEdit to register the localization files.

Localizing The PluginLocalizing The PluginLocalizing The Plugin

Save your work and you're ready to go!


Conclusion

In this series, we've examined exactly what's needed to take advantage of WordPress Boilerplates, how they can help impact our workflow, and help us to focus more on the core of our project rather than re-inventing the wheel and getting up and going by repeating a lot of what's already needed.

Additionally, we built a plugin that can be downloaded for further review.

Hopefully this series has provided a case for why you should be using boilerplate code when applicable, and has helped to show how they can ease the pain of re-writing a lot of the same code in each of your projects.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.