Retired Documentation:  You are using the documentation for version 1.7.3 which was retired in 2013. Go here for the latest version documentation or check here for your available upgrades to the latest version.

Module Tutorial

Naming Your Module

The first step before even creating a single script is to choose a name for your module. Usually a single word or two is sufficient and for this example we are going to simply call our module 'Fortunes', and it will allow an administrator to submit fortune cookie-like messages via the Control Panel and then have users view a different fortune cookie message every day.

Create Module Folder

Once we have the name of our module, we need to create a folder for it in ExpressionEngines's module folder in the system directory. The name of this folder should be a lowercased and space-less version of the module's name. Instead of spaces, you should either use a single word for your module or underscores. For our example, we will create a folder called 'fortunes' that would be located like so:

/system/modules/fortunes/

Create Control Panel and Core Module files

Inside this folder will be two files, one which will control the Control Panel side of the module (installing, uninstalling, and submitting content into the database) and the other which will be used for front-end stuff like form processing and the displaying of content from the database. Their names have to be very specific in order for the system to read them correctly. The Control Panel file must have a prefix of 'mcp', a 'php' suffix, and the name of the module folder. So, for our example the module's Control Panel file would be 'mcp.fortunes.php'. The core module file must have a prefix of 'mod', a 'php' suffix, and the name of the module folder. So, for our example the core module file would be named 'mod.fortunes.php'.

/system/modules/fortunes/mcp.fortunes.php
/system/modules/fortunes/mod.fortunes.php

Create Language File

Every module requires a language file as well, which is used for displaying text in the Control Panel side of the module and displaying the details of each module in the MODULES section of the Control Panel. This file will be located in your default language folder in the /system/language/ directory, and it will have a prefix of 'lang', the 'php' suffix, and use the same name as the module folder. So, for our example, the language file will be called 'lang.fortunes.php' and will be located in the /system/language/english/ directory.

/system/language/english/lang.fortunes.php

As of ExpressionEngine 1.6.8, you can use a local language folder for your language resources, making the module easier to maintain and update.

/system/modules/fortunes/language/english/lang.fortunes.php

Required Language Lines

The Language file contains an array named $L, which is used along with the Language class to display text on a page in whatever language is selected in the user's account settings, if a translation for the module is available in that language of course. There are two required lines in the language file for each module, which allows the name and description of the module to be viewable on the MODULES page:

<?php

$L = array(

//----------------------------------------
// Required for MODULES page
//----------------------------------------

"fortunes_module_name" =>
"Fortunes",

"fortunes_module_description" =>
"Fortune cookie displaying",

//----------------------------------------

// END
''=>''
);
?>

The Control Panel file (mcp.fortunes.php)

The Control Panel file for a module includes a class with a name that is a combination of the modules name with '_CP' tacked on the end. So, for our fortunes module, we will create a class called 'Fortunes_CP'. There is only one required class variable for this class and that is var $version = '1.0'; that should indicate the current version of this module.

Note:  If your module name contains two or more words, then the class name should have the first letter uppercased but all other characters in the name lowercased: Your_module_name_CP

Each Control Panel class has at a minimum two functions, which are used by ExpressionEngine to install or uninstall the module. These functions are required even if your module has no Control Panel side functionality. The names of these two functions are the lower-cased name of the module followed by '_module_install' and '_module_deinstall'. So, for our Fortunes module, they will be called 'fortunes_module_install' and 'fortunes_module_deinstall', respectively.

Installation Function

Here is what our Fortunes module's installation function will look like. Basically, it inserts the module's name, version, and whether it has a backend into the exp_modules table. Since our module will have a Control Panel backend, we set the fourth field to 'y', otherwise it should be set to 'n'. Also, the installation function will create the database table that we will use to store the fortunes in the database.

    // ----------------------------------------
    //  Module installer
    // ----------------------------------------

    function fortunes_module_install()
    {
        global $DB;

        $sql[] = "INSERT INTO exp_modules (module_id,
                                           module_name,
                                           module_version,
                                           has_cp_backend)
                                           VALUES
                                           ('',
                                           'Fortunes',
                                           '$this->version',
                                           'y')";

        $sql[] = "CREATE TABLE IF NOT EXISTS `exp_fortunes` (
                 `fortune_id` INT(6) UNSIGNED NOT NULL AUTO_INCREMENT,
                 `fortune_text` TEXT NOT NULL ,
                 PRIMARY KEY (`fortune_id`));";

        foreach ($sql as $query)
        {
            $DB->query($query);
        }

        return true;
    }
    // END

Occasionally, a module will require that certain actions be performed on the user side of the site. An example of this is when a form is being submitted and the module needs to gather and process the data. When this is required, the module needs to enter the class and method that the system needs to call to perform into the exp_actions database table during the installation of the module. Here is an example from the mcp.comments.php file.

    // --------------------------------
    //  Module installer
    // --------------------------------

    function comment_module_install()
    {
        global $DB;

        $sql[] = "INSERT INTO exp_modules (module_id,
                                           module_name,
                                           module_version,
                                           has_cp_backend)
                                           VALUES
                                           ('',
                                           'Comment',
                                           '$this->version',
                                           'n')";

        $sql[] = "INSERT INTO exp_actions (action_id,
                                           class,
                                           method)
                                           VALUES
                                           ('',
                                           'Comment',
                                           'insert_new_comment')";

        $sql[] = "INSERT INTO exp_actions (action_id,
                                           class,
                                           method)
                                           VALUES
                                           ('',
                                           'Comment_CP',
                                           'delete_comment_notification')";

        foreach ($sql as $query)
        {
            $DB->query($query);
        }

        return true;
    }
    // END

When the actions are entered into the exp_actions database table, they are given a unique action_id that the system will recognize and use to call the class and function required. To discover this action id for use in for a URL or form action, you can usethe Functions class fetch_action_id() function.

$action_id  = $FNS->fetch_action_id('Comment_CP', 'insert_new_comment');

Deinstallation Function

The deinstallation function in the Control Panel class for the module should be pretty standard for every module. Basically, it just clears out all mention of the module in the standard ExpressionEngine database tables. If the module created tables for its own usage, then those will also be deleted in this function.

    // ----------------------------------------
    //  Module de-installer
    // ----------------------------------------

    function fortunes_module_deinstall()
    {
        global $DB;

        $query = $DB->query("SELECT module_id
                             FROM exp_modules
                             WHERE module_name = 'Fortunes'");

        $sql[] = "DELETE FROM exp_module_member_groups
                  WHERE module_id = '".$query->row['module_id']."'";

        $sql[] = "DELETE FROM exp_modules
                  WHERE module_name = 'Fortunes'";

        $sql[] = "DELETE FROM exp_actions
                  WHERE class = 'Fortunes'";

        $sql[] = "DELETE FROM exp_actions
                  WHERE class = 'Fortunes_CP'";

        $sql[] = "DROP TABLE IF EXISTS exp_fortunes";

        foreach ($sql as $query)
        {
            $DB->query($query);
        }

        return true;
    }
    // END

Controller Function

Since our module is going to have a Control Panel interface and ExpressionEngine knows this from the installation query, we need to create a menu with various options and then tell ExpressionEngine where to go when those menu options are selected. For this module, the obvious menu options are when a user wants to add, modify, delete, or view fortunes. The menu will go in the constructor function for the Control Panel file's class (i.e. Fortunes_CP) and will use the 'P' global from the URL to determine which function to call in the class.

    // -------------------------
    //  Constructor
    // -------------------------

    function Fortunes_CP( $switch = TRUE )
    {
        global $IN;

        if ($switch)
        {
            switch($IN->GBL('P'))
            {
                 case 'view'            :	$this->view_fortunes();
                     break;
                 case 'delete'          :	$this->delete_fortune();
                     break;
                 case 'add'             :	$this->modify_fortune();
                     break;
                 case 'modify'          :	$this->modify_fortune();
                     break;
                 case 'update'          :	$this->update_fortune();
                     break;
                 default                :	$this->fortunes_home();
                     break;
            }
        }
    }
    // END

Module's Control Panel Homepage

In order to make a menu visible when a person first views the module through the Control Panel, we set the default switch option to be a function called fortunes_home(), which will contain a list of the menu options for this module. Remember when creating any text that is displayed on a page to create it in the language file array for this module, and then display it using the Language class.

    // ----------------------------------------
    //  Module Homepage
    // ----------------------------------------

    function fortunes_home()
    {
        global $DSP, $LANG;

        $DSP->title = $LANG->line('fortunes_module_name');
        $DSP->crumb = $DSP->anchor(BASE.
                                   AMP.'C=modules'.
                                   AMP.'M=fortunes',
                                   $LANG->line('fortunes_module_name'));
        $DSP->crumb .= $DSP->crumb_item($LANG->line('fortunes_menu'));

        $DSP->body .= $DSP->heading($LANG->line('fortunes_menu'));

        $DSP->body .= $DSP->qdiv('itemWrapper', $DSP->heading($DSP->anchor(BASE.
                                                                           AMP.'C=modules'.
                                                                           AMP.'M=fortunes'.
                                                                           AMP.'P=add',
                                                                           $LANG->line('add_fortune')),
                                                                           5));

        $DSP->body .= $DSP->qdiv('itemWrapper', $DSP->heading($DSP->anchor(BASE.
                                                                           AMP.'C=modules'.
                                                                           AMP.'M=fortunes'.
                                                                           AMP.'P=view',
                                                                           $LANG->line('view_fortunes')),
                                                                           5));
    }
    // END

Everything Else

The remaining functions in the Control Panel file are up to your own devising, and we suggest you examine this example module and the rest of the module documentation to see how we create them.

View Folders and Files

NOTE: If your module doesn't have a control panel, you may skip this step.

A view is simply a web page or page fragment. To create your module control panel using views to show the rendered output, you will start by creating a views folder. In general, each page of your control panel will have its own view file inside the views folder.

/system/modules/fortune/views/files
/system/modules/fortune/views/subfolder/files

Note: You are not required to use a view file to create your output markup. Any string added to $DSP->body is placed inside the control panel page's content container. For very simple pages, this may be the option you choose. However, views are the best architectural choice, as they are modular and easy to read and modify. As such, they are the recommended approach.

Since view files are really just HTML snippets with a bit of PHP added to output your variables, one easy way to get started is by viewing the rendered output of your current module. Using the 'Fortunes' demo module as an example, here is the output html for the home page:

<div id='contentNB'> <h1>Fortunes Control Panel</h1> <div class='itemWrapper' > <h5><a href='index.php?S=0&C=modules&M=fortunes&P=add' >Add Fortune</a></h5> </div> <div class='itemWrapper' > <h5><a href='index.php?S=0&C=modules&M=fortunes&P=view' >View Fortunes</a></h5> </div> </div>

Everything inside the contentNB division will be controlled by your view file. Thus to replicate the current module, you could simply copy the rendered html and replace the variable elements with, well, variables:

<div class='itemWrapper' > <h5><a href="<?=BASE.AMP.'C=modules'.AMP.'M=fortunes'.AMP.'P=add'?>"><?=lang('add_fortune')?></a></h5> </div> <div class='itemWrapper' > <h5>href="<?=BASE.AMP.'C=modules'.AMP.'M=fortunes'.AMP.'P=view'?>"><?=lang('view_fortunes')?></a></h5> </div>

There are a few things to note in the above:

  1. Views are REALLY easy!
  2. Use php short tags in your views for increased legibility. If your server does not support short tags, ExpressionEngine will automatically rewrite them when processing your view file.
  3. The ease of using your language variables: <?=lang('view_fortunes')?>
  4. The Fortunes sample module is kinda ugly.

Let's make the output a bit less ugly. The 'Referrer' module is a nice example. Riffing on that, we end up with a completed view file that was shown above:

<ul> <li><div class="cp_button"><a href="<?=$add_url?>"><?=lang('add_fortune')?></a></div></li> <li><div class="cp_button"><a href="<?=$view_url?>"><?=lang('view_fortunes')?></a></div></li> </ul>

Now instead of creating the markup within the logic of your module's control panel methods, you'll call the view file, and pass along any variables the view file needs:

$DSP->body .= $DSP->view('subfolder/filename', $vars, TRUE);

The first parameter is the name of the view file, with any subfolders if necessary, the second parameter is an associative array of variables where the keys are the variable names, and the values are the variable's values, and lastly the third parameter tells whether or not you want the view file to return its data or add it to the output buffer. In this case, we want to return as a string so we can assign it to $DSP->body.

For a refresher on the basics of working with view files, see the CodeIgniter user guide on view files.

The Core Module file (mod.fortunes.php)

The Core Module file is used for outputting content via the Templates and doing any processing that is required by both the Control Panel and any module tags contained in a template. Inside this file should be a class using the same name of the module and containing at least one class variable, $return_data, which will contain the module's outputted content and is retrieved by the Template parser after the module is done processing.

class Fortunes {

    var $return_data	= '';

    // -------------------------------------
    //  Constructor
    // -------------------------------------

    function Fortunes()
    {
    }

}

In ExpressionEngine, a typical module or plugin tag has an appearance similar to this:

{exp:weblog:weblog_name}

The first part of the tag, exp:, tells ExpressionEngine that this is a tag. The second part, weblog, is the module or the plugin that the tag belongs to, in this case the "weblog" module. The third part (modules only) is the specific function from within the Core Module file's class that is being called; in the above example the function called is named "weblog_name". The third part can be optional for a module and ExpressionEngine will simply call the constructor function for the Core Module class. Since our Fortunes module is not overly complex, we will take advantage of this and our module will simply use the ExpressionEngine tag {exp:fortunes}.

Template parameters and output for Core Module functions are covered in the section regarding the Template class, so we suggest you examine this example module and that area of the Developer Documentation for more information about creating functions within the Core Module file for your module tags.

Top of Page