<?xml version="1.0" encoding="iso-8859-1"?>
<chapter id="coding_addmodule">
  <title>Adding a module to moregroupware</title>
  
  <para>This document describes the way of adding a complete new module to moregroupware.
    It also describes the complete framework of moregroupware which is needed for module
    development. After reading this document, the reader should be able to add his
    first module.
  </para>
  
  <section id="coding_addmodule_structure">
    <title>Directory-Structure of a Module</title>
    
    <para>A module allways has its own folder in the moregroupware root tree. For this
      introduction, we use the module "contacts" as example. Below you see the module
      directory tree for "contacts":
      <figure>
        <title>Module directory tree</title>
        <graphic fileref="images/coding_addmodule/moduletree.png"/>
      </figure>
    </para>
    
    <para>
      <variablelist>
        <varlistentry>
          <term>module-folder</term>
          <listitem>
            <para>The module folder (contacts in this example) resides on the top level
              of the moregroupware tree. It must(!) contain at least the following folders: inc / lang / templates.
              All important scripts which will be triggered by the application should also go into this folder.
            </para>
          </listitem>
        </varlistentry>
        <varlistentry>
          <term>module include folder (inc)</term>
          <listitem>
            <para>The module include folder is the place where you can keep your include files
              with module specific functions or classes. IMPORTANT: you can define a file
              with the name mmmmm_func.inc.php (where mmmmmm = modulename / name of your modulefolder)
              this file will be auto-included in your scripts. More on this below.
            </para>
          </listitem>
        </varlistentry>
        <varlistentry>
          <term>module lang folder (lang)</term>
          <listitem>
            <para>The folder contains the language files for the module following
              this naming convention: mmmmm.xx.lang (where mmmm = modulename, xx = language code) IMPORTANT:
              This language file will be read in automaticly by the framework, so please be careful to
              name it correctly. If you want to know more please refer to "how to localize Moregroupware"
              You should provide at least mmmmm.en.lang. If you can provide more languages yourself,
              add more files to the module lang folder, as you can see above, the contacts module
              has more language files.
            </para>
          </listitem>
        </varlistentry>
        <varlistentry>
          <term>module template folder (templates)</term>
          <listitem>
            <para>This folder doesnt contain any files, but other folder(s). "default" is the
              default skin of your module, you should place your templates into this tree (not the directory
              itself) when you add a new module. There is the chance to add other folders
              into templates for example "blueice" if you want to have a skin called blueice. But
              at the moment, put everything in "default", or more exactly, put everything
              in the folders below default.
            </para>
          </listitem>
        </varlistentry>
        <varlistentry>
          <term>module template output folder (html / wml)</term>
          <listitem>
            <para>This is the folder where your templates actually resides. The framework make it possible
              to output your application data to different target. Right now we moregroupware has html and wml
              defined as output folders. When you develop a new module, you must at least provide "html"
              folder.
            </para>
          </listitem>
        </varlistentry>
        <varlistentry>
          <term>media folder (media)</term>
          <listitem>
            <para>In this folder should you place all your images and other media files which are
              needed by your module
            </para>
          </listitem>
        </varlistentry>
      </variablelist>
    </para>
    
    <important>
      <para>Your minimal module directory tree would look like this:</para>
      <figure>
        <title>Minimal module directory tree</title>
        <graphic fileref="images/coding_addmodule/moduletree_min.png"/>
      </figure>
    </important>
    
  </section>
  
  <section id="coding_addmodule_modappstructure">
    <title>Module Application Structure</title>
    
    <para>The following code block is takem from contacts/index.php, its the main module script for contacts.
      The very first 5 lines are important to be moregroupware-module compliant.
    </para>
    <programlisting>&lt;?php
      $myEnv["module"] = "contact";
      $myEnv["output"] = "html";
      
      include("../config.inc.php");
      include(INCLUDEPATH . "container.inc");
      
      // adding file revision into global array
      revisionInit("\$Revision: 1.7 $", __FILE__);
    </programlisting>
    
    <para> The first line is a declaration of the modulename, the modulename MUST be the same as the folder name. This
      string will be used in the complete framework to determine the different paths in the groupware. The second line
      identifies the output target, this will be normally html.
    </para>
    <para>The next two lines are mandatory, you can copy them into your own module script. In the next chapter there is
      a description of what these two important includes do.
    </para>
    <para>The last line adds the file's revision number to a global array, this is used as a debugging aid.</para>
    
    <section id="coding_addmodule_novarinclude">
      <title>Never use include($var/myinclude.php)!</title>
      <para>Please use never an include like described above, because this way you open a big security hole. Always
        use something like this: include(ROOTPATH . "/folder/myinclude.php")
      </para>
      <para>You should always use $HTTP_*_VARS to access input data! Never rely on register_globals being on! It is deprecated, insecure and clumsy!</para>
    </section>
    <section id="coding_addmodule_nomanualdbcon">
      <title>Don't use your own database connections</title>
      <para>You see parameters like $appconf["dbuser"] or other database based parameters, please dont use
        them to create your own database connections, use the auto-generated $conn ADOdb object instead, more on
        this later.
      </para>
    </section>
    
  </section>
  
  <section id="coding_addmodule_configincphp">
    <title>What is the config.inc.php file?</title>
    
    <para>the config.inc.php file will be created by the setup routine. It defines several important
      parameters which are needed by the moregroupware framework. All parameters are in a hashmap called
      $appconf. Below you can see an config.inc.php example:
    </para>
    <programlisting>&lt;?php
      // this file is auto-generated by setup, only modify if you are sure what you do
      // Config File
      // Moregroupware.org
      // programmed by Marc Logemann
      // 01/04/01 (c)
      $appconf["dbvendor"] = "mysql";
      $appconf["dbhost"] = "localhost";
      $appconf["dbuser"] = "mgwuser";
      $appconf["dbpassword"] = "mgwpass";
      $appconf["dbname"] = "moregw";
      $appconf["rooturl"] = "http://localhost/moregroupware";
      $appconf["rootpath"] = "/home/apache/moregroupware";
      $appconf["theme"] = "default";
      $appconf["auth_method"]= "sql";
      $appconf["auth_ldaptype"]= "0";//0 for openldap; 1 for ADSI
      $appconf["auth_ldaphost"] = "";
      $appconf["auth_ldapbase"] = "";
      $appconf["auth_ldapuid"] = "";
      $appconf["auth_ldappasswd"] = "";
      $appconf["auth_ldapsuffixe"] = "";
      $appconf["create_accounts_on_login"] = 0;
      $appconf["mgw_version"] = "";
      $appconf["mgw_releasedate"] = "";
      $appconf["def_language"] = "de";
      $appconf["def_charset"] = "iso-8859-1";
      $appconf["html_compressor"] = false;
      $appconf["html_compressor_debug"] = false;
      define("SMARTY_DIR", "/home/apache/mgw/include/smarty/");
      define("ROOTPATH","/home/apache/mgw");
      define("INCLUDEPATH","/home/apache/mgw/include/");
      ?&gt;
    </programlisting>
    <para>Its important to know that in each script, that starts with the necessary lines described above, you have
      access to the $appconf hashmap in order to use this settings, you will often need things like $appconf["rooturl"] or
      $appconf["rootpath"]. There are also 2 constant definitions, you will perhaps use ROOTPATH or INCLUDEPATH in order
      to include a different file. 
    </para>
    
  </section>
  <section id="coding_addmodule_cotainerinc">
    <title>What does container.inc do exactly?</title>
    
    <para>container.inc is just a container for other includes. It was introduced to package all includes
      into one file. All the includes which are in the container.inc dont have other includes itself. This mean
      there are only 2 include levels, the first is the container.inc and the second level are the includes inside
      container.inc. There is no 3. level. Below the content of container.inc:
    </para>
    <programlisting>&lt;?php
      /* $Id: coding_addmodule.xml,v 1.7 2003/11/01 21:41:28 emailtotom Exp $ */
      
      require_once(SMARTY_DIR . "Smarty.class.php");
      
      // definition of moregroupware class, holding application vars
      include(INCLUDEPATH . 'mgw.class.inc');
      
      // functions for translation
      include(INCLUDEPATH . 'lang.inc');
      
      // array for tracking file revision number used in sql error report screen
      $Revisions_array = Array();
      
      // important functions like connect to database
      include(INCLUDEPATH . 'userfunc.inc');
      
      // definition of moregroupware version and label
      include(INCLUDEPATH . 'version.inc');
      
      // Auto-Assigns for template engine and global definitions
      include(INCLUDEPATH . 'appconfig.inc');
      
      // include module specific functions if present (they are now in module/inc)
      // must be named module_func.inc.php (i.e. calendar_func.inc.php)
      if(file_exists(ROOTPATH . "/modules/" . $myEnv["module"]. "/inc/" . $myEnv["module"] . "_func.inc.php")) {
      include(ROOTPATH . "/modules/" . $myEnv["module"] . "/inc/" . $myEnv["module"] . "_func.inc.php");
      }
      
      // help system
      if(file_exists(ROOTPATH."/modules/".$myEnv["module"]."/help/index.".$MGW->spkz.".html")) $smarty->assign("helpurl", $myEnv["module"]."/help/index.".$MGW->spkz.".html");
      elseif(file_exists(ROOTPATH."/modules/".$myEnv["module"]."/help/index.en.html")) $smarty->assign("helpurl", $myEnv["module"]."/help/index.en.html");
      elseif(file_exists(ROOTPATH."/modules/general/help/index.".$MGW->spkz.".html")) $smarty->assign("helpurl","general/help/nohelp.".$MGW->spkz.".html");
      else $smarty->assign("helpurl","general/help/nohelp.en.html");
      
      // include module autoexecution of several things like translation, authentication, and more
      include(INCLUDEPATH . 'module_exec.inc');
      ?&gt;
    </programlisting>
    <para>In the next sections, we show the meanings of each include.</para>
    <section id="coding_addmodule_smartyinc">
      <title>require_once(SMARTY_DIR . "Smarty.class.php");</title>
      <para>This include loads the smarty classes which will be used by other includes (appconf.inc) and of
        course by the user scripts. Without this include, there wont be any output at all.
      </para>
    </section>
    <section id="coding_addmodule_mgwclassinc">
      <title>include(INCLUDEPATH .'mgw.class.inc');</title>
      <para>The MGW class will be stored in the user session in form of an object. This object holds many
        user related data such as languagecode, username or his current rights. We use this object for
        checking various things, without being forced to make any database queries. You will learn how
        to use this class later on, because its very important for your module.
      </para>
    </section>
    <section id="coding_addmodule_langinc">
      <title>include(INCLUDEPATH .'lang.inc');</title>
      <para>This include provides the language functions which we need also in appconf.inc and sometimes
        in the userscripts.
      </para>
    </section>
    <section id="coding_addmodule_userfuncinc">
      <title>include(INCLUDEPATH .'userfunc.inc');</title>
      <para>This is the place where we store system wide useful functions. These are functions which could
        useful in each module. Dont place functionalities for specialized module tasks in there.
      </para>
    </section>
    <section id="coding_addmodule_versioninc">
      <title>include(INCLUDEPATH .'version.inc');</title>
      <para>This is the most simple include, the version.inc only contains the release number and the
        name of the product, this will be displayed in some places inside the groupware. See bottom status
        bar for example in each screen.
      </para>
    </section>
    <section id="coding_addmodule_appconfiginc">
      <title>include(INCLUDEPATH .'appconfig.inc');</title>
      <para>This is the most complex include, it does various things, below is a list of things which are
        important to know as a programmer:
      </para>
      <itemizedlist>
        <listitem>
          <para>creation of $appconf["gentemplates"], this points to the general/templates/html folder, this will
            be used for including html fragment from the general folder, instead of the current module template
            folder, within the template you can reach this variable with {$gentemplates}
          </para>
        </listitem>
        <listitem>
          <para>creation of $appconf["imgpath"] and $appconf["imgpathgeneral"], this point to the module image
            path or to the general image path (name of folder is "media"). You will need this variables
            inside templates. You can reach them with {$imgpath} or {$imgpathgeneral} inside templates.
          </para>
        </listitem>
        <listitem>
          <para>creation of $smarty object, this object you will use to program the output of your module,
            you will most likely use methods like $smarty->assign(), $smarty->append() and $smarty->display().
          </para>
        </listitem>
        <listitem>
          <para>the session will be started in this include</para>
        </listitem>
        <listitem>
          <para>following templates variables will be auto-created and should be used in the templates: {$PHPSESSID} (actual
            session id of user), {$rooturl}, {$username}, {$userfullname}, {$phpself}, {$gwversion}, {$gwtitle}. If you dont
            know some meaning, just insert them in your template, you will see what is being thrown out :)
          </para>
        </listitem>
        <listitem>
          <para>creation of main-menu</para>
        </listitem>
      </itemizedlist>
    </section>
    <section id="coding_addmodule_moduleexecinc">
      <title>include(INCLUDE .'module_exec.inc');</title>
      <para>The module_exec include is the only include which you can control from the outside, the operations
        executed inside can be controlled by flags which you can define in your script. Take a look in the next topic
        for a detailed view on this include.
      </para>
    </section>
    
  </section>
  <section id="coding_addmodule_moduleexec">
    <title>Whats special about module_exec.inc</title>
    
    <para>first we take a look at module_exec.inc:</para>
    <para>
      <programlisting>// Authentication of User according his session (its not the main auth)
        if($myEnv["auth"] !== false) checkLogin();
        
        // Connect to database and return $db object
        if($myEnv["connectdb"] !== false) $db = connect_database();
        
        // do module translation and give back a hashmap with all keys and values
        if($myEnv["translate"] !== false) $hashmap = setLocaledText($MGW->spkz, $myEnv["module"]);
        
        // get user settingw and return them in $settings hashmap
        if($myEnv["getsettings"] !== false) $settings = get_settings($myEnv["module"]);
        
        // call standard module submenu (smenu.php) and integrate it
        if($myEnv["stdsmenu"] !== false) 
        if(file_exists(ROOTPATH . "/" . $myEnv["module"]. "/inc/smenu.php"))
        include(ROOTPATH . "/" . $myEnv["module"] . "/inc/smenu.php");
        else
        echo "&lt;b&gt;Error: &lt;/b&gt;Program cannot find Submenu-Definition File (smenu.php) in module \"inc\" folder. Please create this file or set \$myEnv[stdsmenu] to false in your program and define it there.";
      </programlisting>
    </para>
    <para>As you can see, this include does 5 important tasks
      <itemizedlist>
        <listitem>
          <para>Check the user login (and reject him to login if something is wrong)</para>
        </listitem>
        <listitem>
          <para>Connect to the database and return object $db</para>
        </listitem>
        <listitem>
          <para>translate the current module and gives back a hashmap $hashmap</para>
        </listitem>
        <listitem>
          <para>get settings for actual module for actual user (not implemented yet)</para>
        </listitem>
        <listitem>
          <para>create the submenu</para>
        </listitem>
      </itemizedlist>
      TO BE CONTINUED....
    </para>
    
  </section>
  
</chapter>
