1# --
2# Copyright (C) 2001-2020 OTRS AG, https://otrs.com/
3# --
4# This software comes with ABSOLUTELY NO WARRANTY. For details, see
5# the enclosed file COPYING for license information (GPL). If you
6# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
7# --
8
9package Kernel::System::EventHandler;
10## nofilter(TidyAll::Plugin::OTRS::Perl::Pod::FunctionPod)
11
12use strict;
13use warnings;
14
15use Kernel::System::VariableCheck qw(IsArrayRefWithData);
16
17our $ObjectManagerDisabled = 1;
18
19=head1 NAME
20
21Kernel::System::EventHandler - event handler interface
22
23=head1 DESCRIPTION
24
25Inherit from this class if you want to use events there.
26
27    use parent qw(Kernel::System::EventHandler);
28
29In your class, have to call L</EventHandlerInit()> first.
30
31Then, to register events as they occur, use the L</EventHandler()>
32method. It will call the event handler modules which are registered
33for the given event, or queue them for later execution (so-called
34'Transaction' events).
35
36In the destructor, you should add a call to L</EventHandlerTransaction()>
37to make sure that also C<Transaction> events will be executed correctly.
38This is only necessary if you use C<Transaction> events in your class.
39
40=head1 PUBLIC INTERFACE
41
42=head2 EventHandlerInit()
43
44Call this to initialize the event handling mechanisms to work
45correctly with your object.
46
47    $Self->EventHandlerInit(
48        # name of configured event modules
49        Config     => 'Example::EventModule',
50    );
51
52Example 1:
53
54    $Self->EventHandlerInit(
55        Config     => 'Ticket::EventModulePost',
56    );
57
58Example 1 XML config:
59
60    <ConfigItem Name="Example::EventModule###99-EscalationIndex" Required="0" Valid="1">
61        <Description Translatable="1">Example event module updates the example escalation index.</Description>
62        <Group>Example</Group>
63        <SubGroup>Core::Example</SubGroup>
64        <Setting>
65            <Hash>
66                <Item Key="Module">Kernel::System::Example::Event::ExampleEscalationIndex</Item>
67                <Item Key="Event">(ExampleSLAUpdate|ExampleQueueUpdate|ExampleStateUpdate|ExampleCreate)</Item>
68                <Item Key="SomeOption">Some Option accessable via $Param{Config}->{SomeOption} in Run() of event module.</Item>
69                <Item Key="Transaction">(0|1)</Item>
70            </Hash>
71        </Setting>
72    </ConfigItem>
73
74Example 2:
75
76    $Self->EventHandlerInit(
77        Config     => 'ITSM::EventModule',
78    );
79
80Example 2 XML config:
81
82    <ConfigItem Name="ITSM::EventModule###01-HistoryAdd" Required="0" Valid="1">
83        <Description Translatable="1">ITSM event module updates the history for Change and WorkOrder objects..</Description>
84        <Group>ITSM Change Management</Group>
85        <SubGroup>Core::ITSMEvent</SubGroup>
86        <Setting>
87            <Hash>
88                <Item Key="Module">Kernel::System::ITSMChange::Event::HistoryAdd</Item>
89                <Item Key="Event">(ChangeUpdate|WorkOrderUpdate|ChangeAdd|WorkOrderAdd)</Item>
90                <Item Key="SomeOption">Some Option accessable via $Param{Config}->{SomeOption} in Run() of event module.</Item>
91                <Item Key="Transaction">(0|1)</Item>
92            </Hash>
93        </Setting>
94    </ConfigItem>
95    <ConfigItem Name="ITSM::EventModule###02-HistoryAdd" Required="0" Valid="1">
96        <Description Translatable="1">ITSM event module updates the ConfigItem History.</Description>
97        <Group>ITSM Configuration Management</Group>
98        <SubGroup>Core::ITSMEvent</SubGroup>
99        <Setting>
100            <Hash>
101                <Item Key="Module">Kernel::System::ITSMConfigurationManagement::Event::HistoryAdd</Item>
102                <Item Key="Event">(ConfigItemUpdate|ConfigItemAdd)</Item>
103                <Item Key="SomeOption">Some Option accessable via $Param{Config}->{SomeOption} in Run() of event module.</Item>
104                <Item Key="Transaction">(0|1)</Item>
105            </Hash>
106        </Setting>
107    </ConfigItem>
108
109=cut
110
111sub EventHandlerInit {
112    my ( $Self, %Param ) = @_;
113
114    $Self->{EventHandlerInit} = \%Param;
115    $Kernel::OM->ObjectRegisterEventHandler( EventHandler => $Self );
116
117    return 1;
118}
119
120=head2 EventHandler()
121
122call event handler, returns true if it was executed successfully.
123
124Example 1:
125
126    my $Success = $EventHandler->EventHandler(
127        Event => 'TicketStateUpdate',   # event classification, passed to the configured event handlers
128        Data  => {                      # data payload for the event, passed to the configured event handlers
129            TicketID => 123,
130        },
131        UserID => 123,
132        Transaction => 1,               # optional, 0 or 1
133    );
134
135In 'Transaction' mode, all events will be collected and executed together,
136usually in the destructor of your object.
137
138Example 2:
139
140    my $Success = $EventHandler->EventHandler(
141        Event => 'ChangeUpdate',
142        Data  => {
143            ChangeID => 123,
144        },
145        UserID => 123,
146    );
147
148=cut
149
150sub EventHandler {
151    my ( $Self, %Param ) = @_;
152
153    # check needed stuff
154    for (qw(Data Event UserID)) {
155        if ( !$Param{$_} ) {
156            $Kernel::OM->Get('Kernel::System::Log')->Log(
157                Priority => 'error',
158                Message  => "Need $_!"
159            );
160            return;
161        }
162    }
163
164    # get configured modules
165    my $Modules = $Kernel::OM->Get('Kernel::Config')->Get( $Self->{EventHandlerInit}->{Config} );
166
167    # return if there is no one
168    return 1 if !$Modules;
169
170    # remember events only on normal mode
171    if ( !$Self->{EventHandlerTransaction} ) {
172        push @{ $Self->{EventHandlerPipe} }, \%Param;
173    }
174
175    # get main object
176    my $MainObject = $Kernel::OM->Get('Kernel::System::Main');
177
178    # load modules and execute
179    MODULE:
180    for my $Module ( sort keys %{$Modules} ) {
181
182        # If the module has an event configuration, determine if it should be executed for this event,
183        #   and store the result in a small cache to avoid repetition on jobs involving many tickets.
184        if ( !defined $Self->{ExecuteModuleOnEvent}->{$Module}->{ $Param{Event} } ) {
185            if ( !$Modules->{$Module}->{Event} ) {
186                $Self->{ExecuteModuleOnEvent}->{$Module}->{ $Param{Event} } = 1;
187            }
188            else {
189                $Self->{ExecuteModuleOnEvent}->{$Module}->{ $Param{Event} } =
190                    $Param{Event} =~ /$Modules->{$Module}->{Event}/;
191            }
192        }
193
194        if ( $Self->{ExecuteModuleOnEvent}->{$Module}->{ $Param{Event} } ) {
195
196            if ( $Self->{EventHandlerTransaction} && !$Param{Transaction} ) {
197
198                # This is a special case. A new event was fired during processing of
199                #   the queued events in transaction mode. This event must be immediately
200                #   processed.
201            }
202            else {
203
204                # This is the regular case. A new event was fired in regular mode, or
205                #   we are processing a queued event in transaction mode. Only execute
206                #   this if the transaction settings of event and listener are the same.
207
208                # skip if we are not in transaction mode, but module is in transaction
209                next MODULE if !$Param{Transaction} && $Modules->{$Module}->{Transaction};
210
211                # skip if we are in transaction mode, but module is not in transaction
212                next MODULE if $Param{Transaction} && !$Modules->{$Module}->{Transaction};
213            }
214
215            # load event module
216            next MODULE if !$MainObject->Require( $Modules->{$Module}->{Module} );
217
218            # execute event backend
219            my $Generic = $Modules->{$Module}->{Module}->new();
220
221            $Generic->Run(
222                %Param,
223                Config => $Modules->{$Module},
224            );
225        }
226    }
227
228    return 1;
229}
230
231=head2 EventHandlerTransaction()
232
233handle all queued 'Transaction' events which were collected up to this point.
234
235    $EventHandler->EventHandlerTransaction();
236
237Call this method in the destructor of your object which inherits from
238Kernel::System::EventHandler, like this:
239
240    sub DESTROY {
241        my $Self = shift;
242
243        # execute all transaction events
244        $Self->EventHandlerTransaction();
245
246        return 1;
247    }
248
249=cut
250
251sub EventHandlerTransaction {
252    my ( $Self, %Param ) = @_;
253
254    # remember, we are in destroy mode, do not execute new events
255    $Self->{EventHandlerTransaction} = 1;
256
257    # execute events on end of transaction
258    if ( $Self->{EventHandlerPipe} ) {
259
260        for my $Params ( @{ $Self->{EventHandlerPipe} } ) {
261            $Self->EventHandler(
262                %Param,
263                %{$Params},
264                Transaction => 1,
265            );
266        }
267
268        # delete event pipe
269        $Self->{EventHandlerPipe} = undef;
270    }
271
272    # reset transaction mode
273    $Self->{EventHandlerTransaction} = 0;
274
275    return 1;
276}
277
278=head2 EventHandlerHasQueuedTransactions()
279
280Return a true value if there are queued transactions, which
281C<EventHandlerTransaction> handles, when called.
282
283=cut
284
285sub EventHandlerHasQueuedTransactions {
286    my ( $Self, %Param ) = @_;
287
288    return IsArrayRefWithData( $Self->{EventHandlerPipe} );
289}
290
2911;
292
293=head1 TERMS AND CONDITIONS
294
295This software is part of the OTRS project (L<https://otrs.org/>).
296
297This software comes with ABSOLUTELY NO WARRANTY. For details, see
298the enclosed file COPYING for license information (GPL). If you
299did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
300
301=cut
302