Development

How to create a custom module

Today I want to show you how to create your own TYPOlight module. There have been some requests for an "external data" module, therefore, I am going to explain how such a module can be set up. Let's assume that you want to publish your CD collection and you want each CD to be shown with its title, artist name, image and an individual comment. We need a back end module to add the records and a front end module to display them.

Creating the basic file structure

First, we have to create the basic file structure including the following folders:

  • system/modules/cd_collection
  • system/modules/cd_collection/config
  • system/modules/cd_collection/dca
  • system/modules/cd_collection/languages
  • system/modules/cd_collection/templates

As you can see, I have named the module cd_collection.

Setting up the back end module

Let's start with the back end module.

Configuring the module

First of all, we have to set up the configuration file of our module. When TYPOlight is initialized, each module configuration file is loaded allowing you to overwrite existing settings. Thus, you never have to modify any core files and your settings will not be overwritten on a live update. Now, please create a file named config.php and add the following lines:

<?php

// Back end module
$GLOBALS['BE_MOD']['content']['cd_collection'] = array
(
    'tables' => array('tl_cds'),
    'icon'   => 'system/modules/cd_collection/icon.gif'
);

// Front end module
array_insert($GLOBALS['FE_MOD']['miscellaneous'], 0, array
(
    'cd_collection' => 'ModuleCdCollection'
));

?>

Ok, let's see what we have done here. First, we have added a new back end module called cd_collection to the content group. It uses one table called tl_cds and an icon which does not exist yet. Then, we have added a new front end module called cd_collection to the miscellaneous group.

If you use the array_insert function to add a new back end or front end module, you can define its exact position. In our example, we are using the function to make our module the first one within the group.

Setting up the database

Next, we have to create the table which we have defined in the configuration file. Therefore, we simply create a database.sql file and add the following lines:

-- 
-- Table `tl_cds`
-- 

CREATE TABLE `tl_cds` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `tstamp` int(10) unsigned NOT NULL default '0',
  `title` varchar(64) NOT NULL default '',
  `artist` varchar(64) NOT NULL default '',
  `image` varchar(64) NOT NULL default '',
  `comment` text NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

As you can see, we have added a field for each piece of information that we want to include later. In addition, we have defined an ID and a TSTAMP field which is required by TYPOlight. Now, open the TYPOlight install tool (not the live update) and update your database tables.

Creating a data container array

As you (should) know, TYPOlight automatically renders back end forms from the global table configuration array. This table configuration array consists of all data container arrays which are stored in the dca folders of each module. Similar to the configuration files, each dca file is included when the system is initialized allowing you to overwrite existing settings. You can find a complete list of options at DevelopmentConfiguration.

Now, please create a new file called system/modules/cd_collection/dca/tl_cds.php and add the following lines to it:

<?php

$GLOBALS['TL_DCA']['tl_cds'] = array
(
    // Config
    'config' => array
    (
        'dataContainer'               => 'Table',
        'enableVersioning'            => true
    ),

    // List
    'list' => array
    (
        'sorting' => array
        (
            'mode'                    => 1,
            'fields'                  => array('title'),
            'flag'                    => 1,
            'panelLayout'             => 'search,limit'
        ),
        'label' => array
        (
            'fields'                  => array('title', 'artist'),
            'format'                  => '%s <span style="color:#b3b3b3; padding-left:3px;">[%s]</span>'
        ),
        'global_operations' => array
        (
            'all' => array
            (
                'label'               => &$GLOBALS['TL_LANG']['MSC']['all'],
                'href'                => 'act=select',
                'class'               => 'header_edit_all',
                'attributes'          => 'onclick="Backend.getScrollOffset();"'
            )
        ),
        'operations' => array
        (
            'edit' => array
            (
                'label'               => &$GLOBALS['TL_LANG']['tl_cds']['edit'],
                'href'                => 'act=edit',
                'icon'                => 'edit.gif'
            ),
            'copy' => array
            (
                'label'               => &$GLOBALS['TL_LANG']['tl_cds']['copy'],
                'href'                => 'act=copy',
                'icon'                => 'copy.gif'
            ),
            'delete' => array
            (
                'label'               => &$GLOBALS['TL_LANG']['tl_cds']['delete'],
                'href'                => 'act=delete',
                'icon'                => 'delete.gif',
                'attributes'          => 'onclick="if (!confirm(\` . $GLOBALS['TL_LANG']['MSC']['deleteConfirm'] . '\')) return false; Backend.getScrollOffset();"'
            ),
            'show' => array
            (
                'label'               => &$GLOBALS['TL_LANG']['tl_cds']['show'],
                'href'                => 'act=show',
                'icon'                => 'show.gif'
            )
        )
    ),

    // Palettes
    'palettes' => array
    (
        'default'                     => 'title,artist;image;comment',
    ),

    // Fields
    'fields' => array
    (
        'title' => array
        (
            'label'                   => &$GLOBALS['TL_LANG']['tl_cds']['title'],
            'inputType'               => 'text',
            'search'                  => true,
            'eval'                    => array('mandatory'=>true, 'maxlength'=>64)
        ),
        'artist' => array
        (
            'label'                   => &$GLOBALS['TL_LANG']['tl_cds']['artist'],
            'inputType'               => 'text',
            'search'                  => true,
            'eval'                    => array('mandatory'=>true, 'maxlength'=>64)
        ),
        'image' => array
        (
            'label'                   => &$GLOBALS['TL_LANG']['tl_cds']['image'],
            'inputType'               => 'fileTree',
            'eval'                    => array('files'=>true, 'fieldType'=>'radio')
        ),
        'comment' => array
        (
            'label'                   => &$GLOBALS['TL_LANG']['tl_cds']['comment'],
            'inputType'               => 'textarea'
        )
    )
);

?>

Let's take a look at it file step by step.

In the first group (configuration) we have defined that the module uses a table as data container and we have enabled versioning for it.

In the second group (list) we have defined a sorting mode (1 = sort by default value), the default sorting field (title) and a panel layout which includes the search records function and the limit records function. In addition, we have defined a label (which will be title [artist]) and a couple of navigation icons that will be shown next to each record.

In the third group (palette) we have defined a simple palette containing four fields in three groups. You can separate fields using comma (,) and groups using semicolon (;).

In the last group (fields) we have defined the four input fields that we want to use for our CD collection. The first two fields will be searchable and mandatory and our image field will show the file tree with radio buttons (since we want to have a single image per record).

Adding field labels

At this point, our back end module is almost finished and already working. However, there are a couple of labels missing since we did not "hardcode" them into the source code but want to use a language file. Therefore, please create a new language file called system/modules/cd_collection/languages/en/tl_cds.php and add the following lines:

<?php

// Fields
$GLOBALS['TL_LANG']['tl_cds']['title']   = array('Title', 'Please enter the CD title.');
$GLOBALS['TL_LANG']['tl_cds']['artist']  = array('Artist', 'Please enter the artist\'s name.');
$GLOBALS['TL_LANG']['tl_cds']['image']   = array('Image', 'Please select the CD cover image.');
$GLOBALS['TL_LANG']['tl_cds']['comment'] = array('Comment', 'Please add a comment.');

// Buttons
$GLOBALS['TL_LANG']['tl_cds']['new']    = array('New CD', 'Add a new CD');
$GLOBALS['TL_LANG']['tl_cds']['edit']   = array('Edit CD', 'Edit CD ID %s');
$GLOBALS['TL_LANG']['tl_cds']['copy']   = array('Copy CD', 'Copy CD ID %s');
$GLOBALS['TL_LANG']['tl_cds']['delete'] = array('Delete CD', 'Delete CD ID %s');
$GLOBALS['TL_LANG']['tl_cds']['show']   = array('CD details', 'Show details of CD ID %s');

?>

Then, create file system/modules/cd_collection/languages/en/modules.php:

<?php

// Back end modules
$GLOBALS['TL_LANG']['MOD']['cd_collection'] = array('CD collection', 'Publish your CD collection online.');

// Front end modules
$GLOBALS['TL_LANG']['FMD']['cd_collection'] = array('CD collection', 'This module lists all CDs of your collection.');

?>

Now your back end module is ready to be used. Log in to the back end, click on CD collection and start adding your CDs.

Setting up the front end module

Of course, we also want to display our CDs in the front end. Typically, a front end module is represented by a particular class which extends class Module. If you are not familiar with Object Oriented Programming, you might want to read this article.

Adding the front end module

In order to add a front end module to TYPOlight, we have to extend table tl_module. You already know that configuration files and dca files are included when the system is initialized allowing you to overwrite or extend existing settings. Therefore, we will now create file system/modules/cd_collection/dca/tl_module.php and add the following lines:

<?php

// Add a palette to tl_module
$GLOBALS['TL_DCA']['tl_module']['palettes']['cd_collection'] = 'name,type,headline;align,space,cssID';

?>

Creating a template file

Next, we have to create a template file for the module. Therefore, please create a new file called system/modules/cd_collection/templates/mod_cd_collection.tpl and add the following lines:

<div class="<?php echo $this->class; ?>"<?php echo $this->cssID; ?><?php if ($this->style): ?> style="<?php echo $this->style; ?>"<?php endif; ?>>
<?php if ($this->headline): ?>

<<?php echo $this->hl; ?>><?php echo $this->headline; ?></<?php echo $this->hl; ?>>
<?php endif; ?>

<table cellpadding="4" cellspacing="0" summary="My CD collection"><?php foreach ($this->cds as $cd): ?> 
  <tr>
    <td><img src="<?php echo $cd['src']; ?>" alt="<?php echo $cd['alt']; ?>" /></td>
    <td><strong><?php echo $cd['title']; ?></strong> (<?php echo $cd['artist']; ?>)<p><?php echo $cd['comment']; ?></p></td>
  </tr><?php endforeach; ?> 
</table>

</div>

The template file basically creates a table containing our CD images in the left column and the information in the right column. I have simply copied the surrounding DIV elements from another module template.

Creating the module class

The last thing that we have to do is to create the module class. Therefore, please create a new file called system/modules/cd_collection/ModuleCdCollection.php and add the following lines:

<?php

class ModuleCdCollection extends Module
{
    protected $strTemplate = 'mod_cd_collection';

    protected function compile()
    {
        $arrCds = array();
        $objCds = $this->Database->execute("SELECT * FROM tl_cds ORDER BY title");

        while ($objCds->next())
        {
            $arrCds[] = array
            (
                'title' => $objCds->title,
                'artist' => $objCds->artist,
                'comment' => $objCds->comment,
                'src' => $this->getImage($objCds->image, '120', ''),
                'alt' => specialchars($objCds->title),
            );
        }

        $this->Template->cds = $arrCds;
    }
}

?>

Ok, let's have a closer look at this class. First, we have assigned template mod_cd_collection which we have created earlier. Then, inside method compile, we have selected all records from our database table and added them to an array called $arrCds. Thereby, we have used the built-in image handler to automatically resize our images to 120px width and we have set the CD title as alternative image text. At the bottom, we have added the CDs array to the template file.

Watch the result

Finally, after you have added the module to an article or page layout, you can watch the result in the front end. Provided that you have done everything right, your back end articles preview might look similar to this:

Custom extension

Download the files

If you do not want to create the files manually, you can download a ZIP archive here.

CD_collection.zip
In addition a suitable Icon: CD collection Copy this to directory cd_collection as icon.gif.

I hope that this tutorial has given you an idea of how to develop for TYPOlight. I am planning to publish a more complex example as a narrated screencast soon. In the meantime, if you have any questions please post them in the developer forum.


Tutorial created by Leo

Attachments