Beginner SeriesPlugin Development

WordPress Plugin Development Tutorial for Beginners – Part 1

By December 11, 2018 No Comments

WordPress Plugin Development Tutorial for Beginners

Welcome! If your at all like me, building custom wordpress plugins is why you are here! It surprised me a while back when I was learning what it meant to actually code a wordpress plugin and in my search for beginner tutorials and content that there was not a ton of simple to follow content. Yes, there are amazing websites like sitepoint who do an amazing job at helping you build custom plugins and youtube channels such as Alessandro Castellani and Joshua Berbison who have some great content. But I was still lacking in actual wordpress plugin templates (if they existed) that would help me in my coding journey. Then I FOUND IT!. This post is about that plugin that was a great resource for me in my wordpress plugin development journey. I am far from knowing everything as I’m still new but this was a great source to help me understand what was going on.

wordpress plugin development tutorial for beginners

 

This Plugin is a starting point

This plugin is designed to be used as starting point for a new plugin. It will save you the time of putting in place a file structure, adding common files, creating a basic plugin class, wiring common hooks that you might need, adding an admin menu item, and more. It uses many common, standard WordPress techniques for frequently needed basic plugin features. It is well commented with TODO items to know where to customize it for your own needs.

How to Use this Plugin Template

  1. Copy the entire folder to a new location and folder that you rename for your new plugin.
  2. Rename any of the files in the structure that you want to “re-brand” for your plugin and make sure any code references pointing to those files are also updated.
  3. Go through the code and follow the well-commented TODO items, renaming the class and variables and functions to suit your needs.
  4. Add your own core functionality / graphics / etc.
  5. If you have a lot of common things that you do from plugin to plugin, add those to your main “template” version and they will be available every time you copy it to a new plugin.

Important Notes

  • This plugin includes examples of how we implement a plugin sidebar in our own plugins and exmple widgets like our own mail-chimp newsletter subscription, etc.
  • You will have to either know how to modify these for your own needs or remove them completely.
  • We cannot promise support for specific questions about what to modify in your scenario.

Enjoy!

Features

Example techniques included in this Plugin Template

  1. General file structure
  2. Lots of comments and TODO items to help you know what to change
  3. Using a class approach to building a plugin
  4. Minimal variable updates by re-using class level variables for things like the plugin name and slug
  5. Using common WordPress hooks and filters
  6. Creating an admin menu item under the Settings menu
  7. Implementing the WordPress Settings API
  8. Very basic responsive settings page including a banner
  9. Creating a Sidebar that has its own widget system and can pull data from a feed on your WordPress site (this is more of an advanced feature that we use on our own plugins and it requires additional know-how to tweak for your needs). It can also be removed completely if you don’t need / want it.

Once it’s Installed – Backend

Once the plugin is installed it is accessed through the settings panel based on the current template functions. If you wanted to change that check out my post on creating an admin menu to change that.  The plugin is very simply laid out and gives you some structure to start with.

Once it’s Installed – Code Editor

Looking at the code structure in my code editor – i’m using a program called Atom which I feel is awesome and is free. The code is very clearly laid out for the most part and allows you to be able to work through the main sections and elements and comments.

The code below is the main .php page that is used for the plugin. This page is the “first” page of any plugin.

<?php
/*
  Plugin Name: NS WordPress Plugin Template
  Plugin URI: http://neversettle.it
  Description: A simple, fully functional WordPress plugin template that does NOTHING except give you a huge head start on building a new quality plugin.
  Text Domain: ns-plugin-template
  Author: Never Settle
  Author URI: http://neversettle.it
  Version: 1.0.1
  Tested up to: 4.9.8
  License: GPLv2 or later
*/

/*
  Copyright 2014 Never Settle (email : dev@neversettle.it)
  
  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either version 2
  of the License, or (at your option) any later version.
  
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

if ( ! defined( 'ABSPATH' ) ) {
  exit; // exit if accessed directly!
}

require_once(plugin_dir_path(__FILE__).'ns-sidebar/ns-sidebar.php');

// TODO: rename this class
class NS_Plugin {
  
  var $path; 				// path to plugin dir
  var $wp_plugin_page; 	// url to plugin page on wp.org
  var $ns_plugin_page; 	// url to pro plugin page on ns.it
  var $ns_plugin_name; 	// friendly name of this plugin for re-use throughout
  var $ns_plugin_menu; 	// friendly menu title for re-use throughout
  var $ns_plugin_slug; 	// slug name of this plugin for re-use throughout
  var $ns_plugin_ref; 	// reference name of the plugin for re-use throughout
  
  function __construct(){		
    $this->path = plugin_dir_path( __FILE__ );
    // TODO: update to actual
    $this->wp_plugin_page = "http://wordpress.org/plugins/ns-wordpress-plugin-template";
    // TODO: update to link builder generated URL or other public page or redirect
    $this->ns_plugin_page = "http://neversettle.it/";
    // TODO: update this - used throughout plugin code and only have to update here
    $this->ns_plugin_name = "NS Plugin Template";
    // TODO: update this - used throughout plugin code and only have to update here
    $this->ns_plugin_menu = "NS Plugin Menu";
    // TODO: update this - used throughout plugin code and only have to update here
    $this->ns_plugin_slug = "ns-plugin-template";
    // TODO: update this - used throughout plugin code and only have to update here
    $this->ns_plugin_ref = "ns_plugin_template";
    
    add_action( 'plugins_loaded', array($this, 'setup_plugin') );
    add_action( 'admin_notices', array($this,'admin_notices'), 11 );
    add_action( 'network_admin_notices', array($this, 'admin_notices'), 11 );		
    add_action( 'admin_init', array($this,'register_settings_fields') );		
    add_action( 'admin_menu', array($this,'register_settings_page'), 20 );
    add_action( 'admin_enqueue_scripts', array($this, 'admin_assets') );
    
    // TODO: uncomment this if you want to add custom JS 
    //add_action( 'admin_print_footer_scripts', array($this, 'add_javascript'), 100 );
    
    // TODO: uncomment this if you want to add custom actions to run on deactivation
    //register_deactivation_hook( __FILE__, array($this, 'deactivate_plugin_actions') );
  }

  function deactivate_plugin_actions(){
    // TODO: add any deactivation actions here
  }
  
  /*********************************
   * NOTICES & LOCALIZATION
   */
   
   function setup_plugin(){
   	load_plugin_textdomain( $this->ns_plugin_slug, false, $this->path."lang/" ); 
   }
  
  function admin_notices(){
    $message = '';	
    if ( $message != '' ) {
      echo "<div class='updated'><p>$message</p></div>";
    }
  }

  function admin_assets($page){
   	wp_register_style( $this->ns_plugin_slug, plugins_url("css/ns-custom.css",__FILE__), false, '1.0.0' );
   	wp_register_script( $this->ns_plugin_slug, plugins_url("js/ns-custom.js",__FILE__), false, '1.0.0' );
    if( strpos($page, $this->ns_plugin_ref) !== false  ){
      wp_enqueue_style( $this->ns_plugin_slug );
      wp_enqueue_script( $this->ns_plugin_slug );
    }		
  }
  
  /**********************************
   * SETTINGS PAGE
   */
  
  function register_settings_fields() {
    // TODO: might want to update / add additional sections and their names, if so update 'default' in add_settings_field too
    add_settings_section( 
      $this->ns_plugin_ref.'_set_section', 	// ID used to identify this section and with which to register options
      $this->ns_plugin_name, 					// Title to be displayed on the administration page
      false, 									// Callback used to render the description of the section
      $this->ns_plugin_ref 					// Page on which to add this section of options
    );
    // TODO: update labels etc.
    // TODO: for each field or field set repeat this
    add_settings_field( 
      $this->ns_plugin_ref.'_field1', 	// ID used to identify the field
      'Setting Name', 					// The label to the left of the option interface element
      array($this,'show_settings_field'), // The name of the function responsible for rendering the option interface
      $this->ns_plugin_ref, 				// The page on which this option will be displayed
      $this->ns_plugin_ref.'_set_section',// The name of the section to which this field belongs
      array( 								// args to pass to the callback function rendering the option interface
        'field_name' => $this->ns_plugin_ref.'_field1'
      )
    );
    register_setting( $this->ns_plugin_ref, $this->ns_plugin_ref.'_field1');
  }	

  function show_settings_field($args){
    $saved_value = get_option( $args['field_name'] );
    // initialize in case there are no existing options
    if ( empty($saved_value) ) {
      echo '<input type="text" name="' . $args['field_name'] . '" value="Setting Value" /><br/>';
    } else {
      echo '<input type="text" name="' . $args['field_name'] . '" value="'.$saved_value.'" /><br/>';
    }
  }

  function register_settings_page(){
    add_submenu_page(
      'options-general.php',								// Parent menu item slug	
      __($this->ns_plugin_name, $this->ns_plugin_name),	// Page Title
      __($this->ns_plugin_menu, $this->ns_plugin_name),	// Menu Title
      'manage_options',									// Capability
      $this->ns_plugin_ref,								// Menu Slug
      array( $this, 'show_settings_page' )				// Callback function
    );
  }
  
  function show_settings_page(){
    ?>
    <div class="wrap">
      
      <h2><?php $this->plugin_image( 'banner.png', __('ALT') ); ?></h2>
      
      <!-- BEGIN Left Column -->
      <div class="ns-col-left">
        <form method="POST" action="options.php" style="width: 100%;">
          <?php settings_fields($this->ns_plugin_ref); ?>
          <?php do_settings_sections($this->ns_plugin_ref); ?>
          <?php submit_button(); ?>
        </form>
      </div>
      <!-- END Left Column -->
            
      <!-- BEGIN Right Column -->			
      <div class="ns-col-right">
        <h3>Thanks for using <?php echo $this->ns_plugin_name; ?></h3>
        <?php ns_sidebar::widget( 'subscribe' ); ?>
        <?php ns_sidebar::widget( 'share', array('plugin_url'=>'http://neversettle.it/buy/wordpress-plugins/ns-fba-for-woocommerce/','plugin_desc'=>'Connect WordPress to Google Sheets!','text'=>'Would anyone else you know enjoy NS Google Sheets Connector?') ); ?>
        <?php ns_sidebar::widget( 'donate' ); ?>
        <?php ns_sidebar::widget( 'featured'); ?>
        <?php ns_sidebar::widget( 'links', array('ns-fba') ); ?>
        <?php ns_sidebar::widget( 'random'); ?>
        <?php ns_sidebar::widget( 'support' ); ?>
      </div>
      <!-- END Right Column -->
        
    </div>
    <?php
  }
  
  
  /*************************************
   * FUNCTIONALITY
   */
  
  // TODO: add additional necessary functions here
  
  /*************************************
   * UITILITY
   */
   
   function plugin_image( $filename, $alt='', $class='' ){
   	echo "<img src='".plugins_url("/images/$filename",__FILE__)."' alt='$alt' class='$class' />";
   }
  
}

new NS_Plugin();

The code below is the sidebar page .php page that is used for the plugin.

<?php

// only instantiate the class if it doesn't already exist
if ( !class_exists('ns_sidebar') ) {

  class ns_sidebar {
    
    public static function widget( $widgetname, $args=array() ){
      if( method_exists('ns_sidebar',$widgetname) ){
        ?>
        <div class="ns-side-widget ns-<?php echo $widgetname; ?>-widget">
          <div class="ns-side-widget-content">
            <?php call_user_func_array( array('ns_sidebar',$widgetname), $args ); ?>
          </div>
        </div>
        <?php
      }
    }
    
    static function rate( $text='Has this plugin helped you out? Give back with a 5-star rating!', $wp_plugin_slug ){
      ?>
      <?php if($text) echo "<p>$text</p>"; ?>
      <p><a href="http://wordpress.org/support/view/plugin-reviews/<?php echo $wp_plugin_slug; ?>?rate=5#postform" target="_blank" class="button">Rate it 5 Stars</a></p>
      <?php
    }
    
    static function links( $plugin_utm_source ){
      ?>
      <a href="http://neversettle.it/home/?utm_campaign=in+plugin+referral&utm_source=<?php echo $plugin_utm_source; ?>&utm_medium=plugin&utm_content=social+button+to+ns" target="_blank"><img src="<?php echo plugins_url('ns-visit.png',__FILE__); ?>" alt="Visit NS" /></a>
      <a href="http://facebook.com/neversettle.it" target="_blank"><img src="<?php echo plugins_url('ns-like.png',__FILE__); ?>" alt="Like NS" /></a>
      <a href="https://twitter.com/neversettleit" target="_blank"><img src="<?php echo plugins_url('ns-follow.png',__FILE__); ?>" alt="Follow NS" /></a>
      <?php
    }
    
    static function share( $plugin_url, $plugin_desc, $text='Don\'t be shy, share the love!' ){
      ?>
      <?php if($text) echo "<p>$text</p>"; ?>
      <p>
        <a class="facebook" href="http://www.facebook.com/sharer/sharer.php?u=<?php echo urlencode($plugin_url); ?>&amp;t=<?php echo urlencode($plugin_desc); ?>" target="_blank">
          <img src="<?php echo plugins_url('share-facebook.png',__FILE__); ?>" alt="Share on Facebook" />
        </a>
        <a class="twitter" href="http://twitter.com/share?url=<?php echo urlencode($plugin_url); ?>&amp;text=<?php echo urlencode($plugin_desc); ?>&amp;via=neversettle" target="_blank">
          <img src="<?php echo plugins_url('share-twitter.png',__FILE__); ?>" alt="Share on Twitter" />
        </a>
        <a class="google" href="http://plus.google.com/share?url=<?php echo urlencode($plugin_url); ?>" target="_blank">
          <img src="<?php echo plugins_url('share-googleplus.png',__FILE__); ?>" alt="Share on Google+" />
        </a>
      </p>
      <?php
    }
    
    static function subscribe( $text='Receive updates, beta invites, articles and more!' ){
      ?>
      <?php if($text) echo "<p>$text</p>"; ?>
      <!-- Begin Active Campaign Signup Form -->
      <div class="_form_28"></div><script src="https://neversettle.activehosted.com/f/embed.php?id=28" type="text/javascript" charset="utf-8"></script>
      <!-- End Active Campaign Signup Form -->
      <?php
    }
  
    static function support( $text='Need any help with this plugin, or have ideas to make it better? We\'d love to hear from you.' ){
      ?>
      <?php if($text) echo "<p>$text</p>"; ?>
      <p><a href="http://support.neversettle.it" class="button" target="_blank" style="width:100%;text-align:center;"><?php _e( 'Support & Feature Requests', 'ns-cloner' ); ?></a></p>
      <?php
    }
  
    static function featured(){
      $feed = @fetch_feed( 'http://neversettle.it/plugin-widget-status/featured/feed/' );
      if( !is_array($feed->get_items()) || sizeof($feed->get_items())<1 ) return;
      $items = $feed->get_items();
      $featured = array_shift($items);
      define('NS_SIDEBAR_FEATURED_LINK', $featured->get_link());
      $thumbnail_el = $featured->get_item_tags('http://neversettle.it/','thumbnail');
      ?>
      <a href="<?php echo $featured->get_link(); ?>" target="_blank">
        <img style="max-width:100%" src="<?php echo $thumbnail_el[0]['data']; ?>" />
      </a>
      <?php
    }
    
    static function random( $exclude_links=array() ){
      if( defined('NS_SIDEBAR_FEATURED_LINK') ){
        $exclude_links[] = NS_SIDEBAR_FEATURED_LINK;
      }
      $feed = @fetch_feed( 'http://neversettle.it/feed/?post_type=product' );
      if( !is_array($feed->get_items()) || sizeof($feed->get_items())<1 ) return;
      $items = $feed->get_items();
      $other_items = array_filter( $items, create_function('$i','return false===strpos("'.join(' ',$exclude_links).'",$i->get_link());') );
      $random = $other_items[ array_rand($other_items) ];
      $thumbnail_el = $random->get_item_tags('http://neversettle.it/','thumbnail');
      ?>
      <a href="<?php echo $random->get_link(); ?>" target="_blank">
        <img style="max-width:100%" src="<?php echo $thumbnail_el[0]['data']; ?>" />
      </a>
      <?php
    }

    static function donate($text='Help us provide support and updates') {
      ?>
      <div style="text-align: center;">
        <?php if($text) echo "<p>$text</p>"; ?>
        <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
          <input type="hidden" name="cmd" value="_s-xclick">
          <input type="hidden" name="hosted_button_id" value="RM625PKSQGCCY">
          <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
          <img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
        </form>
      </div>
      <?php
    }
  }
}

Final Thoughts

Now if your sitting there with a blank look on your face because this is still beyond your current knowledge, no worries, i’m going to be breaking down this plugin in way more detail. If your good with the code and are like, “hey I can see how that works” then that is awesome and make sure to dive in an see how you can use this plugin as a starting point for your own custom wordpress plugin.

Leave a Reply