Table of Contents

Hello World Plugin Extended

This tutorial will go through the creation of a version of the Hello World plugin which will:

File structure

Create the following files/folders:

- hello_world.php
- hello_world (directory)
   - .htaccess (with one line: Deny from all)
   - admin.php
   - common_functions.php
   - lang (directory) 
      - .htaccess (with one line: Deny from all)
      - en_US.php

hello_world.php

Here is where we register the plugin. We shall start out by defining some useful constants (this will be cleaner than defining global variables like $thisfile and referencing them later - just give the constants informative names):

<?php
// === Constants ===
define('HELLO_WORLD', basename(__FILE__, '.php'));                              // Plugin ID
define('HELLO_WORLD_PLUGINPATH', GSPLUGINPATH . HELLO_WORLD . '/');             // Path to the plugin's assets folder
define('HELLO_WORLD_CONFIGFILE', GSDATAOTHERPATH . 'hello_world_config.json');  // File name for the plugin's user-configuration file

Next, we load the internationalized strings that are in the plugin's /lang directory (which we will define soon):

// === Internationalization ===
i18n_merge(HELLO_WORLD) || i18n_merge(HELLO_WORLD, 'en_US');  // Loads English language file by default

We will define a number of helpful functions in a separate file, common_functions.php, and load them at this point:

// === Load functions ===
require_once(HELLO_WORLD_PLUGINPATH . 'common_functions.php');

Then we register the plugin. Note that this uses one of the assumed functions that we will define, hello_world_i18n, where there will be language-dependent strings (namely for the plugin name and description):

// === Plugin, Hook and Filter Registration  ===
register_plugin(
  HELLO_WORLD,                                // Plugin id
  hello_world_i18n('PLUGIN_NAME'),            // Plugin name
  '0.1.0',                                    // Plugin version
  'You',                                      // Plugin author
  'http://www.you.com',                       // Author website
  hello_world_i18n('PLUGIN_DESCRIPTION'),     // Plugin description
  'theme',                                    // Page type - on which admin tab to display
  'hello_world_admin'                         // Main function (administration)
);

Lastly, we register the plugin hooks - in this case, one for loading a sidebar menu item, and another for the theme footer:

// Echoes message in footer
add_action('theme-footer', 'hello_world_footer');
 
// Creates sidebar link
add_action('theme-sidebar', 'createSideMenu', array(HELLO_WORLD, hello_world_i18n('PLUGIN_SIDEBAR')));

This is the full file:

hello_world.php
<?php
// === Constants ===
define('HELLO_WORLD', basename(__FILE__, '.php'));
define('HELLO_WORLD_PLUGINPATH', GSPLUGINPATH . HELLO_WORLD . '/');
define('HELLO_WORLD_CONFIGFILE', GSDATAOTHERPATH . 'hello_world_config.json');
 
// === Internationalization ===
i18n_merge(HELLO_WORLD) || i18n_merge(HELLO_WORLD, 'en_US');
 
// === Load functions ===
require_once(HELLO_WORLD_PLUGINPATH . 'common_functions.php');
 
// === Plugin, Hook and Filter Registration  ===
register_plugin(
  HELLO_WORLD,                                // Plugin id
  hello_world_i18n('PLUGIN_NAME'),            // Plugin name
  '0.1.0',                                    // Plugin version
  'You',                                      // Plugin author
  'http://www.you.com',                       // Author website
  hello_world_i18n('PLUGIN_DESCRIPTION'),     // Plugin description
  'theme',                                    // Page type - on which admin tab to display
  'hello_world_admin'                         // Main function (administration)
);
 
// Echoes message in footer
add_action('theme-footer', 'hello_world_footer');
 
// Creates sidebar link
add_action('theme-sidebar', 'createSideMenu', array(HELLO_WORLD, hello_world_i18n('PLUGIN_SIDEBAR')));

hello_world/lang/en_US.php

The PHP files in the /lang directory just need to define an $i18n array of internationalized strings. Once we've merged the plugin's language strings with i18n_merge, these strings will be available via i18n('hello_world/STRING_NAME', true), which we will alias hello_world_i18n('STRING_NAME')

en_US.php
<?php
// en_US.php
$i18n = array();
$i18n['PLUGIN_NAME']        = 'Hello World Plugin (Extended)';
$i18n['PLUGIN_DESCRIPTION'] = 'Echoes "Hello World" (or a message of your choice) in theme footers.';
$i18n['PLUGIN_SIDEBAR']     = 'Hello World description';
$i18n['ADMIN']              = 'Hello World Admin Panel';
$i18n['HELLO_WORLD']        = 'Hello World!';
$i18n['SAVE']               = 'Save Configuration';
$i18n['CONFIG_SAVE_SUCCESS'] = 'Configuration saved!';
$i18n['CONFIG_SAVE_ERROR']   = 'Error saving configuration!';

hello_world/common_functions.php

The first functions that we need to define are hello_world_i18n (i18n alias), hello_world_admin (displays the administration panel) and hello_world_footer (echoes the message into the theme footer). The latter two will just be stubs that get filled in later:

// Alias for getting i18n strings for this plugin
function hello_world_i18n($key) {
  return i18n_r(HELLO_WORLD . '/' . $key);
}
 
// Administration panel
function hello_world_admin() {
  // ...
}
 
// Hook for the theme footer
function hello_world_footer() {
  // ...
}

Now consider the operations that we want to have for the plugin data. The administrator will be able to create a custom message, save it, and then this message will be printed in the theme footer. So we shall create functions for saving this message into a file the data/other folder. To future-proof this plugin, we will save the data as key⇒value pairs, so the message will be a value with a designated key (ideally the key message):

// Given an array of key=>value mappings, save that data to the configuration file
function hello_world_save_config($config = array()) {
  $file     = HELLO_WORLD_CONFIGFILE;
  $contents = json_encode($config);            // Encodes the array into a valid JSON string
 
  return file_put_contents($file, $contents);  // Saves the JSON string into the configuration JSON file
}
 
// Load plugin configuration from the JSON file as an array
function hello_world_get_config() {
  $file     = HELLO_WORLD_CONFIGFILE;
  $contents = file_get_contents($file);
  $json     = json_decode($contents);    // Decodes the string
  $config   = (array) $json;             // Converts the object into a PHP array
 
  return $config;
}

When the plugin is first loaded, the configuration file might not exist. So let's create some utility functions that detect if the file exists, and which create some default data:

// Check that the configuration file exists
function hello_world_config_exists() {
  return file_exists(HELLO_WORLD_CONFIGFILE);
}
 
// Create a default configuration file
function hello_world_init() {
  $config = array('message' => hello_world_i18n('HELLO_WORLD'));
 
  return hello_world_save_config($config);
}

Now we can expand on the administration panel. First we ensure that the configuration file exists:

// Administration panel
function hello_world_admin() {
  // Initialize the configuration file
  $init = hello_world_config_exists() || hello_world_init();
 
  // ...
}

Then we will load the configuration data, and load the admin.php file (which will use this data and display the admin panel):

// Administration panel
function hello_world_admin() {
  // Initialize the configuration file
  $init = hello_world_config_exists() || hello_world_init();
 
  // still more to add...
 
  // Show the admin panel page
  $config = hello_world_get_config();
  include(HELLO_WORLD_PLUGINPATH . '/admin.php');
}

The admin panel will have a <form> whose method is POST and whose action is simply the same page. There will be a text field with the name message, and the submit button in said form will have the name save-configuration. So if we want to save the configuration, we will need to check if the $_POST variable is populated with the save-configuration key:

function hello_world_admin() {
  // Initialize the configuration file
  $init = hello_world_config_exists() || hello_world_init();
 
  // Check if we need to save the configuration
  $save_changes = isset($_POST['save-configuration']);
 
  if ($save_changes) {
    $save_success = hello_world_save_config($data);
  }
 
  // Show the admin panel page
  $config = hello_world_get_config();
  include(HELLO_WORLD_PLUGINPATH . '/admin.php');
}

Lastly, we need the footer hook to actually load the message:

// Hook for the theme footer
function hello_world_footer() {
  $config = hello_world_get_config();
  echo '<p>' . $config['message'] . '</p>';
}

This is the full file. It slightly expands on the administration panel by printing error messages and a introducing a placeholder for validating the submitted data:

common_functions.php
<?php
// === Functions ===
// Hook for the theme footer
function hello_world_footer() {
  $config = hello_world_get_config();
  echo '<p>' . $config['message'] . '</p>';
}
 
// Alias for getting i18n strings for this plugin
function hello_world_i18n($key) {
  return i18n_r(HELLO_WORLD . '/' . $key);
}
 
// Administration panel
function hello_world_admin() {
  // Initialize the configuration file
  $init = hello_world_config_exists() || hello_world_init();
 
  // Check if we need to save the configuration
  $save_changes = isset($_POST['save-configuration']);
 
  if ($save_changes) {
    // Validate and save the config
    $data         = hello_world_validate_config($_POST);
    $save_success = hello_world_save_config($data);
 
    // Display a success/error status message for the admin
    hello_world_display_status_message($save_success);
  }
 
  // Show the admin panel page
  $config = hello_world_get_config();
  include(HELLO_WORLD_PLUGINPATH . '/admin.php');
}
 
// Load plugin configuration into an array
function hello_world_get_config() {
  $file     = HELLO_WORLD_CONFIGFILE;
  $contents = file_get_contents($file);
  $json     = json_decode($contents);
  $config   = (array) $json;
 
  return $config;
}
 
// Save array data to the configuration
function hello_world_save_config($config = array()) {
  $file     = HELLO_WORLD_CONFIGFILE;
  $contents = json_encode($config);
 
  return file_put_contents($file, $contents);
}
 
// Validate an array for saving it as a configuration object
function hello_world_validate_config($data) {
  // Clean up the configuration array...
  $config = array(
    'message' => $data['message'],
  );
 
  return $config;
}
 
// Check that the configuration file exists
function hello_world_config_exists() {
  return file_exists(HELLO_WORLD_CONFIGFILE);
}
 
// Create a default configuration file
function hello_world_init() {
  $config = array('message' => hello_world_i18n('HELLO_WORLD'));
 
  return hello_world_save_config($config);
}
 
// Display the success/error status of a coonfiguration's saving
function hello_world_display_status_message($status) {
  if ($status) {
    $class   = 'success';
    $message = hello_world_i18n('CONFIG_SAVE_SUCCESS');
  } else {
    $class   = 'error';
    $message = hello_world_i18n('CONFIG_SAVE_ERROR');
  }
 
  echo '<div class="' . $class . '">' . $message . '</div>';
}

hello_world/admin.php

Lastly we define the look of the administration panel. As stated above, it will have a <form> with a message input field, save-configuration button, and will send the data from the form via POST. All that is needed is to ensure the input field is populated with the last saved message:

admin.php
<h3><?php echo hello_world_i18n('ADMIN'); ?></h3>
 
<form method="post">
  <input name="message" value="<?php echo $config['message']; ?>">
  <button name="save-configuration"><?php echo hello_world_i18n('SAVE'); ?></button>
</form>