├── styles └── style.css ├── .gitignore ├── pages ├── admin_main.php └── admin_options_page.php ├── scripts └── script.js ├── README ├── widgets └── my-widget.php └── plugin-boilerplate.php /styles/style.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .settings 3 | -------------------------------------------------------------------------------- /pages/admin_main.php: -------------------------------------------------------------------------------- 1 | 5 |
6 |

Plugin boilerplate admin page

7 |

Here is where you will define your own admin page

8 |
9 | -------------------------------------------------------------------------------- /pages/admin_options_page.php: -------------------------------------------------------------------------------- 1 | 5 |
6 |

Plugin boilerplate admin sub page

7 |

Here is where you will define your own admin sub page

8 |
9 | -------------------------------------------------------------------------------- /scripts/script.js: -------------------------------------------------------------------------------- 1 | jQuery(document).ready(function($) { 2 | // $() will work as an alias for jQuery() inside of this function 3 | }); 4 | 5 | (function($) { 6 | // Place your javascript in here 7 | 8 | // This is a self executing anonymous function which will keep your scripts from using up 9 | // names in the global name space, and will prevent collisions with other scripts. 10 | 11 | // $() will work as an alias for jQuery() inside of this function 12 | })(jQuery); 13 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is a boilerplate of a WordPress plugin which contains a number of different things that your plugin might want to implement, like localization, adding scripts and styles, custom post types, custom taxonomies and shortcodes. 2 | 3 | The ambition of this plugin is to be a set of best practices on how to solve common tasks when writing a plugin for WordPress, your input and suggestions are welcome. 4 | 5 | Usage instructions: 6 | 7 | 1. Search for @todo in the file and change the names and stuff so that your plugin will be unique. 8 | 2. In the __construct( ) function; uncomment the different types of functionality that you need. 9 | 2a. (optional) If needed, uncomment the register_activation_hook( ) call 10 | 2b. (optional) If needed, uncomment the call to install_db( ) from the install( ) function and define your tables 11 | 3. Be a good open source developer; post bug reports, fixes, suggestions, ideas on better ways to handle stuff in the plugin. 12 | 13 | Thanks! 14 | 15 | @emomilol 16 | @mikaeljorhult 17 | @rickard2 18 | -------------------------------------------------------------------------------- /widgets/my-widget.php: -------------------------------------------------------------------------------- 1 | __("Plugin boilerplate description", $this->plugin_name)); 13 | parent::WP_Widget(false, __('Plugin boilerplate', $this->plugin_name), $options); 14 | } 15 | 16 | /** 17 | * Logic for handling updates from the widget form 18 | * @param array $new_instance 19 | * @param array $old_instance 20 | * @return array 21 | */ 22 | function update($new_instance, $old_instance) { 23 | // Insert update logic here, and check that all the values in $new_instance are valid for your particular widget 24 | 25 | return $new_instance; 26 | } 27 | 28 | /** 29 | * Function for handling the widget control in admin panel 30 | * @param array $instance An array of current values for this instance 31 | * @return void 32 | */ 33 | function form($instance) { 34 | // Get stored preferences, remember to escape them! 35 | $title = strlen($instance['title']) > 0 ? esc_attr($instance['title']) : esc_attr($this->default_title); 36 | 37 | ?> 38 |
39 |
40 | 0 ? $title : $this->default_title; 59 | 60 | echo $before_widget; 61 | echo $before_title . $title . $after_title; 62 | 63 | // Insert your widget markup here 64 | 65 | echo $after_widget; 66 | } 67 | } 68 | 69 | // register Widget 70 | // @todo rename to your widgets name 71 | function plugin_boilerplate_widget_register() { 72 | register_widget( 'plugin_boilerplate_widget' ); 73 | } 74 | 75 | // @todo rename to the function name 3 lines above 76 | add_action( 'widgets_init', 'plugin_boilerplate_widget_register' ); -------------------------------------------------------------------------------- /plugin-boilerplate.php: -------------------------------------------------------------------------------- 1 | 33 | * @todo Add your name here 34 | * @todo Change class name to something more appropriate 35 | */ 36 | class WP_Plugin_Boilerplate { 37 | /** 38 | * This value will be used for 39 | * - the gettext text domain 40 | * - the sub-folder when adding scripts and styles 41 | * - the handle for scripts and styles 42 | * Please make sure that this value corresponds to the folder name where your plugin resides. 43 | * @var string 44 | * @todo Change this value to your plugin name 45 | */ 46 | private $plugin_name = "plugin_boilerplate"; 47 | 48 | /** 49 | * The constructor is executed when the class is instatiated and the plugin gets loaded. 50 | */ 51 | function __construct() { 52 | // Uncomment any of these calls to add the functionality that you need. 53 | 54 | //add_action('init', array($this, 'init_custom_post_types'), 20); 55 | //add_action('init', array($this, 'init_localization'), 20); 56 | //add_action('init', array($this, 'init_scripts'), 20); 57 | //add_action('init', array($this, 'init_styles'), 20); 58 | //add_action('init', array($this, 'init_shortcodes'), 20); 59 | //add_action('admin_menu', array($this,'init_admin_menu')); 60 | } 61 | 62 | /** 63 | * This function is executed when the plugin is activated. To activate it just uncomment 64 | * the line at the bottom of this file with the register_activation_hook( ... ) function 65 | * @return void 66 | */ 67 | static function install() { 68 | // Uncomment this if you need database tables. 69 | //self::install_db(); 70 | 71 | // Perform your activation tasks in here. 72 | } 73 | 74 | /** 75 | * This function is executed from install( ) and is used if you need database tables. 76 | * @return void 77 | */ 78 | static function install_db() { 79 | global $wpdb; 80 | 81 | /** 82 | * This value represent the current version of the layout of the database tables. This value 83 | * needs to be increased every time you change something in your database layout. 84 | * @var integer 85 | */ 86 | $db_version = 1; 87 | 88 | /** 89 | * To create tables, dbDelta from wp-admin/includes/upgrade.php is needed 90 | */ 91 | require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); 92 | 93 | /** 94 | * @todo Change myplugin to your plugin name 95 | */ 96 | $option_name = 'myplugin-db-version'; 97 | $current_version = null; 98 | $current_version = get_option($option_name); 99 | 100 | // Check if the latest version is already installed 101 | if($current_version == $db_version) { 102 | return; 103 | } 104 | 105 | // In this example two tables is defined, 'table1' and 'table2'. This example tries 106 | // to illustrate how the tables gets upgraded. The layout of the tables is currently at 107 | // version three and two upgrade routines are defined for upgrading the tables from the 108 | // first and second version. Note that the changes in the database layout needs to be added 109 | // to the $structure[ ] array _and_ to the $upgrade[ ] array. $structure[ ] is used when the 110 | // plugin is installed on a new system and $upgrade[ ] is used for upgrading. 111 | 112 | // Define tables 113 | $tables = array('table1', 'table2'); 114 | $structure = array( 115 | 116 | // Definition for 'table1' 117 | "(`id` int(11) unsigned NOT NULL auto_increment, 118 | `field1` varchar(255) NOT NULL, 119 | `field2` varchar(255) NOT NULL, 120 | `field3` varchar(255) default NULL, 121 | `field4` tinyint(3) unsigned NOT NULL default '0', 122 | `field5` timestamp NOT NULL default CURRENT_TIMESTAMP, 123 | PRIMARY KEY (`id`) )", 124 | 125 | // Definition for 'table2' 126 | "(`id` int(11) unsigned NOT NULL auto_increment, 127 | `field1` varchar(255) NOT NULL, 128 | `field2` varchar(255) NOT NULL, 129 | `field3` varchar(255) NOT NULL, 130 | PRIMARY KEY(`id`) )" 131 | ); 132 | $upgrade = array(); 133 | 134 | // Routines for upgrading, the structure is 135 | // $upgrade[ from db version ][ table name ] 136 | // Routine should include %s for table prefix. 137 | // Upgrade routines are applied in order from current version to the newer version. Routines for upgrading 138 | // only has to be defined once. 139 | 140 | // Example of an upgrade routine which will add two fields to table1 when the plugin gets upgraded from 141 | // DB version 1 to a later version. 142 | $upgrade[1] = array("table1" => "ALTER TABLE %stable1 ADD `field3` varchar(255) default NULL, ADD `field4` tinyint(3) unsigned NOT NULL default '0'"); 143 | 144 | // Example of an upgrade routine which will add one field to table1 and one field to table2 when the plugin 145 | // gets upgraded from DB version 2 to a later version. 146 | $upgrade[2] = array("table1" => "ALTER TABLE %stable1 ADD `field5` timestamp NOT NULL default CURRENT_TIMESTAMP", "table2" => "ALTER TABLE %stable2 ADD `field3` varchar(255) NOT NULL" ); 147 | 148 | // Iterate tables and create them 149 | foreach ($tables as $key => $table) { 150 | if($wpdb->get_var(sprintf("SHOW TABLES LIKE '%s%s'", $wpdb->prefix, $table)) != $wpdb->prefix . $table) { 151 | // Table doesn't exist, create it. 152 | 153 | $sql = sprintf("CREATE TABLE %s%s %s DEFAULT CHARSET=utf8", $wpdb->prefix, $table, $structure[$key]); 154 | dbDelta($sql); 155 | } else { 156 | // Table exists, check if there's any upgrade routines defined. 157 | // The upgrade routines are applied in order from previous to current version 158 | 159 | for ($i = $current_version; $i < $db_version; $i++) { 160 | if (strlen($upgrade[ $i ][ $table ]) > 0) { 161 | $sql = sprintf($upgrade[ $i ][ $table ], $wpdb->prefix); 162 | $wpdb->query($sql); 163 | } 164 | } 165 | } 166 | } 167 | 168 | if ($current_version === false) { 169 | add_option($option_name, $db_version); 170 | } else { 171 | update_option($option_name, $db_version); 172 | } 173 | } 174 | 175 | /** 176 | * Loading the gettext textdomain first from the WP languages directory, 177 | * and if that fails try the subfolder /languages/ in the plugin directory. 178 | * @return void 179 | */ 180 | function init_localization() { 181 | if(!load_plugin_textdomain($this->plugin_name, '/wp-content/languages/')) { 182 | load_plugin_textdomain($this->plugin_name, '/wp-content/plugins/' . $this->plugin_name . '/languages/'); 183 | } 184 | } 185 | 186 | /** 187 | * Loading the javascripts, first using jquery using the CDN and then the file 188 | * /scripts/script.js from the plugin directory. By default the script is enqueued 189 | * in the page footer for faster page loading. 190 | * @return void 191 | */ 192 | function init_scripts() { 193 | // Only enqueue scripts on frontend. 194 | if(!is_admin()) { 195 | // Only enqueue the script if it actually exists. 196 | if(file_exists(dirname(__FILE__) . '/scripts/script.js')) { 197 | if(function_exists('plugins_url')) { 198 | wp_enqueue_script($this->plugin_name . '-script', plugins_url('/scripts/script.js', __FILE__), array('jquery'), '1.0', true); 199 | } else { 200 | wp_enqueue_script($this->plugin_name . '-script', WP_PLUGIN_URL . '/' . $this->plugin_name . '/scripts/script.js', array('jquery'), '1.0', true); 201 | } 202 | } 203 | } 204 | } 205 | 206 | /** 207 | * Loading the stylesheet for this plugin. 208 | * @return void 209 | */ 210 | function init_styles() { 211 | // Only enqueue styles on frontend and if the stylesheet actually exists. 212 | if(!is_admin() && file_exists(dirname(__FILE__) . '/styles/style.css')) { 213 | if(function_exists('plugins_url')) { 214 | wp_enqueue_style($this->plugin_name . '-stylesheet', plugins_url('/styles/style.css', __FILE__), array(), '1.0', 'all'); 215 | } else { 216 | wp_enqueue_style($this->plugin_name . '-stylesheet', WP_PLUGIN_URL . '/' . $this->plugin_name . '/styles/styles.css', array(), '1.0', 'all'); 217 | } 218 | } 219 | } 220 | 221 | /** 222 | * Add a shortcode for this plugin. It's recommended to change the code 223 | * to prevent from collisions. (The second value in the array) 224 | * @return void 225 | */ 226 | function init_shortcodes() { 227 | add_shortcode('shortcode', array($this, 'plugin_boilerplate_shortcode')); 228 | } 229 | 230 | /** 231 | * This function will be executed when the plugin shortcode is used in a page 232 | * or post. This is where you put the code to be executed 233 | * @param array $atts 234 | * @param string $contents 235 | * @return string 236 | */ 237 | function plugin_boilerplate_shortcode($atts, $contents = '') { 238 | // Extracting all the values sent as arguments with this shortcode 239 | extract(shortcode_atts(array( 240 | 'attribute' => 'default', 241 | ), $atts)); 242 | 243 | // It's recommended to use output buffering to catch the contents and then return it. 244 | ob_start(); 245 | 246 | // Perform the tasks of this function in here, and echo everything that you want to 247 | // output to the browser. 248 | 249 | // Fetch the output that you just echoed, and put it into the $content variable. 250 | $content = ob_get_clean(); 251 | 252 | // Remember to always RETURN the content from the shortcode function, otherwise 253 | // the result won't be placed in the correct part of the page. 254 | return $content; 255 | } 256 | 257 | /** 258 | * Loading custom post types and taxonomies for this plugin. 259 | * @return void 260 | */ 261 | function init_custom_post_types() { 262 | // Register post_type as custom post type with post_type taxonomy as a taxonomy 263 | register_post_type('post_type', array( 264 | 'labels' => array( 265 | 'name' => __('Post Types', 'plugin-boilerplate'), 266 | 'singular_name' => 'Post type', 267 | 'add_new' => 'Add new', 268 | 'add_new_item' => 'Add new post type', 269 | 'edit_item' => 'Edit post type', 270 | 'new_item' => 'New post type', 271 | 'view_item' => 'Show post type', 272 | 'search_items' => 'Search post type', 273 | 'not_found' => 'Not found', 274 | 'not_found_in_trash' => 'No post type was found in trash', 275 | 'parent_item_colon' => 'Parent:' 276 | ), 277 | 'public' => true, 278 | 'exclude_from_search' => true, 279 | 'query_var' => true, 280 | 'rewrite' => true, 281 | 'capability_type' => 'post', 282 | 'hierarchical' => false, 283 | 'show_in_nav_menus' => false, 284 | 'menu_position' => null, 285 | 'supports' => array('title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments') 286 | )); 287 | 288 | register_taxonomy('post_type_taxonomy', 'taxonomy', array( 289 | 'hierarchical' => true, 290 | 'labels' => array( 291 | 'name' => 'Taxonomies', 292 | 'singular_name' => 'Taxonomy', 293 | 'search_items' => 'Search taxonomy', 294 | 'all_items' => 'All taxonomies', 295 | 'parent_item' => 'Parent taxonomy', 296 | 'parent_item_colon' => 'Parent taxonoy:', 297 | 'edit_item' => 'Edit taxonomy', 298 | 'update_item' => 'Update taxonomy', 299 | 'add_new_item' => 'Add new taxonomy', 300 | 'new_item_name' => 'Taxonomy name', 301 | 'menu_name' => 'Taxonomies', 302 | ), 303 | 'show_ui' => true, 304 | 'query_var' => true, 305 | 'rewrite' => array('slug' => 'taxonomy') 306 | )); 307 | } 308 | 309 | /** 310 | * Adds a new menu with the name "My Admin Menu" and let's anyone that 311 | * can publish posts be able to use it. 312 | * @return void 313 | */ 314 | function init_admin_menu() { 315 | // Add the menu page 316 | add_menu_page('Plugin Boilerplate Admin Menu', 'BP menu', 'publish_posts', $this->plugin_name . '-admin-menu', array($this,'main_menu_page')); 317 | 318 | // Also let's add a submenu 319 | add_submenu_page($this->plugin_name . '-admin-menu', 'Plugin Boilerplate Sub-Menu', 'BP submenu', 'publish_posts', $this->plugin_name . '-admin-submenu', array($this, 'sub_menu_page')); 320 | } 321 | 322 | /** 323 | * This function will be executed when the admin page is to be loaded 324 | * @return void 325 | */ 326 | function main_menu_page() { 327 | // Include the HTML from a separate file to keep the plugin class clean 328 | require "pages/admin_main.php"; 329 | } 330 | 331 | /** 332 | * This function will be executed when the admin sub page is to be loaded 333 | * @return void 334 | */ 335 | function sub_menu_page() { 336 | // Include the HTML from a separate file to keep the plugin class clean 337 | require "pages/admin_options_page.php"; 338 | } 339 | } 340 | 341 | /** 342 | * Register the plugin 343 | * @todo Change 'Plugin_Boilerplate' in the anonymous function to the name of the class 344 | */ 345 | add_action("init", create_function('', 'new WP_Plugin_Boilerplate();')); 346 | 347 | 348 | // Uncomment this if you need an install routine, remember to change the class name. 349 | //register_activation_hook(__FILE__, array('WP_Plugin_Boilerplate', 'install')); 350 | 351 | // Ending PHP tag is not needed, it will only increase the risk of white space 352 | // being sent to the browser before any HTTP headers. --------------------------------------------------------------------------------