<?xml version="1.0" encoding="iso-8859-1"?>
<chapter id="coding_snippets">
  <title>Handling common coding tasks</title>
  
  <para>This chapter has reusable code for common tasks one needs to
    code when working on a module. The code is not only reusable, but
    shows best-practice examples for using the moregroupware framework and
    the libraries we use.
  </para>
  
  <note>
    <para>Coding the way described herein reduces your work, avoids
      errors and makes the code easier to read afterwards!
    </para>
  </note>
  
  <section id="coding_snippets_definition.xml">
    <title>The definition.xml file</title>
    
    <para>Modules have some important information associated with
      them. This information is stored in a file named
      <filename>defintion.xml</filename> in the modules root directory.
    </para>
    
    <para>The type defines whether the module is a core module (type 1,
      always installed, not shown for selection), currently there is only
      one core module, the 'general' module. Modules with type 2 are base
      modules, and are shown on the module selection screen, but are always
      installed. Type 3 denotes user modules, they are selectable at the
      user's will.
    </para>
    
    <example>
      <title>The defintion.xml of the notes module.</title>
      <programlisting role="xml">
        <![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mgw PUBLIC "-//MGW//DTD Moregroupware DTD V1.0//EN" "http://130.89.166.186/mgw/mgw.dtd">
<mgw>
	<moduleDefinition>
		<module type="3">
			<name>notes</name>
			<version>0.8</version>
			<date>2003-02-28</date>
			<author>Karsten Dambekalns</author>
			<email>k.dambekalns@fishfarm.de</email>
			<description>A module for managing notes.</description>
			<compatibility>
				<version>0.6.6</version>
			</compatibility>
			<updates>
			</updates>
			<dependancies>
			</dependancies>
			<cache>
			</cache>
		</module>
	</moduleDefinition>
</mgw>
]]>
</programlisting>
</example>
  
</section>

<section id="coding_snippets_logging">
  <title>Logging functions</title>
    
  <para>A module author will sooner or later want to log what the module
        is doing. Most helpful is logging when the module is not doing what
        it should do, though...
  </para>

<note><para>This is not intended to notify the end user of errors or
tell them what happens. Have a look at the notification functions for
this!</para></note>

  <section>
    <title>Using the core logging in more.groupware</title>
    
<para>In the settings module, in the general section, the are a bunch
of logging settings. The first one, 'enable_logging' is a systemwide
on/off switch for logging. When it is enabled a 'log' link is placed
next to 'help' in the mgw submenu. This link opens a window displaying
all log data for the page you just requested.</para>

<para>There are also 'log level' settings for each module (except
general, which always has all levels enabled). You can turn these all
on/off independantly. If a level is off, all log calls for that level
are discarded.</para>

<para>Only the admin user can change the logging settings. During
normal use, it is probably a good idea to disable logging (disabled by
default after setup) to reduce overhead.</para>

<para>You can always log messages with one simple function call. Those
messages can have one of five logging levels: MGW_FATAL, MGW_ERROR,
MGW_WARNING, MGW_NOTICE, MGW_INFO. Those are predefined constants you
can use right away.</para>

<para>In appconfig the logging class is initiated and can be reached
anywhere using $GLOBALS['log']. To log stuff, use the function
$GLOBALS['log']-&gt;message(). It returns a boolean value to indicate the
call result.</para>

    <example>
      <title>Firing a log message.</title>
      <programlisting role="php">
        <![CDATA[
$GLOBALS['log']->message(MGW_INFO, 'This is a info message.', __LINE__, __FILE__);
]]>
</programlisting>
</example>

<para>If you want to force logging even if a particular log level is
turned off, you can specify a boolean as fifth parameter. If you set
this to true, the message is logged forcibly. But: if logging is
disabled generally, the message will be discarded regardless of this
setting!</para>

<para>Finally you can specify another optional parameter (a string),
to overwrite the module name associated with the message. Ususally
this is taken from $myEnv, so you generally shouldn't need
this.</para>

    <example>
      <title>Firing an extended log message.</title>
      <programlisting role="php">
        <![CDATA[
$GLOBALS['log']->message(MGW_NOTICE, 'Forced logging under a manually set module name.',
                         __LINE__, __FILE__, true, 'test');
]]>
</programlisting>
</example>

<para>Use those calls wherever you see fit, and try to distribute them
logically throughout the code. The main purpose is tracking what the
code really does to aid debugging, so you might want to log error
messages, checkpoints reached, ...</para>
</section>
</section>

<section id="coding_snippets_notification">
  <title>Notification functions</title>
    
  <para>A user usually expects some feedback from a computer. So if
the user created a new entry, deleted something or executed some other
function, you might want to output a message indicating success or
notifying the user about an error that occurred. This is where the
notification comes into play, plus it offers a few more nifty
features.
  </para>

<note><para>This is not intended to create a debugging log of what is
happening internally to the module. Have a look at the logging
functions for this!</para></note>

  <section>
    <title>Using notification in more.groupware</title>

<para>In the settings module, in the general section, are a few
notification settings. Some are accessible for all users, some are for
admin users only. Those are explained elsewhere in the manual.</para>

<para>You can always trigger notification with one simple function
call. The framework knows about several types of messages, namely
NOTIFY_SCREEN, NOTIFY_MIM and NOTIFY_MAIL. Those are predefined
constants you can use right away.</para>

<para>In appconfig the notification class is initiated and can be
reached anywhere using $GLOBALS['notify']. Use the function
$GLOBALS['notify']-&gt;message() to make use of it. (If you noticed the
similarity to the logging function - yes, this is intentional.)</para>

<para>The types of message available are the following: NOTIFY_SCREEN
spools a message for direct display on the screen, either below the
sub menu or as a JavaScript alert() window (depending on user
settings). Those messages will be shown upon next page load, for users
not currently logged in those messages are stored and displayed at the
next login. If used for the current user, this is perfect for
displaying results of user actions.</para>

<para>NOTIFY_MIM sends an instant message to the target user's
MIM. This can be used to notify users of new items that have been
created or, as in todo and calendar, to inform about delegated items
and new appointments.</para>

<para>NOTIFY_MAIL can be used to send a message through email. This
does not use the webmail2 module, but uses PHP's mail() function. This
may cause problems on some systems, and will eventually be
changed.</para>

    <example>
      <title>Displaying a success message.</title>
      <programlisting role="php">
        <![CDATA[
$GLOBALS['notify']->message(NOTIFY_SCREEN, 'Item creation succeeded.');
]]>
</programlisting>
</example>

<para>The example above is the most simple way of using
notification. The message will be displayed for the current user upon
next page load (in most cases 'right after the call', when
$smarty-&gt;display() is called).</para>

<para>to be continued...</para>

</section>
</section>

<section id="coding_snippets_database">
  <title>Database handling</title>
    
  <para>Handling database connections, fetching results, preparing input
    values... Remember to <ulink url="http://php.weblogs.com/ADOdb_manual">read the ADOdb manual
    </ulink> as well!
  </para>
  
</section>

<section id="coding_snippets_sql">
  <title>SQL portability</title>
  
  <para>We want to support more than just MySQL. That's why we use
    ADOdb. But to make moregroupware portable you need to do more than
    just a database abstraction layer. You need to write portable SQL
    statements as well. We learned that the hard way, and you can profit
    from that experience...
  </para>
  
</section>

<section id="coding_snippets_datetime">
  <title>Date and time handling</title>
  
  <para>Date and time handling. You need to make sure that date and time
    values are formatted according to the user's preference settings, you
    need the user to input date and time values, and need to parse input
    submitted by the user? This section is for you.
  </para>
  
</section>

<section id="coding_snippets_smarty">
  <title>Templates with Smarty</title>
  
  <para>Smarty is the template engine used in moregroupware. Read this
    to get a head start on it and the templates and techniques used in
    moregroupware.
  </para>
  
</section>

<section id="coding_snippets_session">
  <title>Session usage</title>
  
  <para>We heavily rely on PHP's session management. This discusses the
    standard ways of using session variables in moregroupware and
    introduces you to some predefined variables and objects.
  </para>
  
  <section>
    <title>Using session standards in more.groupware</title>
    
    <important>
      <para>Since we want more.groupware to work with
        register_globals being set to off, always use $_SESSION to access
        session variables!
      </para>
    </important>
    
    <note>
      <para>When using $_SESSION (as we do), the use of functions like
        session_register() or session_is_registered() is forbidden (see PHP
        manual). Instead one should assign values to the $_SESSION array
        directly, and use isset() instead of session_is_registered().
      </para>
    </note>
    
    <para>There are a few things always available in the $_SESSION array
      for use in your module. One of those things is $_SESSION['MGW'], which
      is an instance of the MGW class, and holds information about the
      current user (e.g. $_SESSION['MGW']-&gt;userid) and some more
      interesting things. There is $_SESSION['MGW']-&gt;settings, which
      holds all of the user settings, there are the users rights, and
      more.
    </para>
    
    <note>
      <para>You do not need to worry about starting the session
        usually. If you work on a regular module, the session will be started
        by the more.groupware code for you.
      </para>
    </note>
    
    <example>
      <title>Checking for the ID and rights of the current user.</title>
      
      <para>Here we do two things together. We check for the ID of the
        current user and access the user rights information.Be aware that
        those will hopefully change ASAP!
      </para>
      
      <programlisting role="php">
        <![CDATA[
if(($_SESSION["MGW"]->rights["contact"]->modify and $row["owner"] == $_SESSION["MGW"]->userid)
   OR ($_SESSION["MGW"]->rights["contact"]->modify_others and $row["owner"] <> $_SESSION["MGW"]->userid)) {
    $editlink = "<a href=\"".$_SERVER['PHP_SELF']."?mod=contact&amp;obj=person&amp;view=editform&amp;id=$myid&amp;act=$action&amp;searchterm=$searchterm&amp;list=$list&amp;npage=$curr_page&amp;".SID."\" class=\"edit\">".$icons["edit"]."</a>";
}
else {
    $editlink = "";
}
]]>
</programlisting>
</example>

<example>
<title>Checking for user settings.</title>

<para>Note the third line, there we check the use_webmail setting.</para>

<programlisting role="php">
<![CDATA[
function linkEmail($email){
    if($email!=""){
        if($_SESSION["MGW"]->settings["use_webmail"] == 1) $email="<a href=\"../webmail2/index.php?rightframe=".rawurlencode("sendmail.php?to=".$email)."&amp;".SID."\">".$email."</a>";
	else $email = "<a href=\"mailto:".$email."\">".$email."</a>";
    }
    return $email;
}
]]>
</programlisting>
</example>

</section>

<section>
<title>Passing sessions around in more.groupware</title>

<para>There was a problem with conflicting session names, caused
relying on the default session name provided by PHP (see bug #550122
and feature request #564819) when using more than one application
using sessions simultaneously.</para>

<para>So we want this to be something other then PHPSESSID, it
defaults to MGWSESSID, and is configurable in config.inc.php. To make
this work, a few things are needed:</para>

<itemizedlist>

<listitem><para>Before every session_start(), a call to session_name()
has to be done. There are only a few places where this is needed. If
you are writing a module there should be no need to worry about
this.</para></listitem>

<listitem><para>The must not use $PHPSESSID (or $MGWSESSID). If this
is used, links will stop to work after changing the session name. Use
the constant SID instead!</para></listitem>

<listitem><para>Templates must use {$SESSION} when creating links
directly, and use {$SESSIONNAME} and {$SESSIONID} when creating hidden
input fields in forms.</para></listitem>
</itemizedlist>

<para>There are three Smarty assignments in appconfig.inc, these
populate the variables explained in above.</para>

<para>The following examples show typical uses of the variables
explained above. The first shows how to create a hidden form field for
passing the session. The second show a line from a template creating a
simple link. The third shows how to create HTML links in PHP.</para>

<example>
<title>Creating a hidden form field for session passing.</title>
<programlisting role="html">
<![CDATA[
<form name="f1" action="{$phpself}" method="POST">
<input type="hidden" name="{$SESSIONNAME}" value="{$SESSIONID}" />
 ...
</form>
]]>
</programlisting>
</example>

<example>
<title>Creating a regular link for session passing.</title>
<programlisting role="html">
<![CDATA[
<a href="index.php?mod=todo&amp;obj=item&amp;view=newform&amp;{$SESSION}" class="new">{$new}</a>
]]>
</programlisting>
</example>

<para>Note the use of the predefined (uppercase) variables in
Smarty. Those get defined in appconfig.inc. The following example uses
the standard PHP constant SID.</para>

<example>
<title>Creating a regular link for session passing in PHP.</title>
<programlisting role="php">
<![CDATA[
// build action urls
$detlink = "<a href=\"".$_SERVER['PHP_SELF']."?mod=todo&amp;obj=item&amp;view=details&amp;id=$myid&amp;ty=dto&amp;".SID."\" CLASS=\"edit\">".$icons["details"]."</a>";
$editlink = "<a href=\"".$_SERVER['PHP_SELF']."?mod=todo&amp;obj=item&amp;view=editform&amp;id=$myid&amp;".SID."\" CLASS=\"edit\">".$icons["edit"]."</a>";
]]>
      </programlisting>
    </example>
    
  </section>
  
  <section>
    <title>Storing per-module information in the session</title>
    
    <para>The todo module uses $_SESSION["todo"][...] to store the state
      of viewing (current items/archive) and the searchterm used in
      searching.  This makes those persistent, without having to worry about
      each and every link, and whether it adds those to the URL, or
      not. Other modules might store more, e.g. the current page being
      viewed, if paged result sets are used.
    </para>
    
    <para>Just use the name of your module as the first array index, to
      avoid name clashes and overwriting other information.
    </para>
  </section>
  
</section>

<section id="coding_snippets_settings">
  <title>Using the settings module</title>
  
  <para>As soon as you want the users to be able to change the behaviour
    of a module you need a possibility to manage user preferences. The
    moregroupware settings module makes this a lot easier. Read on for the
    gory details...
  </para>
  
</section>

</chapter>
