Funktion der Standard Module durch eigene Module erweitern

Locked
darkside

Funktion der Standard Module durch eigene Module erweitern

Post by darkside »

//EDIT: Weiter geht es hier (inkl. kleinem Todo zur Erstellung solcher Module): [Announce] BETA: Modul: HookModules 0.0.2 + Mini-Handbuch

Hallo,
OTRS ist in seiner derzeitigen Version ja bereits ein sehr mächtiges und komplexes Werkzeug geworden. Schön sind dabei die vielfältigen Konfigurationsmöglichkeiten (man wird ja mit der Optionsvielfalt unter Sysconfig förmlich erschlagen) und die Erweiterung durch eigene Module.

Dennoch empfinde ich das OTRS System an einigen Stellen etwas zu unflexibel. Erweiterungen der vorhandenen Module funktionieren meist ja nur, in dem man a) über die GmbH entsprechende Leistung einkauft oder b) selbst in den Modulen "rumschmiert".

Punkt b) ist dabei immer gefährlich, da ja doch zumeist nebst den Frontend-Modulen auch einige Backend-Module verändert werden müssen und man bei Versionsupdates des OTRS Systems die Arbeit hat, die Patches/Hacks immer wieder neu einpflegen zu müssen.

Punkt a) ist natürlich immer vorzuziehen, womal es da ja dann sicher auch einen enstprechenden Pflege- und Supportvertrag dazu gibt.

Leider ist es aber nicht immer möglich entsprechende Geldmittel locker zu machen ... bei mir hier ist das bspw. so. Also pfusche ich selber im Programmcode rum (siehe [Announce] Neuer otrs-forum.de Sammelpatch V1.4).

Da die Änderungen aber immer komplexer wurden und das Portieren dadurch nicht einfach ist, musste eine andere Lösung her. Viele OS Softwareprodukte bieten über Extensions/Plugins/Hooks und wie sie alle heißen, Schnittstellen an, mit denen man die vorhandenen Funktionen erweitern oder gar ersetzen kann.

Ich hab das mal als einen ersten Versuch für OTRS umgesetzt. Auch hier geht es natürlich erst mal nicht ohne Anpassungen des Programmcodes, aber diese beschränken sich auf 3 Dateien mit wenigen Zeilen.

Dieser Hack ermöglicht es dann, eigene Frontend- (und Backend-) Module zu programmieren die dann bspw. die HTML Ausgabe manipulieren (in dem sie zusätzliche Daten anbietet oder gar die vom OTRS Modul erzeugten anpassen bwz. verändern).

Hier mal eine grobe Beschreibung, was denn dieser Hack dann macht:
  • Man programmiere ein eigenes Modul
  • Dieses Modul wird über das mitgelieferte Configfile als "Hook" registriert
  • In dieser Registrierung wird festgelegt, für welches Frontendmodul dieser Hook gilt und WANN er ausgeführt werden soll
  • Ausführung ist Möglich: "Vor dem Ausführen des OTRS Standardmoduls" (PreRun), "Vor der Genrierung der HTML Ausgabe" (LayoutData), "Vor dem versenden von eMails" sofern das Modul dies überhaupt anbietet (MailSendData) und "Nach Ausführen des OTRS Standardmoduls" (AfterRun)
  • Der Hack liest die Konfiguration ein und schaut, ob für das gerade aufgerufene Frontend-Modul ein oder mehrere Hook-Module existieren
  • Ist dem so, so werden diese als Objekte erstellt und mit den selben Parametern gefüttert, wie das OTRS Standardmodul und bekommt sogar das Objekt zum Standardmodul mit übergeben um auf diesem direkt zugreifen zu können.
  • Je nach Konfiguration des Hooks wird dann noch vor dem "run" des Standardmoduls eine Routine (PreRun) der Hookmodule ausgeführt
  • Generiert das OTRS Standardmodul eine Ausgabe (was ja eigentlich fast immer der Fall ist) übergibt es dem Layout Objekt das Template sowie alle zu ersetzenden Daten als Parameter.
  • Bevor dieses Layout Objekt dann die Daten aus dem Template ersetzt, werden wieder die gerade aktiven Hookmodule aufgerufen die dann die vom Standardmodul übergebenen Parameter verändern, löschen oder ergänzen können (LayoutData).
  • Nachdem dann die Ausgabe generiert wurde werden, bevor diese dann an den Browser zu Ausgabe geschickt wird, wieder die aktiven Hookmodule nochmals aufgerufen um ggf. die Ausgabe nochmals anzupassen oder aber einfach irgendwelche Aktionen (DB bspw.) durchzuführen (AfterRun).
  • Sollte eine Standardmodul eine eMail senden wollen (AgentTicketCompose bspw.) dann übergibt dieses Modul auch hier einem für den Mailversand zuständigen eMail-Objekt die Daten.
  • Das eMail-Objekt nun ruft ebenfalls alle aktiven Hook-Module auf und bietet ihm die Maildaten zur Manipulation an.
In der Konfiguration eines jeden Hookmoduls kann dabei festgelegt werden, bei welchen der 4 Aufrufoptionen es auch arbeiten soll. Man könnte bspw. ein Modul programmieren, welches nur im "PreRun" Modus läuft ... und in den anderen 3 gar nicht mehr beachtet wird.

Damit dieser Hack einfach und schnell verwenden werden kann, habe ich die von mir manipulierten Original OTRS Dateien mal in ein OTRS Paket gepackt. Erstellt wurde er unter OTRS Version 2.2.5 ist aber auch mit 2.2.4 getestet und sollte auch mit anderen Versionen aus der 2.2.x Reihe laufen (habe ich aber nicht getestet).

Donwload: //EDIT: Weiter geht es hier (inkl. kleinem Todo zur Erstellung solcher Module): [Announce] BETA: Modul: HookModules 0.0.2 + Mini-Handbuch

Hier für alle die den Hack in Quellcodeform sehen wollen die entsprechenden Auszüge:
Kernel/System/web/InterfaceAgent.pm.diff

Code: Select all

--- InterfaceAgent.pm.orig      2007-09-07 11:12:46.000000000 +0200
+++ InterfaceAgent.pm   2008-03-05 12:02:45.000000000 +0100
@@ -13,7 +13,7 @@

 use strict;

-use vars qw($VERSION @INC);
+use vars qw($VERSION @INC %Hooks);
 $VERSION = '$Revision: 1.23 $';
 $VERSION =~ s/^\$.*:\W(.*)\W.+?$/$1/;

@@ -652,6 +652,7 @@
                     Message => 'Kernel::Modules::' . $Param{Action} .'->new',
                 );
             }
+
             # prove of concept! - create $GenericObject
             my $GenericObject = ('Kernel::Modules::'.$Param{Action})->new(
                 %{$Self},
@@ -659,6 +660,43 @@
                 %UserData,
                 ModuleReg => $ModuleReg,
             );
+
+           %Hooks = ();
+
+           if ($Self->{ConfigObject}->Get('HookModule')) {
+               my $HookModule = $Self->{ConfigObject}->Get('HookModule')->{$Param{Action}};
+               #my @HookModuleList;
+               if ($HookModule && ref($HookModule) eq 'ARRAY') {
+                   foreach (@{$HookModule}) {
+                       if ($Self->{MainObject}->Require('Kernel::Modules::'.$_->[0])) {
+                           my $Hook = ('Kernel::Modules::'.$_->[0])->new(
+                               %{$Self},
+                               %Param,
+                               %UserData,
+                               ModuleReg => $ModuleReg,
+                               ModuleObject => $GenericObject,
+                           );
+                           if ($_->[1]) {
+                               push(@{$Hooks{'Pre'}}, $Hook);
+                           }
+                           if ($_->[2]) {
+                               my %Hook = (Hook => $Hook, Type => $_->[2], Module => $Param{Action}
);
+                               push(@{$Hooks{'Layout'}}, \%Hook);
+                           }
+                           if ($_->[3]) {
+                               push(@{$Hooks{'MailSend'}}, $Hook);
+                           }
+                           if ($_->[4]) {
+                               push(@{$Hooks{'After'}}, $Hook);
+                           }
+                       }
+                   }
+               }
+           }
+           foreach (@{$Hooks{'Pre'}}) {
+               $_->PreRun();
+           }
+
             # debug info
             if ($Self->{Debug}) {
                 $Self->{LogObject}->Log(
@@ -667,7 +705,11 @@
                 );
             }
             # ->Run $Action with $GenericObject
-            $Self->{LayoutObject}->Print(Output => \$GenericObject->Run());
+           my $Output = $GenericObject->Run();
+           foreach (@{$Hooks{'After'}}) {
+               $_->AfterRun(Output => $Output);
+           }
+           $Self->{LayoutObject}->Print(Output => \$Output);
             # log request time
             if ($Self->{ConfigObject}->Get('PerformanceLog')) {
                 if ((!$QueryString && $Param{Action}) || ($QueryString !~ /Action=/)) {
Kernel/Output/HTML/Layout.pm.diff

Code: Select all

--- Layout.pm.orig      2007-12-18 11:28:30.000000000 +0100
+++ Layout.pm   2008-03-05 13:18:25.000000000 +0100
@@ -425,6 +425,17 @@
     else {
         $Param{Data} = {};
     }
+
+    if ($Kernel::System::Web::InterfaceAgent::Hooks{'Layout'} &&
+       @{$Kernel::System::Web::InterfaceAgent::Hooks{'Layout'}}) {
+       foreach (@{$Kernel::System::Web::InterfaceAgent::Hooks{'Layout'}}) {
+           #if ($_->{Type} == 1 || $Param{TemplateFile} =~ /^$_->{Module}.*/) {
+               $Param{Data} = $_->{Hook}->LayoutData(
+                   Data => $Param{Data},
+               );
+           #}
+       }
+    }

     # create %Env for this round!
     my $EnvRef = {};
Kernel/System/Email.pm.diff

Code: Select all

--- Email.pm.orig       2007-11-05 14:18:11.000000000 +0100
+++ Email.pm    2008-03-06 15:21:36.000000000 +0100
@@ -164,6 +164,16 @@
 sub Send {
     my $Self = shift;
     my %Param = @_;
+
+    if ($Kernel::System::Web::InterfaceAgent::Hooks{'MailSend'} &&
+       @{$Kernel::System::Web::InterfaceAgent::Hooks{'MailSend'}}) {
+       foreach (@{$Kernel::System::Web::InterfaceAgent::Hooks{'MailSend'}}) {
+           %Param = %{$_->MailSendData(
+               Data => \%Param
+           )};
+       }
+    }
+
     # check needed stuff
     foreach (qw(Body Charset)) {
         if (!$Param{$_}) {
So, bevor ich nun eine Dokumentation schreibe, wie die Hook-Module aussehen sollten und wie die Registrierung als Hookmodul läuft, eine Frage: Besteht überhaupt Interesse?

Ein erstes Hook-Modul habe ich auch bereits erstellt und werde es gleich noch veröffentlichen.

Andreas
Willi_Sturm
Znuny newbie
Posts: 24
Joined: 06 Nov 2007, 11:01
Znuny Version: 2.4.7
Real Name: Willi Pasternak
Company: Sturm Medical Solutions GmbH
Location: Aldingen

Funktion der Standard Module durch eigene Module erweitern

Post by Willi_Sturm »

Hi darkside,

hört sich garnicht schlecht an was du da programmirt hast da ichoft auch selbst im code "rum pfuschen" muss kommt mir das iwe gerufen :D
bin mal gespannt wie das funktioniert.

MFG Willi
Image
Locked