In this tutorial you will learn more about the basics of plugin development for Piwigo. This tutorial is a follow up on Tutorial: Hello World!. The entire plugin can be installed from the extension gallery under the name 'Copyrights'. Note that this tutorial follows the v1.0 revision of this plugin.
Since the plugin is relatively large to put all the code on this page, we only explicitly put that code here that is important and clarifies the ideas explained. The reader should read the rest of the code from the sources pointed to above.
First of all, download the source code of the Skeleton plugin. We will use that as basis for this plugin.
With this plugin we want to achieve the following:
Therefore, we will write code for:
For the admin panel we need to do two things. First add a link to the plugins menu, second create an admin page.
To add a link to the menu of plugins we put the following code in main.inc.php. This is our first serious encounter with event handlers. To see what events are available we refer to the Event tracer plugin.
/* +-----------------------------------------------------------------------+ * | Plugin admin | * +-----------------------------------------------------------------------+ */ // Add an entry to the plugins menu add_event_handler('get_admin_plugin_menu_links', 'copyrights_admin_menu'); function copyrights_admin_menu($menu) { array_push( $menu, array( 'NAME' => 'Copyrights', 'URL' => get_admin_plugin_menu_link(dirname(__FILE__)).'/admin.php' ) ); return $menu; }
This adds an event handler to the event 'get_admin_plugin_menu_links'. The added handler is the function 'copyrights_admin_menu' that we define right below it. It consists of a simple array_push() to add our link to the plugins menu.
Lets move on to the content of admin.php, since that is what we are linking to.
We would like to do several things on the admin page. First of all, we want to be able to create plugins. Next we should be able to view them, and lastly, we want to be able to edit existing copyrights.
Firstly, however, we load the language file and perform a security check.
load_language('plugin.lang', COPYRIGHTS_PATH); // Check access and exit when user status is not ok check_status(ACCESS_ADMINISTRATOR);
The rest of the code of the admin page does not add much to the learning of plugin writing for Piwigo. It is best read from source directly. The code is commented farely well.
It is, however, good to mention the special use of the '$tab' variable. This is a variable that is used by Piwigo to create the tabbed layout they use. The Copyrights plugin makes use of this variable for deleting and updating Copyrights as if those are 'unvisible' tabs.
The Batch manager comes with two modes, so we will have to write extensions to both.
Here we will again use event handlers to extend the functionality of Piwigo. We use two of them:
// Add copyrights drop down menu to the batch manager add_event_handler('loc_end_element_set_global', 'copyrights_batch_global'); // Add handler to the submit event of the batch manager add_event_handler('element_set_global_action', 'copyrights_batch_global_submit', 50, 2);
Now we should specify the contents of the functions 'copyrights_batch_global' and 'copyrights_batch_global_submit'.
function copyrights_batch_global() { global $template; load_language('plugin.lang', dirname(__FILE__).'/'); // Assign the template for batch management $template->set_filename('CR_batch_global', dirname(__FILE__).'/batch_global.tpl'); // Fetch all the copyrights and assign them to the template $query = sprintf( 'SELECT `cr_id`,`name` FROM %s WHERE `visible`<>0 ;', COPYRIGHTS_ADMIN); $result = pwg_query($query); $CRoptions = array(); while ($row = pwg_db_fetch_assoc($result)) { $CRoptions[$row['cr_id']] = $row['name']; } $template->assign('CRoptions', $CRoptions); // Add info on the "choose action" dropdown in the batch manager $template->append('element_set_global_plugins_actions', array( 'ID' => 'copyrights', // ID of the batch manager action 'NAME' => l10n('Edit copyright'), // Description of the batch manager action 'CONTENT' => $template->parse('CR_batch_global', true) ) ); }
As can be seen, we again load the language file. Next we add the contents of 'batch_global.tpl' to the template and finally assign the list of copyrights obtained from the database to this template.
The file 'batch_global.tpl' contains a small bit of Smarty code to create a copyright selector.
<!-- Template for the copyright selector --> <div id="copyrights"> {'Copyright'|@translate} <select name="copyrightID"> <option value="0">--</option> {html_options options=$CRoptions} </select> </div>
When the 'Submit' button is clicked, an event is fired. We attached the following function to this event.
// Process the submit action function copyrights_batch_global_submit($action, $collection) { // If its our plugin that is called if ($action == 'copyrights') { $crID = pwg_db_real_escape_string($_POST['copyrightID']); // Delete any previously assigned copyrights if (count($collection) > 0) { $query = sprintf( 'DELETE FROM %s WHERE media_id IN (%s) ;', COPYRIGHTS_MEDIA, implode(',', $collection)); pwg_query($query); } // Add the copyrights from the submit form to an array $edits = array(); foreach ($collection as $image_id) { array_push( $edits, array( 'media_id' => $image_id, 'cr_id' => $crID, ) ); } // Insert the array into the database mass_inserts( COPYRIGHTS_MEDIA, // Table name array_keys($edits[0]), // Columns $edits // Data ); } }
So we first check whether it was our action ('Edit copyright') was chosen in the action list of the Batch Manager. If so, we obtain the chosen copyright id from the form $_POST variable.
Next we assign the the copyrights to all photos in the $collection, by first deleting any previously made assignments, and afterwards inserting the whole bunch with Piwigo's mass_inserts function.
To extend the 'Single Mode' or 'Unit Mode' of the Batch Manager, we have to use prefilters. Prefilters are a really nifty feature of Smarty code. See their site for more information on all the functionality.
When speaking for Piwigo, generally a prefilter can be attached to any file X.tpl that exists in the template folder.
To attach a prefilter to such a file, one should find an event near to where the X.tpl is being used. This will be more clear below.
For the Copyrights plugin, we want to modify the Smarty code of the batch_manager_unit.tpl file. We therefore use the following code.
// Add event handlers for the prefilter add_event_handler('loc_end_element_set_unit', 'CR_set_prefilter_batch_single', 55 );
This attaches the function 'CR_set_prefilter_batch_single' to the event 'loc_end_element_set_unit' which is fired when Piwigo starts loading the Single Mode page of the Batch Manager.
The event handler function then attaches a prefilter to batch_manager_unit.tpl.
// Add a prefilter to the template function CR_set_prefilter_batch_single() { global $template; $template->set_prefilter('batch_manager_unit', 'CR_batch_single'); }
This means that when Smarty starts rendering batch_manager_unit.tpl, it will first execute CR_batch_single.
That particular function will of course add the copyright selector to the page.
// Insert the copyright selector to the template function CR_batch_single($content, &$smarty) { $search = "#<td><strong>{'Creation date'#"; // We use the <tr> from the Creation date, and give them a new <tr> $replacement = '<td><strong>{\'Copyright\'|@translate}</strong></td> <td> <select id="copyright-{$element.ID}" name="copyright-{$element.ID}"> <option value="0">--</option> {html_options options=$CRoptions selected=$CRcopyrights[$element.ID]} </select> </td> </tr> <tr> <td><strong>{\'Creation date\''; return preg_replace($search, $replacement, $content); }
Now that the Smarty code of the Single Mode is extended we obviously want to assign the variables. This can be done with another event handler.
// Change the variables used by the function that changes the template add_event_handler('loc_end_element_set_unit', 'CR_add_batch_single_vars_to_template');
Now the smart reader might object that we have to event handlers attached to the same event, the one depending on the other. How do we assure that the prefilter is executed before we try to assign the variables? The even smarter reader might have noticed that the first event_handler line ended with ', 55 );'. This number 55 states the priority of the event handler, which defaults to 50. Hence the first event handler will indeed be executed before the second.
The contents of the second event handler are as follows.
// Assign the variables to the Smarty template function CR_add_batch_single_vars_to_template() { global $template; load_language('plugin.lang', dirname(__FILE__).'/'); // Fetch all the copyrights and assign them to the template $query = sprintf( 'SELECT `cr_id`,`name` FROM %s WHERE `visible`<>0 ;', COPYRIGHTS_ADMIN); $result = pwg_query($query); $CRoptions = array(); while ($row = pwg_db_fetch_assoc($result)) { $CRoptions[$row['cr_id']] = $row['name']; } $template->assign('CRoptions', $CRoptions); // Get the copyright for each element $query = sprintf( 'SELECT `media_id`, `cr_id` FROM %s ;', COPYRIGHTS_MEDIA); $result = pwg_query($query); $CRcopyrights = array(); while ($row = pwg_db_fetch_assoc($result)) { $CRcopyrights[$row['media_id']] = $row['cr_id']; } // Assign the copyrights to the template $template->assign('CRcopyrights', $CRcopyrights); }
Note that we again load the language file. Furthermore we just select the visible copyrights from the database and assign them to the copyright selector.
Finally we want to really do something with the selected copyrights. So we will have to attach something to the submit button. We will again use an event handler.
add_event_handler('loc_begin_element_set_unit', 'CR_batch_single_submit', 50 );
The content of this event handler checks whether the $_POST['submit'] variable is set. If so it executes some lines of code that very closely resemble the submit code for the Global Mode.
// Catch the submit and update the copyrights tables function CR_batch_single_submit() { if (isset($_POST['submit'])) { // The image id's: $collection = explode(',', $_POST['element_ids']); // Delete all existing id's of which the copyright is going to be set if (count($collection) > 0) { $query = sprintf( 'DELETE FROM %s WHERE media_id IN (%s) ;', COPYRIGHTS_MEDIA, implode(',', $collection)); pwg_query($query); } // Add all copyrights to an array $edits = array(); foreach ($collection as $image_id) { // The copyright id's $crID = pwg_db_real_escape_string($_POST['copyright-'.$image_id]); array_push( $edits, array( 'media_id' => $image_id, 'cr_id' => $crID, ) ); } // Insert the array to the database mass_inserts( COPYRIGHTS_MEDIA, // Table name array_keys($edits[0]), // Columns $edits // Data ); } }
This part of the plugin works in the same way as the single mode of the Batch Manager.
The reader should try to understand how this is done by looking at the source code of the plugin.
To show the copyrights to people visiting the gallery, we again make use of a prefilter. Just like the ways mentioned above. It should by now be pretty straight forward to learn this from the source code.
In the file maintain.inc.php one can put code that should be executed at install, activation or deletion of a plugin. The reader can see from the source code of the plugin that the Copyrights plugin inserts several default copyrights into the database by using this file.
One might have noticed from this tutorial that event handlers and in particular prefilters are a mighty tool in developing Piwigo extensions. Still it is difficult to cover everything in a single tutorial. Therefore, the reader should browse and study the source code of Piwigo itself and of other plugins, to gain a stronger sense of how to use these nifty features. In particular, one should also read tutorials on Smarty. Note for example that besides prefilters there also exist postfilters!