<?xml version="1.0" encoding="iso-8859-1"?>
<chapter id="coding_addmodule">
  <title>Erstellen eigener moregroupware Module</title>
  
  <para>In diesem Dokument erfahren Sie mehr dazu, wie Sie eigene Module für
    moregroupware erstellen können. Es beschreibt ebenso den Aufbau von moregroupware,
    den Sie benötigen, wenn Sie eigene Module erstellen wollen. Sie sollten nach dem
    Lesen dieses Dokuments in der Lage sein, eigene Module zu erstellen.
  </para>
  
  <section id="coding_addmodule_structure">
    <title>Verzeichnisstruktur eines Moduls</title>
    
    <para>Ein Modul liegt immer als eigenes Verzeichnis im moregroupware Wurzelverzeichnis.
      Für weitere Erklärungen nehmen wir das Kontaktmodul als Beispiel. Sie sehen hier
      die Verzeichnisstruktur des Moduls "Kontakt":
      <figure>
        <title>Verzeichnisstruktur:</title>
        <graphic fileref="images/coding_addmodule/moduletree.png"/>
      </figure>
    </para>
    
    <para>
      <variablelist>
        <varlistentry>
          <term>Modulverzeichnis</term>
          <listitem>
            <para>Das Modulverzeichnis liegt im Wurzelverzeichnis der moregroupware
            Installation. Es muss zumindest folgende Unterverzeichnisse enthalten:
            inc, lang und templates.
            Alle direkt ausgeführten Scripts sollten ebenso in diesem Verzeichis
            liegen.
            </para>
          </listitem>
        </varlistentry>
        <varlistentry>
          <term>"include" Verzeichnis (inc)</term>
          <listitem>
            <para>Das Verzeichnis "inc" beinhaltet Dateien mit modulspezifischen
              Funktionen oder Klassen.
              <important>
              Eine Datei mit den Namen mmmm_func.inc.php (mmmm = Modulname) wird
              automatisch in die Scripts eingebunden. Mehr dazu später.
              </important>
            </para>
          </listitem>
        </varlistentry>
        <varlistentry>
          <term>Sprachen (lang)</term>
          <listitem>
            <para>Dieser Ordner enthält die Sprachdateien für das Modul und muss
              folgender Benennung entsprechen: mmmm.xx.lang (mmmm = Modulname,
              xx = Sprachcode).
              <important>
              Die Sprachdateien werden automatisch verarbeitet, achten Sie auf richtige
              Benennung. Mehr dazu im Teil "Übersetzungen von moregroupware".
              </important>
              Es sollte zumindest eine englische Sprachdatei (mmmm.en.lang) exisiteren.
              Wenn Sie selbst mehrere Übersetzungen einbringen können, fügen Sie diese
              Sprachdateien hinzu. Wie Sie oben sehen, gibt es für das Kontaktmodul bereits
              einige Übersetzungen.
            </para>
          </listitem>
        </varlistentry>
        <varlistentry>
          <term>Vorlagen (templates)</term>
          <listitem>
            <para>Dieser Ordner enthält keine Dateien, sondern weitere Unterverzeichnisse.
              "default" ist die Standardvorlage für Ihr Modul, Sie sollten Ihre Vorlagen
              (Templates) in diesen Zweig platzieren (nicht direkt im Unterverzeichnis).
              Sie haben die Möglichkeit, weitere Unterverzeichnisse für andere Designs
              zu erstellen, z.B. "himmelblau". Derzeit wird diese Funktion allerdings
              nicht unterstützt, platzieren Sie alles im Ordner "default".
            </para>
          </listitem>
        </varlistentry>
        <varlistentry>
          <term>Ausgabevorlagen (html / wml)</term>
          <listitem>
            <para>In diesem Verzeichnis liegen Ihre Seitenvorlagen. moregroupware
              bietet Ihnen die Möglichkeit, die Ausgaben in bestimmten Formaten
              zu gestalten. Derzeit können Sie die Ausgaben in HTML und WML definieren.
              Für ein neues Modul wird zumindest das Verzeichnis html benötigt, wml
              ist optional.
            </para>
          </listitem>
        </varlistentry>
        <varlistentry>
          <term>Medienverzeichnis (media)</term>
          <listitem>
            <para>Dieses Verzeichnis beherbergt alle Bilder, Grafiken und andere
              Multimediaobjekte, die Sie in Ihrem Modul benötigen.
            </para>
          </listitem>
        </varlistentry>
      </variablelist>
    </para>
    
    <important>
      <para>Ein minimales Modulverzeichnis sollte so aussehen:</para>
      <figure>
        <title>Minimales Modulverzeichnis</title>
        <graphic fileref="images/coding_addmodule/moduletree_min.png"/>
      </figure>
    </important>
    
  </section>
  
  <section id="coding_addmodule_modappstructure">
    <title>Programmstruktur</title>
    
    <para>Die folgenden Zeilen Code sind aus dem Script contacts/index.php entnommen.
      Es ist das Hauptscript des Kontaktmoduls. Wichtig für die Einbindung in moregroupware
      sind die ersten 5 Zeilen.
    </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.6 $", __FILE__);
    </programlisting>
    
    <para>Die erste Zeile setzt den Namen des Moduls fest, dieser muss mit dem
      Verzeichnisname übereinstimmen. Dieser String wird in der kompletten Anwendung
      für die Pfadangaben verwendet. In der zweiten Zeile wird die Ausgabe festgelegt,
      normalerweise html.
    </para>
    <para>Die nächsten zwei Zeilen können Sie ohne Änderungen in Ihr Script übernehmen.
      Im nächsten Kapitel wird näher darauf eingegangen.
    </para>
    <para>In der letzten Zeile wird die Revisionsnummer der Datei in ein globales Array
      übergeben, um die Fehlersuche zu vereinfachen.
    </para>
    
    <section id="coding_addmodule_novarinclude">
      <title>Verwenden Sie niemals include($var/myinclude.php)!</title>
      <para>Bitte verwenden Sie niemals die Funktion include wie oben beschrieben.
        Dadurch entsteht ein großes Sicherheitsloch. Arbeiten Sie nach folgendem
        Beispiel: include(ROOTPATH . "/folder/myinclude.php")
      </para>
      <para>Greifen Sie immer mit $HTTP_*_VARS auf Dateieingaben zurück!
        Verlassen Sie sich nicht darauf, dass die Option "register_globals" aktiviert ist.
      </para>
    </section>
    <section id="coding_addmodule_nomanualdbcon">
      <title>Greifen Sie nicht manuell auf die Datenbank zu!</title>
      <para>Wenn Sie Parameter wie $appconf["dbuser"] oder andere datenbankbasierenden
        Parameter entdecken, verwenden Sie diese nicht für eigene Datenbankverbindungen.
        Dazu bietet sich das automatisch generierte $conn ADOdb Objekt an, mehr dazu
        später.
      </para>
    </section>
    
  </section>
  
  <section id="coding_addmodule_configincphp">
    <title>Wozu dient die Datei config.inc.php</title>
    
    <para>Die Datei config.inc.php wird während der Installationsroutine erstellt.
      Darin werden wichtige Parameter für moregroupware gespeichert. Alle Parameter sind
      im Array $appconf gespeichert. Hier sehen Sie ein Beispiel:
    </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>
      Wissenswert ist, das Ihr Script, das mit den oben beschriebenen Zeilen beginnt,
      Zugang zum Array $appconf hat. Sie können benötigte Einstellungen direkt einbinden,
      wie z.B. $appconf["rooturl"] oder $appconf["rootpath"]. Sie können auch über die
      Konstanten ROOTPATH und INCLUDEPATH zurückgreifen, um Ihre Dateien einzubinden.
    </para>
    
  </section>
  <section id="coding_addmodule_cotainerinc">
    <title>Wozu dient die Datei container.inc?</title>
    
    <para>Die Datei container.inc ist eine Sammlung von anderen Includes. Alle Includes
      werden in dieser Datei zusammengefasst. Alle darin enthaltenen Dateien beinhalte
      keine weiteren Includes. So existieren nur 2 Stufen von Includes: Die Datei
      container.inc und alle darin enthaltenen Includes.
      Unten der Inhalt der Datei container.inc:
    </para>
    <programlisting>&lt;?php
      /* $Id: coding_addmodule.xml,v 1.6 2003/11/01 21:41:06 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>Im nächsten Abschnitt werden die Includes näher erklärt.</para>
    <section id="coding_addmodule_smartyinc">
      <title>require_once(SMARTY_DIR . "Smarty.class.php");</title>
      <para>Diese Zeile ladet die Klassen von Smarty, die auch von anderen Includes
        (z.B: appconf.inc) und den Benutzerscripts verwendet werden. Ohne dieser Zeile
        kann eine Ausgabe gemacht werden.
      </para>
    </section>
    <section id="coding_addmodule_mgwclassinc">
      <title>include(INCLUDEPATH .'mgw.class.inc');</title>
      <para>Die MGW Klasse ist in der Session in Form eines Objekts gespeichert.
        Dieses Objekt enthält entsprechende Daten wie Sprachcode, Benutzername oder
        Benutzerrechte. Über dieses Objekt werden verschiedenste Abfragen gemacht, ohne
        auf die Datenbank zurückzugreifen. Eine nähere Beschreibung erfolgt später,
        weil diese Klasse sehr wichtig für Ihr Modul ist.
      </para>
    </section>
    <section id="coding_addmodule_langinc">
      <title>include(INCLUDEPATH .'lang.inc');</title>
      <para>Dieses Include verarbeitet die Sprachdateien und wird ebenso im Include
        appconf.inc und in Ihrem Script benötigt.
      </para>
    </section>
    <section id="coding_addmodule_userfuncinc">
      <title>include(INCLUDEPATH .'userfunc.inc');</title>
      <para>An diesem Ort werden nützliche, systemweite Funktionen gespeichert.
        Diese Funktionen können in jedem Modul brauchbar sein. Platzieren Sie keine
        Funktionen für Aufgaben Ihres Moduls darin.
      </para>
    </section>
    <section id="coding_addmodule_versioninc">
      <title>include(INCLUDEPATH .'version.inc');</title>
      <para>Im einfachsten Include werden nur die Releasenummer und der Name des Produkts
        gespeichert, die an verschiedenen Plätzen in moregroupware angezeicht werden,
        z.B. in der Statusleiste.
      </para>
    </section>
    <section id="coding_addmodule_appconfiginc">
      <title>include(INCLUDEPATH .'appconfig.inc');</title>
      <para>Dies ist das komplexeste Include, es erledigt eine Menge von Funktionen,
        anschließend eine Liste, die für Programmierer interessant ist:
      </para>
      <itemizedlist>
        <listitem>
          <para>Erstellung der Variable $appconf["gentemplates"], die zum
            allgemeinen Vorlagenverzeichnis (general/templates/html) zeigt.
            Sie wird zum Einbinden von HTML Teilen aus dem allgemeinen Ordner anstatt
            der Vorlagen aus dem Modulverzeichnis. In der Vorlage selbst können
            Sie diese Variable mit {$gentemplates} ansprechen.
          </para>
        </listitem>
        <listitem>
          <para>Erstellung der Variable $appconf["imgpath"] und $appconf["imgpathgeneral"],
            die den Pfad zum Bilderverzeichnis ("media") des Moduls oder des allgemeinen
            Bilderverzeichnis enthält. Sie werden diese Variable in der Vorlage benötigen
            und können Sie mit {$imgpath} und {$imgpathgeneral} ansprechen.
          </para>
        </listitem>
        <listitem>
          <para>Erstellung des $smarty Objekts, das die Ausgabe Ihres Moduls übernimmt.
            Sie werden Methoden wie $smarty->assign(), $smarty->append() oder
            $smarty->display() benötigen.
          </para>
        </listitem>
        <listitem>
          <para>Die Session wird in diesem Include gestartet.</para>
        </listitem>
        <listitem>
          <para>Folgende Variablen werden automatisch erstellt und können direkt
            in der Vorlage angesprochen werden:
            {$PHPSESSID} (akutelle Session des Benutzers), {$rooturl}, {$username},
            {$userfullname}, {$phpself}, {$gwversion}, {$gwtitle}. 
            Wenn Sie die Bedeutung nicht genau kennen, fügen Sie die Variable einfach
            in eine Vorlage ein und warten auf das Ergebnis.
          </para>
        </listitem>
        <listitem>
          <para>Erstellung der Menüleiste.</para>
        </listitem>
      </itemizedlist>
    </section>
    <section id="coding_addmodule_moduleexecinc">
      <title>include(INCLUDE .'module_exec.inc');</title>
      <para>Das Modul module_exec ist das einzige Include, das Sie von außen ansteuern
        können. Die Operationen im Modul können durch Variablen beeinflußt werden.
        Lesen Sie dazu mehr im nächsten Abschnitt.
      </para>
    </section>
    
  </section>
  <section id="coding_addmodule_moduleexec">
    <title>Spezielle Eigenschaften von module_exec.inc</title>
    
    <para>Sehen Sie sich das Script einmal an:</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.inc) and integrate it
        if($myEnv["stdsmenu"] !== false) 
        if(file_exists(ROOTPATH . "/" . $myEnv["module"]. "/inc/smenu.inc"))
        include(ROOTPATH . "/" . $myEnv["module"] . "/inc/smenu.inc");
        else
        echo "&lt;b&gt;Error: &lt;/b&gt;Program cannot find Submenu-Definition File (smenu.inc) in module \"inc\" folder. Please create this file or set \$myEnv[stdsmenu] to false in your program and define it there.";
      </programlisting>
    </para>
    <para>Wie Sie sehen können, übernimmt das Include fünf Aufgaben:
      <itemizedlist>
        <listitem>
          <para>Überprüfen der akutellen Anmeldung (und Neuanmeldung, wenn
            ein Fehler auftritt)
          </para>
        </listitem>
        <listitem>
          <para>Verbindung zur Datenbank und Übergabe des Objekts $db</para>
        </listitem>
        <listitem>
          <para>Übersetzung des aktuellen Moduls und Übergabe in das Array $hashmap</para>
        </listitem>
        <listitem>
          <para>Übergabe von Einstellungen für das aktuelle Modul und den aktuell
            angemeldeten Benutzer (derzeit nicht in Verwendung)</para>
        </listitem>
        <listitem>
          <para>Erstellung des Untermenüs</para>
        </listitem>
      </itemizedlist>
        ...
    </para>
    
  </section>
  
</chapter>
