<chapter id="dev-custom-modules">
<title>Entwicklerhandbuch - OTRS Module schreiben</title>
<para>
Dieses Kapitel wird Ihnen weitere Inforamtionen über das Schreiben von einfachen OTRS Modulen geben.
</para>
<para>

</para>

<section id="dev-custom-modules-notify">
<title>Agenten-Oberfläche Benachrichtigungsmodule</title>
<para>
Mit Agent-Benachrichtigungsmodulen können Sie Agenten neue Information zukommen lassen. Die Navigation sieht normalerweise folgendermaßen aus:
<screenshot>
<screeninfo>Navigation</screeninfo>
<graphic srccredit="navigation - screenshot" scale="40" fileref="screenshots/agent-notification-without.png"></graphic>
</screenshot>
</para>
<para>
Dies ist ein Beispiel für ein simples Benachrichtigungsmodul. Speichern Sie es unter Kernel/Output/HTML/NotificationMotd.pm. 
<programlisting>
# --
# Kernel/Output/HTML/NotificationMotd.pm - message of the day  
# Copyright (C) 2003 Hans Mueller mail@example.com
# --
# $Id: developer-guide-custom-modules.sgml,v 1.5 2004/04/23 08:21:17 martin Exp $
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see http://www.gnu.org/licenses/gpl.txt.
# --

package Kernel::Output::HTML::NotificationMotd;

use strict;

# --
sub new {
    my $Type = shift;
    my %Param = @_;

    # allocate new hash for object
    my $Self = {};
    bless ($Self, $Type);

    # get needed objects
    foreach (qw(ConfigObject LogObject DBObject LayoutObject UserID)) {
        $Self->{$_} = $Param{$_} || die "Got no $_!";
    }
    return $Self;
}
# --
sub Run {
    my $Self = shift;
    my %Param = @_;
    return $Self->{LayoutObject}->Notify(Info => 'Tägliche Neuigkeiten!');
}
# --

1;
</programlisting>
Wenn Sie dieses Module nun auch verwenden möchten, so müssen Sie es in der Konfigurationsdatei (Kernel/Config.pm) aktivieren und Ihren Webserver neu starten (letzteres nur, wenn Sie mod_perl verwenden).
<programlisting>

    # Frontend::NotifyModule - module name (50-Motd)
    $Self->{'Frontend::NotifyModule'}->{'50-Motd'} = {
        Module => 'Kernel::Output::HTML::NotificationMotd',
    };

</programlisting>
Im Ergebnis sehen Sie eine Nachricht in der Agenten Oberfläche:
<screenshot>
<screeninfo>navigation</screeninfo>
<graphic srccredit="navigation - screenshot" scale="40" fileref="screenshots/agent-notification.png"></graphic>
</screenshot>
Normalerweise möchten Sie Nachrichten mit einem gewissen Sinngehalt in Ihre Benachrichtigungsfunktion aufnehmen. Diese Art der Benachrichtigung ist beispeilsweise sinnvoll für Eskalationsinformationen.
</para>
<para>
Die standardmäßigen Benachrichtigungsmodule sind die folgenden (Kernel/Config.pm):
<programlisting>

    # agent interface notification module to check the used charset
    $Self->{'Frontend::NotifyModule'}->{'1-CharsetCheck'} = {
        Module => 'Kernel::Output::HTML::NotificationCharsetCheck',
    };

    # agent interface notification module to check the admin user id
    # (don't work with user id 1 notification)
    $Self->{'Frontend::NotifyModule'}->{'2-UID-Check'} = {
        Module => 'Kernel::Output::HTML::NotificationUIDCheck',
    };

    # show online agents
    $Self->{'Frontend::NotifyModule'}->{'3-ShowAgentOnline'} = {
        Module => 'Kernel::Output::HTML::NotificationAgentOnline',
    };
    # show online customers
    $Self->{'Frontend::NotifyModule'}->{'4-ShowCustomerOnline'} = {
        Module => 'Kernel::Output::HTML::NotificationCustomerOnline',
    };

</programlisting>
</para>
</section>

<section id="dev-custom-modules-notify-customer">
<title>Kunden-Oberfläche Benachrichtigungsmodule</title>
<para>
Mit Kunden-Benachrichtigungsmodulen können Sie Agenten neue Information zukommen lassen.
Es sind die gleichen Module wie die Agent-Oberfläche Benachrichtigungsmodule.
</para>
<para>
Die standardmäßigen Benachrichtigungsmodule sind die folgenden (Kernel/Config.pm):
<programlisting>

    # show online agents
    $Self->{'CustomerFrontend::NotifyModule'}->{'1-ShowAgentOnline'} = {
        Module => 'Kernel::Output::HTML::NotificationAgentOnline',
    };

</programlisting> 
</para>
</section>


<section id="dev-custom-modules-ticketnumber">
<title>Ticketnummer Generator</title>
<para>
Wenn Sie Ihr eigenes Format in der Ticketnummer nutzen wolen, erstellen 
Sie Ihr eigenes Ticketnummer-Modul.
</para>
<para>
Die Module befinden sich unter "Kernel/System/Ticket/Number/*.pm".
Schauen Sie in der Konfigurationsdatei unter "Ticket number format" nach für die Standard-Module.
</para>
<para>
Ein Beispiel eines einfachen Ticketnummer-Moduls, speichern Sie es unter Kernel/System/Ticket/Number/Simple.pm. Sie benötigen nur zwei Funktionen, CreateTicketNr() and GetTNByString():
<programlisting>
# --
# Ticket/Number/Simple.pm - a ticket number auto increment generator
# Copyright (C) 2003 Hans Mueller mail@example.com
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see http://www.gnu.org/licenses/gpl.txt.
# --
# Note: 
# available objects are: ConfigObject, LogObject and DBObject
# 
# Generates auto increment ticket numbers like ss.... (e. g. 1010138, 1010139, ...)
# --

package Kernel::System::Ticket::Number::Simple;

use strict;

use vars qw($VERSION);
$VERSION = '$Revision: 1.5 $';
$VERSION =~ s/^\$.*:\W(.*)\W.+?$/$1/;

sub CreateTicketNr {
    my $Self = shift;
    my $JumpCounter = shift || 0;
    # get needed config options 
    my $CounterLog = $Self->{ConfigObject}->Get('CounterLog');
    my $SystemID = $Self->{ConfigObject}->Get('SystemID');
    my $MinSize = 5;
    my $Count = 0;
    if (-f $CounterLog) {
        open (COUNTER, "< $CounterLog") || die "Can't open $CounterLog: $!";
        my $Line = &lt;COUNTER>;
        ($Count) = split(/;/, $Line);
        close (COUNTER);
        if ($Self->{Debug} > 0) {
            $Self->{LogObject}->Log(
              Priority => 'debug',
              Message => "Read counter from $CounterLog: $Count",
            );
        }
    }
    # count auto increment ($Count++)
    $Count++;
    $Count = $Count + $JumpCounter;
    # write new count
    if (open (COUNTER, "> $CounterLog")) {
        flock (COUNTER, 2) || warn "Can't set file lock ($CounterLog): $!";
        print COUNTER $Count . "\n";
        close (COUNTER);
        if ($Self->{Debug} > 0) {
            $Self->{LogObject}->Log(
              Priority => 'debug',
              Message => "Write counter: $Count",
            );
        }
    }
    else {
        $Self->{LogObject}->Log(
            Priority => 'error',
            Message => "Can't write $CounterLog: $!",
        );
        die "Can't write $CounterLog: $!";
    }
    # pad ticket number with leading '0' to length $MinSize (config option)
    while (length($Count) < $MinSize) {
        $Count = "0".$Count;
    }
    # create new ticket number
    my $Tn = $SystemID . $Count;
    # Check ticket number. If exists generate new one! 
    if ($Self->CheckTicketNr(Tn=>$Tn)) {
        $Self->{LoopProtectionCounter}++;
        if ($Self->{LoopProtectionCounter} >= 1000) {
          # loop protection
          $Self->{LogObject}->Log(
            Priority => 'error',
            Message => "CounterLoopProtection is now $Self->{LoopProtectionCounter}!".
                   " Stoped CreateTicketNr()!",
          );
          return;
        }
        # create new ticket number again
        $Self->{LogObject}->Log(
          Priority => 'notice',
          Message => "Tn ($Tn) exists! Creating new one.",
        );
        $Tn = $Self->CreateTicketNr($Self->{LoopProtectionCounter});
    }
    return $Tn;
}
# --
sub GetTNByString {
    my $Self = shift;
    my $String = shift || return;
    # get needed config options 
    my $SystemID = $Self->{ConfigObject}->Get('SystemID');
    my $TicketHook = $Self->{ConfigObject}->Get('TicketHook');
    my $TicketDivider = $Self->{ConfigObject}->Get('TicketDivider') || ': ';
    my $MinSize = 5;
    my $MaxSize = $MinSize + 5;
    # check ticket number
    if ($String =~ /$TicketHook:+.{0,1}($SystemID\d{$MinSize,$MaxSize})\-FW/i) {
        return $1;
    }
    else {
        if ($String =~ /$TicketHook:+.{0,1}($SystemID\d{$MinSize,$MaxSize})/i) {
            return $1;
        }
        else {
            return;
        }
    }
}
# --
1;
</programlisting> 
Um dieses Modul zu nutzen, fürgen Sie Ihrer Kernel/Config.pm die folgenden Zeilen hinzu und starten Sie Ihren Webserver neu (falls Sie mod_perl benutzen):
<programlisting>

    $Self->{TicketNumberGenerator} = 'Kernel::System::Ticket::Number::Simple';

</programlisting> 
Nun wird Ihr Ticketnummer-Modul genutzt.
</para>
</section>

<section id="dev-custom-modules-aauth">
<title>Module zur Authentifizierung von Agenten</title>
<para>
OTRS kommt bereits mit mehreren Module zur Authentifizierung von Agenten (DB, LDAP und HTTPBasicAuth). Darüber hinaus können Sie Ihre eigenen Module entwickeln.
</para>
<para>
Die Module befinden sich unter "Kernel/System/Auth/*.pm".
Schauen Sie in der Konfigurationsdatei unter "User Auth Backend" nach.
</para>
<para>
Ein Beispiel eines einfachen Ticketnummer-Moduls, speichern Sie es unter Kernel/System/Auth/Simple.pm. Sie benötigen nur drei Funktionen, new(), GetOption() and Auth(). Die Authentifizierung ist erfolgreich, wenn Auth() wahr (also 'true') ist.
<programlisting> 
# --
# Kernel/System/Auth/Simple.pm - provides the db authentification 
# Copyright (C) 2001-2004 Martin Edenhofer martin+code at otrs.org
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see 
# the enclosed file COPYING for license information (GPL). If you 
# did not receive this file, see http://www.gnu.org/licenses/gpl.txt.
# --
# Note: 
# available objects are: ConfigObject, LogObject and DBObject
# --

package Kernel::System::Auth::Simple;

use strict;
    
# --
sub new {
    my $Type = shift;
    my %Param = @_;

    # allocate new hash for object
    my $Self = {};
    bless ($Self, $Type);

    # check needed objects
    foreach (qw(LogObject ConfigObject DBObject)) {
        $Self->{$_} = $Param{$_} || die "No $_!";
    }

    # Debug 0=off 1=on
    $Self->{Debug} = 0;

    return $Self;
}
# --
sub GetOption {
    my $Self = shift;
    my %Param = @_;
    # check needed stuff
    if (!$Param{What}) {
        $Self->{LogObject}->Log(Priority => 'error', Message => "Need What!");
        return;
    }
    # module options
    my %Option = (
        PreAuth => 0,
    );
    # return option
    return $Option{$Param{What}};
}
# --
sub Auth {
    my $Self = shift;
    my %Param = @_;
    # check needed stuff
    if (!$Param{User}) {
        $Self->{LogObject}->Log(Priority => 'error', Message => "Need User!");
        return;
    }
    # get params
    my $User = $Param{User} || '';
    my $Pw = $Param{Pw} || '';
    my $RemoteAddr = $ENV{REMOTE_ADDR} || 'Got no REMOTE_ADDR env!';
    my $UserID = '';
    my $GetPw = '';
    # sql query
    my $SQL = "SELECT pw, user ".
      " FROM ".
      " users ".
      " WHERE ".
      " user = '$User'";
    $Self->{DBObject}->Prepare(SQL => $SQL);
    while (my @RowTmp = $Self->{DBObject}->FetchrowArray()) {
        $GetPw = $RowTmp[0];
        $UserID = $RowTmp[1];
    }

    my $Salt = $GetPw;
    $Salt =~ s/^(..).*/$1/;
    my $CryptedPw = crypt($Pw, $Salt);

    # just a note 
    if (!$Pw) {
        $Self->{LogObject}->Log(
          Priority => 'notice',
          Message => "User: $User without Pw!!! (REMOTE_ADDR: $RemoteAddr)",
        );
        return;
    }
    # login note
    elsif ((($GetPw)&&($User)&&($UserID)) && $CryptedPw eq $GetPw) {
        $Self->{LogObject}->Log(
          Priority => 'notice',
          Message => "User: $User logged in (REMOTE_ADDR: $RemoteAddr).",
        );
        return $User;
    }
    # just a note
    elsif (($UserID) && ($GetPw)) {
        $Self->{LogObject}->Log(
          Priority => 'notice',
          Message => "User: $User with wrong Pw!!! (REMOTE_ADDR: $RemoteAddr)"
        );
        return;
    }
    # just a note
    else {
        $Self->{LogObject}->Log(
          Priority => 'notice',
          Message => "User: $User doesn't exist or is invalid!!! (REMOTE_ADDR: $RemoteAddr)"
        );
        return;
    }
}
# --

1;
</programlisting> 
</para>
</section>

<section id="dev-custom-modules-cauth">
<title>Module zur Authentifizierung von Kundenbenutzern</title>
<para>
Verfahren Sie wie bei den Modulen zur Authentifizierung von Agenten, die Module liegen diesmal unter Kernel/System/CustomerAuth/*.pm.
</para>
</section>

<section id="dev-custom-modules-cuser">
<title>Module für Kundenbenutzer</title>
<para>
...
</para>
</section>

<section id="dev-custom-modules-postmaster">
<title>PostMaster Filter Module</title>
<para>
Siehe auch das Kapitel "PostMaster". Dieses Beispiel sortiert eMails von "email@example.com" in die Queue "Some::System::Queue" und setzt freien Text. 
</para>
<para>
Für eine Übersicht über alle X-Header-Optionen, siehe "doc/X-OTRS-Headers.txt".
</para>
<para>
Ein Beispiel eines simplen Postmaster Filter Moduls. Speichern Sie unter Kernel/System/PostMaster/Filter/Simple.pm ab. Es werden nur zwei Funktionen benötigt, new() and Run(): 
<programlisting>
# --
# Kernel/System/PostMaster/Filter/Simple.pm - sub part of PostMaster.pm
# Copyright (C) 2001-2003 Martin Edenhofer &lt;martin+code otrs.org&gt;
# --
# $Id: developer-guide-custom-modules.sgml,v 1.5 2004/04/23 08:21:17 martin Exp $
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see 
# the enclosed file COPYING for license information (GPL). If you 
# did not receive this file, see http://www.gnu.org/licenses/gpl.txt.
# --

package Kernel::System::PostMaster::Filter::Simple;

use strict;

use vars qw($VERSION);
$VERSION = '$Revision: 1.5 $';
$VERSION =~ s/^.*:\s(\d+\.\d+)\s.*$/$1/;

# --
sub new {
    my $Type = shift;
    my %Param = @_;

    # allocate new hash for object
    my $Self = {};
    bless ($Self, $Type);

    $Self->{Debug} = $Param{Debug} || 0;

    # get needed objects
    foreach (qw(ConfigObject LogObject DBObject)) {
        $Self->{$_} = $Param{$_} || die "Got no $_!";
    }

    return $Self;
}
# --
sub Run {
    my $Self = shift;
    my %Param = @_;
    if ($Param{GetParam}->{From} =~ /email\@example.com/i) {
        $Param{GetParam}->{'X-OTRS-Queue'} = 'Some::System::Queue';
        $Param{GetParam}->{'X-OTRS-TicketKey1'} = 'Planet';
        $Param{GetParam}->{'X-OTRS-TicketValue1'} = 'Sun';
    }
    return 1; 
}
# --
1;
</programlisting>
</para>
</section>

<section id="dev-custom-modules-generic-agent">
<title>Generic Agent Modules</title>
<para>
See also chapter "GenericAgent".
</para>
<para>
This module will notify the current ticket owner.
</para>
<para>
A example of a simple generic agent module, save it under Kernel/System/GenericAgent/Simple.pm. You just need 2 functions, new() and Run(). Run() will be get the ticket id:
<programlisting>
# --
# Kernel/System/GenericAgent/Simple.pm - generic agent notifications
# Copyright (C) 2001-2004 Martin Edenhofer &lt;martin+code otrs.org&gt;
# --
# $Id: developer-guide-custom-modules.sgml,v 1.5 2004/04/23 08:21:17 martin Exp $
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see http://www.gnu.org/licenses/gpl.txt.
# --

package Kernel::System::GenericAgent::Simple;
    
use strict;
use Kernel::System::User;
use Kernel::System::Email;
    
use vars qw(@ISA $VERSION);
$VERSION = '$Revision: 1.5 $';
$VERSION =~ s/^\$.*:\W(.*)\W.+?$/$1/;

# --
sub new {
    my $Type = shift;
    my %Param = @_;

    # allocate new hash for object
    my $Self = {};
    bless ($Self, $Type);

    # check needed objects
    foreach (qw(DBObject ConfigObject LogObject TicketObject)) {
        $Self->{$_} = $Param{$_} || die "Got no $_!";
    }

    $Self->{UserObject} = Kernel::System::User->new(%Param);
    $Self->{EmailObject} = Kernel::System::Email->new(%Param);

    return $Self;
}
# --
sub Run {
    my $Self = shift;
    my %Param = @_;

    my %Ticket = $Self->{TicketObject}->GetTicket(%Param);

    my %User = $Self->{UserObject}->GetUserData(UserID => $Ticket{UserID});
    if ($User{UserEmail}) {
        $Self->{EmailObject}->Send(
                 To => $User{UserEmail},
                Subject => "[$Ticket{TicketNumber}] Ticket Notification!",
                Body => "Hi $User{Firstname}, some info ... about $Ticket{'TicketNumber'}".
                Loop => 1,
        );
    }

    return 1;
}
# --
1;
</programlisting>
</para>
</section>

<section id="dev-custom-modules-permission">
<title>Agenten-Ticketberechtigungen Module</title>
<para>
...
</para>
</section>

<section id="dev-custom-modules-cpermission">
<title>Kunden-Ticketberechtigungen Module</title>
<para>
...
</para>
</section>

</chapter>
