V4 Sample Module - Hello World

Discussion in 'Developing, APIs and extending' started by DeanV, Dec 16, 2010.

  1. DeanV Established Member

    Hello everyone,

    I have started making my own module for version 4 and though I would share a basic principles behind adding one to version 4.

    This guide will be in similar format to the version 3 equivalent Creating a module: Hello World (Complete guide)

    This Guide Covers

    • Creating a new module / installation process
    • Creating custom files
    • Adding menu items to the admin/staff cp
    • Adding custom language file
    • Adding and processing a submit call. (Please see this post. I couldn't added it here because the total post size is to big)
    • Adding how to use grid rendering (Please see this post. I couldn't added it here because the total post size is to big)
    • Overview of module structure
    **Edits**
    December 17, 2010

    • Added how to add and use language files.
    • Also changed the class.Controller_HW.php section to include some new user interface functions.
    January 07, 2011

    • Fixed an error with the menu ID's
    • Added the section for the submit process
    • Added how to use the grid rendering

    This will be a very simple module that simple says hello but will contain the founding principles for creating other modules.

    [IMG]

    Creating The New Module

    1. Create a folder called helloword in the __modules folder.
    2. Add a folder called config
    3. Create a file called config.xml
    4. Create another file called class.SWIFT_SetupDatabase_helloworld.php
    So at this point you should have a structure that looks like this
    __modules\helloworld\config\config.xml
    __modules\helloworld\config\class.SWIFT_SetupDatabase_helloworld.php


    config.xml
    This file contains some very generic information about your module.
    An example one looks like this.
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <config>
        <title>Hello World</title>
        <description>This is a simple module that displays hello world.</description>
        <author>Your Name (yourname@example.com)</author>
        <link>http://www.yourdomain.com</link>
        <version>0.1</version>
        <compatibleWith>4.01.99</compatibleWith>
    </config>
    
    class.SWIFT_SetupDatabase_helloworld.php
    This file is where all of the install and module registering functions are now kept.

    I am going to break the code down into its respected sections and I will have on code block with the entire solution at the end.

    Class Declaration
    you declare a custom class for your new module. The name follows the structure SWIFT_SetupDatabase_yourmodulename

    it is also extended from the base class SWIFT_SetupDatabase

    Example:
    PHP:

    <?php
    class SWIFT_SetupDatabase_helloworld extends SWIFT_SetupDatabase
    {
    }
    ?>
    That's the basic class for your new module.

    Constructor
    The constructor for our module will be very simple, it will just call the base constructor and return true.

    The base constructor takes in a string which is the name of your module.

    Example:
    PHP:

    public function __construct()
    {
        
    parent::__construct("helloworld");
        return 
    true;
    }


    Destructor

    This is the code that destroys your class when it is no longer being used / out of scope in the code. (Memory management)

    All you do is call the base destructor and return true for this.

    Example:
    PHP:

    public function __destruct()
    {
        
    parent::__destruct();
        return 
    true;
    }


    Installer
    This function is responsible for installing your module. In this example our installer is very simple.

    Example:
    PHP:

    public function Install($_pageIndex)
    {
        
    parent::Install($_pageIndex);
        return 
    true;
    }
    Uninstaller
    This function is responsible for uninstalling our module and again is very simple for our example.

    Example:
    PHP:

    public function Uninstall()
    {
        
    parent::Uninstall();
        return 
    true;
    }
    Upgrader
    This function is responsible for doing tasks when the upgrade script is run when upgrading between versions of Kayako. Again it is very simple in this example.

    Example:
    PHP:

    public function Upgrade()
    {
        return 
    parent::Upgrade();
    }
    Load Tables
    This function is responsible for creating and loading your custom tables. In this example we will make a very simple table.

    The table is called helloworld and has 3 columns: helloworldid, phrase1, phrase2

    the id is set to be the primary key and will auto increment, phrase2 is set up to have a default value of "Default Value".

    For more information about the table adding functions look at the code files located at __swift\library\Setup

    Example:
    PHP:

    public function LoadTables()
    {        
        
    $this->AddTable('helloworld', new SWIFT_SetupDatabaseTable(TABLE_PREFIX ."helloworld","helloworldid I PRIMARY AUTO NOTNULL,
                                                                                                    phrase1 C(255) NOTNULL,
                                                                                                    phrase2 C(255) DEFAULT 'Default Value' NOTNULL"
    ));
        return 
    true;
    }
    That is all that is required to successfully install a module into kayako 4 below is the finale code compilation.
    PHP:

    <?php

    class SWIFT_SetupDatabase_helloworld extends SWIFT_SetupDatabase
    {
        public function 
    __construct()
        {
            
    parent::__construct("helloworld");
            return 
    true;
        }
        
        public function 
    __destruct()
        {
            
    parent::__destruct();
            return 
    true;
        }
        
        public function 
    Install($_pageIndex)
        {
            
    parent::Install($_pageIndex);
            return 
    true;
        }
        
        public function 
    Uninstall()
        {
            
    parent::Uninstall();
            return 
    true;
        }
        
        public function 
    Upgrade()
        {
            return 
    parent::Upgrade();
        }
        
        public function 
    LoadTables()
        {        
            
    $this->AddTable('helloworld', new SWIFT_SetupDatabaseTable(TABLE_PREFIX ."helloworld","helloworldid I PRIMARY AUTO NOTNULL,
                                                                                                    phrase1 C(255) NOTNULL,
                                                                                                    phrase2 C(255) DEFAULT 'Default Value' NOTNULL"
    ));
            return 
    true;
        }
    }

    ?>
    Custom PHP Files
    Next we need to add some custom php files for using our new module/ db table.

    Admin Functions
    Here we will add the functionality for our administrators.

    1. First create a folder in your module directory called admin.
      Your hierarchy should look like __modules\helloword\admin\
    2. Next create a file called class.Controller_HW.php
      This file we will be using to add to our new table.
      The code for the file will look like this:
      PHP:
      <?php
           
           
      class Controller_HW extends Controller_admin
           
      {
               
      // Core Constants
               
      const MENU_ID 111;
               const 
      NAVIGATION_ID 1;
               
               public function 
      __construct()
               {
                   
      parent::__construct();
                   return 
      true;
               }
               
               public function 
      __destruct()
               {
                   
      parent::__destruct();
                   return 
      true;
               }
               
               public function 
      Render()
               {
                   if (!
      $this->GetIsClassLoaded())
                   {
                       throw new      
      SWIFT_Exception(SWIFT_CLASSNOTLOADED);
               
                       return 
      false;
              }
              
              
      $this->UserInterface->Header"Title"self::MENU_IDself::NAVIGATION_ID);
              
      $this->UserInterface->Start(get_class($this), '/helloworld/HWInsert/Render'SWIFT_UserInterface::MODE_INSERTfalse);
              
              
      $this->View->Render();
              
              
      $this->UserInterface->End();
              
      $this->UserInterface->Footer();
               }
           }
           
           
      ?>
      Note the calls to the UserInterface. the first call to the Header function allows us to set the page title, as well as have our menus be persistant. Meaning they stay selected after loading the page, otherwise it will be reset back to the last page you were on.
      The next call to the Start function sets up some basic drawing information and starts to draw our page.
      After the start call we are free to draw what we want on the screen.
      Then we call the end function to tell it that we are done drawing and to finishanything started in the start function.
      Then we call the footer function wich adds some basic information at the end of the page.


    3. Next we need to make a file in the admin director called class.View_HW.php
      This will be used for rendering our module's screens. The code for this file looks like this:
      PHP:
      <?php
           
           
      class View_HW extends SWIFT_View
           
      {
               public function 
      __construct()
               {
                   
      parent::__construct();
           
                   return 
      true;
               }
               
               public function 
      __destruct()
               {
                   
      parent::__destruct();
           
                   return 
      true;
               }
               
               public function 
      Render()
               {
                   echo 
      "Hello World!";
               }
           }
           
           
      ?>
    How this works is that Kayako will look for the controller and view files of like names and load them into an object it references. That way from the controller you can easily reference the view file without having to manually include it. If the view doesn't exist it won’t load it but then you can't reference it.

    Admin CP Menu Changes
    This is how to add new menu items to your top menus in both the staff and admin control panel.

    [IMG]

    To do this we simple edit our conifg.xml again. An example of a new menu item is as follows:
    HTML:
    <tabs>
            <tab type='admin' title='Hello World' id='111' width='100' permission='false'>
                <tabitem link='/helloworld/HW/Render'>Hello World</tabitem>
            </tab>
        </tabs>
    so we have the <tabs> element that contains all the menu changes we need to make for this module.
    Next under that we have individual <tab> items with the following editable attributes:

    • type - This is who gets the item in their menu. admin, staff and client
    • title - This is the text you want to be displayed for the button
    • id - unique id for you menu item, can be any number > 10 to not conflict with the default kayako menu items.
    • width - how wide the button is in pixels
    • permission - I am not sure exactly what this does but i believe it has to do if you want to see if specific users have permission to use the module.
    After the main menu item is made we need to add the sub menu items, these are classified by the <tabitem> tags. They only have one attribute that I am aware of and that is link, which is what it will link to when it is clicked (more on links below). In between the start and finish portions of the tag is where we put what we want the button to say.

    Once those are done then your menu is ready to go.

    [IMG]

    Language Files
    Next we are going to look at adding custom language files. These are used for displaying certain titles/phrases in different langues. This example will just add an additional en-us language file to the module.

    First we need to make the folders for this, create a locale folder under your module directory.
    Example:
    __modules/helloworld/locale/

    Then we need to make the folder for the specific language we are going to create. It should be the same name as under the __swift/locale folder.
    Example:
    __modules/helloworld/locale/en-us/

    Next we need to create our first language file, create a file called helloworld.php in here we will add the following code:
    PHP:
    <?php

    $__LANG 
    = array (
        
    // ======= General =======
        
    "hw_name" => "Hello World",
    );
    ?>
    What this does is tell the code that when we want to show the hw_name in the given language it will look for it in this array and return the proper value.

    Now to test it! First we need to go and open our class.Controller_HW.php and add this line to the constructor:
    PHP:
    $this->Language->Load('helloworld');
    So it looks like this:
    PHP:
    public function __construct()
        {
            
    parent::__construct();
            
    $this->Language->Load('helloworld');
            return 
    true;
        }
    Then we need to edit our render function. In the render function find this line:
    PHP:
    $this->UserInterface->Header'Title'self::MENU_IDself::NAVIGATION_ID);
    And change it to this:
    PHP:
    $this->UserInterface->Header$this->Language->Get('hw_name'), self::MENU_IDself::NAVIGATION_ID);
    So then we are using the value located in the language file rather than the hard coded "Title" we had.

    After that is done our page should have a nice new title like this:
    [IMG]


    Module Structure
    Here I will go over how to structure your files/folders to make everything work, this is mainly for references and doesn't pertain to the example module above.

    Folders
    first step is setting up your folders. the first folder is in the __modules directory. The name of this folder should be whatever your module is named and shouldn't contain any spaces.
    Example: __modules\moduleName\

    Next is sub folders in your module directory, there are a few that need to be there but the rest can be named whatever you would like. The ones that need to be there are:

    • config - This holds all of the configuration files to install/uninstall/upgrade your module
    • admin - This is where you place any functionality specific to the admin cp. The menu items that have a type of admin will look here for the files
    • staff - This is where you place any functionality specific to the staff cp. The menu items that have a type of staff will look here for the files
    • client - This is where you place any functionality specific to the client cp. The menu items that have a type of client will look here for the files.
    • locale - This is where you place any custom language files that you make for you module.
    There may be more but these are the ones i have come across thus far.

    The last 3 (admin,staff,client) do not need to be there if you are not adding functionality for them specifically.

    so an example structure could look like:
    __modules\moduleName\config\
    __modules\moduleName\admin\
    __modules\moduleName\staff\
    __modules\moduleName\client\
    __modules\moduleName\locale\

    Files
    each file needs to have a specific naming convention in order for it to work properly with the system.

    The config folder has 2 files that are needed:

    • config.xml - contains information about your module for the system and display purposes.
    • class.SWIFT_SetupDataBase_moduleName.php - This contains some setup functions for your module.
      Example:
      PHP:
      <?php
           
           
      class SWIFT_SetupDatabase_moduleName extends SWIFT_SetupDatabase
           
      {
               const 
      PAGE_COUNT 1;
               
               public function 
      __construct()
               {
                        
      parent::__construct("moduleName");
                   return 
      true;
               }
               
               public function 
      __destruct()
               {
                   
      parent::__destruct();
                   return 
      true;
               }
               
               public function 
      Install($_pageIndex)
               {
                   
      parent::Install($_pageIndex);
                   return 
      true;
               }
               
               public function 
      Uninstall()
               {
                   
      parent::Uninstall();
                   return 
      true;
               }
               
               public function 
      Upgrade()
               {
                   return 
      parent::Upgrade();
               }
               
               public function 
      GetPageCount()
               {
                   return 
      self::PAGE_COUNT;
               }
               
               public function 
      LoadTables()
               {        
           
               }
           }
           
           
      ?>
    The other folders (admin,staff,client etc) have files that they expect in this certain format:

    • controller - this file is responsible for driving the actions of your module.
      File name example: class.Controller_controllerName.php
      PHP example:
      PHP:
      class Controller_controllerName extends Controller_admin
           
      {
               
      // Core Constants
               
      const MENU_ID 111;
               const 
      NAVIGATION_ID 1;
               
               public function 
      __construct()
               {
                   
      parent::__construct();
                   return 
      true;
               }
               
               public function 
      __destruct()
               {
                   
      parent::__destruct();
                   return 
      true;
               }
               
               public function 
      Render()
               {
                   if (!
      $this->GetIsClassLoaded())
                   {
                       throw new      
      SWIFT_Exception(SWIFT_CLASSNOTLOADED);
               
                       return 
      false;
                   }
                  
      //do stuff here
               
      }
           }
    • view - this file is responsible for the rendering of the modules functions.
      File name example: class.View_controllerName.php
      PHP example:
      PHP:
      class View_controllerName extends SWIFT_View
           
      {
               public function 
      __construct()
               {
                   
      parent::__construct();
           
                   return 
      true;
               }
               
               public function 
      __destruct()
               {
                   
      parent::__destruct();
           
                   return 
      true;
               }
               
               public function 
      Render()
               {
                   
      //do stuff here
               
      }
           }
    To use functions located in the view file from the controller file you simply have to do this in the controller file.
    PHP:
    $this->View->Render();
    The reason for this naming convention will be covered next.

    The locale folder has a structure of additional folders for each language underit. Under each of those folders you place your desired language files. The only one that I have found that needs a specific name is the language file for you settings, called settings.php other than that they can be named whatever you like.

    Links
    This is what the links from the menu tabs (in the config.xml) are expecting:
    link="/moduleName/controllerName/function/"
    where the moduleName is the name of you module, controllerName is the name of the controller file specified above and function is the name of the php function in the controller file above.


    That should cover the basics for making a module in version 4. As i figure out more I will update this to be more accurate.

    Thanks to Bagaiev for helping me with figuring out the link format.
    zepower likes this.
    • Kayako Staff

    Jamie Edwards Chief Limey

    Hi Dean,

    This is superb, thank you for putting this together! Do you mind if I copy it over to the Wiki to see what else people may contribute to it?
  2. Bagaiev Established Member

    Dean, you are welcome.

    I'd like to know if someone could tell us how to create and include Javascript files for the new custom modules.

    Jamie, could you please help us with that?
  3. simond Member

    Have a look at

    __modules\knowledgebase\library\Render\class.SWIFT_KnowledgebaseRenderManager.php

    for an example of including markup.
  4. Bagaiev Established Member

    Simond,

    Thank your for the assistance, but I haven't found any links/references to *.js files in that section.

    Looks like we'll have to add "<script>" tags into View's output.
  5. DeanV Established Member

    Sure you can copy it over :)
  6. supportskins Kayako Guru

    This is really good! I hoped Kayako had provided this in their documentation rather than waiting for the community to contribute.
  7. DeanV Established Member

    I'm glad people seem to be finding this useful :)

    I have done my first real edit to the origional post.

    First: I noticed that there were functions in place that should be used to properly render our custom module pages. These changes have been added to the class.Controller_HW.php overview.

    Second: I figured out how to make and use custom language files for your module. Allowing you more controll over how words are displayed for your module.

    One thing i have really liked about this new module format is that everything is contained in your modules directory. No need to go out and edit other kayako files in order for your module to work. Makes upgrading and installing a breeze :)
    • Kayako Staff

    Jamie Edwards Chief Limey

  8. chatzworld Established Member

    i have tried to do thsi but get the error

    [Notice]: Undefined offset: 111 (UserInterface/class.SWIFT_UserInterfaceControlPanel.php:390)

    Fatal error: Call to a member function Render() on a non-object in /__modules/helloworld/admin/class.Controller_HW.php on line 34
  9. Bagaiev Established Member

    Hello Chatz,

    You should create class.View_HW.php and Render function in it, in order to output data with your method..
  10. chatzworld Established Member

    found the fault my mistake :D
  11. danielgwood Member

    Can't believe I missed this, damn Christmas!

    Thanks Dean that is very helpful indeed. I had managed to work out most of it, but for the life of me I couldn't get my modules to install.

    I don't suppose you've looked at hooks?
  12. DeanV Established Member

    I'm glad you found this helpful but i havn't looked into hooks yet, sorry. :(
  13. danielgwood Member

    :p worth a try.

    I've had a look myself, at some point I'll scrape together the notes I have on them. From what I could tell they weren't activated/finished yet.
  14. DeanV Established Member

    I don't think they are quite finished yet but i could be wrong. Just think I remeber reading that somewhere.

    It would be interesting to see your notes, perhaps I could assist in helping figure it out with your start as a base.
  15. dwatrous New Member

    Ajax submission within module

    Hi Guys. This is a great post. I've build a module, but it requires the user to provide values to generate a custom report. I have two questions.

    1) How can I post a form from within the module? The module doesn't render on it's own URL so I can't just leave action blank and let the page re-render.

    2) Are there some resources available for rendering HTML tables that look consistent with the rest of the site?

    Thanks in advance!
  16. dwatrous New Member

    I found out how to run a database query as follows:

    PHP:

            $billingresults 
    = array();
            
    $this->Database->QueryLimit($this->billing_query);
            while (
    $this->Database->NextRecord())
            {
                
    array_push($billingresults$this->Database->Record);
            }
    I'm still trying to figure out how to submit the form from the page. I'm guessing there are some AJAX resources available. I would love some direction...
  17. dwatrous New Member

    One more thing. I can't seem to get rid of this error

    [Notice]: Undefined offset: 111 (UserInterface/class.SWIFT_UserInterfaceControlPanel.php:390)

    My page renders fine, but it has that bright red error at the top...
  18. Dynamo Effects New Member

    That error just means that your menu IDs aren't matching up. There's an error in the example where the menu is created with an ID of 100 but then referenced with 111. Change 111 to 100 and it'll go away.

    Reference the modules in __modules/ and __swift/modules for examples of how to handle form submissions. Search for the word "Submit" in the controller files.

    Excellent guide Dean!

Share This Page