Write your own Module

Thank you for your contribution: 

Containers

Modules are the "sidebar" content and widgets. They often work off of existing content and databases. An example would be a Latest Articles module with a list of the most recently added articles. As you will see in this chapter, to get full advantage of Joomla you take advantage of the Joomla framework contained in the libraries/joomla folder. Joomla uses object oriented PHP so much of what you find in the libraries/joomla folder is files of classes. By including these in your program you let Joomla do your heavy lifting for you.

Joomla programs by convention. It assumes you will structure your program and name your files and classes in a certain way. This is one area where you don't want to be too creative.

Backend modules are contained in the administrator/modules folder and frontend modules are in the modules folder. Within those folders, each module has its own folder which starts with mod_.

The example we'll be working through is a Contact List which in this example will be used to display a list of branches. The code is in a file attachment at the end of this tutorial. This is what the module will look like on the frontend:

contact-list-acontact-list-a

In the backend you will be able to select the category and how many contacts to display. (The backend screenshot in this tutorial is using the Hathor administrative template.)

contact-list-bcontact-list-b

There are six main files, some in subfolders, in the module/mod_contact_list folder. In addition to these files, each folder should contain a dummy index.html file.

Path File Purpose
modules/mod_contact_list mod_contact_list.xml Define the module and parameters
modules/mod_contact_list mod_contact_list.php Main processing file - the controller
modules/mod_contact_list helper.php Helper functions to get the data - the model
modules/mod_contact_list/tmpl default.php The HTML for displaying the module - the view
modules/mod_contact_list/language/en-GB en-GB_mod_contact_list.ini English language file
modules/mod_contact_list/language/en-GB en-GB_mod_contact_list.sys.ini English language file for system strings

mod_contact_list.xml

The mod_contact_list.xml file defines the module, what version of Joomla it runs on, the files it uses and the parameters to be used. This file is necessary for the module to be installed. This is the first part of the file gives the basic description of the module:

<?xml version="1.0" encoding="UTF-8"?>
<extension type="module" version="1.7" client="site" method="upgrade">
   <name>MOD_CONTACT_LIST</name>
   <author>Andrea Tarr</author>
	<creationDate>November 2011</creationDate>
	<copyright>Copyright (C) 2011 Tarr Consulting. All rights reserved.</copyright>
	<license>GNU General Public License version 2 or later</license>
	<authorEmail>atarr@tarrconsulting.com</authorEmail>
	<authorUrl>www.tarrconsulting.com</authorUrl>
   <version>1.0</version>
   <description>MOD_CONTACT_LIST_XML_DESCRIPTION</description>

The <extension> tag defines the type as module, the minimum version of Joomla and whether this is a frontend module (0) or a backend module (1). The method "upgrade" indicates that if a module folder with the same name is found, it will be assumed to be an earlier version of the same program that can be updated. If you use "install", any duplicated folder will prevent installation. The <name> and <description> tags are using language strings that will be translated in the language file. The language files will be explained later in this tutorial.

The next part lists the files. During the installation, these are the files that will be copied. If you have extra files in the zip file you are installing, they will be ignored. If you list a file that is not in the zip file, the module will not install.

    <files>
        <filename>mod_contact_list.xml</filename>
        <filename module="mod_contact_list">mod_contact_list.php</filename>
        <filename>index.html</filename>
        <filename>helper.php</filename>
        <folder>tmpl</folder>
        <folder>language</folder>
    </files>

The main calling file is signified with the module attribute. The <folder> tag will copy all the files and subfolders in that folder.

The next section defines the parameters that you see on the right column in the backend. This section is enclosed in a <config> tag. The group of parameters is in a <fields> tag with the name attribute of "params". Each of the sliders is defined with a separate <fieldset>. First are the Basic parameters, where we choose the category and number of articles:

    <config>
        
        <fields name="params">
            <fieldset name="basic">
                <field
                    name="catid"
                    type="category"
                    extension="com_contact"
                    multiple="true"
                    default=""
                    size="10"
                    label="JCATEGORY"
                    description="MOD_CONTACT_LIST_FIELD_CATEGORY_DESC" >
                </field>

                <field
                    name="count"
                    type="text"
                    default="5"
                    label="MOD_CONTACT_LIST_FIELD_ITEMS_LABEL"
                    description="MOD_CONTACT_LIST_FIELD_ITEMS_DESC" />

            </fieldset>    

Each of the individual parameters are in a <field> tag. The name attribute is used to get the parameter in your program. The type attribute defines what type of field this is. Each of the types are defined in the Joomla framework. Common types used are text, list, editor, textarea, category, calendar, radio, checkbox, checkboxes, media, folderlist, and filelist. For a full list, see http://docs.joomla.org/Standard_form_fie.... You can also create your own types as explained in http://docs.joomla.org/Creating_a_custom.... The label and description attributes use a language strings found in either the global language files or in the specified extension language files.

The following Advanced parameters are the stock parameters which you should put on all your modules unless you don't want users to have these standard capabilities. All except for the moduleclass_sfx will work automatically just by including this code. For the moduleclass_sfx to work you need to add <?php echo $moduleclass_sfx; ?> to the class tag in the HTML layout where you want to allow the user to define a special class.

            <fieldset
                name="advanced">

                <field
                    name="layout"
                    type="modulelayout"
                    label="JFIELD_ALT_LAYOUT_LABEL"
                    description="JFIELD_ALT_MODULE_LAYOUT_DESC" />

                <field
                    name="moduleclass_sfx"
                    type="text"
                    label="COM_MODULES_FIELD_MODULECLASS_SFX_LABEL"
                    description="COM_MODULES_FIELD_MODULECLASS_SFX_DESC" />

                <field
                    name="cache"
                    type="list"
                    default="1"
                    label="COM_MODULES_FIELD_CACHING_LABEL"
                    description="COM_MODULES_FIELD_CACHING_DESC">
                    <option
                        value="1">JGLOBAL_USE_GLOBAL</option>
                    <option
                        value="0">COM_MODULES_FIELD_VALUE_NOCACHING</option>
                </field>

                <field
                    name="cache_time"
                    type="text"
                    default="900"
                    label="COM_MODULES_FIELD_CACHE_TIME_LABEL"
                    description="COM_MODULES_FIELD_CACHE_TIME_DESC" />

                <field
                    name="cachemode"
                    type="hidden"
                    default="itemid">
                    <option
                        value="itemid"></option>
                </field>

            </fieldset>

Finish off the file by closing the tags:

        </fields>
    </config>
</extension>

mod_contact_list.php

The mod_contact_list.php is the main processing file for your program. It works as the controller in a Model-View-Controller structure. In the same way that we separate content from presentation and behavior by having separate files for HTML/CSS/JavaScript, we separate the control of the program the data (model) and the display (view). The file starts out by checking to see that the file is being called by Joomla and not directly:

<?php
/**
 * Contact List
 * 
 */
 
// no direct access
defined('_JEXEC') or die;

All your php files should start with this code.

We will be putting our data retrieval code in the helper.php file, so we need to include that file. It contains a class definition, so we need to use the require_once. The dirname(__FILE__) brings in the path of the current file so it can be used as the path for the helper.php file. Remember that a class definition doesn't actually do anything at the time it is included.

// Include the class of the syndicate functions only once
require_once(dirname(__FILE__).'/helper.php');

Next we will get the data by doing a static call to the class defined in the helper.php file and putting the result into $list. The $params is an object that contains all the parameters defined in the xml file.

// Static call to the class
$list = modContactListHelper::getList($params);

The next line just does a bit of housekeeping. We will be using the module class suffix parameter in the layout to construct a class, so we want to do some sanitizing first. By putting it here, we ensure that it is done even if a designer does a template override.

$moduleclass_sfx = htmlspecialchars($params->get('moduleclass_sfx'));

Finally, we call the framework module processor which will put everything together and pass back the HTML to be displayed based on the layout file (tmpl/default.php). Since this is done as an include, any variables are still in scope.

require(JModuleHelper::getLayoutPath('mod_contact_list'));

This is the end of the file. Do not include a closing ?> tag. The practice in Joomla is to skip all closing php tags because characters after the php tag, including some control characters, trigger sending HTML headers prematurely, which causes errors.

helper.php

We are using the helper.php file to retrieve the data. We start the php file in the standard manner:

<?php
// no direct access
defined('_JEXEC') or die;

We want to list the contacts in the Joomla contact table in given categories. Since we are using a table from a component that is defined in the standard Joomla way, we can use existing model definitions in our program. To do that we include the part of the Joomla framework that processes component models and do a static call to include the models from the com_contact component.

jimport('joomla.application.component.model');
JModel::addIncludePath(JPATH_ADMINISTRATOR.'/components/com_contact/models', 'ContactModel');

Now it's time to define the class definition. This class has no properties and getList() is the only method:

class modContactListHelper
{
    /**
     * Retrieves the list of contacts
     *
     * @param array $params An object containing the module parameters
     * @access public
     */    
    public function getList($params)
    {

The function starts by getting the global information, which is retrieved by a static call to the Application. This is what replaces the old global $mainframe from earlier Joomla programing.

		$app	= JFactory::getApplication();

Next we get the database connection:

		$db		= JFactory::getDbo();

Now we need to create a model object from the contacts. We use a static call to JModel telling it the component (Contacts) and the class prefix (ContactModel). Processing the model sets states to remember what state the model is in (like what the filters are set to) . When you are creating a module, you usually don't want to affect any states that the main component is in, so the ignore_request tells it to not remember the state from this processing.

		// Get an instance of the generic contact model
		$model = JModel::getInstance('Contacts', 'ContactModel', array('ignore_request' => true));

Next we set the application parameters in the model:

		$appParams = JFactory::getApplication()->getParams();
		$model->setState('params', $appParams);

Then we set the filters based on the module parameters. The list.start is set to 0 to start at the beginning and we set the end based on the count parameter that we entered in the module parameters. The filter.published set to 1 says to only get published contacts. The list.select lists the fields to return.

		$model->setState('list.start', 0);
		$model->setState('list.limit', (int) $params->get('count', 5));
		
		$model->setState('filter.published', 1);

		$model->setState('list.select', 'a.id, a.name, a.catid' . 
				', a.address, a.suburb, a.postcode, a.state, a.telephone ' .
				', a.published, a.access, a.ordering, a.language'.
				', a.publish_up, a.publish_down');

The next filter is for the ACL to make sure that only contacts that are allowed to be seen are chosen for display.

		$access = !JComponentHelper::getParams('com_contact')->get('show_noauth');
		$authorised = JAccess::getAuthorisedViewLevels(JFactory::getUser()->get('id'));
		$model->setState('filter.access', $access);

Then we filter for the category based on the parameter that we entered in the module parameters. Note that this is an array since we allowed multiples when we defined the parameter in the xml file.

		$model->setState('filter.category_id', $params->get('catid', array()));

The last filters are for the language and to set the order of the contacts in the list.

		$model->setState('filter.language',$app->getLanguageFilter());
		$model->setState('list.ordering', 'ordering');
		$model->setState('list.direction', 'ASC');

Finally, we call the getItems() method in the $model object. Since we are using the getItems() method from the contacts component we don't need to write it ourselves. We can just use the one that already exists. All we needed to do was define the state of the filters. Then we return the list we just retrieved and close out the function and class. Notice that again we don't include a closing php tag

		$items = $model->getItems();
		      
		return $items;
    }
}

tmpl/default.php

Now all we need to do is write the HTML that will display the list of information we have gathered. By separating out the HTML and putting it into a layout file in the tmpl folder we allow designers to use template overrides to change the HTML as they need. This file starts out as the other php files have: with the check to be sure that only Joomla has called it.

<?php
/**
 * Contact List Module Entry Point
 */ 

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

Next we put the HTML to display the list. It's a good idea to enclose the whole thing in a <div> with a class to identify the module type so that designers (or you) can add styling just for this module. This is also a good place to add the module class suffix. Putting the php code immediately following the module type class gives designer the most options.

<div class="contact_list<?php echo $moduleclass_sfx; ?>">

Finally, we create an unordered list and loop through $list to display each of the lines. We then close up the enclosing div to end the file.

<ul>
<?php foreach ($list as $item) :?>
	<li><h4><?php echo htmlspecialchars($item->name); ?></h4>
	<p><?php echo nl2br(htmlspecialchars($item->address)); ?><br />
	<?php echo htmlspecialchars($item->suburb); ?>, 
	<?php echo htmlspecialchars($item->state); ?> 
	<?php echo htmlspecialchars($item->postcode); ?><br />
	<?php echo htmlspecialchars($item->telephone); ?></p></li>
<?php endforeach; ?>
</ul>
</div>

language/en-GB/en-GB_mod_contact_list.ini

This is the main language file for the module. You put the language keys in your program in all caps with a prefix of MOD_CONTACT_LIST. Assign the language string to be used with an equal sign and double quotes around the string. This is a different structure from 1.5. This new structure, which is much faster, does not allow blanks in the language key. This is an ini file, so you don't use the jexec or die at the beginning.

; Note : All ini files need to be saved as UTF-8 - No BOM

MOD_CONTACT_LIST="Contact List"
MOD_CONTACT_LIST_FIELD_CATEGORY_DESC="Select Contacts from a specific Category or Categories."
MOD_CONTACT_LIST_FIELD_ITEMS_DESC="The number of Contacts to display within this module"
MOD_CONTACT_LIST_FIELD_ITEMS_LABEL="Number of Contacts"
MOD_CONTACT_LIST_XML_DESCRIPTION="The Contact List will display a fixed number of contacts from a specific category or categories."

language/en-GB/en-GB_mod_contact_list.sys.ini

The last file is the sys.ini language file. This file is just used on the Install and Update screens in the backend and only needs these keys. Those two screens have to access many extensions each of which could have large language files. By including short sys.ini files for each extension, the performance is improved.

; Note : All ini files need to be saved as UTF-8 - No BOM

MOD_CONTACT_LIST="Contact List"
MOD_CONTACT_LIST_XML_DESCRIPTION="The Contact List will display a fixed number of contacts from a specific category or categories."
MOD_CONTACT_LIST_LAYOUT_DEFAULT="Default"

index.html

You should put an index.html file in the root and in each folder/subfolder in your module to prevent the public from being able to get a list of the files by entering a directory in the address bar. The file can be as simple as:

<!DOCTYPE html><title></title>

Packaging the Module for Installation

Since we've already created the xml file, the only thing you need to do to create an installation package is to zip up the files and folders in the module folder. Be sure to just zip the folders and files in the mod_contact_list folder and not to include the top level mod_contact_list folder itself.

If your files are already in Joomla site you can use the Extensions/Extension Manager/Discover to install the module instead. Click on the Discover Icon to look for extension files that aren't installed. When your module shows up, check mark the box next to it and click Install.

AttachmentSize
mod_contact_list-v2.zip5.63 KB

Comments

Thank you this is so useful :)

Wow, thanks. So easily explained. Just the thing I was looking for. Thank You so much!!!

Sweet tutorial on making modules. However, I think you have a couple typos. The language file names are incorrect. At least they didn't work for me.

Instead of language/en-GB/en-GB_mod_contact_list.ini and language/en-GB/en-GB_mod_contact_list.sys.ini

Shouldn't they be language/en-GB/en-GB.mod_contact_list.ini and language/en-GB/en-GB.mod_contact_list.sys.ini

Thanks...other than that it works like a charm

This Tutorial is just awesome...I found only this tutorial useful to write own module, from allover websites aafter searching on google.

Thanks a Lot.