Creating A Simple Joomla! Plugin

What Is A Plugin?

A plugin is a software component that adds specific functionality to an existing program. Joomla supports plugins for customized we experience. A Joomla plugin is composed of two parts: an XML model, and a PHP application. The XML piece contains information about a plugin, provides a set of controls, and describes an administration view. The PHP piece contains at lest one event listener which is triggered by a Joomla event.

Motivation

A website administrator wants all content licensed under Creative Commons. Each article will display license information pertaining to the specific article, including: license icon, article title, article author, license type, and article link.

Creative Commons License
Article Title by Article Author is licensed under a Creative Commons License Type Here .
Based on a work at Site Name.

This could easily be achieved by manually appending the appropriate HTML to the end of each article.

<p align="CENTER">
   <
a href="http://creativecommons.org/licenses/license_type/4.0/"
rel="license">
    
<img style="border-width: 0;"
       src="https://i.creativecommons.org/l/license_type/4.0/88x31.png"
       alt="Creative Commons License" />
  
</a><br />
   Article Title by
   <a href="author.site.here" rel="cc:attributionURL">
  
Article Author
   </a>
   is licensed under a
   <a href="http://creativecommons.org/licenses_type/4.0/"
     rel="license">
  
Creative Commons License Type Here
   </a>.<br />
   Based on a work at
   <a href="your.site.here/path_to_article">Site Name</a>.
</p>

However, the site administrator does not want to do this manually to each page. The manual method also leaves room for error. Furthermore, this method is not dynamic. In the case that an article name is changed or migrated to a new domain, the administrator will have to edit the HTML. In the worst case scenario, the administrator will want to change the license terms of the license, in which case each page will have to be edited manually.

So, this project requires that we retrieve server and article information on each page request, and then dynamically generate a license, which will be appended to the article.

The Empty Plugin

To start with, we will create a new plugin, containing a single XML file and a single PHP file, which does nothing, yet. We will then package the package the plugin contents into a tar archive. This archive will be uploaded and installed onto a Joomla server.

Create a new folder called "creativecommons". Create a file called creativecommons.xml, this is our model. Copy the following XML starter code into this file.

< ?xml version = "1.0" encoding = "utf-8"? >
< extension
   version = "1.0"
   type = "plugin"
  
group = "content"
  
method = "upgrade" >
  < name >plg_content_creativecommons< /name >
  < author >Jesse Riggs< /author >
  < creationDate >July 2016< /creationDate >
  < copyright >
   
Copyright (C) 2016 Jesse Riggs. All rights reserved.
 
< /copyright >
  < license >
   
GNU General Public License version 2 or later; see LICENSE.txt
 
< /license >
  < authorEmail>< /authorEmail >
  < authorUrl >< /authorUrl >
  < version >1.0< /version >
  < description >Creative Commons License Plugin< /description >
  < files >
   < filename plugin = "creativecommons" >
    
creativecommons.php
  
< /filename >
  < /files >
  < languages >
  < /languages >
  < config >
  < fields name = "params" >
   < fieldset name = "basic" >
     <!-- We will add parameters here, later. -->
   < /fieldset >
   < /fields >
  < /config >
< /extension >

What this XML tells Joolmla is that there is an extension of type plugin in group content, and will be installed into the subdirectory plugins/content/. The plugin is named plg_content_creativecommons, and will be listed under extensions with this name. This file also tell Joomla that there is a PHP file associated with this extension, creativecommons.php. There is a tag called fields, which is a set of parameters that will both be shown in the admin view and be passed to creativecommons.php.

Now we will need to define a class inside the PHP file.

<?php

/**
 * Joomla plugin exercize
 * @package Joomla.Plugin
  * @subpackage Content.CreativeCommons
  *
  * @copyright Copyright (C) 2016 Jesse Riggs All rights reserved.
  * @license GNU General Public License version 2 or later; see
LICENSE.txt
  */

defined( '_JEXEC' ) or die;
use Joomla\Registry\Registry;

/**
  * Creative Commons License Plugin
  *
  * @since 3.5
  */

class PlgContentCreativeCommons extends JPlugin
{

    // We will write a function here, later.

}

This is a simple PHP class with no functions. It is empty. But, this is the framework that we will use to construct a functioning plugin.

Now that we have the two fundamental pieces of a Joomla plugin, we can package them into an archive which can be installed in Joomla. While inside the directory creativecommons, run the following bash command:

$ tar cvvzf ../creativecommons.tar.gz *

This new package can be uploaded and installed from Extensions > Manage > Install > Upload From File. The plugin can now be found under Extensions > Plugins > plg_content_creativecommons.

Adding Prameters

The file creativecommons.xml allows us to add fields which model our plugin's parameters. Joomla uses these fields to construct a set of controls which are displayed in the administrator view. Fields include buttons, lists and categories from which the administrator can choose values to be mapped to their respective field name. At plugin execution, these values will be passed to event handlers as parameters.

Add the following code to creativecommons.xml, under the tag <fieldset name="basic">.

< field name = "cc_adaptations_list"
  type = "list"
  default = "derivatives"
  label = "Adaptations"
  description
   = "Allow adaptations of your work to be shared?" >
   < option value = "derivatives" >Yes< /option >
   < option value = "no_derivatives" >No< /option >
   < option value = "share_alike" >Yes, as long as others share alike< /option >
  < /field >
  < field name = "cc_commercial_button"
    type = "radio"
    class = "radio btn-group"
    default = "0"
    label = "Commercial"
    description
     = "Allow commercial uses of your work?" >
   < option value = "1" >Yes< /option >
   < option value = "0" >No< /option >
  < /field >
  < field name = "cc_icon_list"
    type = "list"
    default = "normal"
    label = "Icon"
    description
     = "Icon to be displayed with license" >
    < option value = "normal" >Normal< /option >
    < option value = "compact" >Compact< /option >
   < /field >
< field name = "category_tobe_included"
  type = "category"
  extension = "com_content"
  default = ""
  label = "Included Categories"
  description
   = "Multiple select the categories which will include this plugin."
  multiple = "true"
  size = "5" / >

If we inspect the plugin now, we will find new fields to populate; magic. ;)

Admin view with fields

Events And Handlers

Joomla plugins are triggered by events, so let's look at how this works. Joomla implements an observer design pattern to handle events, at the heart of which is JEventDispatcher. The dispatcher is a subject that maintains a list of observers and updates their states as events are triggered.

JPlugin extends an observer class JEvent such that, when JEventDispatcher is notified of an event, JPlugin will be updated and the appropriate event handler will be called. JPlugin is a useful abstraction which preforms common event tasks for us.

Diagram of observer class

Ordinarily, an observer pattern would require the coder to attach an observer to it's subject.Since plugins are decendents of JEvent, and JEvent is a decendent of JObserver the constructorautomatically attaches the eventhandler object to the dispatcher(which is a JObservable descendant). This means when the Event Handlers are initialized they are automatically added to the observer stack of the dispatcher.

A complete list of Joomla events can be found at https://docs.joomla.org/Plugin/Events. For this plugin, we are looking to append, to article text, a string of HTML. We will use an event which notifies the dispatcher that an article model has been created, and that the article text has been retrieved from the database. For this, we listen for onContentPrepare. When this event is triggered by ContentViewArticle, a reference to an article object will be passed to the dispatcher and then passed to any observer with an onContentPrepare event handler.

Dummy License

Now that we know which event we want to handle, lets throw together a simple dummy license. All we need to do is append some text to the text element of the article argument. Let's add the event handler to our PlgContentCreativeCommons class.

/**
  * Plugin that displays Creative Commons license
  *
  * @param string $context The context of the content being
    passed to the plugin.
  * @param mixed &$article An object with a "text" property
  * @param mixed $params Additional parameters.
  * @param integer $page Optional page number. Unused. Defaults
    to zero.
  *
  * @return boolean True on success.
  */

public function onContentPrepare(
$context, &$article, &$params, $page = 0 )
{
  // Let's see if this is an appropriate context in which to
  // display a license.

  $allowed_contexts = array( 'com_content.category',
  'com_content.article', 'com_content.featured' );
  if ( !in_array( $context, $allowed_contexts ) )
  {
    return true;
  }

  // Later, we will create a license here.

  $license ="This is a license.";
  $article->text .= $license;
  return true;
}

Accessing Parameters

A reference to a composite object called $params is passed from the dispatcher to its observers when an event is triggered. $params contains administrator populated states from control panel. Recall the Adaptations field we added to the file creativecommons.xml, which now displays a list. The list name, cc_adaptations_list, is now mapped to its value. Likewise, $category_tobe_included is mapped to a list of included categories.

Each Joomla article belongs to a category, even if that category is uncategorized. This property allows a Joomla developer to decide which articles are to be affected by a plugin. The administration control panel allows a user to select multiple categories from the category field. When plg_content_creativecommons handles onContentPrepare, we would like it to return true, without modifying the article, upon call.

// Let's see if this article is in an appropriate catagory in
// which to display a license.

$enable_cc_license = '0';
$category_tobe_included
  = $this -> params -> get( 'category_tobe_included' );
if( isset( $article -> id ) ) {
  if( is_array( $category_tobe_included ) ) {
    if( in_array( $article->catid, $category_tobe_included ) )
      $enable_cc_license = '1';
    }
  }
if( $enable_cc_license == '0' )
  return true;

Let's consider how a Creative Commons license should look. First we want to generate a description that will be presented to a client that will look something like "Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License". Then we will want to generate a code that creativecommons.org will use to find the license description and accompanying image, e.g. "by-nc-sa". This is done by gathering license information provided by $params.

// If params->cc_adaptations_list = derivatives then set
// share to ""

// If params->cc_adaptations_list = no_derivatives then set
// share to"-nd"

// If params->cc_adaptations_list = share_alike then set share
// to"-sa"

$share = "";
$shareDiscription = "";
$cc_adaptations = $this->params->get( 'cc_adaptations_list' );
switch( $cc_adaptations ){
  case"no_derivitives":
    $share ="-nd";
    $shareDiscription ="-NoDerivitives";
    break;
  case"share_alike":
    $share ="-sa";
    $shareDiscription ="-ShareAlike";
    break;
  default:
    break;
}

// If params->cc_commercial_button = 1 then $commercial is ""
// If params->cc_commercial_button = 0 then $commercial is
// "-nc"

$commercial = "";
$commercialDiscription = "";
if( $this->params->get( "cc_commercial_button" ) == '0' ) {
  $commercial ="-nc";
  $commercialDiscription ="-NonCommercial";
}
$licenseType = "by" . $commercial.$share;
$licenseDescription
  ="Attribution" . $commercialDiscription . $shareDiscription;

// If params->cc_icon_list = normal then $icon is "88x31.png"
// If params->cc_icon_list = compact then $icon is "80x15.png"
$icon ="88x31.png";
if( $this->params->get( "cc_icon_list" ) == "compact" )
  $icon ="80x15.png";

Next, we want to gather some information about the article, such as author name and URL.

$title = $article->title;
$author_id = $article->created_by;
$user = JFactory::getUser( $author_id );
$author = $user->name;
$siteURL = $_SERVER[ 'SERVER_NAME' ];
$pageURL = $_SERVER[ 'REQUEST_URI' ];

We now have enough information to construct an HTML string to append to the article.

$license ="<p align=\"CENTER\"><a href=\"http://creativecommons.org/licenses/$licenseType/4.0/\" rel=\"license\">";
$license .="<img style=\"border-width: 0;\" src=\"https://i.creativecommons.org/l/$licenseType/4.0/$icon\"";
$license .="alt=\"Creative Commons License\" /></a><br />$title by ";
$license .="<a href=\"http://$siteURL\" rel=\"cc:attributionURL\">$author";
$license .="</a> is licensed under a <a href=\"http://creativecommons.org/licenses/".$licenseType."/4.0/\" rel=\"license\">";
$license .="Creative Commons $licenseDescription 4.0 International License</a>.<br />";
$license .="Based on a work at <a href=\"$pageURL\" rel=\"dct:source\">$siteURL</a>.</p>";

Now, all we have to do is get a reference to the article text and then append the license to it.

$article->text .= $license;

Viola, you now have an implementation of Creative Commons to present in articles, just like the one below.

Creative Commons License
Creating A Simple Joomla! Plugin by Jesse Riggs is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Based on a work at jesse-riggs.com.