Custom WordPress Plugins
WordPress is a wonderful website development and management tool. It’s used by an estimated 1/3 to 1/2 of all websites. It’s widely supported and provides a great common framework for customization, administration and user content management.
Mark created the basic plugin template and tutorial video below to help other LAMP and PHP coders to more easily begin creating custom WordPress plugins.
EXAMPLE PLUGIN: ms-data-display
This demo plugin and tutorial are intended for PHP/JavaScript developers who want to quickly understand the basics for creating custom applications for deployment within the WordPress CMS environment.
The table shown below is filled with dummy data, which is queried from an external database and displayed by the ms-data-display plugin. The plugin formats the table data and markup, and tells WordPress to simply display that instead, wherever it comes across a specific “shortcode” phrase like this…
[ms-data-display-product-table]
.
The website’s content administrators simply type the shortcode into any webpage on the site, wherever they want to display the table. The table’s sort field and order can also be set in the plugin’s simple administration section.
This plugin is only around 300 lines of descriptively-commented code. It serves as a well-rounded teaching template for new plugin developers, touching on a number of common aspects of plugin development, such as…
- Plugin basic creation and security
- Interaction with the built-in WordPress Database and others
- Creating a Plugin Administration Menu link and Settings Page
- Using the built-in $wpdb object and options table to store and retrieve settings data
- Creation of shortcodes for deployment of output
- Registering an “Uninstall” hook to clean everything up upon removal
You’ll have to supply your own external database for your dummy data of course, but the plugin is free, as-is, under the GPL2 license, the code is below, and the tutorial video should explain it all. 🙂
Here’s the Dummy Data that replaces the shortcode…
…and here’s the code that makes that happen (download here)…
<?php
/**
* Plugin Name: Mark Speciale - Tutorial Plugin: Data Display
* Plugin URI: https://markspeciale.com/custom-wordpress-plugins/
* Description: Basic plugin example that displays data with shortcode and has a simple admin section.
* Version: 1.0.0
* Author: Mark Speciale
* Author URI: https://markspeciale.com
* License: GPL2
* Unique Prefix: ms225CF2
* Shortcode: ms-data-display-products
*/
////////// First, don't allow hackers to access this page directly
if ( ! defined( 'ABSPATH' ) ) {// WordPress defines ABSPATH in the normal 'loading' stage
exit; // Shut it down if it's not a legit call
}
/////////////////////////////// The Main Plugin Class: ms225CF2_data_display
////////////////////////////// Replaces shortcode placed in website pages with database records.
class ms225CF2_data_display {
//////////////////////// DEFAULT FUNCTION (METHOD) - runs automagically
///////////////////// __construct()
public function __construct() {
////// Plugin settings administration and management
// Create a 'settings' page link in WordPress' Admin menu
add_action( 'admin_menu', array( $this, 'ms225CF2_create_plugin_settings_page_link' ) );
// Set up the Admin page's sections and form fields (for simple "SORT BY" and "ORDER" settings)
add_action( 'admin_init', array( $this, 'ms225CF2_setup_sections' ) );
add_action( 'admin_init', array( $this, 'ms225CF2_setup_fields' ) );
// Add a style sheet for the data table formatting
add_action('wp_enqueue_scripts', array( $this, 'ms225CF2_add_scripts'));
////// Plugin main functionality
// Replace the shortcode on the page with the data, sorted as set in the admin page
add_shortcode('ms-data-display-products', array($this, 'ms225CF2_data_display_get_data'));
}//End __construct
//////////////////////// OTHER METHODS
//////////////////////// ms225CF2_add_scripts()
// Register or enqueue scripts/styles as needed, here
function ms225CF2_add_scripts(){
$plugin_url = plugin_dir_url( __FILE__ );
wp_enqueue_style('ms225CF2-data-display-style', $plugin_url . 'css/ms-data-display.css');// The style sheet for the table
}
///////////////////// ms225CF2_data_display_get_data()
// Gets and formats the data that replaces the shortcode
public function ms225CF2_data_display_get_data(){
// Check for existing 'SORT BY' and 'ORDER' settings from this plugin
$sort_by_array = get_option( 'ms_data_display_field' );// SORT BY
$sort_by = trim($sort_by_array[0]);
$order_array = get_option( 'ms_data_display_order' );// ORDER
$order = trim($order_array[0]);
// Set up the basic query
$query = 'SELECT * FROM Products';
// If there's an existing setting for the $sort_by variable, add the ORDER BY section
if($sort_by){
$query .= ' ORDER BY ' . $sort_by;
}
// if there's a set value for $order, add it to the query last
if($order){
$query .= ' ' . $order;
}
// Include the connection constants for the external database (EXTERNAL_DB, EXTERNAL_DB_USER, and EXTERNAL_DB_PASS
include_once(dirname(__FILE__) . '/inc/ms-data-display-db.inc');
// Get the data from an external database using a new instance of WordPress' built-in wpdb object for consistency
$ProductsDB = new wpdb(EXTERNAL_DB_USER,EXTERNAL_DB_PASS,EXTERNAL_DB,'localhost');
$ProductRows = $ProductsDB->get_results($query);
$MarkUp_String = "<div class='ms225CF2DivTable'>";// Output the top row
$MarkUp_String .= "<div class='ms225CF2DivTableBody'>";
$MarkUp_String .= "<div class='ms225CF2DivTableRow'>";
$MarkUp_String .= "<div class='ms225CF2DivTableCell'><strong>IMAGE</strong></div>";
$MarkUp_String .= "<div class='ms225CF2DivTableCell'><strong>PRODUCT</strong></div>";
$MarkUp_String .= "<div class='ms225CF2DivTableCell'><strong>PRICE</strong></div>";
$MarkUp_String .= "<div class='ms225CF2DivTableCell'><strong>TYPE</strong></div>";
$MarkUp_String .= "<div class='ms225CF2DivTableCell'><strong>POWER OUTPUT (Watts)</strong></div>";
$MarkUp_String .= "</div>";
//Loop through the records and display them in a div-based table
foreach ($ProductRows as $Field){
$MarkUp_String .= "<div class='ms225CF2DivTableRow'>";
$MarkUp_String .= "<div class='ms225CF2DivTableCell'><img width='80px' height='80px' src='data:image/jpeg;base64,".base64_encode( $Field->Thumbnail )."' /></div>";
$MarkUp_String .= "<div class='ms225CF2DivTableCell'>".$Field->ProductName."</div>";
$MarkUp_String .= "<div class='ms225CF2DivTableCell'>$ ".money_format('%.2n',$Field->Price)."</div>";
$MarkUp_String .= "<div class='ms225CF2DivTableCell'>".$Field->Type."</div>";
$MarkUp_String .= "<div class='ms225CF2DivTableCell'>".$Field->PowerOut."</div>";
$MarkUp_String .= "</div>";// End the row
}//End foreach product
// Close up the remaining open divs
$MarkUp_String .= "</div></div>";
// Return the markup and data
return $MarkUp_String;
}//End get_data
/////////////////////// ms225CF2_create_plugin_settings_page_link()
///////// Creates the admin settings page menu link
public function ms225CF2_create_plugin_settings_page_link() {
// Set up some variables for the Admin page
$page_title = 'MS Data Display - plugin settings';
$menu_title = 'MS Data Display'; // Without the underscore
$capability = 'manage_options';// Permissions level
$slug = 'ms_data_display';// Identifies all the settings in the options table
$callback = array( $this, 'ms225CF2_plugin_settings_page_content' );
$icon = plugin_dir_url( __FILE__ ).'inc/msBlackWhiteIcon16.png';// The Icon that shows in the menu
$position = 100;// Essentially the bottom, by default. Use decimals to fine-tune
// Add the admin menu page
add_menu_page( $page_title, $menu_title, $capability, $slug, $callback, $icon, $position );
}//End create settings page
/////////////////////// ms225CF2_plugin_settings_page_content()
//////// Show the form and the Admin Notice if it's been updated
public function ms225CF2_plugin_settings_page_content() {?>
<div class="wrap">
<p><img src="<?php echo(plugin_dir_url( __FILE__ ).'inc/msLogo.png'); ?>" /></p>
<h2><strong>MS Data Display - Simple Demonstration Plugin</strong></h2><hr />
<ul>
<li>Plugin Name: Mark Speciale - Tutorial Plugin: Data Display</li>
<li>Plugin URI: <a href="https://markspeciale.com/custom-wordpress-plugins/" target="_blank">https://markspeciale.com/custom-wordpress-plugins/</a></li>
<li>Description: Basic plugin example that displays data with shortcode and has a simple admin section.</li>
<li>Version: 1.0.0</li>
<li>Author: Mark Speciale</li>
<li>Author URI: <a href="https://markspeciale.com" target="_blank">https://markspeciale.com</a></li>
<li>License: GPL2</li>
<li>Unique Prefix: ms225CF2</li>
<li>Shortcode: ms-data-display-products</li>
</ul>
<?php
// If the form was just submitted, let them know it's been successful
if ( isset( $_GET['settings-updated'] ) && $_GET['settings-updated'] ){//'settings-updated' is added after the form is processed in options.php
$this->ms225CF2_admin_notice();
}
?>
<form method="POST" action="options.php">
<?php
settings_fields( 'ms_data_display' );
do_settings_sections( 'ms_data_display' );
submit_button();
?>
</form></div><?php
}//End settings page content
/////////////////////// ms225CF2_admin_notice()
/////// Define the Admin Notice
public function ms225CF2_admin_notice() {
echo('<div class="notice notice-success is-dismissible"><p>Your settings have been updated!</p></div>');
}//End admin notice
//////////////////////// ms225CF2_setup_sections()
////// Set up the Admin page sections with WordPress
public function ms225CF2_setup_sections() {
// add_settings_section( string $id, string $title, callable $callback, string $page )
add_settings_section( 'header_section', '<hr />Products Table - Sort By/Order Settings', array( $this, 'ms225CF2_section_callback' ), 'ms_data_display' );
add_settings_section( 'body_section', '', array( $this, 'ms225CF2_section_callback' ), 'ms_data_display' );
add_settings_section( 'footer_section', '', array( $this, 'ms225CF2_section_callback' ), 'ms_data_display' );
}//End setup sections
//////////////////////// ms225CF2_section_callback()
////// Add initial content to the sections. The fields are in the body_section by default.
public function ms225CF2_section_callback( $arguments ) {
switch( $arguments['id'] ){
case 'header_section':
echo 'Edit the "Sort By" and "Order" settings for this simple demonstration plugin.<br /><br />';
break;
case 'body_section':
echo "";
break;
case 'footer_section':
echo '<hr />©' . date("Y") . ' Mark Speciale. Use as-is, no warranties. Open source GPL2 license. Enjoy :)';
break;
}
}//End section callback
//////////////////////// ms225CF2_setup_fields()
////// Set up the array of fields that will be displayed with do_settings_sections()
public function ms225CF2_setup_fields() {
$fields = array(
// The "SORT BY:" drop-down
array(
'uid' => 'ms_data_display_field',
'label' => 'SORT BY:',
'section' => 'body_section',
'type' => 'select',
'helper' => 'Choose the field you want to sort by.',
'supplemental' => 'Select one of the choices from the drop-down, above.',
'options' => array(
'ProductName' => 'ProductName',
'Type' => 'Type',
'Price' => 'Price',
'PowerOut' => 'PowerOut'
),
'default' => array()// No default
),
// The "ORDER:" drop-down
array(
'uid' => 'ms_data_display_order',
'label' => 'ORDER:',
'section' => 'body_section',
'type' => 'select',
'helper' => 'Choose the order.',
'supplemental' => 'Select one of the choices from the drop-down, above.',
'options' => array(
'ASC' => 'ASC',
'DESC' => 'DESC'
),
'default' => array()// No default
)
); //End construction of the $fields array
// Loop through the $fields array and register them one-by-one with WordPress
foreach( $fields as $field ){
add_settings_field( $field['uid'], $field['label'], array( $this, 'ms225CF2_field_callback' ), 'ms_data_display', $field['section'], $field );
register_setting( 'ms_data_display', $field['uid'] );
}
}//End setup fields
////////////////////////////////// field_callback()
// Returns the HTML markup for the fields
public function ms225CF2_field_callback( $arguments ) {
// Check for the field name in the db
$existing_value_array = get_option( $arguments['uid'] );
// If this field's not found in the db, use the empty array for this
if( ! $existing_value_array ) {
$existing_value_array = $arguments['default'];
}
// These are both select fields, so we need to process the options for each
$attributes = '';
$options_markup = '';
// Loop through the options and format the markup for the "options" part of the form element.
// If it matches the existing value if there is one, it adds "selected='selected'" to the markup
foreach( $arguments['options'] as $key => $label ){
$options_markup .= sprintf( '<option value="%s" %s>%s</option>', $key, selected( $existing_value_array[ array_search( $key, $existing_value_array, true ) ], $key, false ), $label );
}
// Now output the formatted HTML for the form field.
printf( '<select name="%1$s[]" id="%1$s" %2$s>%3$s</select>', $arguments['uid'], $attributes, $options_markup );
// The helper text to the right...
if( $helper = $arguments['helper'] ){
printf( '<span class="helper"> %s</span>', $helper );
}
// ...and the supplemental text below the field in its own paragraph
if( $supplimental = $arguments['supplimental'] ){
printf( '<p class="description">%s</p>', $supplimental );
}
}//End field callback
///////////////////// ms225CF2_data_display_uninstall()
// Cleans up when we uninstall the plugin
public function ms225CF2_data_display_uninstall(){
// Access the built-in WP database
global $wpdb;
//Remove all the now unused shortcodes from all the pages
add_shortcode( 'ms-data-display-products', '__return_false' );
// Remove the settings from the built-in WP options table
$DeleteFieldSetting = $wpdb->delete( $wpdb->options, "'option_name' LIKE '%ms_data_display_field%'" );
$DeleteOrderSetting = $wpdb->delete( $wpdb->options, "'option_name' LIKE '%ms_data_display_order%'" );
}//End uninstall
}//End the main class (ms225CF2_data_display())
////////////////////////// Instantiate the class
$ms225CF2_data_display = new ms225CF2_data_display();
// Set up what happens when this plugin gets uninstalled (we need to remove the options from the built-in WP table)
register_uninstall_hook( __FILE__, array($ms225CF2_data_display, 'ms225CF2_data_display_uninstall'));
<?php
////////// Keep the hackers out
if ( ! defined( 'ABSPATH' ) ) {// WordPress defines ABSPATH in the normal 'loading' stage
exit; // Shut it down if it's not a legit call
}
// Define connection vars for the external database
define('EXTERNAL_DB', 'YOUR_DATABASE_NAME');
define('EXTERNAL_DB_USER', 'YOUR_DATABASE_USER');
define('EXTERNAL_DB_PASS', 'YOUR_DATABASE_USER_PASSWORD');
/* CSS Document */
.ms225CF2DivTable{
display: table;
width: 100%;
text-align: center;
}
.ms225CF2DivTableRow {
display: table-row;
}
.ms225CF2DivTableHeading {
background-color: #EEE;
display: table-header-group;
}
.ms225CF2DivTableCell, .ms225CF2DivTableHead {
border: 1px solid #DDDDDD;
display: table-cell;
padding: 2px 4px;
vertical-align: middle;
}
.ms225CF2DivTableHeading {
background-color: #EEE;
display: table-header-group;
font-weight: bold;
}
.ms225CF2DivTableFoot {
background-color: #EEE;
display: table-footer-group;
font-weight: bold;
}
.ms225CF2DivTableBody {
display: table-row-group;
}
Download Mark's Resume
Download a PDF copy of Mark’s current resume!
A Million Ways to Make Things Happen For You
Mark maintains a large and ever-growing skill set and also won’t hesitate to bring in other qualified experts to fulfill your needs, where required.
By leveraging the vast resources of the Internet to find and use the most current, stable, relevant technologies, he’s better able to find solutions, solve problems and exceed requirements.