Actions

J2.5

Difference between revisions of "Creating a content plugin"

From Joomla! Documentation

(Add reason to incomplete tags)
(Remove needs reviews)
 
(19 intermediate revisions by one user not shown)
Line 1: Line 1:
{{version|2.5}}{{incomplete|1.5 page needs updating 2.5+ info}}
+
{{version|2.5}}
 
==Description==
 
==Description==
 
There are lots of abilities of the use of a Content Plugin. They all have to do with the display of your content and so your articles. You will need at least two files for this Plugin. An XML file and a PHP file. Because there are so many differences between two Content Plugins in the PHP file, two examples of them will be explained in this document. Also a part about internationalization (with INI files) is added. And last but not least: the Joomla! Core Content Plugin coding examples and the quick tips.
 
There are lots of abilities of the use of a Content Plugin. They all have to do with the display of your content and so your articles. You will need at least two files for this Plugin. An XML file and a PHP file. Because there are so many differences between two Content Plugins in the PHP file, two examples of them will be explained in this document. Also a part about internationalization (with INI files) is added. And last but not least: the Joomla! Core Content Plugin coding examples and the quick tips.
Line 62: Line 62:
 
</source>
 
</source>
 
*'''Field name:''' The name of the parameter. You will need this when creating the PHP file.
 
*'''Field name:''' The name of the parameter. You will need this when creating the PHP file.
*'''Field type:''' You could choose between several types of parameters. Look at this document to learn something about the different types: [[Using the core parameter types]]
+
*'''Field type:''' You could choose between several types of parameters. Look at this document to learn something about the different types: [[Form_field|Different Joomla Form Fields]]
 
*'''Field default:''' The default setting for this parameter.
 
*'''Field default:''' The default setting for this parameter.
 
*'''Field label:''' The name of this parameter displayed in the edit screen of this Plugin in the Plugin Manager.
 
*'''Field label:''' The name of this parameter displayed in the edit screen of this Plugin in the Plugin Manager.
Line 96: Line 96:
 
This prevents people from directly accessing this PHP file.
 
This prevents people from directly accessing this PHP file.
  
After that, you should import the general plugin file of Joomla!'s library
+
After that to indicate that you're writing a Content plugin, add the following code.
<source lang="php">jimport( 'joomla.plugin.plugin' );</source>
+
 
+
To indicate that you're writing a Content plugin, add the following code.
+
 
<source lang="php">class plgContentNameofplugin extends JPlugin {</source>
 
<source lang="php">class plgContentNameofplugin extends JPlugin {</source>
 
Please note the use of capital letters. You should replace Nameofplugin by the name of your own plugin.
 
Please note the use of capital letters. You should replace Nameofplugin by the name of your own plugin.
 
Now, it's time to construct the plugin:
 
<source lang="php">/**
 
* Constructor
 
*
 
* @param object $subject The object to observe
 
* @param object $params  The object that holds the plugin parameters
 
* @since 1.5
 
*/
 
function plgContentNameofplugin( &$subject, $params )
 
{
 
parent::__construct( $subject, $params );
 
}</source>
 
  
 
You should choose now at which moment the plugin should be rendered. You can choose from the following:
 
You should choose now at which moment the plugin should be rendered. You can choose from the following:
  
* onBeforeContentSave : This is an event that is called right before the content is saved into the database.
+
* onContentBeforeDelete : This is an event that is called right before the content is deleted.
* onAfterContentSave : This is an event that is called after the content is saved into the database.
+
* onContentAfterDelete : This is an event that is called right after the content is deleted.
* onPrepareContent : This is the first stage in preparing content for output and is the most common point for content orientated plugins to do their work.
+
* onContentBeforeDisplay : This is a request for information that should be placed immediately before the generated content.
* onAfterDisplayTitle : This is a request for information that should be placed between the content title and the content body.
+
* onContentAfterDisplay: This is a request for information that should be placed immediately after the generated content.
* onBeforeDisplayContent : This is a request for information that should be placed immediately before the generated content.
+
* onContentBeforeSave : This is an event that is called right before the content is saved into the database.
* onAfterDisplayContent : This is a request for information that should be placed immediately after the generated content.
+
* onContentAfterSave : This is an event that is called after the content is saved into the database.
 +
* onContentAfterTitle : This is a request for information that should be placed between the content title and the content body.
 +
* onContentChangeState : This is an event that is called when the contents state is changed.
 +
* onContentPrepare : This is the first stage in preparing content for output and is the most common point for content orientated plugins to do their work.
  
 
More information on which type you should use for your plugin, including several examples, can be found here: [[Plugin events/Content]]
 
More information on which type you should use for your plugin, including several examples, can be found here: [[Plugin events/Content]]
  
===onBeforeContentSave===
+
===onContentBeforeDelete===
 +
Use the following code:
 +
<source lang="php"> function onContentBeforeDelete($context, $data)
 +
{
 +
//add your plugin codes here
 +
return true;
 +
}</source>
 +
The parameters context and data should contain the following:
 +
* context : The context of the content passed to the plugin.
 +
* data : The data relating to the content that is to be deleted.
 +
 
 +
===onContentAfterDelete===
 +
Use the following code:
 +
<source lang="php"> function onContentAfterDelete($context, $data)
 +
{
 +
//add your plugin codes here
 +
return true;
 +
}</source>
 +
The parameters context and data should contain the following:
 +
* context : The context of the content passed to the plugin.
 +
* data : The data relating to the content that was deleted.
 +
 
 +
===onContentChangeState===
 +
Use the following code:
 +
<source lang="php"> function onContentChangeState($context, $pks, $value)
 +
{
 +
//add your plugin codes here
 +
return true;
 +
}</source>
 +
The parameters context, pks and value should contain the following:
 +
* context : The context of the content passed to the plugin.
 +
* pks : A list of primary key ids of the content that has changed state.
 +
* value : The value of the state that the content has been changed to.
 +
 
 +
===onContentBeforeSave===
 
Use the following code:
 
Use the following code:
<source lang="php"> function onBeforeContentSave( &$article, $isNew )
+
<source lang="php"> function onContentBeforeSave($context, &$article, $isNew)
 
{
 
{
global $mainframe;
 
 
//add your plugin codes here
 
//add your plugin codes here
 
return true;
 
return true;
 
}</source>
 
}</source>
The parameters article and isNew should contain the following:
+
The parameters context, article and isNew should contain the following:
 +
* context : The context of the content passed to the plugin.
 
* article : A reference to the JTableContent object that is being saved which holds the article data.
 
* article : A reference to the JTableContent object that is being saved which holds the article data.
 
* isNew : A boolean which is set to true if the content is about to be created.
 
* isNew : A boolean which is set to true if the content is about to be created.
  
===onAfterContentSave===
+
===onContentAfterSave===
 
Use the following code:
 
Use the following code:
<source lang="php"> function onAfterContentSave( &$article, $isNew )
+
<source lang="php"> function onContentAfterSave($context, &$article, $isNew)
 
{
 
{
global $mainframe;
 
 
//add your plugin codes here
 
//add your plugin codes here
 
return true;
 
return true;
 
}</source>
 
}</source>
The parameters article and isNew should contain the following:
+
The parameters context, article and isNew should contain the following:
 +
* context : The context of the content passed to the plugin.
 
* article : A reference to the JTableContent object that was saved which holds the article data.
 
* article : A reference to the JTableContent object that was saved which holds the article data.
 
* isNew : A boolean which is set to true if the content was created.
 
* isNew : A boolean which is set to true if the content was created.
  
===onPrepareContent===
+
===onContentPrepare===
 
Use the following code:
 
Use the following code:
<source lang="php"> function onPrepareContent( &$article, &$params, $limitstart )
+
<source lang="php"> function onContentPrepare($context, &$article, &$params, $limitstart)
 
{
 
{
global $mainframe;
 
 
//add your plugin codes here
 
//add your plugin codes here
 
//no return value
 
//no return value
 
}</source>
 
}</source>
The parameters article, params and limitstart should contain the following:
+
The parameters context, article, params and limitstart should contain the following:
 +
* context : The context of the content passed to the plugin.
 
* article : A reference to the article that is being rendered by the view.
 
* article : A reference to the article that is being rendered by the view.
 
* params : A reference to an associative array of relevant parameters. The view determines what it considers to be relevant and passes that information along.
 
* params : A reference to an associative array of relevant parameters. The view determines what it considers to be relevant and passes that information along.
 
* limitstart : An integer that determines the "page" of the content that is to be generated. Note that in the context of views that might not generate HTML output, a page is a reasonably abstract concept that depends on the context.
 
* limitstart : An integer that determines the "page" of the content that is to be generated. Note that in the context of views that might not generate HTML output, a page is a reasonably abstract concept that depends on the context.
  
===onAfterDisplayTitle===
+
===onContentAfterTitle===
 
Use the following code:
 
Use the following code:
<source lang="php"> function onAfterDisplayTitle( &$article, &$params, $limitstart )
+
<source lang="php"> function onContentAfterTitle($context, &$article, &$params, $limitstart)
 
{
 
{
global $mainframe;
 
 
//add your plugin codes here
 
//add your plugin codes here
 
return '';
 
return '';
Line 174: Line 194:
 
                 // Most templates display this placeholder after the article separator.
 
                 // Most templates display this placeholder after the article separator.
 
}</source>
 
}</source>
The parameters article, params and limitstart should contain the following:
+
The parameters context, article, params and limitstart should contain the following:
 +
* context : The context of the content passed to the plugin.
 
* article : A reference to the article that is being rendered by the view.
 
* article : A reference to the article that is being rendered by the view.
 
* params : A reference to an associative array of relevant parameters. The view determines what it considers to be relevant and passes that information along.
 
* params : A reference to an associative array of relevant parameters. The view determines what it considers to be relevant and passes that information along.
 
* limitstart : An integer that determines the "page" of the content that is to be generated. Note that in the context of views that might not generate HTML output, a page is a reasonably abstract concept that depends on the context.
 
* limitstart : An integer that determines the "page" of the content that is to be generated. Note that in the context of views that might not generate HTML output, a page is a reasonably abstract concept that depends on the context.
+
 
===onBeforeDisplayContent===
+
===onContentBeforeDisplay===
 
Use the following code:
 
Use the following code:
<source lang="php"> function onBeforeDisplayContent( &$article, &$params, $limitstart )
+
<source lang="php"> function onContentBeforeDisplay($context, &$article, &$params, $limitstart)
 
{
 
{
global $mainframe;
 
 
//add your plugin codes here
 
//add your plugin codes here
 
return '';
 
return '';
Line 190: Line 210:
 
}</source>
 
}</source>
 
 
The parameters article, params and limitstart should contain the following:
+
The parameters context, article, params and limitstart should contain the following:
 +
* context : The context of the content passed to the plugin.
 
* article : A reference to the article that is being rendered by the view.
 
* article : A reference to the article that is being rendered by the view.
 
* params : A reference to an associative array of relevant parameters. The view determines what it considers to be relevant and passes that information along.
 
* params : A reference to an associative array of relevant parameters. The view determines what it considers to be relevant and passes that information along.
 
* limitstart : An integer that determines the "page" of the content that is to be generated. Note that in the context of views that might not generate HTML output, a page is a reasonably abstract concept that depends on the context.
 
* limitstart : An integer that determines the "page" of the content that is to be generated. Note that in the context of views that might not generate HTML output, a page is a reasonably abstract concept that depends on the context.
  
===onAfterDisplayContent===
+
===onContentAfterDisplay===
 
Use the following code:
 
Use the following code:
<source lang="php"> function onAfterDisplayContent( &$article, &$params, $limitstart )
+
<source lang="php"> function onContentAfterDisplay($context, &$article, &$params, $limitstart)
 
{
 
{
global $mainframe;
 
 
//add your plugin codes here
 
//add your plugin codes here
 
return '';
 
return '';
Line 206: Line 226:
 
}</source>
 
}</source>
 
 
The parameters article, params and limitstart should contain the following:
+
The parameters context, article, params and limitstart should contain the following:
 +
* context : The context of the content passed to the plugin.
 
* article : A reference to the article that is being rendered by the view.
 
* article : A reference to the article that is being rendered by the view.
 
* params : A reference to an associative array of relevant parameters. The view determines what it considers to be relevant and passes that information along.
 
* params : A reference to an associative array of relevant parameters. The view determines what it considers to be relevant and passes that information along.
Line 212: Line 233:
  
 
==PHP file - Example==
 
==PHP file - Example==
This example PHP file is about the Content Plugin load module. It is a plugin made for displaying modules within the articles.  
+
This example PHP file is about the Content Plugin load module from Joomla 2.5.11. It is a plugin made for displaying modules within the articles.  
The commented PHP file used in this example is the file which will be included in Joomla! 1.6. This has been done because the content plugin files in Joomla! 1.5 are not refactored yet to the new standards.
+
 
 
<source lang="php">
 
<source lang="php">
 
<?php
 
<?php
 
// The general information at the top of each file
 
// The general information at the top of each file
 
/**
 
/**
* @version $Id$
+
* @package Joomla.Plugin
* @package Joomla
+
* @subpackage Content.loadmodule
* @copyright Copyright (C) 2005 - 2009 Open Source Matters, Inc. All rights reserved.
+
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License, see LICENSE.php
+
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
+
*/
  
// No direct access allowed to this file
+
// no direct access
defined( '_JEXEC' ) or die( 'Restricted access' );
+
defined('_JEXEC') or die;
 
+
// Import Joomla! Plugin library file
+
jimport('joomla.plugin.plugin');
+
  
 
//The Content plugin Loadmodule
 
//The Content plugin Loadmodule
 
class plgContentLoadmodule extends JPlugin
 
class plgContentLoadmodule extends JPlugin
 
{
 
{
 +
protected static $modules = array();
 +
protected static $mods = array();
 
/**
 
/**
* Plugin that loads module positions within content
+
* Plugin that loads module positions within content
*/
+
*
// onPrepareContent, meaning the plugin is rendered at the first stage in preparing content for output
+
* @param string The context of the content being passed to the plugin.
public function onPrepareContent( &$row, &$params, $page=0 )
+
* @param object The article object.  Note $article->text is also available
 +
* @param object The article params
 +
* @param int The 'page' number
 +
*/
 +
// onContentPrepare, meaning the plugin is rendered at the first stage in preparing content for output
 +
public function onContentPrepare($context, &$article, &$params, $page = 0)
 
{
 
{
                // A database connection is created
+
// Don't run this plugin when the content is being indexed
$db = JFactory::getDBO();
+
if ($context == 'com_finder.indexer') {
 +
return true;
 +
}
 +
 
 
// simple performance check to determine whether bot should process further
 
// simple performance check to determine whether bot should process further
if ( JString::strpos( $row->text, 'loadposition' ) === false ) {
+
if (strpos($article->text, 'loadposition') === false && strpos($article->text, 'loadmodule') === false) {
 
return true;
 
return true;
 
}
 
}
  
// expression to search for
+
// expression to search for (positions)
$regex = '/{loadposition\s*.*?}/i';
+
$regex = '/{loadposition\s+(.*?)}/i';
 +
$style = $this->params->def('style', 'none');
 +
// expression to search for(modules)
 +
$regexmod = '/{loadmodule\s+(.*?)}/i';
 +
$title = null;
 +
$stylemod = $this->params->def('style', 'none');
  
// check whether plugin has been unpublished
+
// Find all instances of plugin and put in $matches for loadposition
if ( !$this->params->get( 'enabled', 1 ) ) {
+
// $matches[0] is full pattern match, $matches[1] is the position
$row->text = preg_replace( $regex, '', $row->text );
+
preg_match_all($regex, $article->text, $matches, PREG_SET_ORDER);
return true;
+
// No matches, skip this
 +
if ($matches) {
 +
foreach ($matches as $match) {
 +
 
 +
$matcheslist = explode(',', $match[1]);
 +
 
 +
// We may not have a module style so fall back to the plugin default.
 +
if (!array_key_exists(1, $matcheslist)) {
 +
$matcheslist[1] = $style;
 +
}
 +
 
 +
$position = trim($matcheslist[0]);
 +
$style    = trim($matcheslist[1]);
 +
 
 +
$output = $this->_load($position, $style);
 +
// We should replace only first occurrence in order to allow positions with the same name to regenerate their content:
 +
$article->text = preg_replace("|$match[0]|", addcslashes($output, '\\$'), $article->text, 1);
 +
}
 
}
 
}
 +
// Find all instances of plugin and put in $matchesmod for loadmodule
  
// find all instances of plugin and put in $matches
+
preg_match_all($regexmod, $article->text, $matchesmod, PREG_SET_ORDER);
preg_match_all( $regex, $row->text, $matches );
+
// If no matches, skip this
 +
if ($matchesmod){
 +
foreach ($matchesmod as $matchmod) {
  
// Number of plugins
+
$matchesmodlist = explode(',', $matchmod[1]);
$count = count( $matches[0] );
+
//We may not have a specific module so set to null
 +
if (!array_key_exists(1, $matchesmodlist)) {
 +
$matchesmodlist[1] = null;
 +
}
 +
// We may not have a module style so fall back to the plugin default.
 +
if (!array_key_exists(2, $matchesmodlist)) {
 +
$matchesmodlist[2] = $stylemod;
 +
}
  
// plugin only processes if there are any instances of the plugin in the text
+
$module = trim($matchesmodlist[0]);
if ( $count ) {
+
$name  = trim($matchesmodlist[1]);
// Get plugin parameters
+
$style  = trim($matchesmodlist[2]);
$style = $this->params->def( 'style', -2 );
+
// $match[0] is full pattern match, $match[1] is the module,$match[2] is the title
$this->_process( $row, $matches, $count, $regex, $style );
+
$output = $this->_loadmod($module, $name, $style);
 +
// We should replace only first occurrence in order to allow positions with the same name to regenerate their content:
 +
$article->text = preg_replace("|$matchmod[0]|", addcslashes($output, '\\$'), $article->text, 1);
 +
}
 
}
 
}
// No return value
 
 
}
 
}
// The proccessing function
+
 
protected function _process( &$row, &$matches, $count, $regex, $style )
+
// The function who takes care for the 'completing' of the plugins' actions : loading the module(s) by position
 +
protected function _load($position, $style = 'none')
 
{
 
{
for ( $i=0; $i < $count; $i++ )
+
if (!isset(self::$modules[$position])) {
{
+
self::$modules[$position] = '';
$load = str_replace( 'loadposition', '', $matches[0][$i] );
+
$document = JFactory::getDocument();
$load = str_replace( '{', '', $load );
+
$renderer = $document->loadRenderer('module');
$load = str_replace( '}', '', $load );
+
$modules = JModuleHelper::getModules($position);
$load = trim( $load );
+
$params = array('style' => $style);
 +
ob_start();
  
$modules = $this->_load( $load, $style );
+
foreach ($modules as $module) {
$row->text = preg_replace( '{'. $matches[0][$i] .'}', $modules, $row->text );
+
echo $renderer->render($module, $params);
}
+
}
  
  // removes tags without matching module positions
+
self::$modules[$position] = ob_get_clean();
$row->text = preg_replace( $regex, '', $row->text );
+
}
 +
return self::$modules[$position];
 
}
 
}
// The function who takes care for the 'completing' of the plugins' actions : loading the module(s)
+
 
protected function _load( $position, $style=-2 )
+
// This is always going to get the first instance of the module type unless
 +
// there is a title.
 +
// The function who takes care for the 'completing' of the plugins' actions : loading the module(s) by type or title
 +
protected function _loadmod($module, $title, $style = 'none')
 
{
 
{
$document = &JFactory::getDocument();
+
if (!isset(self::$mods[$module])) {
$renderer = $document->loadRenderer('module');
+
self::$mods[$module] = '';
$params = array('style'=>$style);
+
$document = JFactory::getDocument();
 +
$renderer = $document->loadRenderer('module');
 +
$mod = JModuleHelper::getModule($module, $title);
 +
// If the module without the mod_ isn't found, try it with mod_.
 +
// This allows people to enter it either way in the content
 +
if (!isset($mod)){
 +
$name = 'mod_'.$module;
 +
$mod  = JModuleHelper::getModule($name, $title);
 +
}
 +
$params = array('style' => $style);
 +
ob_start();
 +
 
 +
echo $renderer->render($mod, $params);
  
$contents = '';
+
self::$mods[$module] = ob_get_clean();
foreach (JModuleHelper::getModules($position) as $mod)  {
+
$contents .= $renderer->render($mod, $params);
+
 
}
 
}
return $contents;
+
return self::$mods[$module];
 
}
 
}
 
}
 
}
Line 314: Line 392:
 
   
 
   
 
Start your INI file with something like this:  
 
Start your INI file with something like this:  
<source lang="ini"># $Id: en-GB.plg_content_nameofplugin.ini
+
<source lang="ini">; $Id: en-GB.plg_content_nameofplugin.ini
 
; Joomla! Project
 
; Joomla! Project
 
; Copyright (C) 2005 - 2007 Open Source Matters. All rights reserved.
 
; Copyright (C) 2005 - 2007 Open Source Matters. All rights reserved.
Line 323: Line 401:
 
Backend Example:  
 
Backend Example:  
 
<source lang="XML">
 
<source lang="XML">
<param name="mode" type="list" default="1" label="PLG_CONTENT_EXAMPLE_MODE" description="PLG_CONTENT_EXAMPLE_EMAILS_DISPLAYED">
+
<field name="mode" type="list" default="1" label="PLG_CONTENT_EXAMPLE_MODE" description="PLG_CONTENT_EXAMPLE_EMAILS_DISPLAYED">
 
   <option value="0">PLG_CONTENT_EXAMPLE_NONLINKABLE_TEXT</option>
 
   <option value="0">PLG_CONTENT_EXAMPLE_NONLINKABLE_TEXT</option>
 
   <option value="1">PLG_CONTENT_EXAMPLE_LINKABLE_TEXT</option>
 
   <option value="1">PLG_CONTENT_EXAMPLE_LINKABLE_TEXT</option>
</param></source>
+
</field></source>
  
 
The localized strings are translated in the ini file:
 
The localized strings are translated in the ini file:
Line 339: Line 417:
  
 
The localized tags can then be translated into multiple languages.When you want to make your Content Plugin available in more languages, first add them to the <languages> tag in the XML file. Then create the same INI file, and change the part after the =, for example the dutch version would be:  
 
The localized tags can then be translated into multiple languages.When you want to make your Content Plugin available in more languages, first add them to the <languages> tag in the XML file. Then create the same INI file, and change the part after the =, for example the dutch version would be:  
<source lang="INI">
+
<source lang="ini">
 
PLG_CONTENT_EXAMPLE_MODE=Modus
 
PLG_CONTENT_EXAMPLE_MODE=Modus
 
PLG_CONTENT_EXAMPLE_EMAILS_DISPLAYED=Selecteer hoe de e-mails worden weergegeven
 
PLG_CONTENT_EXAMPLE_EMAILS_DISPLAYED=Selecteer hoe de e-mails worden weergegeven
Line 347: Line 425:
  
 
Frontend Example:
 
Frontend Example:
On Joomla 1.6+ you must create a separate file for the front end - as described above. For example:
 
  
<source lang="INI">
+
<source lang="ini">
 
PLG_CONTENT_EXAMPLE_LINKNAME="The Name of the Link Goes Here!"
 
PLG_CONTENT_EXAMPLE_LINKNAME="The Name of the Link Goes Here!"
 
</source>
 
</source>
  
In the plugin's php file, you must load the language file:
+
In the plugin's php file, you must load the language file, however unlike Joomla 1.5, we can now do this by overriding the constructor, with a function provided by Joomla in JPlugin.
<source lang="PHP">
+
<source lang="php">
// Load user_profile plugin language
+
function __construct(& $subject, $config)
$lang = JFactory::getLanguage();
+
{
$lang->load('plg_content_simpleattachment', JPATH_ADMINISTRATOR);
+
$this->loadLanguage();
echo JText::_('PLG_CONTENT_EXAMPLE_LINKNAME');
+
parent::__construct($subject, $config);
 +
}
 
</source>
 
</source>
  
Line 366: Line 444:
  
 
<source lang="PHP">
 
<source lang="PHP">
$lang->load('com_someothercomponent',JPATH_ADMINISTRATOR);
+
$lang->load('com_someothercomponent', JPATH_ADMINISTRATOR);
 
</source>
 
</source>
  
 
==Coding examples==
 
==Coding examples==
There are seven Joomla! Core Content Plugins. These are all not yet refactored to the new standards. For learning how to write good plugins, its better to look at the (refactored) files of Joomla! 1.6.
+
There are seven Joomla! Core Content Plugins. For learning how to write good plugins, its good to look in the files of Joomla! 2.5 or 3.x
You can see them 'working' when you go to the Back-end of your Joomla! 1.5 installation, then go to the menu 'Extensions' and select the 'Plugin Manager'. Click on the name of the Plugin to edit it; and see it working. Also watch the content and see what happens when you insert a pagebreak for example.
+
You can see them 'working' when you go to the Back-end of your Joomla! installation, then go to the menu 'Extensions' and select the 'Plugin Manager'. Click on the name of the Plugin to edit it; and see it working. Also watch the content and see what happens when you insert a pagebreak for example.
  
 
==Quick tips==
 
==Quick tips==
 
*In the PHP file you often forget to place an semicolon (;) at the end of a row, which causes errors. Check this before you test your Plugin and you will eliminate many errors.  
 
*In the PHP file you often forget to place an semicolon (;) at the end of a row, which causes errors. Check this before you test your Plugin and you will eliminate many errors.  
*Take care of the fact that the parameters in the XML file are closed correctly. When you add options for example, you need to close it with </param>.  
+
*Take care of the fact that the parameters in the XML file are closed correctly. When you add options for example, you need to close it with </field>.  
 
*It is easy to test on a localhost when you are still busy with editing your Plugin.  
 
*It is easy to test on a localhost when you are still busy with editing your Plugin.  
 
*A typical zip will contain the following:  
 
*A typical zip will contain the following:  
**nameofplugin.XML
+
**nameofplugin.xml
**nameofplugin.PHP
+
**nameofplugin.php
 
**en-GB.plg_content_nameofplugin.ini
 
**en-GB.plg_content_nameofplugin.ini
 +
 +
[[Category:Plugin Development]]
 +
[[Category:Specifications]]

Latest revision as of 08:50, 10 August 2013

Contents

Description

There are lots of abilities of the use of a Content Plugin. They all have to do with the display of your content and so your articles. You will need at least two files for this Plugin. An XML file and a PHP file. Because there are so many differences between two Content Plugins in the PHP file, two examples of them will be explained in this document. Also a part about internationalization (with INI files) is added. And last but not least: the Joomla! Core Content Plugin coding examples and the quick tips.

XML file

The XML file is named the same as the PHP file, and is one of the two required files. Always start off with the XML tag and define that it is written in a UTF-8 format.

<?xml version="1.0" encoding="utf-8"?>

To define that the plugin has to be a content plugin, add this line:

<extension version="2.5" type="plugin" group="content">

The type will define it is a Plugin, the group defines the Plugin is in the group of Content Plugins.

After that, add some information about yourself and the Plugin, like this:

<name>Name of your Content Plugin</name>
<creationDate>Created Date</creationDate>
<author>Your name</author>
<authorEmail>Your e-mail address</authorEmail>
<authorUrl>Your website</authorUrl>
<copyright>Copyright</copyright>
<license>License, for example GNU/GPL</license>
<version>Version of the plugin</version>
<description>Description of the Plugin; showed with installation and when editing 
the Plugin in the Plugin Manager</description>

And now include your PHP file to the Content Plugin. The name of this file should be the same as the name of this XML file. Put this name also behind the plugin="" part.

You could also add more files for your plugin, for example an image. Just add another row between <files> and </files>, and then place the file between <filename> tags.

<files>
   <filename plugin="nameofplugin">nameofplugin.php</filename>
</files>

Some files, like .js or .css, are not recognized and will cause an error when you try to install the plugin. For these, simply create a new folder in the plugin directory, then add the files to the folder. Then add a line for the folder in the <files> tags. Of course, .js and .css files might be better managed better as part of the template, but if these are plugin specific then they could go here.

<files>
   <folder>scripts</folder>
   <folder>css</folder>
</files>

For the internationalization, we will use language files. This is not required, but people from other countries will love it if they can easily translate your plugin to their own language. Plugin language files are always installed in administrator/languages/xx-XX/ where xx-XX is the code for a language already installed in Joomla. The language tags can be found here: [1] (use the ISO 639-1 column) and here: [2]

<languages>
   <language tag="en-GB">en-GB.plg_content_nameofplugin.ini</language>
</languages>

Optionally, you could add some parameters to the Plugin. These will look like this:

<config>
        <fields name="params">
                <fieldset name="basic">
                        <field name="field1" type="text"
                                description="Example field 1"
                                label="Example field 1"
                        />
                </fieldset>
        </fields>
</config>
  • Field name: The name of the parameter. You will need this when creating the PHP file.
  • Field type: You could choose between several types of parameters. Look at this document to learn something about the different types: Different Joomla Form Fields
  • Field default: The default setting for this parameter.
  • Field label: The name of this parameter displayed in the edit screen of this Plugin in the Plugin Manager.
  • Field description: The text which appears as a tool tip for this parameter.

When you do not want to use parameters, add the following tag:

<config />

And do not forget to end your XML file with the following tag:

</extension>

PHP file

Start your PHP file with the general licensing and author information about your content plugin.

<?php
/**
 * @version             $Id: nameofplugin.php revision date lasteditedby $
 * @package             Joomla
 * @subpackage  Content
 * @copyright   Copyright (C) 2005 - 2008 Open Source Matters. All rights reserved.
 * @license             GNU/GPL, see LICENSE.php
 * Joomla! is free software. This version may have been modified pursuant
 * to the GNU General Public License, and as distributed it includes or
 * is derivative of works licensed under the GNU General Public License or
 * other free or open source software licenses.
 * See COPYRIGHT.php for copyright notices and details.
 */

Next, always put the following code in any PHP file:

// no direct access
defined( '_JEXEC' ) or die( 'Restricted access' );

This prevents people from directly accessing this PHP file.

After that to indicate that you're writing a Content plugin, add the following code.

class plgContentNameofplugin extends JPlugin {

Please note the use of capital letters. You should replace Nameofplugin by the name of your own plugin.

You should choose now at which moment the plugin should be rendered. You can choose from the following:

  • onContentBeforeDelete : This is an event that is called right before the content is deleted.
  • onContentAfterDelete : This is an event that is called right after the content is deleted.
  • onContentBeforeDisplay : This is a request for information that should be placed immediately before the generated content.
  • onContentAfterDisplay: This is a request for information that should be placed immediately after the generated content.
  • onContentBeforeSave : This is an event that is called right before the content is saved into the database.
  • onContentAfterSave : This is an event that is called after the content is saved into the database.
  • onContentAfterTitle : This is a request for information that should be placed between the content title and the content body.
  • onContentChangeState : This is an event that is called when the contents state is changed.
  • onContentPrepare : This is the first stage in preparing content for output and is the most common point for content orientated plugins to do their work.

More information on which type you should use for your plugin, including several examples, can be found here: Plugin events/Content

onContentBeforeDelete

Use the following code:

        function onContentBeforeDelete($context, $data)
        {
                //add your plugin codes here
                return true;
        }

The parameters context and data should contain the following:

  • context : The context of the content passed to the plugin.
  • data : The data relating to the content that is to be deleted.

onContentAfterDelete

Use the following code:

        function onContentAfterDelete($context, $data)
        {
                //add your plugin codes here
                return true;
        }

The parameters context and data should contain the following:

  • context : The context of the content passed to the plugin.
  • data : The data relating to the content that was deleted.

onContentChangeState

Use the following code:

        function onContentChangeState($context, $pks, $value)
        {
                //add your plugin codes here
                return true;
        }

The parameters context, pks and value should contain the following:

  • context : The context of the content passed to the plugin.
  • pks : A list of primary key ids of the content that has changed state.
  • value : The value of the state that the content has been changed to.

onContentBeforeSave

Use the following code:

        function onContentBeforeSave($context, &$article, $isNew)
        {
                //add your plugin codes here
                return true;
        }

The parameters context, article and isNew should contain the following:

  • context : The context of the content passed to the plugin.
  • article : A reference to the JTableContent object that is being saved which holds the article data.
  • isNew : A boolean which is set to true if the content is about to be created.

onContentAfterSave

Use the following code:

        function onContentAfterSave($context, &$article, $isNew)
        {
                //add your plugin codes here
                return true;
        }

The parameters context, article and isNew should contain the following:

  • context : The context of the content passed to the plugin.
  • article : A reference to the JTableContent object that was saved which holds the article data.
  • isNew : A boolean which is set to true if the content was created.

onContentPrepare

Use the following code:

        function onContentPrepare($context, &$article, &$params, $limitstart)
        {
                //add your plugin codes here
                //no return value
        }

The parameters context, article, params and limitstart should contain the following:

  • context : The context of the content passed to the plugin.
  • article : A reference to the article that is being rendered by the view.
  • params : A reference to an associative array of relevant parameters. The view determines what it considers to be relevant and passes that information along.
  • limitstart : An integer that determines the "page" of the content that is to be generated. Note that in the context of views that might not generate HTML output, a page is a reasonably abstract concept that depends on the context.

onContentAfterTitle

Use the following code:

        function onContentAfterTitle($context, &$article, &$params, $limitstart)
        {
                //add your plugin codes here
                return '';
                //return a string value. Returned value from this event will be displayed in a placeholder. 
                // Most templates display this placeholder after the article separator.
        }

The parameters context, article, params and limitstart should contain the following:

  • context : The context of the content passed to the plugin.
  • article : A reference to the article that is being rendered by the view.
  • params : A reference to an associative array of relevant parameters. The view determines what it considers to be relevant and passes that information along.
  • limitstart : An integer that determines the "page" of the content that is to be generated. Note that in the context of views that might not generate HTML output, a page is a reasonably abstract concept that depends on the context.

onContentBeforeDisplay

Use the following code:

        function onContentBeforeDisplay($context, &$article, &$params, $limitstart)
        {
                //add your plugin codes here
                return '';
                //return a string value. Returned value from this event will be displayed in a placeholder. 
                // Most templates display this placeholder after the article separator. 
        }

The parameters context, article, params and limitstart should contain the following:

  • context : The context of the content passed to the plugin.
  • article : A reference to the article that is being rendered by the view.
  • params : A reference to an associative array of relevant parameters. The view determines what it considers to be relevant and passes that information along.
  • limitstart : An integer that determines the "page" of the content that is to be generated. Note that in the context of views that might not generate HTML output, a page is a reasonably abstract concept that depends on the context.

onContentAfterDisplay

Use the following code:

        function onContentAfterDisplay($context, &$article, &$params, $limitstart)
        {
//add your plugin codes here
                return '';
                //return a string value. Returned value from this event will be displayed in a placeholder. 
                // Most templates display this placeholder after the article separator.
        }

The parameters context, article, params and limitstart should contain the following:

  • context : The context of the content passed to the plugin.
  • article : A reference to the article that is being rendered by the view.
  • params : A reference to an associative array of relevant parameters. The view determines what it considers to be relevant and passes that information along.
  • limitstart : An integer that determines the "page" of the content that is to be generated. Note that in the context of views that might not generate HTML output, a page is a reasonably abstract concept that depends on the context.

PHP file - Example

This example PHP file is about the Content Plugin load module from Joomla 2.5.11. It is a plugin made for displaying modules within the articles.

<?php
// The general information at the top of each file
/**
 * @package             Joomla.Plugin
 * @subpackage  Content.loadmodule
 * @copyright   Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
 * @license             GNU General Public License version 2 or later; see LICENSE.txt
 */
 
// no direct access
defined('_JEXEC') or die;
 
//The Content plugin Loadmodule
class plgContentLoadmodule extends JPlugin
{
        protected static $modules = array();
        protected static $mods = array();
        /**
         * Plugin that loads module positions within content
         *
         * @param       string  The context of the content being passed to the plugin.
         * @param       object  The article object.  Note $article->text is also available
         * @param       object  The article params
         * @param       int             The 'page' number
         */
// onContentPrepare, meaning the plugin is rendered at the first stage in preparing content for output
        public function onContentPrepare($context, &$article, &$params, $page = 0)
        {
                // Don't run this plugin when the content is being indexed
                if ($context == 'com_finder.indexer') {
                        return true;
                }
 
                // simple performance check to determine whether bot should process further
                if (strpos($article->text, 'loadposition') === false && strpos($article->text, 'loadmodule') === false) {
                        return true;
                }
 
                // expression to search for (positions)
                $regex          = '/{loadposition\s+(.*?)}/i';
                $style          = $this->params->def('style', 'none');
                // expression to search for(modules)
                $regexmod       = '/{loadmodule\s+(.*?)}/i';
                $title          = null;
                $stylemod       = $this->params->def('style', 'none');
 
                // Find all instances of plugin and put in $matches for loadposition
                // $matches[0] is full pattern match, $matches[1] is the position
                preg_match_all($regex, $article->text, $matches, PREG_SET_ORDER);
                // No matches, skip this
                if ($matches) {
                        foreach ($matches as $match) {
 
                        $matcheslist = explode(',', $match[1]);
 
                        // We may not have a module style so fall back to the plugin default.
                        if (!array_key_exists(1, $matcheslist)) {
                                $matcheslist[1] = $style;
                        }
 
                        $position = trim($matcheslist[0]);
                        $style    = trim($matcheslist[1]);
 
                                $output = $this->_load($position, $style);
                                // We should replace only first occurrence in order to allow positions with the same name to regenerate their content:
                                $article->text = preg_replace("|$match[0]|", addcslashes($output, '\\$'), $article->text, 1);
                        }
                }
                // Find all instances of plugin and put in $matchesmod for loadmodule
 
                preg_match_all($regexmod, $article->text, $matchesmod, PREG_SET_ORDER);
                // If no matches, skip this
                if ($matchesmod){
                        foreach ($matchesmod as $matchmod) {
 
                                $matchesmodlist = explode(',', $matchmod[1]);
                                //We may not have a specific module so set to null
                                if (!array_key_exists(1, $matchesmodlist)) {
                                        $matchesmodlist[1] = null;
                                }
                                // We may not have a module style so fall back to the plugin default.
                                if (!array_key_exists(2, $matchesmodlist)) {
                                        $matchesmodlist[2] = $stylemod;
                                }
 
                                $module = trim($matchesmodlist[0]);
                                $name   = trim($matchesmodlist[1]);
                                $style  = trim($matchesmodlist[2]);
                                // $match[0] is full pattern match, $match[1] is the module,$match[2] is the title
                                $output = $this->_loadmod($module, $name, $style);
                                // We should replace only first occurrence in order to allow positions with the same name to regenerate their content:
                                $article->text = preg_replace("|$matchmod[0]|", addcslashes($output, '\\$'), $article->text, 1);
                        }
                }
        }
 
// The function who takes care for the 'completing' of the plugins' actions : loading the module(s) by position
        protected function _load($position, $style = 'none')
        {
                if (!isset(self::$modules[$position])) {
                        self::$modules[$position] = '';
                        $document       = JFactory::getDocument();
                        $renderer       = $document->loadRenderer('module');
                        $modules        = JModuleHelper::getModules($position);
                        $params         = array('style' => $style);
                        ob_start();
 
                        foreach ($modules as $module) {
                                echo $renderer->render($module, $params);
                        }
 
                        self::$modules[$position] = ob_get_clean();
                }
                return self::$modules[$position];
        }
 
        // This is always going to get the first instance of the module type unless
        // there is a title.
// The function who takes care for the 'completing' of the plugins' actions : loading the module(s) by type or title
        protected function _loadmod($module, $title, $style = 'none')
        {
                if (!isset(self::$mods[$module])) {
                        self::$mods[$module] = '';
                        $document       = JFactory::getDocument();
                        $renderer       = $document->loadRenderer('module');
                        $mod            = JModuleHelper::getModule($module, $title);
                        // If the module without the mod_ isn't found, try it with mod_.
                        // This allows people to enter it either way in the content
                        if (!isset($mod)){
                                $name = 'mod_'.$module;
                                $mod  = JModuleHelper::getModule($name, $title);
                        }
                        $params = array('style' => $style);
                        ob_start();
 
                        echo $renderer->render($mod, $params);
 
                        self::$mods[$module] = ob_get_clean();
                }
                return self::$mods[$module];
        }
}

INI file(s)

For internationalization it is good to use the INI files. You can add to the language file everything which outputs text to the user, in this order:

  • XML description tag
  • XML label and description attributes from parameters
  • JText::_( 'string' ) used by the plugin

You should create two files: One for the backend and one for the front end. The naming should match the path of the extension. For example, if we were creating a content plugin named 'example' and it is installed at plugins/content/example, then our files should be named:

  • en-GB.plg_content_example.ini (Frontend)
  • en-GB.plg_content_example.sys.ini (Backend)


Start your INI file with something like this:

; $Id: en-GB.plg_content_nameofplugin.ini
; Joomla! Project
; Copyright (C) 2005 - 2007 Open Source Matters. All rights reserved.
; License http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL, see LICENSE.php
; Note : All ini files need to be saved as UTF-8 - No BOM

Of course, you could also add other information, like the author.

Backend Example:

<field name="mode" type="list" default="1" label="PLG_CONTENT_EXAMPLE_MODE" description="PLG_CONTENT_EXAMPLE_EMAILS_DISPLAYED">
   <option value="0">PLG_CONTENT_EXAMPLE_NONLINKABLE_TEXT</option>
   <option value="1">PLG_CONTENT_EXAMPLE_LINKABLE_TEXT</option>
</field>

The localized strings are translated in the ini file:

PLG_CONTENT_EXAMPLE_MODE=Mode
PLG_CONTENT_EXAMPLE_EMAILS_DISPLAYED=Select how the e-mails will be displayed
PLG_CONTENT_EXAMPLE_NONLINKABLE_TEXT=Nonlinkable text
PLG_CONTENT_EXAMPLE_LINKABLE_TEXT=As linkable mailto address

Best practice dictates that LOCALIZED_TAGS are all upper case, no spaces, and begin with the same name as the extension - to avoid interfering with other strings.

The localized tags can then be translated into multiple languages.When you want to make your Content Plugin available in more languages, first add them to the