# Application Modules - Creating a Simple Module / Step by Step Instructions

These step-by-step instructions will help you to create a simple (basic) module.
Take in account that we use following terms and signs:
[MODULE_NAME] - module name, e.g Blog or Orders
[MODULE_NAME_LC] - module lowercase name, e.g blog or orders
[MODULE_DESCRIPTION] - module text description
[CONTROLLER_NAME] - the main controller for module (module may have few controller), e.g Posts
[CONTROLLER_NAME_LC] - the main controller lowercase name, e.g posts
[MODEL_NAME] - the main model for module (module may have few models), e.g Posts
[PROPERTY_KEY] - property key, e.g. shortcode
[PROPERTY_VALUE] - property value, e.g. {module:blog}
[PROPERTY_NAME] - property name, e.g. Shortcode
[PROPERTY_DESCRIPTION] - property description, e.g. This shortcode allows you to display...
[PRIVILEGE_CODE] - the code of privilege, e.g: menus_add, menus_edit or menus_delete

## Content

- Step 1. Creating Module Directory.
- Step 2. Creating CHANGELOG File.
- Step 3. Creating info.xml File.
- Step 4. Creating Main Component.
- Step 5. Creating Config Files.
- Step 6. Creating SQL Dump Files.
- Step 7. Creating Controllers.
- Step 8. Creating Models.
- Step 9. Creating Views.
- Download Module Code.

### Step 1. Creating Module Directory. ¶

Create a new directory named `[MODULE_NAME_LC]` in `protected/modules/`

- protected/
  - modules/ directory where all modules are placed
    - [MODULE_NAME_LC]/

### Step 2. Creating CHANGELOG File. ¶

Create in `protected/modules/[MODULE_NAME_LC]/` a `CHANGELOG` file with a following content:

    Version 0.0.1
    ----------------------------
    Initial release

### Step 3. Creating info.xml File. ¶

This file contains information about the new module. It defines the files that need to be installed by the ApPHP Framework installer and specifies configuration parameters for the module. Below the content of this file:

- modules/ directory where all modules are placed
  - [MODULE_NAME_LC]/ directory for storing module files
    - components/ module components folder
      - [MODULE_NAME]Component.php module main component
    - config/
      - main.php main configuration
      - [MODULE_NAME_LC].php special configuration (movable file)
    - controllers/ module controllers
      - [CONTROLLER_NAME]Controller.php
    - data/ module data files
      - install.mysql.sql
      - uninstall.mysql.sql
      - update.002.mysql.sql
    - images/ module images
      - icon.png
    - messages/ module messages (localization files)
      - [MODULE_NAME_LC].php
    - models/ module models
      - [MODEL_NAME].php
    - vews/ model views
      - [CONTROLLER_NAME_LC]/ view files for controller
        - add.php
        - edit.php
        - manage.php
        - view.php
        - viewall.php
    - CHANGELOG change log file
    - info.xml main information file

### Step 4. Creating Main Component. ¶

Create directory `components/` and create there an empty component file, called `[MODULE_NAME]Component.php`.

    <?php
    /**
    * [MODULE_NAME]Component
    *
    * PUBLIC:                  PRIVATE
    * -----------              ------------------
    * prepareTab
    * drawShortcode
    *
    * STATIC
    * -------------------------------------------
    * init
    *
    */

    class [MODULE_NAME]Component extends CComponent{

        const NL = "\n";

        public static function init()
        {
            return parent::init(__CLASS__);
        }

        /**
         * Prepares [MODULE_NAME] module tabs
         * @param string $activeTab
         */
        public static function prepareTab($activeTab = 'info')
        {
            return CWidget::create('CTabs', array(
                'tabsWrapper'=>array('tag'=>'div', 'class'=>'title'),
                'tabsWrapperInner'=>array('tag'=>'div', 'class'=>'tabs'),
                'contentWrapper'=>array(),
                'contentMessage'=>'',
                'tabs'=>array(
                    A::t('[MODULE_CODE]', 'Settings') => array(
                        'href'=>'modules/settings/code/[MODULE_CODE]',
                        'id'=>'tabSettings', 'content'=>'', 'active'=>false,
                        'htmlOptions'=>array('class'=>'modules-settings-tab')
                    ),
                    A::t('[MODULE_CODE]', '[CONTROLLER_NAME]') => array(
                        'href'=>'[CONTROLLER_NAME]/index', 'id'=>'tabInfo', 'content'=>'',
                        'active'=>($activeTab == 'info' ? true : false)
                    ),
                ),
                'events'=>array(
                    //'click'=>array('field'=>$errorField)
                ),
                'return'=>true,
            ));
        }

        /**
         * Draws shortcode output
         * @param array $params
         */
        public static function drawShortcode($params = array())
        {
            $output = '';
            // Your code is here ...

            return $output;
        }

    }

### Step 5. Creating Config Files. ¶

There are minimum two (2) types of configuration files, that present in module:

- main.php - includes module general settings (stays in the origianl module directory after installation)
- [MODULE_CODE].php - includes information about module component classes and will be copied into `protected/config/` directory during installation process, then merged by Framework with other configuration files.

Lets see the examples of such files, here the `main.php`:

    <?php

    return array(
        // Module classes
        'classes' => array(
            '[MODULE_NAME]Component',
            '[MODEL_NAME]',
        ),
        // Management links
        'managementLinks' => array(
            A::t('[MODULE_CODE]', '[MODULE_NAME]') => '[MODEL_NAME]/manage'
        ),
    );

and here the `[MODULE_CODE].php`:

    <?php

    return array(
        // Module components
        'components' => array(
            '[MODULE_NAME]Component' => array('enable' => true, 'class' => '[MODULE_NAME]Component'),
        ),

    );

### Step 6. Creating SQL Dump Files. ¶

Each module must have at least two (2) SQL dump files for successfull installation: `install.sql` and `uninstall.sql`. As an option you may also create a file for updating `update.sql` (if you're planning to release updates for your module in the future).
All these files must be placed in `data/` directory and will be used by ApPHP Framework and Directy CMF for installation of the module.
Each SQL dump file must include at minimum SQL commands, lets check them.
`install.mysql.sql`:

INSERT INTO `<DB_PREFIX>modules` (`id`, `code`, `name`, `description`, `version`, `icon`, `show_on_dashboard`, `show_in_menu`, `is_installed`, `is_system`, `is_active`, `sort_order`) VALUES (NULL, '[MODULE_CODE]', '[MODULE_NAME]', '[MODULE_DESCRIPTION]', '0.0.1', 'icon.png', 0, 0, 1, 0, 1, 0); INSERT INTO `<DB_PREFIX>module_settings` (`id`, `module_code`, `property_key`, `property_value`, `name`, `description`, `property_type`, `property_source`, `is_required`) VALUES (NULL, '[MODULE_CODE]', '[PROPERTY_KEY]', '[PROPERTY_VALUE]', '[PROPERTY_NAME]', '[PROPERTY_DESCRIPTION]', 'label', '', '0'); INSERT INTO `<DB_PREFIX>privileges` (`id`, `category`, `code`, `name`, `description`) VALUES (NULL, '[MODULE_CODE]', '[PRIVILEGE_ACTION]', '[PRIVILEGE_NAME]', '[PRIVILEGE_DESCRIPTION]'); INSERT INTO `<DB_PREFIX>role_privileges` (`id`, `role_id`, `privilege_id`, `is_active`) VALUES (NULL, 1, (SELECT MAX(id) FROM `<DB_PREFIX>privileges`), 1), (NULL, 2, (SELECT MAX(id) FROM `<DB_PREFIX>privileges`), 1), (NULL, 3, (SELECT MAX(id) FROM `<DB_PREFIX>privileges`), 0); `update.mysql.sql`:

# any update, insert, delete, etc. operations related to module changes `uninstall.mysql.sql`:

DELETE FROM `<DB_PREFIX>modules` WHERE `code` = '[MODULE_CODE]'; DELETE FROM `<DB_PREFIX>module_settings` WHERE `module_code` = '[MODULE_CODE]'; DELETE FROM `<DB_PREFIX>role_privileges` WHERE `privilege_id` IN (SELECT id FROM `<DB_PREFIX>privileges` WHERE `category` = '[MODULE_CODE]'); DELETE FROM `<DB_PREFIX>privileges` WHERE `category` = '[MODULE_CODE]'; # delete other tables related to this module

### Step 7. Creating Controllers. ¶

Generally each module has at least one (main) controller class (there is only one exception, when you create a module that has no public access at all).
Below you may see the standard definition of the controller class (`[CONTROLLER_NAME]Controller.php` file).
Remember that this is only a sample code for controller class, more examples available in the module archive dummy_module.zip.

    <?php
    /**
    * Class [CONTROLLER_NAME]Controller
    *
    * PUBLIC:                  PRIVATE
    * -----------              ------------------
    * __construct
    * indexAction
    */
    class [CONTROLLER_NAME]Controller extends CController
    {
        /**
         * Class default constructor
         */
        public function __construct()
        {
            parent::__construct();

            // Block access if the module is not installed
            if(!Modules::model()->exists("code = '[MODULE_CODE]' AND is_installed = 1")){
                if(CAuth::isLoggedInAsAdmin()){
                    $this->redirect('modules/index');
                }else{
                    $this->redirect('index/index');
                }
            }

            // Set meta tags according to active language
            Website::setMetaTags(array('title'=>A::t('[MODULE_CODE]', '[CONTROLLER_NAME] Management')));
        }

        /**
         * Controller default action handler
         */
        public function indexAction()
        {
            //$this->redirect('.../manage');
        }

    }

### Step 8. Creating Models. ¶

Like in case of controllers, module has at least one (main) controller class (there is only one exception, when your module doesn't store information at all).
Here the example of the standard model class (`[MODEL_NAME].php` file).
Remember that this is only a sample code for model class, more examples available in the module archive dummy_module.zip.

    <?php
    /**
     * Template of [MODEL_NAME] model
     *
     * PUBLIC:                 PRIVATE
     * -----------             ------------------
     * __construct
     * relations
     *
     * STATIC:
     * ---------------------------------------------------------------
     * model
     *
     */
    class [MODEL_NAME] extends CActiveRecord
    {

        /** @var string */
        protected $_table = '[MODEL_TABLE]';

        public function __construct()
        {
            parent::__construct();
        }

        /**
         * Returns the static model of the specified AR class
         */
        public static function model()
        {
            return parent::model(__CLASS__);
        }

    	/**
         * Used to define relations between different tables in database and current $_table
    	 * This method should be overridden
    	 */
        protected function _relations()
        {
           /* return array(
            *    'field_name' => array(
            *        self::BELONGS_TO,
            *        'table_name',
            *        'field_name',
            *        'condition'=>'',
            *        'joinType'=>self::LEFT_OUTER_JOIN,
            *        'fields'=>array('name'=>'language_name')
            *    ),
            * );
            */
        }
    }

### Step 9. Creating Views. ¶

View presentation file must be created for each controller's action (there is only exception when you're planning to use view from another action or perform redirection).
Here the example of the standard view file (`manage.php` file).
Remember that this is only a sample code for view files, more examples available in the module archive dummy_module.zip.

<?php $this->_activeMenu = '[MODULE_CODE]/manage'; $this->_breadCrumbs = array( array('label'=>A::t('[MODULE_CODE]', 'Modules'), 'url'=>'modules/'), array('label'=>A::t('[MODULE_CODE]', '[MODULE_NAME]'), 'url'=>'modules/settings/code/[MODULE_CODE]'), array('label'=>A::t('[MODULE_CODE]', '[CONTROLLER_NAME] Management'), 'url'=>'[MODULE_CODE]/manage'), ); ?> <h1><?php echo A::t('[MODULE_CODE]', '[CONTROLLER_NAME] Management'); ?></h1> <div class="bloc"> <?php echo $tabs; ?> <div class="content"> <?php echo $actionMessage; if(Admins::hasPrivilege('modules', 'edit') && Admins::hasPrivilege('[MODEL_CODE]', 'add')){ echo '<a href="[CONTROLLER_NAME]/add" class="add-new">'.A::t('[MODULE_CODE]', 'Add [CONTROLLER_NAME]').'</a>'; } echo CWidget::create('CGridView', array( 'model'=>'[MODEL_NAME]', 'actionPath'=>'[CONTROLLER_NAME_LC]/manage', 'condition'=>'', //'defaultOrder'=>array('field_1'=>'DESC'), 'passParameters'=>true, 'pagination'=>array('enable'=>true, 'pageSize'=>20), 'sorting'=>true, 'filters'=>array( 'field_1' => array('title'=>'Field 1', 'type'=>'textbox', 'operator'=>'=', 'width'=>'', 'maxLength'=>''), 'field_2' => array('title'=>'Field 2', 'type'=>'enum', 'operator'=>'=', 'width'=>'', 'source'=>array('0'=>'No', '1'=>'Yes')), 'field_3' => array('title'=>'Field 3', 'type'=>'datetime', 'operator'=>'=', 'width'=>'80px', 'maxLength'=>'', 'format'=>''), ), 'fields'=>array( 'field_1' => array('title'=>'Field 1', 'type'=>'concat', 'align'=>'', 'width'=>'', 'class'=>'left', 'headerClass'=>'left', 'isSortable'=>true, 'concatFields'=>array('first_name', 'last_name'), 'concatSeparator'=>', ',), 'field_2' => array('title'=>'Field 2', 'type'=>'decimal', 'align'=>'', 'width'=>'', 'class'=>'right', 'headerClass'=>'right', 'isSortable'=>true, 'format'=>'american|european'), 'field_3' => array('title'=>'Field 3', 'type'=>'enum', 'align'=>'', 'width'=>'', 'class'=>'center', 'headerClass'=>'center', 'isSortable'=>true, 'source'=>array('0'=>'No', '1'=>'Yes')), 'field_4' => array('title'=>'Field 4', 'type'=>'image', 'align'=>'', 'width'=>'', 'class'=>'center', 'headerClass'=>'center', 'isSortable'=>false, 'imagePath'=>'images/flags/', 'defaultImage'=>'', 'imageWidth'=>'16px', 'imageHeight'=>'16px', 'alt'=>''), 'field_5' => array('title'=>'Field 5', 'type'=>'label', 'align'=>'', 'width'=>'', 'class'=>'left', 'headerClass'=>'left', 'isSortable'=>true, 'definedValues'=>array(), 'format'=>''), 'field_6' => array('title'=>'Field 6', 'type'=>'link', 'align'=>'', 'width'=>'', 'class'=>'center', 'headerClass'=>'center', 'isSortable'=>false, 'linkUrl'=>'path/to/param/{id}', 'linkText'=>''), ), 'actions'=>array( 'edit' => array( 'disabled'=>!Admins::hasPrivilege('modules', 'edit') || !Admins::hasPrivilege('[MODEL_CODE]', 'edit'), 'link'=>'[CONTROLLER_NAME_LC]/edit/id/{id}', 'imagePath'=>'templates/backend/images/edit.png', 'title'=>A::t('[MODULE_CODE]', 'Edit this record') ), 'delete' => array( 'disabled'=>!Admins::hasPrivilege('modules', 'edit') || !Admins::hasPrivilege('[MODEL_CODE]', 'delete'), 'link'=>'[CONTROLLER_NAME_LC]/delete/id/{id}', 'imagePath'=>'templates/backend/images/delete.png', 'title'=>A::t('[MODULE_CODE]', 'Delete this record'), 'onDeleteAlert'=>true ) ), 'return'=>true, )); ?> </div> </div>

### Download Module Code. ¶

Here you may download code examples for "dummy module".
Remember, that this only an example, you may need to add more features and procedures to make your module works as expected.
Download Module ▼