I've written a few tutorials (that will need revision) on plugin development and am not too sure where to put them. Thought here would be best for now.
---------
A sensible structure for your plugin would be as follows:
Let's go over the PHP files and their contents.
yourplugin.php
This file will be where we register the plugin, load the correct language files and initialize the plugin. A sensible way of doing this will be to encapsulate those procedures in a class and use only a few methods, like below:
Below, you can define global functions that you think the admin might need, but otherwise, the yourpluginname.php file does not need any further alteration done to it.
yourpluginname/lib/plugin.class.php
Now we need to ensure that the class actually exists. The following methods need to be made public:
__construct
In the class constructor, we pass the id so that it can be accessed via the other functions (we'll need the id for the register method, for example).
The autoload method will call the spl_autoload_register function and register our autoloader method, a private method that we will define next.
autoload
The autoloader method takes a class name as a parameter. It will see if a file name of the form classname.class.php exists in the lib/ directory and load it if it does. Note that the classname is lower case. This is just a preference of mine - as long as you remain consistent, you can decide on whatever naming convention that you want.
i18nMerge
In order to load the correct language file, i18nMerge will call the i18n_merge function. If a language file for the active langauge on the GetSimple installation exists, that will be loaded; otherwise, we will default to English (en_US). The plugin id is required (hence why we stored it as a property of the class).
register
Next, to actually get the plugin working, the plugin's details must be registered. Certain properties such as the plugin title will actually be done using the i18n_r function (so that the title/description/sidebar label change if the language changes). To do all of this, we make one call to the register_plugin function:
init
Now we can set up any initialization queries that should be done prior to the admin getting control. This can range from setting up the plugin hooks/filters to creating folders/files in the /data/other directory. This will all depend on the job of the plugin. The below example will put a sidebar link on the 'pages' page of the admin panel.
displayAdminPanel
Finally we tie everything together with the admin panel itself. This function will be called when the admin panel is accessed (i.e. when the admin visits /admin/load.php?id=yourpluginname). Conceptually, all you need is an if/else or switch statement to change the back-end page in accordance with the url query:
Whilst quicker to do, it can easily muddy the plugin class as the admin panel becomes more complex. So it is best to modulate the code and handle the admin panel (both its display and its functions) using separate classes.
yourplugin/inc/yourpluginadmin.class.php
This class will contain all of the functions needed for the back-end - e.g. saving settings, creating/editing content, etc...
yourplugin/inc/yourpluginadmindisplay.class.php
By extending the admin class, this plugin will let you define the admin pages themselves but still be able to use the admin class methods as though it were one class. The if-else conditional is best to have here, in the getPage method.
yourplugin/lang/en_US.php
With these things set up, we just need to ensure that the correct hashes exist in the langauge file. The fact that the current don't isn't a huge problem - it will not cause an error. But instead of showing the text that we want, it'll show the placeholder (e.g. {yourplugin/PLUGIN_TITLE}). The langauge file merely consists of an array called i18n whose keys are the placeholder titles and whose values are the text we want to replace them with.
Langauge placeholders can be called within your plugin using the i18n function (for printing/echoing) and i18n_r function (for returning a string - useful for editing the string first). Remember that there are many hashes within the core language file, so consult that hash table first to see if certain phrases have already been translated (this will save you some work). To use those hashes, simply don't prefix the hash with 'yourplugin/' (e.g. i18n('BTN_SAVECHANGES')).
Everything from here on out is dependent on the purpose and complexity of the individual plugin.
Credit:
- GetSimple Wiki for their Anatomy of a Plugin article
- shawn_a for previous comments on plugin framework topic
Download Example
---------
A sensible structure for your plugin would be as follows:
- yourplugin.php
- yourplugin/
- assets/ (non-php files your plugin may need)
- css/
- images/
- js/
- xml/ (default xml files you might need to copy to the /data directory)
- css/
- inc/ (php files, excluding classes and language files)
- lang/ (langauge files, e.g. en_US.php)
- lib/ (php class files, e.g. plugin.class.php)
- assets/ (non-php files your plugin may need)
Let's go over the PHP files and their contents.
yourplugin.php
This file will be where we register the plugin, load the correct language files and initialize the plugin. A sensible way of doing this will be to encapsulate those procedures in a class and use only a few methods, like below:
PHP Code:
<?php
// get id of plugin based on this file name
$yourpluginId = basename(__FILE__, '.php');
// include the main plugin class
include($yourpluginId . '/lib/plugin.class.php');
// instantiate an object so that we can initialize and register the plugin
$yourplugin = new YourPlugin($yourpluginId);
$yourplugin->autoload(); // autoloader, so classes in /lib/ are automatically included
$yourplugin->i18nMerge(); // load correct language file
$yourplugin->register(); // register the plugin
$yourplugin->init(); // initialize the plugin, set defaults
?>
Below, you can define global functions that you think the admin might need, but otherwise, the yourpluginname.php file does not need any further alteration done to it.
yourpluginname/lib/plugin.class.php
Now we need to ensure that the class actually exists. The following methods need to be made public:
PHP Code:
<?php
class YourPlugin {
private $id; // plugin id
public function __construct($id) { }
public function autoload() { }
public function i18nMerge() { }
public function register() { }
public function init() { }
public function displayAdminPanel() { }
}
?>
__construct
In the class constructor, we pass the id so that it can be accessed via the other functions (we'll need the id for the register method, for example).
PHP Code:
public function __construct($id) {
$this->id = $id;
}
The autoload method will call the spl_autoload_register function and register our autoloader method, a private method that we will define next.
autoload
PHP Code:
public function autoload() {
spl_autoload_register(array($this, 'autoloader'));
}
PHP Code:
private function autoloader($class) {
if (file_exists($file = GSPLUGINPATH . $this->id . '/lib/' . strtolower($class) . '.class.php')) {
include($file);
}
}
i18nMerge
In order to load the correct language file, i18nMerge will call the i18n_merge function. If a language file for the active langauge on the GetSimple installation exists, that will be loaded; otherwise, we will default to English (en_US). The plugin id is required (hence why we stored it as a property of the class).
PHP Code:
public function i18nMerge() {
i18n_merge($this->id) || i18n_merge($this->id, 'en_US');
}
Next, to actually get the plugin working, the plugin's details must be registered. Certain properties such as the plugin title will actually be done using the i18n_r function (so that the title/description/sidebar label change if the language changes). To do all of this, we make one call to the register_plugin function:
PHP Code:
public function register() {
register_plugin(
$this->id, // id
i18n_r($this->id . '/PLGN_TITLE'), // title
'0.1', // version
'You', // author
'http://yourwebsite/', // author site
i18n_r($this->id . '/PLGN_DESC'), // description
'pages', // admin page (currently 'Pages')
array($this, 'displayAdminPanel') // admin panel function
);
}
init
Now we can set up any initialization queries that should be done prior to the admin getting control. This can range from setting up the plugin hooks/filters to creating folders/files in the /data/other directory. This will all depend on the job of the plugin. The below example will put a sidebar link on the 'pages' page of the admin panel.
PHP Code:
public function init() {
// put sidebar link in admin page (pages)
add_action('pages-sidebar','createSideMenu', array($this->id, i18n_r($this->id . '/PLGN_SIDEBAR')));
// other initialization actions...
}
displayAdminPanel
Finally we tie everything together with the admin panel itself. This function will be called when the admin panel is accessed (i.e. when the admin visits /admin/load.php?id=yourpluginname). Conceptually, all you need is an if/else or switch statement to change the back-end page in accordance with the url query:
PHP Code:
public function displayAdminPanel() {
if (condition) {
// a page
}
elseif (othercondition) {
// another page
}
else {
// main page
}
}
Whilst quicker to do, it can easily muddy the plugin class as the admin panel becomes more complex. So it is best to modulate the code and handle the admin panel (both its display and its functions) using separate classes.
PHP Code:
public function displayAdminPanel() {
$admin = new YourPluginAdminDisplay($this->id); // load admin panel object
$admin->getPage(); // get the correct admin panel page
}
yourplugin/inc/yourpluginadmin.class.php
This class will contain all of the functions needed for the back-end - e.g. saving settings, creating/editing content, etc...
PHP Code:
<?php
class YourPluginAdmin {
protected $id; // plugin id
protected $url; // admin url
// constructor
public function __construct($id) {
$this->id = $id;
$this->url = 'load.php?id='. $this->id;
}
// add your required admin methods here (and make them protected)
}
?>
yourplugin/inc/yourpluginadmindisplay.class.php
By extending the admin class, this plugin will let you define the admin pages themselves but still be able to use the admin class methods as though it were one class. The if-else conditional is best to have here, in the getPage method.
PHP Code:
<?php
class YourPluginAdminDisplay extends YourPluginAdmin {
// display correct admin panel page
public function getPage() {
// use if/else statements to show the correct admin panel page
// below is just a default that shows the title and description
?>
<h3><?php i18n($this->id . '/PLGN_TITLE'); ?></h3>
<p><?php i18n($this->id . '/PLGN_DESC'); ?></p>
<?php
}
}
?>
yourplugin/lang/en_US.php
With these things set up, we just need to ensure that the correct hashes exist in the langauge file. The fact that the current don't isn't a huge problem - it will not cause an error. But instead of showing the text that we want, it'll show the placeholder (e.g. {yourplugin/PLUGIN_TITLE}). The langauge file merely consists of an array called i18n whose keys are the placeholder titles and whose values are the text we want to replace them with.
PHP Code:
<?php
/** ENGLISH LANGAUGE FILE
* each hash corresponds to a placeholder
* access these placeholders using i18n (echo) or i18n_r (return) functions
* e.g. <?php i18n('yourplugin/PLGN_TITLE'); ?> */
$i18n = array();
$i18n['PLGN_TITLE'] = 'Your Plugin Title';
$i18n['PLGN_DESC'] = 'Your Plugin description.';
$i18n['PLGN_SIDEBAR'] = 'Your Plugin';
?>
Langauge placeholders can be called within your plugin using the i18n function (for printing/echoing) and i18n_r function (for returning a string - useful for editing the string first). Remember that there are many hashes within the core language file, so consult that hash table first to see if certain phrases have already been translated (this will save you some work). To use those hashes, simply don't prefix the hash with 'yourplugin/' (e.g. i18n('BTN_SAVECHANGES')).
Everything from here on out is dependent on the purpose and complexity of the individual plugin.
Credit:
- GetSimple Wiki for their Anatomy of a Plugin article
- shawn_a for previous comments on plugin framework topic
Download Example