Coppermine Photo Gallery v1.5.x: Documentation and Manual

Table of Contents

Creating plugins: step by step

This page is meant to give plugin authors step-by-step directions how to create a plugin. It assumes that you have read Plugin writing for Coppermine as an introduction.

Getting started with the plugin template

The Coppermine developers understand that it might be asked a bit much to come up with a working plugin from scratch, that's why we're providing an empty plugin template that you can use to start to come up with a plugin of your own. You will find the plugin template as an attachment inside the docs folder, so these are the steps to get you going:

1. Extract the plugin template

As suggested above, the plugin template comes as an attachment inside the documentation folder, so you can simply download it by clicking here. Alternatively, you can get it from the download repository at sourceforge.net. After downloading, extract the content of the archive into a temporary folder. As an example, we'll assume that you have extracted it to c:\temp\, so you should have a sub-folder c:\temp\template\

2. Choose a name

Pick a name for your plugin. It's mandatory not to skip this step - you mustn't leave the name "template". Please refer to Choose a name for details. It's important that your plugin name is meaningfull and doesn't contain to many characters (consider 20 characters as a maximum). As an example, we'll assume that our plugin will brew coffee, that's why we'll refer to it on this page as coffee_maker.

3. Rename the folder

Rename the plugin template inside the temporary folder from c:\temp\template\ to the name you have chosen as a short name for your plugin (e.g. c:\temp\coffee_maker\ in our example here)

4. Rename the placeholders inside the files

The plugin template contains several PHP-files already. In this step you have to loop through all those files and replace the placeholder word template with the name you have chosen (e.g. coffee_maker in our example). To accomplish this it's advisable to use an editor that can replace a string in various files at once; both tools that are capable to loop through all files within a particular folder (see Replace version number in all files) as well as advanced plain text editors like Notepad++ can do this.
Don't forget the english language file inside the lang-subfolder ( c:\temp\coffee_maker\lang\english.php).
The screenshots show the dialogs in Notepad++ as examples. Click on the thumbnails to enlarge the screenshots.
The first screenshot above shows the dialog with all relevant files opened inside the editor. Click on the button "Replace in all open Documents" to perform the replacement. The screenshot at the right shows another possible option in Notepad++, where you can perform a replace operation on particular files within a given folder. This is the most elegant and recommended solution for bigger replacement operations.

Please note that the above screenshots have been added as examples. Coppermine is not afiliated with Notepad++, nor do we support that particular application. However, we recommend it!

5. Copy the new plugin folder to the target

Inside your temporary folder ( c:\temp\) you should by now have a unique plugin based on the plugin template that is ready to be modified further by you. If you want to test it right away, copy your new custom plugin to the plugin folder of your Coppermine gallery (as suggested in the "regular" plugin documentation). As your custom plugin doesn't do anything yet (remember: the plugin template is just an empty shell to get you going easily), you will probably want to edit your plugin further, so you should copy it to the plugin folder of your testbed (if you have one) and continue there.
For the sake of this article we'll assume that your local testbed or the mirror that corresponds to the files on your webserver resides at c:\mysite\. Therefore, the plugin folder would be c:\mysite\plugins\ and the example plugin that we will refer to in this article will reside in c:\mysite\plugins\coffee_maker\.

 

Creating a config record for your plugin

This section is assuming you have done as suggested above: that you're using the plugin template and that you have renamed both the plugin folder as well as all placeholders labelled "template" with the short name of your plugin. In this example, we'll asume the plugin name to be coffee_maker. How the code of the config screen is composed is being explained in Adding a config section to your plugin in case you want to figure out what actually happens on the config screen. However, that it's not really necessary to read that section up - you should get away with just following the steps below.
For our example plugin we'll assume that you only will have a handfull of config settings, so we'll use Coppermine's already existing config table and populate that with additional records to store our custom plugin config settings as well.

1. Decide for an option type

First, you need to determine of what type the config option will be: Depending on this decision you will need different language strings and you will need to apply different piece of code for the actual config screen. For now, just decide on one option and keep that in mind throughout the rest of the process. If you assistance in making up your mind, it's recommended to take a look at the paragraph about forms within the usability section of the coding guidelines for Coppermine.
For the sake of brevity, we will use a checkbox in the example boxes below

2. Choose a record name

Choose a short yet meaningfull name for your config option. Avoid cryptic abbreviations, only use lowercase alphanumerals and the underscore.
If your plugin's name is coffee_maker and your config option is meant to toggle an option that determines if the coffee will be served with or without an extra donut, we could be calling the option simply donut. However, that may lead to misunderstandings or double definitions, that's why it's advisable to prefix the name of the config record with the words plugin followed by the plugin's short name coffee_maker. Finally, let's separate the words with underscores for better readability, so the option's record name would be plugin_coffee_maker_donut, which is what we'll use in the following examples.
Keep in mind that the overall length of the record name mustn't exceed 40 characters! If you need longer names, you can not use Coppermine's config table to store your records in - you will have to create a table of your own.

3. Create the record during plugin install

During the install of the plugin we need to perform the database query once to create the needed record. To accomplish that, edit the codebase file that contains the actual plugin code (e.g. c:\mysite\plugins\coffee_maker\codebase.php) with a plain text editor, find // Add the config options for the plugin within the function that determines the additionmal actions during the install of your custom plugin. Below that (into a line of it's own), enter
cpg_db_query("INSERT IGNORE INTO {$CONFIG['TABLE_CONFIG']} (`name`, `value`) VALUES ('plugin_coffee_maker_donut', '0')");
In the line above you of course have to edit the section highlighted in orange - that section needs to correspond to the config record name that you have chosen in the previous step. Additionally, you have to determine the default value of the config option (highlighted in red in the line above).

4. Create the uninstall query

You have to make sure that your plugin doesn't leave needless config records behind if it should get uninstalled in the future, that's why you need to come up with a query that will delete the config record, similar to the step above where you created the config record. To accomplish that, edit the codebase file that contains the actual plugin code (e.g. c:\mysite\plugins\coffee_maker\codebase.php) with a plain text editor, find // Delete the plugin config records within the function that determines the additionmal actions during the uninstall of your custom plugin. Below that (into a line of it's own), enter
cpg_db_query("DELETE FROM {$CONFIG['TABLE_CONFIG']} WHERE name = 'plugin_coffee_maker_donut'");
In the line above you of course have to edit the section highlighted in orange in a similar manner as for the install - that section needs to correspond to the config record name that you have chosen when you decided for a plugin record name.

5. Specify the scope of your config record

As suggested in tutorial section that explains how to add config options you need to determine on the page that actually composes the config page (c:\mysite\plugins\coffee_maker\admin.php) for your plugin what the scope of your config option will be, i.e. what config field type you have determined above and what the minimum and maximum value will be. To accomplish this, edit the file admin.php in your custom plugin folder (not the file admin.php in Coppermine's root folder; that file has only been named in analogy) with a plain text editor. Find $sanitization_array = array( and add below (in a line of it's own)
'plugin_coffee_maker_donut' => array('type' => 'checkbox', 'min' => '0', 'max' => '1'),
Again, you need of course to edit the section highlighted in orange in the line above in a similar manner as for the install and the other sections above - that section needs to correspond to the config record name that you have chosen when you decided for a plugin record name.
Additionally, you have to determine the scope of the config option using the section highlighted in red in the line above. The table below explains what you need to replace depending on your choice of option type in the initial step "Decide for an option type". This is basically the same table as in Adding a config section to your plugin
option type scope Usage (range) Parameters Example
  • Checkbox
  • Radio button
  • Dropdown (option list) with one selectable item
checkbox Radio buttons and checkboxes with the values of the individual fields set to integers. The difference lies in the way that browsers handle empty checkboxes: if a checkbox is not ticked, there is no data submit with the form, just as if the checkbox wasn't there in the first place. This can cause issues if you explicitely empty a checkbox. The type 'int' can't handle this, but the type 'checkbox' can.
  • 'min' -> minimum value
  • 'max' -> maximum value
'plugin_coffee_maker_donut' =>
array('type' => 'checkbox', 'min' => '0', 'max' => '1'),
  • Text input field (one line) for numerical input (integers)
int Text input fields where only integers are allowed
  • 'min' -> minimum value
  • 'max' -> maximum value
'plugin_coffee_maker_lumps' =>
array('type' => 'int', 'min' => '0', 'max' => '9'),
  • Text input field (one line) for input of any kind of text (not just numbers)
  • Text input field (multiple lines, aka textarea)
raw Use this for text input fields where you expect other content than integers - you can sanitize the input further using the parameter for this field.
'plugin_coffee_maker_customer_name' =>
array('type' => 'raw', 'regex' => '/^[A-Za-z ]+$/'),
  • Dropdown (option list) with multiple selectable items
array Use this for an array of possible results that comes from one field, separated with a particular delimiter
  • 'regex_ok' -> regular expression that the submit data needs to match against, similar to the examples given above for the type 'regex'
  • 'delimiter' -> character that is being used to delimit one array element from the other inside the string, e.g. a slash
'plugin_coffee_maker_ingredients' =>
array('type' => 'array', 'regex' => '/^[A-Za-z ]+$/', 'delimiter' => '|'),
As you can see in the table above, the scope selection "checkbox" does not only apply for the form field item checkbox, but as well for radio buttons and select dropdowns with one selectable item. Subsequently, if you plan to implement a radion button with three possible selections (e.g. for a selector that let's visitors choose the strength of their coffee from "weak" to "normal" to "strong"), the scope definition would be something like
'plugin_coffee_maker_strength' => array('type' => 'checkbox', 'min' => '0', 'max' => '2'),
with the possible options "0" corresponding to "weak", "1" to "normal" and "2" to "strong" (but we're going to determine that in a later step; for now, we'll just set the possible number of options to choose from).

6. Populate form options

The radio buttons, checkboxes and select dropdowns need to be populated with values based on the corresponding values. That's why you need to keep on editing c:\mysite\plugins\coffee_maker\admin.php, find
// Set the option output stuff 
and add below it into a line of it's own a small code block that determines the state of the radio button, checkbox or select dropdown. The code differs slightly, depending on the number of possible states: a checkbox can only have two states (checked or not checked). A radio buttons can have two or more states.
For our example from above that is meant to create a checkbox that toggles on or off wether the coffee will be served with a donut or not, the code you would have to add would be:
if ($CONFIG['plugin_coffee_maker_donut'] == '1') {
	$option_output['plugin_coffee_maker_donut'] = 'checked="checked"';
} else {
	$option_output['plugin_coffee_maker_donut'] = '';
}
Once more, you need of course to edit the section highlighted in orange in the line above in a similar manner as for the install and the other sections above - that section needs to correspond to the config record name that you have chosen when you decided for a plugin record name.
For a radio button (we'll use the other example from above that determines the strength of the coffee with three possible options to choose from), the corresponding code would be
if ($CONFIG['plugin_coffee_maker_strength'] == '0') {
	$option_output['plugin_coffee_maker_strength'][0] = 'checked="checked"';
	$option_output['plugin_coffee_maker_strength'][1] = '';
	$option_output['plugin_coffee_maker_strength'][2] = '';
} elseif ($CONFIG['plugin_coffee_maker_strength'] == '1') {
	$option_output['plugin_coffee_maker_strength'][0] = '';
	$option_output['plugin_coffee_maker_strength'][1] = 'checked="checked"';
	$option_output['plugin_coffee_maker_strength'][2] = '';
} elseif ($CONFIG['plugin_coffee_maker_strength'] == '2') {
	$option_output['plugin_coffee_maker_strength'][0] = '';
	$option_output['plugin_coffee_maker_strength'][1] = '';
	$option_output['plugin_coffee_maker_strength'][2] = 'checked="checked"';
}

7. Output the form elements (rows)

In this step we finally output the actual form element - one for each plugin config option. The output basically is in HTML, with the PHP variables that we populated in the previous step shining through our HTML. That's why the entire code block that we're going to edit is using the heredoc syntax, which is the recommended method as per Coppermine coding guidelines. Subsequently, all PHP variables that are meant to shine through the HTML output are being put into curly brackets.
As the HTML in itself will differ substantially depending on the option type you have chosen in the initial step of this set of instructions, here's a list of code blocks that you need to paste into c:\mysite\plugins\coffee_maker\admin.php into a new line of it's own below
<!-- insert config option form code start -->

a. Checkbox

	<tr>
		<td>
			<label for="plugin_coffee_maker_donut" class="clickable_option">{$lang_plugin_coffee_maker['donut']}</label>
		</td>
		<td>
			<input type="checkbox" name="plugin_coffee_maker_donut" id="plugin_coffee_maker_donut" class="checkbox" value="1" {$option_output['plugin_coffee_maker_donut']} />
			<label for="plugin_coffee_maker_donut" class="clickable_option">{$lang_common['yes']}</label>
		</td>
	</tr>

b. Radio button

	<tr>
		<td style="vertical-align:top;">
			{$lang_plugin_coffee_maker['strength']}
		</td>
		<td>
			<input type="radio" name="plugin_coffee_maker_strength" id="plugin_coffee_maker_strength_0" class="radio" value="0" {$option_output['plugin_coffee_maker_strength'][0]} />
			<label for="plugin_coffee_maker_strength_0" class="clickable_option">{$lang_plugin_coffee_maker['weak']}</label><br />
			<input type="radio" name="plugin_coffee_maker_strength" id="plugin_coffee_maker_strength_1" class="radio" value="1" {$option_output['plugin_coffee_maker_strength'][1]} />
			<label for="plugin_coffee_maker_strength_1" class="clickable_option">{$lang_plugin_coffee_maker['normal']}</label><br />
			<input type="radio" name="plugin_coffee_maker_strength" id="plugin_coffee_maker_strength_2" class="radio" value="2" {$option_output['plugin_coffee_maker_strength'][2]} />
			<label for="plugin_coffee_maker_strength_2" class="clickable_option">{$lang_plugin_coffee_maker['strong']}</label>
		</td>
	</tr>
The example above of course will only work if you have three radio buttons; if your number of radio buttons differs, you have to add/remove lines, keeping an eye on the index numbers at the end of the strings (highlighted in dark blue in the example above).

c. Dropdown (select/option list)

	<tr>
		<td valign="top">
			{$lang_plugin_coffee_maker['taste']}
		</td>
		<td>
			<select name="plugin_coffee_maker_taste" id="plugin_coffee_maker_taste" class="listbox">
				<option value="0" {$option_output['plugin_coffee_maker_taste'][0]}>{$lang_plugin_coffee_maker['espresso']}</option>
				<option value="1" {$option_output['plugin_coffee_maker_taste'][1]}>{$lang_plugin_coffee_maker['italian']}</option>
				<option value="2" {$option_output['plugin_coffee_maker_taste'][2]}>{$lang_plugin_coffee_maker['latte']}</option>
			</select>
		</td>
	</tr>

d. Text input field (one line), both for numerical input (integers) as well as any other textual input

	<tr>
		<td valign="top">
			{$lang_plugin_coffee_maker['sugar_lumps']}
		</td>
		<td>
			<input type="text" name="plugin_coffee_maker_sugar" id="plugin_coffee_maker_sugar" class="textinput" size="2" maxlength="2" value="{$CONFIG['plugin_coffee_maker_sugar']}" {$option_output['plugin_coffee_maker_sugar']} />
			{$lang_plugin_coffee_maker['pieces']}
		</td>
	</tr>

e. Text input field (multiple lines, aka textarea)

	<tr>
		<td valign="top">
			{$lang_plugin_coffee_maker['remarks']}
		</td>
		<td>
			<textarea name="plugin_coffee_maker_remarks" id="plugin_coffee_maker_remarks" class="textinput" rows="7" cols="40" style="width:100%" {$option_output['plugin_coffee_maker_remarks']} />{$CONFIG['plugin_coffee_maker_remarks']}</textarea>
		</td>
	</tr>

8. Create the needed translation strings

Basically, we're done already, except for the language strings that we had to come up with in the form elements: our config option needed a name and maybe some explanatory text, and the option labels (if applicable) required additional strings. Those strings need to be defined in the language file: our plugin template already contains an English language file that we need to populate no matter what, that's why you need to edit that file (c:\mysite\plugins\coffee_maker\lang\english.php) with your plain text editor as well. The structure of the language files is pretty straightforward: you just define array records as suggested in the translation guide for "regular" Coppermine language files that applies for plugin language files just as well. Just keep in mind that apostrophes (single quotes) in your language strings need to be escaped with a leading backslash.
For the examples given above, the following translation strings would have to be added to the language file:
$lang_plugin_coffee_maker['donut'] = 'Donut';
$lang_plugin_coffee_maker['strength'] = 'Strength';
$lang_plugin_coffee_maker['weak'] = 'weak';
$lang_plugin_coffee_maker['normal'] = 'normal';
$lang_plugin_coffee_maker['strong'] = 'strong';
$lang_plugin_coffee_maker['taste'] = 'Taste';
$lang_plugin_coffee_maker['espresso'] = 'Espresso';
$lang_plugin_coffee_maker['italian'] = 'Italian';
$lang_plugin_coffee_maker['latte'] = 'Latte';
$lang_plugin_coffee_maker['sugar_lumps'] = 'Sugar lumps';
$lang_plugin_coffee_maker['pieces'] = 'pieces';
$lang_plugin_coffee_maker['remarks'] = 'Remarks';
This is all that needs to be done to add config options to your custom plugin. Remember though that adding the option records alone will of course not do anything yet in terms of functionality of your plugin - you need to come up with code as well that is making use of your plugin's config options. When using your config options from within functions, don't forget to set the scope of the variable $CONFIG using the keyword global.
Keep in mind as well (before testing your plugin's config options) that you need to uninstall and re-install it, as the database records are being created during plugin install only.