Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Once you’ve selected a name, create a directory in the /modules directory with the name you selected.  Next, create a file in the new directory with the same name as the directory followed by .php.  For example, if the name of your module is helloworldnc, then create a file helloworldnc.php in the helloworldnc directory.  In that file, copy the required if statement from the top of another module, then add a new class with the same name as the file that extends the Module class.  The case of the class name isn’t important, though it is convention to use an uppercase letter at the start of each word.  Here’s an example:

Code Block
languagephp
<?php
if (!defined('_CAN_LOAD_FILES_'))
    exit;
class HelloWorldNC extends Module
{
}
?>

...

Although your module will now appear in the Other Modules section on the Modules tab, it will not display a name or description.  You will need to add a constructor inside the class to add that information.  Here’s an example constructor:

Code Block
languagephp
public function __construct()
{
    $this->name = 'helloworldnc';
    $this->tab = 'front_office_features';
    $this->version = '1.0';
    $this->author = 'Nethercott Constructions';
    parent::__construct();
    $this->displayName = $this->l('Hello World');
    $this->description = $this->l('This is an example module');
    $this->confirmUninstall = $this->l('Are you sure you want to uninstall?');
    if (!Configuration::get('HELLO_WORLD_NC_NAME'))
        $this->warning = $this->l('No name provided');
}

...

The next thing you should do is add a config.xml file to your module.  Although one isn’t required, it helps to reduce the amount of memory required by the Modules tab by loading only the file instead of the entire module to get the module’s name and description.  Here is an example config.xml file:

Code Block
languagexml
<?xml version="1.0" encoding="UTF-8" ?>
<module>
    <name><![CDATA[helloworldnc]]></name>
    <displayName><![CDATA[Hello World]]></displayName>
    <version><![CDATA[1.0]]></version>
    <author><![CDATA[Nethercott Constructions]]></author>
    <description><![CDATA[This is an example module]]></description>
    <tab><![CDATA[front_office_features]]></tab>
    <is_configurable>1</is_configurable>
    <need_instance>1</need_instance>
    <limited_countries></limited_countries>
</module>

...

The limited_countries variable can optionally be used if a module should only be available in a specific country. For example, the following line uses the country ISO code to limit a module to France only:

Code Block
languagexml
<limited_countries>fr</limited_countries>

...

Although your module is now displayed on the Modules tab, you won’t be able to install it yet.  You will need to add an install() function inside the class before you can install the module.  Here’s an example install() function:

Code Block
languagephp
public function install()
{
    if (!parent::install() OR
        !$this->registerHook('leftColumn') OR
        !$this->registerHook('header') OR
        !Configuration::updateValue('HELLO_WORLD_NC_SHOW_NAME', 1) OR
        !Configuration::updateValue('HELLO_WORLD_NC_NAME', 'World'))
        return false;
    return true;
}

...

It is also a good idea to add an uninstall() function, so that all the module’s settings are removed from the database when the module is not being used.  For example:

Code Block
languagephp
public function uninstall()
{
    if (!parent::uninstall() OR
        !Configuration::deleteByName('HELLO_WORLD_NC_SHOW_NAME') OR
        !Configuration::deleteByName('HELLO_WORLD_NC_NAME'))
        return false;
    return true;
}

...

Although your module can now be installed and uninstalled, you won’t be able to configure the module.  If your module has settings that should be easy to change, you should add a configuration page.  The getContent() function is called when the Configure link on a module is clicked.  It displays the name of the module, then calls the displayForm() function to display the configuration page form.  Here’s an example:

Code Block
languagephp
public function getContent()
{
    $output = '<h2>'.$this->displayName.'</h2>';
    return $output.$this->displayForm();
}
public function displayForm()
{
    return '
    <form action="'.$_SERVER['REQUEST_URI'].'" method="post">
        <fieldset>
            <legend><img src="'.$this->_path.'logo.gif" alt="" title="" />'.$this->l('Settings').'</legend>
            <label>'.$this->l('Name').'</label>
            <div class="margin-form">
                <input type="text" name="name" value="'.Tools::getValue('name', Configuration::get('HELLO_WORLD_NC_NAME')).'" />
                <p class="clear">'.$this->l('The name that will appear after Hello').'</p>
            </div>
            <label>'.$this->l('Show Name').'</label>
            <div class="margin-form">
                <input type="radio" name="showName" id="showName_on" value="1" '.(Tools::getValue('showName', Configuration::get('HELLO_WORLD_NC_SHOW_NAME')) ? 'checked="checked" ' : '').'/>
                <label class="t" for="showName_on"> <img src="../img/admin/enabled.gif" alt="'.$this->l('Enabled').'" title="'.$this->l('Enabled').'" /></label>
                <input type="radio" name="showName" id="showName_off" value="0" '.(!Tools::getValue('showName', Configuration::get('HELLO_WORLD_NC_SHOW_NAME')) ? 'checked="checked" ' : '').'/>
                <label class="t" for="showName_off"> <img src="../img/admin/disabled.gif" alt="'.$this->l('Disabled').'" title="'.$this->l('Disabled').'" /></label>
                <p class="clear">'.$this->l('Whether to show the name after Hello').'</p>
            </div>
            <center><input type="submit" name="submitHelloWorldNC" value="'.$this->l('Save').'" class="button" /></center>
        </fieldset>
    </form>';
}

This function simply returns a single long text string that contains the HTML of the configuration page.  The <form> line is used to post the current configuration settings to the module.  If you add a file field as one of the configuration settings, you must change this to the following before the files will be posted:

Code Block
languagexml
<form action="'.$_SERVER['REQUEST_URI'].'" method="post" enctype="multipart/form-data">

...

Although you’ve now got a configuration page, you can’t save any of the settings.  To do that, you will need to add more code to the getContent() function.  Here’s an example:

Code Block
languagephp
public function displayErrors()
{
    $errors = 
        '<div class="error">
            <img src="../img/admin/error2.png" />
            '.sizeof($this->_errors).' '.(sizeof($this->_errors) > 1 ? $this->l('errors') : $this->l('error')).'
            <ol>';
    foreach ($this->_errors AS $error)
        $errors .= '<li>'.$error.'</li>';
    $errors .= '
            </ol>
        </div>';
    return $errors;
}
public function displayConf($conf)
{
    return
        '<div class="conf">
            <img src="../img/admin/ok2.png" /> '.$conf.'
        </div>';
}
public function getContent()
{ 
    $this->_errors = array();
    $output = '<h2>'.$this->displayName.'</h2>';
    if (Tools::isSubmit('submitHelloWorldNC'))
    {
        $name = Tools::getValue('name');
        $showName = (int)(Tools::getValue('showName'));
        if ($showName != 0 AND $showName != 1)
            $this->_errors[] = $this->l('Show Name: Invalid choice.');
        if (sizeof($this->_errors) == 0)
        {
            Configuration::updateValue('HELLO_WORLD_NC_NAME', $name);
            Configuration::updateValue('HELLO_WORLD_NC_SHOW_NAME', $showName);
            $output .= $this->displayConf($this->l('Settings updated'));
        }
    }
    else
        $output .= $this->displayErrors();
    return $output.$this->displayForm();
}

...

Although your module now has a fully working configuration page, it doesn’t do anything on the website.  To make the module do something, you must register hooks the module can be placed in.  Here’s an example:

Code Block
languagephp
public function hookLeftColumn($params)
{
    global $smarty;
    $smarty->assign(array(
        'name' => Configuration::get('HELLO_WORLD_NC_NAME'),
        'showName' => (int)(Configuration::get('HELLO_WORLD_NC_SHOW_NAME'))
    ));
    return $this->display(__FILE__, 'helloworldnc.tpl');
}
public function hookRightColumn($params)
{
    return $this->hookLeftColumn($params);
}
public function hookHeader()
{
    Tools::addJS($this->_path.'js/helloworldnc.js');
    Tools::addCSS($this->_path.'css/helloworldnc.css', 'all');
}

...

The hookHeader function is used to add Javascript and CSS code inside the <head> tag of the website.  Be careful how you use it, since it is easy to confuse it with the hookTop function, which adds code to the top of the website in the header.  The first line in the function adds modules/helloworldnc/js/examplenc.js to the list of Javascript files and the second line adds modules/helloworldnc/css/helloworldnc.css to the list of CSS files with media type all.  This will add the following code in the <head> tag of the website (if CCC is disabled):

Code Block
languagexml
<link href="http://www.yoursite.com/modules/helloworldnc/css/helloworldnc.css" rel="stylesheet" type="text/css" media="all" />
<script type="text/javascript" src="http://www.yoursite.com/modules/helloworldnc/js/helloworldnc.js"></script>

...

Smarty templates are files ending in .tpl that contain HTML code and embedded PHP code using a special Smarty syntax.  See the official Smarty documentation for more information.  Here’s an example helloworldnc.tpl:

Code Block
languagexml
<div id="hello_world_block_left" class="block">
    <h4>{l s='Message' mod='helloworldnc'}</h4>
    <div class="block_content">
        <p>Hello, {if isset($showName) AND $showName AND isset($name) AND $name}{$name}{else}World{/if}</p>
    </div>
</div>

...

It’s a good idea to put the CSS in a separate css directory and the JavaScript in a separate js directory to keep a clean file structure that matches the structure PrestaShop uses.  Here’s an example helloworldnc.css:

Code Block
languagecss
div#hello_world_block_left p { font-weight: bold }

In PrestaShop, it is convention to put CSS blocks containing only one line on a single line, and the last line of a CSS block doesn’t have a semicolon.  When there are multiple lines, it is convention to display the lines indented like this:

Code Block
languagecss
div#hello_world_block_left p {
    font-weight: bold;
    color: green
}

PrestaShop uses jQuery v1.4.4, so including it in your module is unnecessary. The only JavaScript you need to include is any third-party libraries you require that build on jQuery. If you need to include inline JavaScript to the module's TPL file, be aware that Smarty will generate an error if there are any curly braces in the code. To prevent the errors, you should add {literal} before the JavaScript code and {/literal} after like this:

Code Block
languagephp
{literal}
function testFunction() { alert('Test'); }
{/literal}

If you need to combine both JavaScript and Smarty tags, then it's better to replace the JavaScript curly braces with the {ldelim} and {rdelim} tags like this:

Code Block
languagephp
function testFunction() {ldelim} alert('{$message}'); {rdelim}

...

A new tab must be in the following format:

Code Block
languagephp
include_once(PS_ADMIN_DIR.'/../classes/AdminTab.php');
 
class AdminHelloWorldNC extends AdminTab
{

}

...

The next step is to add the constructor inside the class.  Here’s an example constructor:

Code Block
languagephp
private $_helloWorld = null;
 
public function __construct()
{
     $this->className = 'Configuration';
     $this->table = 'configuration';

     $this->_helloWorld = new HelloWorldNC();

     parent::__construct();
}

...

Lastly, the display() and postProcess() functions need to be added.  For example:

Code Block
languagephp
public function display()
{
     $this->_helloWorld->displayForm();
}
 
public function postProcess()
{
     echo $this->_helloWorld->getContent();
}

...

To call a hook, use the code Module::hookExec('name'), where name is from the name column below. In PrestaShop, it is convention to put the code generated by the hook in a variable called HOOK_NAME.  For example, the code to call all the modules in the left column blocks hook and then pass them into a TPL is:

Code Block
languagephp
$smarty->assign('HOOK_LEFT_COLUMN' => Module::hookExec('leftColumn'));

...

The customer cookie is read on line 94 (in PrestaShop v1.4.2) of init.php and the employee cookie is read on line 32 of admin/init.php.  To access the cookie from inside PrestaShop, add global $cookie; (or add $cookie to the list of global variables) to the top of the function in a class or at the top of a non-class file.  A variable in the cookie can then be accessed or changed using $cookie->variable.  To access the cookie from outside of PrestaShop, use code like the following:

Code Block
languagephp
include_once('path_to_prestashop/config/config.inc.php');
include_once('path_to_prestashop/config/settings.inc.php');
include_once('path_to_prestashop/classes/Cookie.php');
$cookie = new Cookie('ps');

...

There are also variables for product customisation.  For example, pictures_1 contains the filenames of the images the customer has uploaded to product 1 (in the upload directory) and textfields_1 contains the text the customer has uploaded to product 1.  Use the following code to get the customisation files and textfields of product 1:

Code Block
languagephp
$files = $cookie->getFamily('pictures_1');
$textFields = $cookie->getFamily('textFields_1');

...