Mastering System Events
In this article we are going to explain how to develop a new Joomla! plugin from scratch by focusing on the extendibility of VikAppointments.
Preamble
The goal of this guide is to develop a new plugin to implement the management of the difficulty of the services in VikAppointments. Even if you are not interested in using a similar feature, you might still consider to follow these guide to understand how to build a plugin.
The development of this plugin is divided into several parts:
- Creating the basic structure of the plugin;
- Introducing the field to specify the services difficulty;
- Defining the callback to save the difficulty;
- Display the difficulty into the front-end.
At the end of the article you'll find a .zip archive containing the complete structure of the plugin described in this guide.
Create Plugin Structure
The creation of a new plugin follows the directives defined by the Joomla! platform, which can be seen from the link below:
https://docs.joomla.org/J3.x:Creating_a_Plugin_for_Joomla
The first thing to do is creating a new folder with the following files structure:
- plg_vikappointments_servicesdifficulty
- servicesdifficulty.xml
- servicesdifficulty.php
- assets
- style.css
- script.js
- sql
- install.mysql.utf8.sql
- uninstall.mysql.utf8.sql
- languages
- en-GB.plg_vikappointments_servicesdifficulty.ini
- en-GB.plg_vikappointments_servicesdifficulty.sys.ini
XML
Once the files structure has been created, we need to open the XML file and add the code below.
<?xml version="1.0" encoding="utf-8"?>
<extension version="3.1" type="plugin" group="vikappointments">
<name>plg_vikappointments_servicesdifficulty</name>
<author>[YOUR_NAME]</author>
<creationDate>[CREATION_DATE]</creationDate>
<copyright>[COPYRIGHT_INFO]</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>[YOUR_EMAIL]</authorEmail>
<authorUrl>[YOUR_WEBSITE]</authorUrl>
<version>1.0</version>
<description>PLG_VIKAPPOINTMENTS_SERVICESDIFFICULTY_XML_DESCRIPTION</description>
<install>
<sql>
<file driver="mysql" charset="utf8">sql/install.mysql.utf8.sql</file>
</sql>
</install>
<uninstall>
<sql>
<file driver="mysql" charset="utf8">sql/uninstall.mysql.utf8.sql</file>
</sql>
</uninstall>
<files>
<filename plugin="servicesdifficulty">servicesdifficulty.php</filename>
<folder>assets</folder>
<folder>sql</folder>
</files>
<languages>
<language tag="en-GB">languages/en-GB.plg_vikappointments_servicesdifficulty.ini</language>
<language tag="en-GB">languages/en-GB.plg_vikappointments_servicesdifficulty.sys.ini</language>
</languages>
</extension>
PHP
Move to the PHP file and define the class below to start supporting the plugin processor.
<?php
// no direct access
defined( '_JEXEC' ) or die;
class plgVikAppointmentsServicesdifficulty extends JPlugin
{
/**
* Load the language file on instantiation. Note this is only available in Joomla 3.1 and higher.
* If you want to support 3.0 series you must override the constructor
*
* @var boolean
* @since 3.1
*/
protected $autoloadLanguage = true;
/**
* The application instance.
*
* @var JApplication
* @since 1.5
*/
protected $app;
}
INI
If you are interested in localizing the plugin, you should use the INI files to add all the translations used by the plugin.
The .sys file is used to display the configuration parameters of the plugin. In our case:
PLG_VIKAPPOINTMENTS_SERVICESDIFFICULTY="VikAppointments - Services Difficulty"
PLG_VIKAPPOINTMENTS_SERVICESDIFFICULTY_XML_DESCRIPTION="Adds support to the services difficulty."
The other file is used to translate the contents displayed by the plugin events. We'll leave it blank for the moment.
SQL - Install
Open the sql/install.mysql.utf8.sql file and introduce the code below to automatically alter the services database table during the plugin installation.
ALTER TABLE `#__vikappointments_service`
ADD COLUMN `difficulty` int(3) unsigned DEFAULT NULL;
SQL - Uninstall
Open the sql/uninstall.mysql.utf8.sql file and introduce the code below to automatically drop the created column during the uninstallation of the plugin.
ALTER TABLE `#__vikappointments_service`
DROP COLUMN `difficulty`;
Display Difficulty Parameter
The difficulty parameter should be displayed within the management page of a service. It is possible to introduce the field in any supported fieldset. In this example we are going to display an input number at the end of the Details > Service fields box. This can be accomplished by defining a new class method able to respond to the hook used to display new fields. Therefore it is needed to open the created PHP file and copy the code below before the curly brace closure.
public function onDisplayViewService($view)
{
return [
// inject parameters within the details fieldset
'service' => [
// create difficulty input (the key is equals to the parameter name)
'difficulty' => [
// display an input number
'type' => 'number',
// display the input label
'label' => JText::_('PLG_VAP_SERDIFF_INPUT_LABEL'),
// display an optional description of the field
'help' => JText::_('PLG_VAP_SERDIFF_INPUT_DESC'),
// define the minimum supported value (0%)
'min' => 0,
// define the maximum supported value (100%)
'max' => 100,
// define the number of supported steps
'step' => 1,
// define the default value to use
'default' => 0,
// define the last saved value, if any
'value' => isset($view->service->difficulty) ? (int) $view->service->difficulty : null,
],
],
];
}
In order to localize the label and the description of the field, it is needed to open the languages/en-GB.plg_vikappointments_servicesdifficulty.ini file and insert the code below:
PLG_VAP_SERDIFF_INPUT_LABEL="Difficulty"
PLG_VAP_SERDIFF_INPUT_DESC="Enter here the difficulty (in percentage) of this service."
The resulting HTML should look like this one:
Save Difficulty Parameter
Now it is time to handle the saving process by creating a new callback (or event). The latter will be able to fetch the specified difficulty from the request and bind it into the object/array that is going to be saved. Under the method previously created, we need to create an additional method built as the following one.
public function onBeforeSaveService(&$data)
{
$data = (array) $data;
// get service difficulty from request
$difficulty = $this->app->input->getUint('difficulty', null);
if (!is_null($difficulty))
{
// difficulty specified, bind it into the array to save
$data['difficulty'] = $difficulty;
}
return true;
}
Display Difficulty in Site
The management of the difficulty parameter has been completed. Now we can move to the front-end section to display the difficulty with a sort of progress bar. In this example we decided to introduce the difficulty within the service details page, immediately before the price.
Since there is no hook to manipulate the HTML, it is still possible to introduce new elements into the document via Javascript. In our case, it is enough to include the created .js file (and .css one) just before loading the details of a service.
JS
You should open the created assets/script.js file and introduce the following code.
(function($) {
'use strict';
$(function() {
// create progress bar
const progressBar = $(
'<span class="progress-bar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></span>'
);
// update progress bar value
progressBar.css('width', window['serviceDifficulty'] + '%');
progressBar.attr('aria-valuenow', window['serviceDifficulty']);
// wrap progress bar in a parent
const progress = $('<span class="progress"></span>').append(progressBar);
// create info box
const infoBox = $('<span class="vap-price-info-box service-difficulty"></span>');
// append difficulty label
infoBox.append(
$('<span class="difficulty-label"></span>')
.text(Joomla.JText._('PLG_VAP_SERDIFF_INPUT_LABEL'))
);
// append progress bar
infoBox.append(progress);
// append difficulty value
infoBox.append(
$('<span class="difficulty-value"></span>')
.text(window['serviceDifficulty'] + '%')
);
// insert info box before the service price
infoBox.insertBefore('.vapempcontactsp .service-price');
});
})(jQuery);
CSS
At this point we can open the assets/style.css file and define some rules to improve the layout of the progress box.
.vapempcontactsp .service-difficulty .difficulty-label {
font-weight: normal;
}
.vapempcontactsp .progress {
background-color: #eaedf0;
border-radius: .25rem;
font-size: .75rem;
}
.vapempcontactsp .progress,
.vapempcontactsp .progress-bar {
display: inline-block;
height: 20px;
}
.vapempcontactsp .progress {
width: 100px;
overflow: hidden;
margin: -4px 6px;
text-align: left;
}
.vapempcontactsp .progress-bar {
background-color: #da1a35;
}
PHP
Once the CSS and the JS have been properly defined, it is time to load them via PHP. Open the PHP file and create a new method built as the following one.
public function onBuildServiceSearchData($service)
{
// check whether the service specifies a difficulty
if (!empty($service->difficulty))
{
$root = JUri::root() . 'plugins/vikappointments/servicesdifficulty/assets/';
// yep, load our assets
JHtml::_('script', $root . 'script.js', ['version' => '1.0']);
JHtml::_('stylesheet', $root . 'style.css', ['version' => '1.0']);
// assign service difficulty to a global variable in order to make it accessible from our script file
JFactory::getDocument()->addScriptDeclaration("window['serviceDifficulty'] = {$service->difficulty};");
// localize "Difficulty" label for the script file
JText::script('PLG_VAP_SERDIFF_INPUT_LABEL');
}
}
Here's how the front-end should display the difficulty bar under the details page of a service.
Conclusion
If you properly followed all the steps, you should now proceed with the installation of the plugin from the back-end of your Joomla. After the installation, don't forget to activate the plugin from the Extensions > Plugins section, otherwise VikAppointments wouldn't be able to trigger the specified events.
As pointed in the preamble section, here's the link to download the installer of the plugin described in this example.