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::Modules::AgentTicketActionCommon;
10
11use strict;
12use warnings;
13
14use Kernel::System::EmailParser;
15use Kernel::System::VariableCheck qw(:all);
16use Kernel::Language qw(Translatable);
17
18our $ObjectManagerDisabled = 1;
19
20sub new {
21    my ( $Type, %Param ) = @_;
22
23    # allocate new hash for object
24    my $Self = {%Param};
25    bless( $Self, $Type );
26
27    # Try to load draft if requested.
28    if (
29        $Kernel::OM->Get('Kernel::Config')->Get("Ticket::Frontend::$Self->{Action}")->{FormDraft}
30        && $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam( Param => 'LoadFormDraft' )
31        && $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam( Param => 'FormDraftID' )
32        )
33    {
34        $Self->{LoadedFormDraftID} = $Kernel::OM->Get('Kernel::System::Web::Request')->LoadFormDraft(
35            FormDraftID => $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam( Param => 'FormDraftID' ),
36            UserID      => $Self->{UserID},
37        );
38    }
39
40    # get article for whom this should be a reply, if available
41    my $ReplyToArticle = $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam( Param => 'ReplyToArticle' ) || '';
42    my $TicketID       = $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam( Param => 'TicketID' )       || '';
43
44    # check if ReplyToArticle really belongs to the ticket
45    my %ReplyToArticleContent;
46    my @ReplyToAdresses;
47    if ($ReplyToArticle) {
48
49        my $ArticleBackendObject = $Kernel::OM->Get('Kernel::System::Ticket::Article')->BackendForArticle(
50            TicketID  => $TicketID,
51            ArticleID => $ReplyToArticle,
52        );
53        %ReplyToArticleContent = $ArticleBackendObject->ArticleGet(
54            TicketID      => $TicketID,
55            ArticleID     => $ReplyToArticle,
56            DynamicFields => 0,
57        );
58
59        $Self->{ReplyToArticle}        = $ReplyToArticle;
60        $Self->{ReplyToArticleContent} = \%ReplyToArticleContent;
61
62        # get sender of original note (to inform sender about answer)
63        if ( $ReplyToArticleContent{CreateBy} ) {
64            my @ReplyToSenderID = ( $ReplyToArticleContent{CreateBy} );
65            $Self->{ReplyToSenderUserID} = \@ReplyToSenderID;
66        }
67
68        # if article belongs to other ticket, don't use it as reply
69        if ( $ReplyToArticleContent{TicketID} ne $Self->{TicketID} ) {
70            $Self->{ReplyToArticle} = "";
71        }
72
73        # if article is not of type note-internal, don't use it as reply
74        if (
75            $ArticleBackendObject->ChannelNameGet() ne 'Internal'
76            || (
77                $ArticleBackendObject->ChannelNameGet() eq 'Internal'
78                && $ReplyToArticleContent{SenderType} ne 'agent'
79            )
80            )
81        {
82            $Self->{ReplyToArticle} = "";
83        }
84    }
85
86    # get form id
87    $Self->{FormID} = $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam( Param => 'FormID' );
88
89    # create form id
90    if ( !$Self->{FormID} ) {
91        $Self->{FormID} = $Kernel::OM->Get('Kernel::System::Web::UploadCache')->FormIDCreate();
92    }
93
94    return $Self;
95}
96
97sub Run {
98    my ( $Self, %Param ) = @_;
99
100    # get needed objects
101    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
102    my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
103    my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
104    my $ParamObject  = $Kernel::OM->Get('Kernel::System::Web::Request');
105
106    # check needed stuff
107    if ( !$Self->{TicketID} ) {
108        return $LayoutObject->ErrorScreen(
109            Message => Translatable('No TicketID is given!'),
110            Comment => Translatable('Please contact the administrator.'),
111        );
112    }
113
114    # get config of frontend module
115    my $Config = $ConfigObject->Get("Ticket::Frontend::$Self->{Action}");
116
117    # check permissions
118    my $Access = $TicketObject->TicketPermission(
119        Type     => $Config->{Permission},
120        TicketID => $Self->{TicketID},
121        UserID   => $Self->{UserID}
122    );
123
124    # error screen, don't show ticket
125    if ( !$Access ) {
126        return $LayoutObject->NoPermission(
127            Message => $LayoutObject->{LanguageObject}->Translate( 'You need %s permissions!', $Config->{Permission} ),
128            WithHeader => 'yes',
129        );
130    }
131
132    # get ACL restrictions
133    my %PossibleActions = ( 1 => $Self->{Action} );
134
135    my $ACL = $TicketObject->TicketAcl(
136        Data          => \%PossibleActions,
137        Action        => $Self->{Action},
138        TicketID      => $Self->{TicketID},
139        ReturnType    => 'Action',
140        ReturnSubType => '-',
141        UserID        => $Self->{UserID},
142    );
143    my %AclAction = $TicketObject->TicketAclActionData();
144
145    # check if ACL restrictions exist
146    if ( $ACL || IsHashRefWithData( \%AclAction ) ) {
147
148        my %AclActionLookup = reverse %AclAction;
149
150        # show error screen if ACL prohibits this action
151        if ( !$AclActionLookup{ $Self->{Action} } ) {
152            return $LayoutObject->NoPermission( WithHeader => 'yes' );
153        }
154    }
155
156    # Check for failed draft loading request.
157    if (
158        $ParamObject->GetParam( Param => 'LoadFormDraft' )
159        && !$Self->{LoadedFormDraftID}
160        )
161    {
162        return $LayoutObject->ErrorScreen(
163            Message => Translatable('Loading draft failed!'),
164            Comment => Translatable('Please contact the administrator.'),
165        );
166    }
167
168    my %Ticket = $TicketObject->TicketGet(
169        TicketID      => $Self->{TicketID},
170        DynamicFields => 1,
171    );
172
173    my $LoadedFormDraft;
174    if ( $Self->{LoadedFormDraftID} ) {
175        $LoadedFormDraft = $Kernel::OM->Get('Kernel::System::FormDraft')->FormDraftGet(
176            FormDraftID => $Self->{LoadedFormDraftID},
177            GetContent  => 0,
178            UserID      => $Self->{UserID},
179        );
180
181        my @Articles = $Kernel::OM->Get('Kernel::System::Ticket::Article')->ArticleList(
182            TicketID => $Self->{TicketID},
183            OnlyLast => 1,
184        );
185
186        if (@Articles) {
187            my $LastArticle = $Articles[0];
188
189            my $LastArticleSystemTime;
190            if ( $LastArticle->{CreateTime} ) {
191                my $LastArticleSystemTimeObject = $Kernel::OM->Create(
192                    'Kernel::System::DateTime',
193                    ObjectParams => {
194                        String => $LastArticle->{CreateTime},
195                    },
196                );
197                $LastArticleSystemTime = $LastArticleSystemTimeObject->ToEpoch();
198            }
199
200            my $FormDraftSystemTimeObject = $Kernel::OM->Create(
201                'Kernel::System::DateTime',
202                ObjectParams => {
203                    String => $LoadedFormDraft->{ChangeTime},
204                },
205            );
206            my $FormDraftSystemTime = $FormDraftSystemTimeObject->ToEpoch();
207
208            if ( !$LastArticleSystemTime || $FormDraftSystemTime <= $LastArticleSystemTime ) {
209                $Param{FormDraftOutdated} = 1;
210            }
211        }
212    }
213
214    if ( IsHashRefWithData($LoadedFormDraft) ) {
215
216        $LoadedFormDraft->{ChangeByName} = $Kernel::OM->Get('Kernel::System::User')->UserName(
217            UserID => $LoadedFormDraft->{ChangeBy},
218        );
219    }
220
221    $LayoutObject->Block(
222        Name => 'Properties',
223        Data => {
224            FormDraft      => $Config->{FormDraft},
225            FormDraftID    => $Self->{LoadedFormDraftID},
226            FormDraftTitle => $LoadedFormDraft ? $LoadedFormDraft->{Title} : '',
227            FormDraftMeta  => $LoadedFormDraft,
228            FormID         => $Self->{FormID},
229            ReplyToArticle => $Self->{ReplyToArticle},
230            %Ticket,
231            %Param,
232        },
233    );
234
235    # show right header
236    $LayoutObject->Block(
237        Name => 'Header' . $Self->{Action},
238        Data => {
239            %Ticket,
240        },
241    );
242
243    # get lock state
244    if ( $Config->{RequiredLock} ) {
245        if ( !$TicketObject->TicketLockGet( TicketID => $Self->{TicketID} ) ) {
246
247            my $Lock = $TicketObject->TicketLockSet(
248                TicketID => $Self->{TicketID},
249                Lock     => 'lock',
250                UserID   => $Self->{UserID}
251            );
252
253            if ($Lock) {
254
255                # Set new owner if ticket owner is different then logged user.
256                if ( $Ticket{OwnerID} != $Self->{UserID} ) {
257
258                    # Remember previous owner, which will be used to restore ticket owner on undo action.
259                    $Param{PreviousOwner} = $Ticket{OwnerID};
260
261                    $TicketObject->TicketOwnerSet(
262                        TicketID  => $Self->{TicketID},
263                        UserID    => $Self->{UserID},
264                        NewUserID => $Self->{UserID},
265                    );
266                }
267
268                # Show lock state.
269                $LayoutObject->Block(
270                    Name => 'PropertiesLock',
271                    Data => {
272                        %Param,
273                        TicketID => $Self->{TicketID},
274                    },
275                );
276            }
277        }
278        else {
279            my $AccessOk = $TicketObject->OwnerCheck(
280                TicketID => $Self->{TicketID},
281                OwnerID  => $Self->{UserID},
282            );
283            if ( !$AccessOk ) {
284                my $Output = $LayoutObject->Header(
285                    Type      => 'Small',
286                    Value     => $Ticket{Number},
287                    BodyClass => 'Popup',
288                );
289                $Output .= $LayoutObject->Warning(
290                    Message => Translatable('Sorry, you need to be the ticket owner to perform this action.'),
291                    Comment => Translatable('Please change the owner first.'),
292                );
293                $Output .= $LayoutObject->Footer(
294                    Type => 'Small',
295                );
296                return $Output;
297            }
298
299            # show back link
300            $LayoutObject->Block(
301                Name => 'TicketBack',
302                Data => {
303                    %Param,
304                    TicketID => $Self->{TicketID},
305                },
306            );
307        }
308    }
309    else {
310        $LayoutObject->Block(
311            Name => 'TicketBack',
312            Data => {
313                %Param,
314                %Ticket,
315            },
316        );
317    }
318
319    # get params
320    my %GetParam;
321    for my $Key (
322        qw(
323        NewStateID NewPriorityID TimeUnits IsVisibleForCustomer Title Body Subject NewQueueID
324        Year Month Day Hour Minute NewOwnerID NewResponsibleID TypeID ServiceID SLAID
325        Expand ReplyToArticle StandardTemplateID CreateArticle FormDraftID Title
326        )
327        )
328    {
329        $GetParam{$Key} = $ParamObject->GetParam( Param => $Key );
330    }
331
332    # ACL compatibility translation
333    my %ACLCompatGetParam = (
334        StateID       => $GetParam{NewStateID},
335        PriorityID    => $GetParam{NewPriorityID},
336        QueueID       => $GetParam{NewQueueID},
337        OwnerID       => $GetParam{NewOwnerID},
338        ResponsibleID => $GetParam{NewResponsibleID},
339    );
340
341    # get dynamic field values form http request
342    my %DynamicFieldValues;
343
344    # define the dynamic fields to show based on the object type
345    my $ObjectType = ['Ticket'];
346
347    # only screens that add notes can modify Article dynamic fields
348    if ( $Config->{Note} ) {
349        $ObjectType = [ 'Ticket', 'Article' ];
350    }
351
352    # get the dynamic fields for this screen
353    my $DynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
354        Valid       => 1,
355        ObjectType  => $ObjectType,
356        FieldFilter => $Config->{DynamicField} || {},
357    );
358
359    # get dynamic field backend object
360    my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
361
362    # cycle trough the activated Dynamic Fields for this screen
363    DYNAMICFIELD:
364    for my $DynamicFieldConfig ( @{$DynamicField} ) {
365        next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
366
367        # extract the dynamic field value from the web request
368        $DynamicFieldValues{ $DynamicFieldConfig->{Name} } = $DynamicFieldBackendObject->EditFieldValueGet(
369            DynamicFieldConfig => $DynamicFieldConfig,
370            ParamObject        => $ParamObject,
371            LayoutObject       => $LayoutObject,
372        );
373    }
374
375    # convert dynamic field values into a structure for ACLs
376    my %DynamicFieldACLParameters;
377    DYNAMICFIELD:
378    for my $DynamicFieldItem ( sort keys %DynamicFieldValues ) {
379        next DYNAMICFIELD if !$DynamicFieldItem;
380        next DYNAMICFIELD if !defined $DynamicFieldValues{$DynamicFieldItem};
381
382        $DynamicFieldACLParameters{ 'DynamicField_' . $DynamicFieldItem } = $DynamicFieldValues{$DynamicFieldItem};
383    }
384    $GetParam{DynamicField} = \%DynamicFieldACLParameters;
385
386    # transform pending time, time stamp based on user time zone
387    if (
388        defined $GetParam{Year}
389        && defined $GetParam{Month}
390        && defined $GetParam{Day}
391        && defined $GetParam{Hour}
392        && defined $GetParam{Minute}
393        )
394    {
395        %GetParam = $LayoutObject->TransformDateSelection(
396            %GetParam,
397        );
398    }
399
400    # rewrap body if no rich text is used
401    if ( $GetParam{Body} && !$LayoutObject->{BrowserRichText} ) {
402        $GetParam{Body} = $LayoutObject->WrapPlainText(
403            MaxCharacters => $ConfigObject->Get('Ticket::Frontend::TextAreaNote'),
404            PlainText     => $GetParam{Body},
405        );
406    }
407
408    # get upload cache object
409    my $UploadCacheObject = $Kernel::OM->Get('Kernel::System::Web::UploadCache');
410
411    if (
412        $Self->{Subaction} eq 'Store'
413        || $Self->{LoadedFormDraftID}
414        )
415    {
416
417        # challenge token check for write action
418        if ( $Self->{Subaction} eq 'Store' ) {
419            $LayoutObject->ChallengeTokenCheck();
420        }
421
422        $GetParam{IsVisibleForCustomer} //= 0;
423
424        # store action
425        my %Error;
426
427        # get all attachments meta data
428        my @Attachments = $UploadCacheObject->FormIDGetAllFilesMeta(
429            FormID => $Self->{FormID},
430        );
431
432        # Get and validate draft action.
433        my $FormDraftAction = $ParamObject->GetParam( Param => 'FormDraftAction' );
434        if ( $FormDraftAction && !$Config->{FormDraft} ) {
435            return $LayoutObject->ErrorScreen(
436                Message => Translatable('FormDraft functionality disabled!'),
437                Comment => Translatable('Please contact the administrator.'),
438            );
439        }
440
441        my %FormDraftResponse;
442
443        # Check draft name.
444        if (
445            $FormDraftAction
446            && ( $FormDraftAction eq 'Add' || $FormDraftAction eq 'Update' )
447            )
448        {
449            my $Title = $ParamObject->GetParam( Param => 'FormDraftTitle' );
450
451            # A draft name is required.
452            if ( !$Title ) {
453
454                %FormDraftResponse = (
455                    Success      => 0,
456                    ErrorMessage => $Kernel::OM->Get('Kernel::Language')->Translate("Draft name is required!"),
457                );
458            }
459
460            # Chosen draft name must be unique.
461            else {
462                my $FormDraftList = $Kernel::OM->Get('Kernel::System::FormDraft')->FormDraftListGet(
463                    ObjectType => 'Ticket',
464                    ObjectID   => $Self->{TicketID},
465                    Action     => $Self->{Action},
466                    UserID     => $Self->{UserID},
467                );
468                DRAFT:
469                for my $FormDraft ( @{$FormDraftList} ) {
470
471                    # No existing draft with same name.
472                    next DRAFT if $Title ne $FormDraft->{Title};
473
474                    # Same name for update on existing draft.
475                    if (
476                        $GetParam{FormDraftID}
477                        && $FormDraftAction eq 'Update'
478                        && $GetParam{FormDraftID} eq $FormDraft->{FormDraftID}
479                        )
480                    {
481                        next DRAFT;
482                    }
483
484                    # Another draft with the chosen name already exists.
485                    %FormDraftResponse = (
486                        Success      => 0,
487                        ErrorMessage => $Kernel::OM->Get('Kernel::Language')
488                            ->Translate( "FormDraft name %s is already in use!", $Title ),
489                    );
490                    last DRAFT;
491                }
492            }
493        }
494
495        # Perform draft action instead of saving form data in ticket/article.
496        if ( $FormDraftAction && !%FormDraftResponse ) {
497
498            # Reset FormDraftID to prevent updating existing draft.
499            if ( $FormDraftAction eq 'Add' && $GetParam{FormDraftID} ) {
500                $ParamObject->{Query}->param(
501                    -name  => 'FormDraftID',
502                    -value => '',
503                );
504            }
505
506            my $FormDraftActionOk;
507            if (
508                $FormDraftAction eq 'Add'
509                ||
510                ( $FormDraftAction eq 'Update' && $GetParam{FormDraftID} )
511                )
512            {
513                $FormDraftActionOk = $ParamObject->SaveFormDraft(
514                    UserID         => $Self->{UserID},
515                    ObjectType     => 'Ticket',
516                    ObjectID       => $Self->{TicketID},
517                    OverrideParams => {
518                        ReplyToArticle => undef,
519                    },
520                );
521            }
522
523            if ($FormDraftActionOk) {
524                $FormDraftResponse{Success} = 1;
525            }
526            else {
527                %FormDraftResponse = (
528                    Success      => 0,
529                    ErrorMessage => 'Could not perform requested draft action!',
530                );
531            }
532        }
533
534        if (%FormDraftResponse) {
535
536            # build JSON output
537            my $JSON = $LayoutObject->JSONEncode(
538                Data => \%FormDraftResponse,
539            );
540
541            # send JSON response
542            return $LayoutObject->Attachment(
543                ContentType => 'application/json; charset=' . $LayoutObject->{Charset},
544                Content     => $JSON,
545                Type        => 'inline',
546                NoCache     => 1,
547            );
548        }
549
550        # get state object
551        my $StateObject = $Kernel::OM->Get('Kernel::System::State');
552
553        # check pending time
554        if ( $GetParam{NewStateID} ) {
555            my %StateData = $StateObject->StateGet(
556                ID => $GetParam{NewStateID},
557            );
558
559            # check state type
560            if ( $StateData{TypeName} =~ /^pending/i ) {
561
562                # check needed stuff
563                for my $Needed (qw(Year Month Day Hour Minute)) {
564                    if ( !defined $GetParam{$Needed} ) {
565                        $Error{'DateInvalid'} = 'ServerError';
566                    }
567                }
568
569                # create datetime object
570                my $PendingDateTimeObject = $Kernel::OM->Create(
571                    'Kernel::System::DateTime',
572                    ObjectParams => {
573                        %GetParam,
574                        Second => 0,
575                    },
576                );
577
578                # get current system epoch
579                my $CurSystemDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
580
581                # check date
582                if (
583                    !$PendingDateTimeObject
584                    || $PendingDateTimeObject < $CurSystemDateTimeObject
585                    )
586                {
587                    $Error{'DateInvalid'} = 'ServerError';
588                }
589            }
590        }
591
592        if ( $Config->{Note} && $Config->{NoteMandatory} ) {
593
594            # check subject
595            if ( !$GetParam{Subject} ) {
596                $Error{'SubjectInvalid'} = 'ServerError';
597            }
598
599            # check body
600            if ( !$GetParam{Body} ) {
601                $Error{'BodyInvalid'} = 'ServerError';
602            }
603        }
604
605        # check owner
606        if ( $Config->{Owner} && $Config->{OwnerMandatory} ) {
607            if ( !$GetParam{NewOwnerID} ) {
608                $Error{'NewOwnerInvalid'} = 'ServerError';
609            }
610        }
611
612        # check responsible
613        if ( $Config->{Responsible} && $Config->{ResponsibleMandatory} ) {
614            if ( !$GetParam{NewResponsibleID} ) {
615                $Error{'NewResponsibleInvalid'} = 'ServerError';
616            }
617        }
618
619        # check title
620        if ( $Config->{Title} && !$GetParam{Title} ) {
621            $Error{'TitleInvalid'} = 'ServerError';
622        }
623
624        # check type
625        if (
626            ( $ConfigObject->Get('Ticket::Type') )
627            &&
628            ( $Config->{TicketType} ) &&
629            ( !$GetParam{TypeID} )
630            )
631        {
632            $Error{'TypeIDInvalid'} = ' ServerError';
633        }
634
635        # check service
636        if (
637            $ConfigObject->Get('Ticket::Service')
638            && $Config->{Service}
639            && $GetParam{SLAID}
640            && !$GetParam{ServiceID}
641            )
642        {
643            $Error{'ServiceInvalid'} = ' ServerError';
644        }
645
646        # check mandatory service
647        if (
648            $ConfigObject->Get('Ticket::Service')
649            && $Config->{Service}
650            && $Config->{ServiceMandatory}
651            && !$GetParam{ServiceID}
652            )
653        {
654            $Error{'ServiceInvalid'} = ' ServerError';
655        }
656
657        # check mandatory sla
658        if (
659            $ConfigObject->Get('Ticket::Service')
660            && $Config->{Service}
661            && $Config->{SLAMandatory}
662            && !$GetParam{SLAID}
663            )
664        {
665            $Error{'SLAInvalid'} = ' ServerError';
666        }
667
668        # check mandatory queue
669        if ( $Config->{Queue} && $Config->{QueueMandatory} ) {
670            if ( !$GetParam{NewQueueID} ) {
671                $Error{'NewQueueInvalid'} = 'ServerError';
672            }
673        }
674
675        # check mandatory state
676        if ( $Config->{State} && $Config->{StateMandatory} ) {
677            if ( !$GetParam{NewStateID} ) {
678                $Error{'NewStateInvalid'} = 'ServerError';
679            }
680        }
681
682        # check time units, but only if the current screen has a note
683        #   (accounted time can only be stored if and article is generated)
684        if (
685            $ConfigObject->Get('Ticket::Frontend::NeedAccountedTime')
686            && $Config->{Note}
687            && $GetParam{TimeUnits} eq ''
688            )
689        {
690            $Error{'TimeUnitsInvalid'} = ' ServerError';
691        }
692
693        # check expand
694        if ( $GetParam{Expand} ) {
695            %Error = ();
696            $Error{Expand} = 1;
697        }
698
699        # create html strings for all dynamic fields
700        my @TicketTypeDynamicFields;
701        my @ArticleTypeDynamicFields;
702
703        # cycle trough the activated Dynamic Fields for this screen
704        DYNAMICFIELD:
705        for my $DynamicFieldConfig ( @{$DynamicField} ) {
706            next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
707
708            my $PossibleValuesFilter;
709
710            my $IsACLReducible = $DynamicFieldBackendObject->HasBehavior(
711                DynamicFieldConfig => $DynamicFieldConfig,
712                Behavior           => 'IsACLReducible',
713            );
714
715            if ($IsACLReducible) {
716
717                # get PossibleValues
718                my $PossibleValues = $DynamicFieldBackendObject->PossibleValuesGet(
719                    DynamicFieldConfig => $DynamicFieldConfig,
720                );
721
722                # check if field has PossibleValues property in its configuration
723                if ( IsHashRefWithData($PossibleValues) ) {
724
725                    # convert possible values key => value to key => key for ACLs using a Hash slice
726                    my %AclData = %{$PossibleValues};
727                    @AclData{ keys %AclData } = keys %AclData;
728
729                    # set possible values filter from ACLs
730                    my $ACL = $TicketObject->TicketAcl(
731                        %GetParam,
732                        Action        => $Self->{Action},
733                        TicketID      => $Self->{TicketID},
734                        ReturnType    => 'Ticket',
735                        ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name},
736                        Data          => \%AclData,
737                        UserID        => $Self->{UserID},
738                    );
739                    if ($ACL) {
740                        my %Filter = $TicketObject->TicketAclData();
741
742                        # convert Filer key => key back to key => value using map
743                        %{$PossibleValuesFilter} = map { $_ => $PossibleValues->{$_} }
744                            keys %Filter;
745                    }
746                }
747            }
748
749            my $ValidationResult;
750
751            # Do not validate only if object type is Article and CreateArticle value is not defined.
752            if ( !( $DynamicFieldConfig->{ObjectType} eq 'Article' && !$GetParam{CreateArticle} ) ) {
753
754                $ValidationResult = $DynamicFieldBackendObject->EditFieldValueValidate(
755                    DynamicFieldConfig   => $DynamicFieldConfig,
756                    PossibleValuesFilter => $PossibleValuesFilter,
757                    ParamObject          => $ParamObject,
758                    Mandatory =>
759                        $Config->{DynamicField}->{ $DynamicFieldConfig->{Name} } == 2,
760                );
761
762                if ( !IsHashRefWithData($ValidationResult) ) {
763                    return $LayoutObject->ErrorScreen(
764                        Message =>
765                            $LayoutObject->{LanguageObject}->Translate(
766                            'Could not perform validation on field %s!', $DynamicFieldConfig->{Label}
767                            ),
768                        Comment => Translatable('Please contact the administrator.'),
769                    );
770                }
771
772                # Propagate validation error to the Error variable to be detected by the frontend.
773                if ( $ValidationResult->{ServerError} )
774                {
775                    $Error{ $DynamicFieldConfig->{Name} } = ' ServerError';
776                }
777            }
778
779            if ( $DynamicFieldConfig->{ObjectType} eq 'Ticket' ) {
780
781                # Get field html.
782                my $DynamicFieldHTML = $DynamicFieldBackendObject->EditFieldRender(
783                    DynamicFieldConfig   => $DynamicFieldConfig,
784                    PossibleValuesFilter => $PossibleValuesFilter,
785                    ServerError          => $ValidationResult->{ServerError} || '',
786                    ErrorMessage         => $ValidationResult->{ErrorMessage} || '',
787                    Mandatory            => $Config->{DynamicField}->{ $DynamicFieldConfig->{Name} } == 2,
788                    LayoutObject         => $LayoutObject,
789                    ParamObject          => $ParamObject,
790                    AJAXUpdate           => 1,
791                    UpdatableFields      => $Self->_GetFieldsToUpdate(),
792                );
793
794                push @TicketTypeDynamicFields, {
795                    Name  => $DynamicFieldConfig->{Name},
796                    Label => $DynamicFieldHTML->{Label},
797                    Field => $DynamicFieldHTML->{Field},
798                };
799            }
800            elsif ( $DynamicFieldConfig->{ObjectType} eq 'Article' ) {
801                my $Class            = '';
802                my $MandatoryTooltip = 0;
803
804                if ( $Config->{DynamicField}->{ $DynamicFieldConfig->{Name} } == 2 ) {
805                    if (
806                        $Config->{NoteMandatory} ||
807                        $ConfigObject->Get('Ticket::Frontend::NeedAccountedTime')
808                        )
809                    {
810                        $Class = 'Validate_Required';
811                    }
812                    else {
813                        $Class            = 'Validate_DependingRequiredAND Validate_Depending_CreateArticle';
814                        $MandatoryTooltip = 1;
815                    }
816                }
817
818                # Get field html.
819                my $DynamicFieldHTML = $DynamicFieldBackendObject->EditFieldRender(
820                    DynamicFieldConfig   => $DynamicFieldConfig,
821                    PossibleValuesFilter => $PossibleValuesFilter,
822                    ServerError          => $ValidationResult->{ServerError} || '',
823                    ErrorMessage         => $ValidationResult->{ErrorMessage} || '',
824                    Mandatory            => ( $Class eq 'Validate_Required' ) ? 1 : 0,
825                    Class                => $Class,
826                    LayoutObject         => $LayoutObject,
827                    ParamObject          => $ParamObject,
828                    AJAXUpdate           => 1,
829                    UpdatableFields      => $Self->_GetFieldsToUpdate(),
830                );
831
832                push @ArticleTypeDynamicFields, {
833                    Name             => $DynamicFieldConfig->{Name},
834                    Label            => $DynamicFieldHTML->{Label},
835                    Field            => $DynamicFieldHTML->{Field},
836                    MandatoryTooltip => $MandatoryTooltip,
837                };
838            }
839        }
840
841        # Make sure we don't save form if a draft was loaded.
842        if ( $Self->{LoadedFormDraftID} ) {
843            %Error = ( LoadedFormDraft => 1 );
844        }
845
846        # check errors
847        if (%Error) {
848
849            my $Output = $LayoutObject->Header(
850                Type      => 'Small',
851                Value     => $Ticket{TicketNumber},
852                BodyClass => 'Popup',
853            );
854            $Output .= $Self->_Mask(
855                Attachments       => \@Attachments,
856                TimeUnitsRequired => (
857                    $ConfigObject->Get('Ticket::Frontend::NeedAccountedTime')
858                    ? 'Validate_Required'
859                    : ''
860                ),
861                %Ticket,
862                TicketTypeDynamicFields  => \@TicketTypeDynamicFields,
863                ArticleTypeDynamicFields => \@ArticleTypeDynamicFields,
864
865                %GetParam,
866                %Error,
867            );
868            $Output .= $LayoutObject->Footer(
869                Type => 'Small',
870            );
871            return $Output;
872        }
873
874        # set new title
875        if ( $Config->{Title} ) {
876            if ( defined $GetParam{Title} ) {
877                $TicketObject->TicketTitleUpdate(
878                    Title    => $GetParam{Title},
879                    TicketID => $Self->{TicketID},
880                    UserID   => $Self->{UserID},
881                );
882            }
883        }
884
885        # set new type
886        if ( $ConfigObject->Get('Ticket::Type') && $Config->{TicketType} ) {
887            if ( $GetParam{TypeID} ) {
888                $TicketObject->TicketTypeSet(
889                    Action   => $Self->{Action},
890                    TypeID   => $GetParam{TypeID},
891                    TicketID => $Self->{TicketID},
892                    UserID   => $Self->{UserID},
893                );
894            }
895        }
896
897        # set new service
898        if ( $ConfigObject->Get('Ticket::Service') && $Config->{Service} ) {
899            if ( defined $GetParam{ServiceID} ) {
900                $TicketObject->TicketServiceSet(
901                    %GetParam,
902                    %ACLCompatGetParam,
903                    Action         => $Self->{Action},
904                    ServiceID      => $GetParam{ServiceID},
905                    TicketID       => $Self->{TicketID},
906                    CustomerUserID => $Ticket{CustomerUserID},
907                    UserID         => $Self->{UserID},
908                );
909            }
910            if ( defined $GetParam{SLAID} ) {
911                $TicketObject->TicketSLASet(
912                    Action   => $Self->{Action},
913                    SLAID    => $GetParam{SLAID},
914                    TicketID => $Self->{TicketID},
915                    UserID   => $Self->{UserID},
916                );
917            }
918        }
919
920        my $UnlockOnAway = 1;
921
922        # move ticket to a new queue, but only if the queue was changed
923        if (
924            $Config->{Queue}
925            && $GetParam{NewQueueID}
926            && $GetParam{NewQueueID} ne $Ticket{QueueID}
927            )
928        {
929
930            # move ticket (send notification if no new owner is selected)
931            my $BodyAsText = '';
932            if ( $LayoutObject->{BrowserRichText} ) {
933                $BodyAsText = $LayoutObject->RichText2Ascii(
934                    String => $GetParam{Body} || 0,
935                );
936            }
937            else {
938                $BodyAsText = $GetParam{Body} || 0;
939            }
940            my $Move = $TicketObject->TicketQueueSet(
941                QueueID            => $GetParam{NewQueueID},
942                UserID             => $Self->{UserID},
943                TicketID           => $Self->{TicketID},
944                SendNoNotification => $GetParam{NewUserID},
945                Comment            => $BodyAsText,
946                Action             => $Self->{Action},
947            );
948            if ( !$Move ) {
949                return $LayoutObject->ErrorScreen();
950            }
951        }
952
953        # set new owner
954        my @NotifyDone;
955        if ( $Config->{Owner} ) {
956            my $BodyText = $LayoutObject->RichText2Ascii(
957                String => $GetParam{Body} || '',
958            );
959            if ( $GetParam{NewOwnerID} ) {
960                $TicketObject->TicketLockSet(
961                    TicketID => $Self->{TicketID},
962                    Lock     => 'lock',
963                    UserID   => $Self->{UserID},
964                );
965                my $Success = $TicketObject->TicketOwnerSet(
966                    TicketID  => $Self->{TicketID},
967                    UserID    => $Self->{UserID},
968                    NewUserID => $GetParam{NewOwnerID},
969                    Comment   => $BodyText,
970                );
971                $UnlockOnAway = 0;
972
973                # remember to not notify owner twice
974                if ( $Success && $Success eq 1 ) {
975                    push @NotifyDone, $GetParam{NewOwnerID};
976                }
977            }
978        }
979
980        # set new responsible
981        if ( $ConfigObject->Get('Ticket::Responsible') && $Config->{Responsible} ) {
982            if ( $GetParam{NewResponsibleID} ) {
983                my $BodyText = $LayoutObject->RichText2Ascii(
984                    String => $GetParam{Body} || '',
985                );
986                my $Success = $TicketObject->TicketResponsibleSet(
987                    TicketID  => $Self->{TicketID},
988                    UserID    => $Self->{UserID},
989                    NewUserID => $GetParam{NewResponsibleID},
990                    Comment   => $BodyText,
991                );
992
993                # remember to not notify responsible twice
994                if ( $Success && $Success eq 1 ) {
995                    push @NotifyDone, $GetParam{NewResponsibleID};
996                }
997            }
998        }
999
1000        # add note
1001        my $ArticleID = '';
1002        my $ReturnURL;
1003
1004        # set priority
1005        if ( $Config->{Priority} && $GetParam{NewPriorityID} ) {
1006            $TicketObject->TicketPrioritySet(
1007                TicketID   => $Self->{TicketID},
1008                PriorityID => $GetParam{NewPriorityID},
1009                UserID     => $Self->{UserID},
1010            );
1011        }
1012
1013        # set state
1014        if ( $Config->{State} && $GetParam{NewStateID} ) {
1015            $TicketObject->TicketStateSet(
1016                TicketID     => $Self->{TicketID},
1017                StateID      => $GetParam{NewStateID},
1018                UserID       => $Self->{UserID},
1019                DynamicField => $GetParam{DynamicField},
1020            );
1021
1022            # unlock the ticket after close
1023            my %StateData = $StateObject->StateGet(
1024                ID => $GetParam{NewStateID},
1025            );
1026
1027            # set unlock on close state
1028            if ( $StateData{TypeName} =~ /^close/i ) {
1029                $TicketObject->TicketLockSet(
1030                    TicketID => $Self->{TicketID},
1031                    Lock     => 'unlock',
1032                    UserID   => $Self->{UserID},
1033                );
1034            }
1035
1036            # set pending time on pending state
1037            elsif ( $StateData{TypeName} =~ /^pending/i ) {
1038
1039                # set pending time
1040                $TicketObject->TicketPendingTimeSet(
1041                    UserID   => $Self->{UserID},
1042                    TicketID => $Self->{TicketID},
1043                    %GetParam,
1044                );
1045            }
1046
1047            # redirect parent window to last screen overview on closed tickets
1048            if (
1049                $StateData{TypeName} =~ /^close/i
1050                && !$ConfigObject->Get('Ticket::Frontend::RedirectAfterCloseDisabled')
1051                )
1052            {
1053                $ReturnURL = $Self->{LastScreenOverview} || 'Action=AgentDashboard';
1054            }
1055        }
1056
1057        if (
1058            $GetParam{CreateArticle}
1059            && $Config->{Note}
1060            && ( $GetParam{Subject} || $GetParam{Body} )
1061            )
1062        {
1063
1064            if ( !$GetParam{Subject} ) {
1065                if ( $Config->{Subject} ) {
1066                    my $Subject = $LayoutObject->Output(
1067                        Template => $Config->{Subject},
1068                    );
1069                    $GetParam{Subject} = $Subject;
1070                }
1071                $GetParam{Subject} = $GetParam{Subject}
1072                    || $LayoutObject->{LanguageObject}->Translate('No subject');
1073            }
1074
1075            # get pre loaded attachment
1076            my @Attachments = $UploadCacheObject->FormIDGetAllFilesData(
1077                FormID => $Self->{FormID},
1078            );
1079
1080            # get submit attachment
1081            my %UploadStuff = $ParamObject->GetUploadAll(
1082                Param => 'FileUpload',
1083            );
1084            if (%UploadStuff) {
1085                push @Attachments, \%UploadStuff;
1086            }
1087
1088            my $MimeType = 'text/plain';
1089            if ( $LayoutObject->{BrowserRichText} ) {
1090                $MimeType = 'text/html';
1091
1092                # remove unused inline images
1093                my @NewAttachmentData;
1094                ATTACHMENT:
1095                for my $Attachment (@Attachments) {
1096                    my $ContentID = $Attachment->{ContentID};
1097                    if (
1098                        $ContentID
1099                        && ( $Attachment->{ContentType} =~ /image/i )
1100                        && ( $Attachment->{Disposition} eq 'inline' )
1101                        )
1102                    {
1103                        my $ContentIDHTMLQuote = $LayoutObject->Ascii2Html(
1104                            Text => $ContentID,
1105                        );
1106
1107                        # workaround for link encode of rich text editor, see bug#5053
1108                        my $ContentIDLinkEncode = $LayoutObject->LinkEncode($ContentID);
1109                        $GetParam{Body} =~ s/(ContentID=)$ContentIDLinkEncode/$1$ContentID/g;
1110
1111                        # ignore attachment if not linked in body
1112                        next ATTACHMENT
1113                            if $GetParam{Body} !~ /(\Q$ContentIDHTMLQuote\E|\Q$ContentID\E)/i;
1114                    }
1115
1116                    # remember inline images and normal attachments
1117                    push @NewAttachmentData, \%{$Attachment};
1118                }
1119                @Attachments = @NewAttachmentData;
1120
1121                # verify html document
1122                $GetParam{Body} = $LayoutObject->RichTextDocumentComplete(
1123                    String => $GetParam{Body},
1124                );
1125            }
1126
1127            my $From = "\"$Self->{UserFullname}\" <$Self->{UserEmail}>";
1128            my @NotifyUserIDs;
1129
1130            # get list of users that will be informed without selection in informed/involved list
1131            my @UserListWithoutSelection
1132                = split( ',', $ParamObject->GetParam( Param => 'UserListWithoutSelection' ) || "" );
1133
1134            # get inform user list
1135            my @InformUserID = $ParamObject->GetArray( Param => 'InformUserID' );
1136
1137            # get involved user list
1138            my @InvolvedUserID = $ParamObject->GetArray( Param => 'InvolvedUserID' );
1139
1140            if ( $Config->{InformAgent} ) {
1141                push @NotifyUserIDs, @InformUserID;
1142            }
1143
1144            if ( $Config->{InvolvedAgent} ) {
1145                push @NotifyUserIDs, @InvolvedUserID;
1146            }
1147
1148            if ( $Self->{ReplyToArticle} ) {
1149                push @NotifyUserIDs, @UserListWithoutSelection;
1150            }
1151
1152            if ( $Self->{Action} eq 'AgentTicketEmailOutbound' ) {
1153                $ArticleID = $Kernel::OM->Get('Kernel::System::Ticket::Article::Backend::Email')->ArticleSend(
1154                    TicketID                        => $Self->{TicketID},
1155                    SenderType                      => 'agent',
1156                    From                            => $From,
1157                    MimeType                        => $MimeType,
1158                    Charset                         => $LayoutObject->{UserCharset},
1159                    UserID                          => $Self->{UserID},
1160                    HistoryType                     => $Config->{HistoryType},
1161                    HistoryComment                  => $Config->{HistoryComment},
1162                    ForceNotificationToUserID       => \@NotifyUserIDs,
1163                    ExcludeMuteNotificationToUserID => \@NotifyDone,
1164                    UnlockOnAway                    => $UnlockOnAway,
1165                    Attachment                      => \@Attachments,
1166                    %GetParam,
1167                );
1168            }
1169            else {
1170                $ArticleID = $Kernel::OM->Get('Kernel::System::Ticket::Article::Backend::Internal')->ArticleCreate(
1171                    TicketID                        => $Self->{TicketID},
1172                    SenderType                      => 'agent',
1173                    From                            => $From,
1174                    MimeType                        => $MimeType,
1175                    Charset                         => $LayoutObject->{UserCharset},
1176                    UserID                          => $Self->{UserID},
1177                    HistoryType                     => $Config->{HistoryType},
1178                    HistoryComment                  => $Config->{HistoryComment},
1179                    ForceNotificationToUserID       => \@NotifyUserIDs,
1180                    ExcludeMuteNotificationToUserID => \@NotifyDone,
1181                    UnlockOnAway                    => $UnlockOnAway,
1182                    Attachment                      => \@Attachments,
1183                    %GetParam,
1184                );
1185            }
1186
1187            if ( !$ArticleID ) {
1188                return $LayoutObject->ErrorScreen();
1189            }
1190
1191            # time accounting
1192            if ( $GetParam{TimeUnits} ) {
1193                $TicketObject->TicketAccountTime(
1194                    TicketID  => $Self->{TicketID},
1195                    ArticleID => $ArticleID,
1196                    TimeUnit  => $GetParam{TimeUnits},
1197                    UserID    => $Self->{UserID},
1198                );
1199            }
1200
1201            # remove pre submitted attachments
1202            $UploadCacheObject->FormIDRemove( FormID => $Self->{FormID} );
1203        }
1204
1205        # set dynamic fields
1206        # cycle through the activated Dynamic Fields for this screen
1207        DYNAMICFIELD:
1208        for my $DynamicFieldConfig ( @{$DynamicField} ) {
1209            next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
1210
1211            # set the object ID (TicketID or ArticleID) depending on the field configration
1212            my $ObjectID = $DynamicFieldConfig->{ObjectType} eq 'Article' ? $ArticleID : $Self->{TicketID};
1213
1214            # set the value
1215            my $Success = $DynamicFieldBackendObject->ValueSet(
1216                DynamicFieldConfig => $DynamicFieldConfig,
1217                ObjectID           => $ObjectID,
1218                Value              => $DynamicFieldValues{ $DynamicFieldConfig->{Name} },
1219                UserID             => $Self->{UserID},
1220            );
1221        }
1222
1223        # If form was called based on a draft,
1224        #   delete draft since its content has now been used.
1225        if (
1226            $GetParam{FormDraftID}
1227            && !$Kernel::OM->Get('Kernel::System::FormDraft')->FormDraftDelete(
1228                FormDraftID => $GetParam{FormDraftID},
1229                UserID      => $Self->{UserID},
1230            )
1231            )
1232        {
1233            return $LayoutObject->ErrorScreen(
1234                Message => Translatable('Could not delete draft!'),
1235                Comment => Translatable('Please contact the administrator.'),
1236            );
1237        }
1238
1239        # load new URL in parent window and close popup
1240        $ReturnURL ||= "Action=AgentTicketZoom;TicketID=$Self->{TicketID};ArticleID=$ArticleID";
1241
1242        return $LayoutObject->PopupClose(
1243            URL => $ReturnURL,
1244        );
1245    }
1246    elsif ( $Self->{Subaction} eq 'AJAXUpdate' ) {
1247        my %Ticket         = $TicketObject->TicketGet( TicketID => $Self->{TicketID} );
1248        my $CustomerUser   = $Ticket{CustomerUserID};
1249        my $ElementChanged = $ParamObject->GetParam( Param => 'ElementChanged' ) || '';
1250
1251        my $ServiceID;
1252
1253        # get service value from param if field is visible in the screen
1254        if ( $ConfigObject->Get('Ticket::Service') && $Config->{Service} ) {
1255            $ServiceID = $GetParam{ServiceID} || '';
1256        }
1257
1258        # otherwise use ticket service value since it can't be changed
1259        elsif ( $ConfigObject->Get('Ticket::Service') ) {
1260            $ServiceID = $Ticket{ServiceID} || '';
1261        }
1262
1263        my $QueueID = $GetParam{NewQueueID} || $Ticket{QueueID};
1264        my $StateID = $GetParam{NewStateID} || $Ticket{StateID};
1265
1266        # get list type
1267        my $TreeView = 0;
1268        if ( $ConfigObject->Get('Ticket::Frontend::ListType') eq 'tree' ) {
1269            $TreeView = 1;
1270        }
1271
1272        my $Owners = $Self->_GetOwners(
1273            %GetParam,
1274            QueueID  => $QueueID,
1275            StateID  => $StateID,
1276            AllUsers => $GetParam{OwnerAll},
1277        );
1278        my $OldOwners = $Self->_GetOldOwners(
1279            %GetParam,
1280            QueueID  => $QueueID,
1281            StateID  => $StateID,
1282            AllUsers => $GetParam{OwnerAll},
1283        );
1284        my $ResponsibleUsers = $Self->_GetResponsible(
1285            %GetParam,
1286            QueueID  => $QueueID,
1287            StateID  => $StateID,
1288            AllUsers => $GetParam{OwnerAll},
1289        );
1290        my $Priorities = $Self->_GetPriorities(
1291            %GetParam,
1292        );
1293        my $Services = $Self->_GetServices(
1294            %GetParam,
1295            CustomerUserID => $CustomerUser,
1296            QueueID        => $QueueID,
1297            StateID        => $StateID,
1298        );
1299        my $Types = $Self->_GetTypes(
1300            %GetParam,
1301            CustomerUserID => $CustomerUser,
1302            QueueID        => $QueueID,
1303            StateID        => $StateID,
1304        );
1305        my $NewQueues = $Self->_GetQueues(
1306            %GetParam,
1307        );
1308
1309        # reset previous ServiceID to reset SLA-List if no service is selected
1310        if ( !defined $ServiceID || !$Services->{$ServiceID} ) {
1311            $ServiceID = '';
1312        }
1313        my $SLAs = $Self->_GetSLAs(
1314            %GetParam,
1315            CustomerUserID => $CustomerUser,
1316            QueueID        => $QueueID,
1317            StateID        => $StateID,
1318            ServiceID      => $ServiceID,
1319        );
1320        my $NextStates = $Self->_GetNextStates(
1321            %GetParam,
1322            CustomerUserID => $CustomerUser || '',
1323            QueueID        => $QueueID,
1324            StateID        => $StateID,
1325        );
1326
1327        # update Dynamic Fields Possible Values via AJAX
1328        my @DynamicFieldAJAX;
1329
1330        # cycle trough the activated Dynamic Fields for this screen
1331        DYNAMICFIELD:
1332        for my $DynamicFieldConfig ( @{$DynamicField} ) {
1333            next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
1334
1335            my $IsACLReducible = $DynamicFieldBackendObject->HasBehavior(
1336                DynamicFieldConfig => $DynamicFieldConfig,
1337                Behavior           => 'IsACLReducible',
1338            );
1339            next DYNAMICFIELD if !$IsACLReducible;
1340
1341            my $PossibleValues = $DynamicFieldBackendObject->PossibleValuesGet(
1342                DynamicFieldConfig => $DynamicFieldConfig,
1343            );
1344
1345            # convert possible values key => value to key => key for ACLs using a Hash slice
1346            my %AclData = %{$PossibleValues};
1347            @AclData{ keys %AclData } = keys %AclData;
1348
1349            # set possible values filter from ACLs
1350            my $ACL = $TicketObject->TicketAcl(
1351                %GetParam,
1352                Action        => $Self->{Action},
1353                TicketID      => $Self->{TicketID},
1354                QueueID       => $QueueID,
1355                ReturnType    => 'Ticket',
1356                ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name},
1357                Data          => \%AclData,
1358                UserID        => $Self->{UserID},
1359            );
1360            if ($ACL) {
1361                my %Filter = $TicketObject->TicketAclData();
1362
1363                # convert Filer key => key back to key => value using map
1364                %{$PossibleValues} = map { $_ => $PossibleValues->{$_} } keys %Filter;
1365            }
1366
1367            my $DataValues = $DynamicFieldBackendObject->BuildSelectionDataGet(
1368                DynamicFieldConfig => $DynamicFieldConfig,
1369                PossibleValues     => $PossibleValues,
1370                Value              => $DynamicFieldValues{ $DynamicFieldConfig->{Name} },
1371            ) || $PossibleValues;
1372
1373            # add dynamic field to the list of fields to update
1374            push(
1375                @DynamicFieldAJAX,
1376                {
1377                    Name        => 'DynamicField_' . $DynamicFieldConfig->{Name},
1378                    Data        => $DataValues,
1379                    SelectedID  => $DynamicFieldValues{ $DynamicFieldConfig->{Name} },
1380                    Translation => $DynamicFieldConfig->{Config}->{TranslatableValues} || 0,
1381                    Max         => 100,
1382                }
1383            );
1384        }
1385
1386        my $StandardTemplates = $Self->_GetStandardTemplates(
1387            %GetParam,
1388            QueueID => $QueueID || '',
1389        );
1390
1391        my @TemplateAJAX;
1392
1393        # update ticket body and attachements if needed.
1394        if ( $ElementChanged eq 'StandardTemplateID' ) {
1395            my @TicketAttachments;
1396            my $TemplateText;
1397
1398            # remove all attachments from the Upload cache
1399            my $RemoveSuccess = $UploadCacheObject->FormIDRemove(
1400                FormID => $Self->{FormID},
1401            );
1402            if ( !$RemoveSuccess ) {
1403                $Kernel::OM->Get('Kernel::System::Log')->Log(
1404                    Priority => 'error',
1405                    Message  => "Form attachments could not be deleted!",
1406                );
1407            }
1408
1409            # get the template text and set new attachments if a template is selected
1410            if ( IsPositiveInteger( $GetParam{StandardTemplateID} ) ) {
1411                my $TemplateGenerator = $Kernel::OM->Get('Kernel::System::TemplateGenerator');
1412
1413                # set template text, replace smart tags (limited as ticket is not created)
1414                $TemplateText = $TemplateGenerator->Template(
1415                    TemplateID => $GetParam{StandardTemplateID},
1416                    TicketID   => $Self->{TicketID},
1417                    UserID     => $Self->{UserID},
1418                );
1419
1420                # if ReplyToArticle is given, get this article to generate
1421                # the quoted article content
1422                if ( $Self->{ReplyToArticle} ) {
1423
1424                    # get article to quote
1425                    my $Body = $LayoutObject->ArticleQuote(
1426                        TicketID          => $Self->{TicketID},
1427                        ArticleID         => $Self->{ReplyToArticle},
1428                        FormID            => $Self->{FormID},
1429                        UploadCacheObject => $UploadCacheObject,
1430                    );
1431
1432                    # prepare quoted body content
1433                    $Body = $Self->_GetQuotedReplyBody(
1434                        %{ $Self->{ReplyToArticleContent} },
1435                        Body => $Body,
1436                    );
1437
1438                    if ( $LayoutObject->{BrowserRichText} ) {
1439                        $TemplateText = $TemplateText . '<br><br>' . $Body;
1440                    }
1441                    else {
1442                        $TemplateText = $TemplateText . "\n\n" . $Body;
1443                    }
1444                }
1445
1446                # create StdAttachmentObject
1447                my $StdAttachmentObject = $Kernel::OM->Get('Kernel::System::StdAttachment');
1448
1449                # add std. attachments to ticket
1450                my %AllStdAttachments = $StdAttachmentObject->StdAttachmentStandardTemplateMemberList(
1451                    StandardTemplateID => $GetParam{StandardTemplateID},
1452                );
1453                for ( sort keys %AllStdAttachments ) {
1454                    my %AttachmentsData = $StdAttachmentObject->StdAttachmentGet( ID => $_ );
1455                    $UploadCacheObject->FormIDAddFile(
1456                        FormID      => $Self->{FormID},
1457                        Disposition => 'attachment',
1458                        %AttachmentsData,
1459                    );
1460                }
1461
1462                # send a list of attachments in the upload cache back to the clientside JavaScript
1463                # which renders then the list of currently uploaded attachments
1464                @TicketAttachments = $UploadCacheObject->FormIDGetAllFilesMeta(
1465                    FormID => $Self->{FormID},
1466                );
1467
1468                for my $Attachment (@TicketAttachments) {
1469                    $Attachment->{Filesize} = $LayoutObject->HumanReadableDataSize(
1470                        Size => $Attachment->{Filesize},
1471                    );
1472                }
1473            }
1474
1475            @TemplateAJAX = (
1476                {
1477                    Name => 'UseTemplateNote',
1478                    Data => '0',
1479                },
1480                {
1481                    Name => 'RichText',
1482                    Data => $TemplateText || '',
1483                },
1484                {
1485                    Name     => 'TicketAttachments',
1486                    Data     => \@TicketAttachments,
1487                    KeepData => 1,
1488                },
1489            );
1490        }
1491
1492        my $JSON = $LayoutObject->BuildSelectionJSON(
1493            [
1494
1495                {
1496                    Name         => 'NewOwnerID',
1497                    Data         => $Owners,
1498                    SelectedID   => $GetParam{NewOwnerID},
1499                    Translation  => 0,
1500                    PossibleNone => 1,
1501                    Max          => 100,
1502                },
1503                {
1504                    Name         => 'NewResponsibleID',
1505                    Data         => $ResponsibleUsers,
1506                    SelectedID   => $GetParam{NewResponsibleID},
1507                    Translation  => 0,
1508                    PossibleNone => 1,
1509                    Max          => 100,
1510                },
1511                {
1512                    Name         => 'NewStateID',
1513                    Data         => $NextStates,
1514                    SelectedID   => $GetParam{NewStateID},
1515                    Translation  => 1,
1516                    PossibleNone => $Config->{StateDefault} ? 0 : 1,
1517                    Max          => 100,
1518                },
1519                {
1520                    Name         => 'NewPriorityID',
1521                    Data         => $Priorities,
1522                    SelectedID   => $GetParam{NewPriorityID},
1523                    PossibleNone => 0,
1524                    Translation  => 1,
1525                    Max          => 100,
1526                },
1527                {
1528                    Name         => 'ServiceID',
1529                    Data         => $Services,
1530                    SelectedID   => $GetParam{ServiceID},
1531                    PossibleNone => 1,
1532                    Translation  => 0,
1533                    TreeView     => $TreeView,
1534                    Max          => 100,
1535                },
1536                {
1537                    Name         => 'SLAID',
1538                    Data         => $SLAs,
1539                    SelectedID   => $GetParam{SLAID},
1540                    PossibleNone => 1,
1541                    Translation  => 0,
1542                    Max          => 100,
1543                },
1544                {
1545                    Name         => 'StandardTemplateID',
1546                    Data         => $StandardTemplates,
1547                    SelectedID   => $GetParam{StandardTemplateID},
1548                    PossibleNone => 1,
1549                    Translation  => 1,
1550                    Max          => 100,
1551                },
1552                {
1553                    Name         => 'TypeID',
1554                    Data         => $Types,
1555                    SelectedID   => $GetParam{TypeID},
1556                    PossibleNone => 1,
1557                    Translation  => 0,
1558                    Max          => 100,
1559                },
1560                {
1561                    Name         => 'NewQueueID',
1562                    Data         => $NewQueues,
1563                    SelectedID   => $GetParam{NewQueueID},
1564                    PossibleNone => 1,
1565                    Translation  => 0,
1566                    TreeView     => $TreeView,
1567                    Max          => 100,
1568                },
1569                @DynamicFieldAJAX,
1570                @TemplateAJAX,
1571            ],
1572        );
1573        return $LayoutObject->Attachment(
1574            ContentType => 'application/json; charset=' . $LayoutObject->{Charset},
1575            Content     => $JSON,
1576            Type        => 'inline',
1577            NoCache     => 1,
1578        );
1579    }
1580    else {
1581
1582        my $Body = '';
1583
1584        # if ReplyToArticle is given, get this article to generate
1585        # the quoted article content
1586        if ( $Self->{ReplyToArticle} ) {
1587
1588            # get article to quote
1589            $Body = $LayoutObject->ArticleQuote(
1590                TicketID          => $Self->{TicketID},
1591                ArticleID         => $Self->{ReplyToArticle},
1592                FormID            => $Self->{FormID},
1593                UploadCacheObject => $UploadCacheObject,
1594            );
1595
1596            # prepare quoted body content
1597            $Body = $Self->_GetQuotedReplyBody(
1598                %{ $Self->{ReplyToArticleContent} },
1599                Body => $Body,
1600            );
1601        }
1602
1603        # if a body content was pre defined, add this before the quoted article content
1604        if ( $GetParam{Body} ) {
1605
1606            # make sure body is rich text
1607            if ( $LayoutObject->{BrowserRichText} ) {
1608                $GetParam{Body} = $LayoutObject->Ascii2RichText(
1609                    String => $GetParam{Body},
1610                );
1611            }
1612
1613            $Body = $GetParam{Body} . $Body;
1614        }
1615
1616        # fillup configured default vars
1617        if ( $Body eq '' && $Config->{Body} ) {
1618            $Body = $LayoutObject->Output(
1619                Template => $Config->{Body},
1620            );
1621
1622            # make sure body is rich text
1623            if ( $LayoutObject->{BrowserRichText} ) {
1624                $Body = $LayoutObject->Ascii2RichText(
1625                    String => $Body,
1626                );
1627            }
1628        }
1629
1630        # set Body var to calculated content
1631        $GetParam{Body} = $Body;
1632
1633        my %SafetyCheckResult = $Kernel::OM->Get('Kernel::System::HTMLUtils')->Safety(
1634            String => $GetParam{Body},
1635
1636            # Strip out external content if BlockLoadingRemoteContent is enabled.
1637            NoExtSrcLoad => $ConfigObject->Get('Ticket::Frontend::BlockLoadingRemoteContent'),
1638
1639            # Disallow potentially unsafe content.
1640            NoApplet     => 1,
1641            NoObject     => 1,
1642            NoEmbed      => 1,
1643            NoSVG        => 1,
1644            NoJavaScript => 1,
1645        );
1646        $GetParam{Body} = $SafetyCheckResult{String};
1647
1648        if ( $Self->{ReplyToArticle} ) {
1649            my $TicketSubjectRe = $ConfigObject->Get('Ticket::SubjectRe') || 'Re';
1650            $GetParam{Subject} = $TicketSubjectRe . ': ' . $Self->{ReplyToArticleContent}{Subject};
1651        }
1652        elsif ( !defined $GetParam{Subject} && $Config->{Subject} ) {
1653            $GetParam{Subject} = $LayoutObject->Output(
1654                Template => $Config->{Subject},
1655            );
1656        }
1657
1658        my @TicketTypeDynamicFields;
1659        my @ArticleTypeDynamicFields;
1660
1661        # cycle trough the activated Dynamic Fields for this screen
1662        DYNAMICFIELD:
1663        for my $DynamicFieldConfig ( @{$DynamicField} ) {
1664            next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
1665
1666            my $PossibleValuesFilter;
1667
1668            my $IsACLReducible = $DynamicFieldBackendObject->HasBehavior(
1669                DynamicFieldConfig => $DynamicFieldConfig,
1670                Behavior           => 'IsACLReducible',
1671            );
1672
1673            if ($IsACLReducible) {
1674
1675                # get PossibleValues
1676                my $PossibleValues = $DynamicFieldBackendObject->PossibleValuesGet(
1677                    DynamicFieldConfig => $DynamicFieldConfig,
1678                );
1679
1680                # check if field has PossibleValues property in its configuration
1681                if ( IsHashRefWithData($PossibleValues) ) {
1682
1683                    # convert possible values key => value to key => key for ACLs using a Hash slice
1684                    my %AclData = %{$PossibleValues};
1685                    @AclData{ keys %AclData } = keys %AclData;
1686
1687                    # set possible values filter from ACLs
1688                    my $ACL = $TicketObject->TicketAcl(
1689                        %GetParam,
1690                        Action        => $Self->{Action},
1691                        TicketID      => $Self->{TicketID},
1692                        ReturnType    => 'Ticket',
1693                        ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name},
1694                        Data          => \%AclData,
1695                        UserID        => $Self->{UserID},
1696                    );
1697                    if ($ACL) {
1698                        my %Filter = $TicketObject->TicketAclData();
1699
1700                        # convert Filer key => key back to key => value using map
1701                        %{$PossibleValuesFilter} = map { $_ => $PossibleValues->{$_} }
1702                            keys %Filter;
1703                    }
1704                }
1705            }
1706
1707            # to store dynamic field value from database (or undefined)
1708            my $Value;
1709
1710            if ( $DynamicFieldConfig->{ObjectType} eq 'Ticket' ) {
1711
1712                # Only get values for Ticket fields (all screens based on AgentTickeActionCommon
1713                # generates a new article, then article fields will be always empty at the beginning).
1714                # Value is stored in the database from Ticket.
1715                $Value = $Ticket{ 'DynamicField_' . $DynamicFieldConfig->{Name} };
1716
1717                # Get field html.
1718                my $DynamicFieldHTML = $DynamicFieldBackendObject->EditFieldRender(
1719                    DynamicFieldConfig   => $DynamicFieldConfig,
1720                    PossibleValuesFilter => $PossibleValuesFilter,
1721                    Value                => $Value,
1722                    Mandatory            => $Config->{DynamicField}->{ $DynamicFieldConfig->{Name} } == 2,
1723                    LayoutObject         => $LayoutObject,
1724                    ParamObject          => $ParamObject,
1725                    AJAXUpdate           => 1,
1726                    UpdatableFields      => $Self->_GetFieldsToUpdate(),
1727                );
1728
1729                push @TicketTypeDynamicFields, {
1730                    Name  => $DynamicFieldConfig->{Name},
1731                    Label => $DynamicFieldHTML->{Label},
1732                    Field => $DynamicFieldHTML->{Field},
1733                };
1734            }
1735            elsif ( $DynamicFieldConfig->{ObjectType} eq 'Article' ) {
1736                my $Class            = '';
1737                my $MandatoryTooltip = 0;
1738
1739                if ( $Config->{DynamicField}->{ $DynamicFieldConfig->{Name} } == 2 ) {
1740                    if (
1741                        $Config->{NoteMandatory} ||
1742                        $ConfigObject->Get('Ticket::Frontend::NeedAccountedTime')
1743                        )
1744                    {
1745                        $Class = 'Validate_Required';
1746                    }
1747                    else {
1748                        $Class            = 'Validate_DependingRequiredAND Validate_Depending_CreateArticle';
1749                        $MandatoryTooltip = 1;
1750                    }
1751                }
1752
1753                # Get field html.
1754                my $DynamicFieldHTML = $DynamicFieldBackendObject->EditFieldRender(
1755                    DynamicFieldConfig   => $DynamicFieldConfig,
1756                    PossibleValuesFilter => $PossibleValuesFilter,
1757                    Value                => $Value,
1758                    Mandatory            => ( $Class eq 'Validate_Required' ) ? 1 : 0,
1759                    Class                => $Class,
1760                    LayoutObject         => $LayoutObject,
1761                    ParamObject          => $ParamObject,
1762                    AJAXUpdate           => 1,
1763                    UpdatableFields      => $Self->_GetFieldsToUpdate(),
1764                );
1765
1766                push @ArticleTypeDynamicFields, {
1767                    Name             => $DynamicFieldConfig->{Name},
1768                    Label            => $DynamicFieldHTML->{Label},
1769                    Field            => $DynamicFieldHTML->{Field},
1770                    MandatoryTooltip => $MandatoryTooltip,
1771                };
1772            }
1773        }
1774
1775        # print form ...
1776        my $Output = $LayoutObject->Header(
1777            Type      => 'Small',
1778            Value     => $Ticket{TicketNumber},
1779            BodyClass => 'Popup',
1780        );
1781        $Output .= $Self->_Mask(
1782            TimeUnitsRequired => (
1783                $ConfigObject->Get('Ticket::Frontend::NeedAccountedTime')
1784                ? 'Validate_Required'
1785                : ''
1786            ),
1787            TicketTypeDynamicFields  => \@TicketTypeDynamicFields,
1788            ArticleTypeDynamicFields => \@ArticleTypeDynamicFields,
1789            %GetParam,
1790            %Ticket,
1791        );
1792        $Output .= $LayoutObject->Footer(
1793            Type => 'Small',
1794        );
1795        return $Output;
1796    }
1797}
1798
1799sub _Mask {
1800    my ( $Self, %Param ) = @_;
1801
1802    # get list type
1803    my $TreeView = 0;
1804
1805    # get config object
1806    my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
1807
1808    if ( $ConfigObject->Get('Ticket::Frontend::ListType') eq 'tree' ) {
1809        $TreeView = 1;
1810    }
1811
1812    # get needed objects
1813    my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
1814
1815    my %Ticket = $TicketObject->TicketGet( TicketID => $Self->{TicketID} );
1816
1817    # get config of frontend module
1818    my $Config = $ConfigObject->Get("Ticket::Frontend::$Self->{Action}");
1819
1820    # get layout object
1821    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
1822
1823    # Define the dynamic fields to show based on the object type.
1824    my $ObjectType = ['Ticket'];
1825
1826    # Only screens that add notes can modify Article dynamic fields.
1827    if ( $Config->{Note} ) {
1828        $ObjectType = [ 'Ticket', 'Article' ];
1829    }
1830
1831    # Get dynamic fields for this screen.
1832    my $DynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
1833        Valid       => 1,
1834        ObjectType  => $ObjectType,
1835        FieldFilter => $Config->{DynamicField} || {},
1836    );
1837
1838    # Widget Ticket Actions
1839    if (
1840        ( $ConfigObject->Get('Ticket::Type') && $Config->{TicketType} )
1841        ||
1842        ( $ConfigObject->Get('Ticket::Service')     && $Config->{Service} )     ||
1843        ( $ConfigObject->Get('Ticket::Responsible') && $Config->{Responsible} ) ||
1844        $Config->{Title}    ||
1845        $Config->{Queue}    ||
1846        $Config->{Owner}    ||
1847        $Config->{State}    ||
1848        $Config->{Priority} ||
1849        scalar @{ $Param{TicketTypeDynamicFields} } > 0
1850        )
1851    {
1852        $LayoutObject->Block(
1853            Name => 'WidgetTicketActions',
1854        );
1855    }
1856
1857    if ( $Config->{Title} ) {
1858        $LayoutObject->Block(
1859            Name => 'Title',
1860            Data => \%Param,
1861        );
1862    }
1863
1864    my $DynamicFieldNames = $Self->_GetFieldsToUpdate(
1865        OnlyDynamicFields => 1,
1866    );
1867
1868    # send data to JS
1869    $LayoutObject->AddJSData(
1870        Key   => 'DynamicFieldNames',
1871        Value => $DynamicFieldNames,
1872    );
1873
1874    # types
1875    if ( $ConfigObject->Get('Ticket::Type') && $Config->{TicketType} ) {
1876        my %Type = $TicketObject->TicketTypeList(
1877            %Param,
1878            Action => $Self->{Action},
1879            UserID => $Self->{UserID},
1880        );
1881        $Param{TypeStrg} = $LayoutObject->BuildSelection(
1882            Class        => 'Validate_Required Modernize ' . ( $Param{Errors}->{TypeIDInvalid} || '' ),
1883            Data         => \%Type,
1884            Name         => 'TypeID',
1885            SelectedID   => $Param{TypeID},
1886            PossibleNone => 1,
1887            Sort         => 'AlphanumericValue',
1888            Translation  => 0,
1889        );
1890        $LayoutObject->Block(
1891            Name => 'Type',
1892            Data => {%Param},
1893        );
1894    }
1895
1896    # services
1897    if ( $ConfigObject->Get('Ticket::Service') && $Config->{Service} ) {
1898        my $Services = $Self->_GetServices(
1899            %Param,
1900            Action         => $Self->{Action},
1901            CustomerUserID => $Ticket{CustomerUserID},
1902            UserID         => $Self->{UserID},
1903        );
1904
1905        # reset previous ServiceID to reset SLA-List if no service is selected
1906        if ( !$Param{ServiceID} || !$Services->{ $Param{ServiceID} } ) {
1907            $Param{ServiceID} = '';
1908        }
1909
1910        $Param{ServiceStrg} = $LayoutObject->BuildSelection(
1911            Data       => $Services,
1912            Name       => 'ServiceID',
1913            SelectedID => $Param{ServiceID},
1914            Class      => "Modernize "
1915                . ( $Config->{ServiceMandatory} ? 'Validate_Required ' : '' )
1916                . ( $Param{ServiceInvalid} || '' ),
1917            PossibleNone => 1,
1918            TreeView     => $TreeView,
1919            Sort         => 'TreeView',
1920            Translation  => 0,
1921            Max          => 200,
1922        );
1923
1924        $LayoutObject->Block(
1925            Name => 'Service',
1926            Data => {
1927                ServiceMandatory => $Config->{ServiceMandatory} || 0,
1928                %Param,
1929            },
1930        );
1931
1932        my %SLA = $TicketObject->TicketSLAList(
1933            %Param,
1934            Action => $Self->{Action},
1935            UserID => $Self->{UserID},
1936        );
1937
1938        $Param{SLAStrg} = $LayoutObject->BuildSelection(
1939            Data       => \%SLA,
1940            Name       => 'SLAID',
1941            SelectedID => $Param{SLAID},
1942            Class      => "Modernize "
1943                . ( $Config->{SLAMandatory} ? 'Validate_Required ' : '' )
1944                . ( $Param{ServiceInvalid} || '' ),
1945            PossibleNone => 1,
1946            Sort         => 'AlphanumericValue',
1947            Translation  => 0,
1948            Max          => 200,
1949        );
1950
1951        $LayoutObject->Block(
1952            Name => 'SLA',
1953            Data => {
1954                SLAMandatory => $Config->{SLAMandatory},
1955                %Param,
1956            },
1957        );
1958    }
1959
1960    if ( $Config->{Queue} ) {
1961
1962        # fetch all queues
1963        my %MoveQueues = $TicketObject->TicketMoveList(
1964            TicketID => $Self->{TicketID},
1965            UserID   => $Self->{UserID},
1966            Action   => $Self->{Action},
1967            Type     => 'move_into',
1968        );
1969
1970        # set move queues
1971        $Param{QueuesStrg} = $LayoutObject->AgentQueueListOption(
1972            Data     => { %MoveQueues, '' => '-' },
1973            Multiple => 0,
1974            Size     => 0,
1975            Class    => 'NewQueueID Modernize '
1976                . ( $Config->{QueueMandatory} ? 'Validate_Required ' : '' )
1977                . ( $Param{NewQueueInvalid} || '' ),
1978            Name           => 'NewQueueID',
1979            SelectedID     => $Param{NewQueueID},
1980            TreeView       => $TreeView,
1981            CurrentQueueID => $Param{QueueID},
1982            OnChangeSubmit => 0,
1983        );
1984
1985        $LayoutObject->Block(
1986            Name => 'Queue',
1987            Data => {
1988                QueueMandatory => $Config->{QueueMandatory} || 0,
1989                %Param
1990            },
1991        );
1992    }
1993
1994    # get needed objects
1995    my $QueueObject = $Kernel::OM->Get('Kernel::System::Queue');
1996    my $UserObject  = $Kernel::OM->Get('Kernel::System::User');
1997    my $GroupObject = $Kernel::OM->Get('Kernel::System::Group');
1998
1999    if ( $Config->{Owner} ) {
2000
2001        # get user of own groups
2002        my %ShownUsers;
2003        my %AllGroupsMembers = $UserObject->UserList(
2004            Type  => 'Long',
2005            Valid => 1,
2006        );
2007        if ( $ConfigObject->Get('Ticket::ChangeOwnerToEveryone') ) {
2008            %ShownUsers = %AllGroupsMembers;
2009        }
2010        else {
2011            my $GID        = $QueueObject->GetQueueGroupID( QueueID => $Ticket{QueueID} );
2012            my %MemberList = $GroupObject->PermissionGroupGet(
2013                GroupID => $GID,
2014                Type    => 'owner',
2015            );
2016            for my $UserID ( sort keys %MemberList ) {
2017                $ShownUsers{$UserID} = $AllGroupsMembers{$UserID};
2018            }
2019        }
2020
2021        my $ACL = $TicketObject->TicketAcl(
2022            %Ticket,
2023            Action        => $Self->{Action},
2024            ReturnType    => 'Ticket',
2025            ReturnSubType => 'NewOwner',
2026            Data          => \%ShownUsers,
2027            UserID        => $Self->{UserID},
2028        );
2029
2030        if ($ACL) {
2031            %ShownUsers = $TicketObject->TicketAclData();
2032        }
2033
2034        # get old owner
2035        my @OldUserInfo = $TicketObject->TicketOwnerList( TicketID => $Self->{TicketID} );
2036        my @OldOwners;
2037        my %OldOwnersShown;
2038        my %SeenOldOwner;
2039        if (@OldUserInfo) {
2040            my $Counter = 1;
2041            USER:
2042            for my $User ( reverse @OldUserInfo ) {
2043
2044                # skip if old owner is already in the list
2045                next USER if $SeenOldOwner{ $User->{UserID} };
2046                $SeenOldOwner{ $User->{UserID} } = 1;
2047                my $Key   = $User->{UserID};
2048                my $Value = "$Counter: $User->{UserFullname}";
2049                push @OldOwners, {
2050                    Key   => $Key,
2051                    Value => $Value,
2052                };
2053                $OldOwnersShown{$Key} = $Value;
2054                $Counter++;
2055            }
2056        }
2057
2058        my $OldOwnerSelectedID = '';
2059        if ( $Param{OldOwnerID} ) {
2060            $OldOwnerSelectedID = $Param{OldOwnerID};
2061        }
2062        elsif ( $OldUserInfo[0]->{UserID} ) {
2063            $OldOwnerSelectedID = $OldUserInfo[0]->{UserID} . '1';
2064        }
2065
2066        my $OldOwnerACL = $TicketObject->TicketAcl(
2067            %Ticket,
2068            Action        => $Self->{Action},
2069            ReturnType    => 'Ticket',
2070            ReturnSubType => 'OldOwner',
2071            Data          => \%OldOwnersShown,
2072            UserID        => $Self->{UserID},
2073        );
2074
2075        if ($OldOwnerACL) {
2076            %OldOwnersShown = $TicketObject->TicketAclData();
2077        }
2078
2079        # build string
2080        $Param{OwnerStrg} = $LayoutObject->BuildSelection(
2081            Data       => \%ShownUsers,
2082            SelectedID => $Param{NewOwnerID},
2083            Name       => 'NewOwnerID',
2084            Class      => 'Modernize '
2085                . ( $Config->{OwnerMandatory} ? 'Validate_Required ' : '' )
2086                . ( $Param{NewOwnerInvalid} || '' ),
2087            Size         => 1,
2088            PossibleNone => 1,
2089            Filters      => {
2090                OldOwners => {
2091                    Name   => $LayoutObject->{LanguageObject}->Translate('Previous Owner'),
2092                    Values => \%OldOwnersShown,
2093                },
2094            },
2095        );
2096
2097        $LayoutObject->Block(
2098            Name => 'Owner',
2099            Data => {
2100                OwnerMandatory => $Config->{OwnerMandatory} || 0,
2101                %Param,
2102            },
2103        );
2104    }
2105
2106    if ( $ConfigObject->Get('Ticket::Responsible') && $Config->{Responsible} ) {
2107
2108        # get user of own groups
2109        my %ShownUsers;
2110        my %AllGroupsMembers = $UserObject->UserList(
2111            Type  => 'Long',
2112            Valid => 1,
2113        );
2114        if ( $ConfigObject->Get('Ticket::ChangeOwnerToEveryone') ) {
2115            %ShownUsers = %AllGroupsMembers;
2116        }
2117        else {
2118            my $GID        = $QueueObject->GetQueueGroupID( QueueID => $Ticket{QueueID} );
2119            my %MemberList = $GroupObject->PermissionGroupGet(
2120                GroupID => $GID,
2121                Type    => 'responsible',
2122            );
2123            for my $UserID ( sort keys %MemberList ) {
2124                $ShownUsers{$UserID} = $AllGroupsMembers{$UserID};
2125            }
2126        }
2127
2128        my $ACL = $TicketObject->TicketAcl(
2129            %Ticket,
2130            Action        => $Self->{Action},
2131            ReturnType    => 'Ticket',
2132            ReturnSubType => 'Responsible',
2133            Data          => \%ShownUsers,
2134            UserID        => $Self->{UserID},
2135        );
2136
2137        if ($ACL) {
2138            %ShownUsers = $TicketObject->TicketAclData();
2139        }
2140
2141        # get responsible
2142        $Param{ResponsibleStrg} = $LayoutObject->BuildSelection(
2143            Data       => \%ShownUsers,
2144            SelectedID => $Param{NewResponsibleID},
2145            Name       => 'NewResponsibleID',
2146            Class      => 'Modernize '
2147                . ( $Config->{ResponsibleMandatory} ? 'Validate_Required ' : '' )
2148                . ( $Param{NewResponsibleInvalid} || '' ),
2149            PossibleNone => 1,
2150            Size         => 1,
2151        );
2152        $LayoutObject->Block(
2153            Name => 'Responsible',
2154            Data => {
2155                ResponsibleMandatory => $Config->{ResponsibleMandatory} || 0,
2156                %Param,
2157            },
2158        );
2159
2160    }
2161
2162    if ( $Config->{State} ) {
2163
2164        my %State;
2165        my %StateList = $TicketObject->TicketStateList(
2166            Action   => $Self->{Action},
2167            TicketID => $Self->{TicketID},
2168            UserID   => $Self->{UserID},
2169        );
2170        if ( !$Param{NewStateID} ) {
2171            if ( $Config->{StateDefault} ) {
2172                $State{SelectedValue} = $Config->{StateDefault};
2173            }
2174        }
2175        else {
2176            $State{SelectedID} = $Param{NewStateID};
2177        }
2178
2179        # build next states string
2180        $Param{StateStrg} = $LayoutObject->BuildSelection(
2181            Data  => \%StateList,
2182            Name  => 'NewStateID',
2183            Class => 'Modernize '
2184                . ( $Config->{StateMandatory} ? 'Validate_Required ' : '' )
2185                . ( $Param{NewStateInvalid} || '' ),
2186            PossibleNone => $Config->{StateDefault} ? 0 : 1,
2187            %State,
2188        );
2189        $LayoutObject->Block(
2190            Name => 'State',
2191            Data => {
2192                StateMandatory => $Config->{StateMandatory} || 0,
2193                %Param,
2194            },
2195        );
2196
2197        if ( IsArrayRefWithData( $Config->{StateType} ) ) {
2198
2199            STATETYPE:
2200            for my $StateType ( @{ $Config->{StateType} } ) {
2201
2202                next STATETYPE if !$StateType;
2203                next STATETYPE if $StateType !~ /pending/i;
2204
2205                # get used calendar
2206                my $Calendar = $TicketObject->TicketCalendarGet(
2207                    %Ticket,
2208                );
2209
2210                $Param{DateString} = $LayoutObject->BuildDateSelection(
2211                    %Param,
2212                    Format           => 'DateInputFormatLong',
2213                    YearPeriodPast   => 0,
2214                    YearPeriodFuture => 5,
2215                    DiffTime         => $ConfigObject->Get('Ticket::Frontend::PendingDiffTime')
2216                        || 0,
2217                    Class                => $Param{DateInvalid} || ' ',
2218                    Validate             => 1,
2219                    ValidateDateInFuture => 1,
2220                    Calendar             => $Calendar,
2221                );
2222
2223                $LayoutObject->Block(
2224                    Name => 'StatePending',
2225                    Data => \%Param,
2226                );
2227
2228                last STATETYPE;
2229            }
2230        }
2231    }
2232
2233    # get priority
2234    if ( $Config->{Priority} ) {
2235
2236        my %Priority;
2237        my %PriorityList = $TicketObject->TicketPriorityList(
2238            UserID   => $Self->{UserID},
2239            TicketID => $Self->{TicketID},
2240            Action   => $Self->{Action},
2241        );
2242        if ( !$Config->{PriorityDefault} ) {
2243            $PriorityList{''} = '-';
2244        }
2245        if ( !$Param{NewPriorityID} ) {
2246            if ( $Config->{PriorityDefault} ) {
2247                $Priority{SelectedValue} = $Config->{PriorityDefault};
2248            }
2249        }
2250        else {
2251            $Priority{SelectedID} = $Param{NewPriorityID};
2252        }
2253        $Priority{SelectedID} ||= $Param{PriorityID};
2254        $Param{PriorityStrg} = $LayoutObject->BuildSelection(
2255            Data  => \%PriorityList,
2256            Name  => 'NewPriorityID',
2257            Class => 'Modernize',
2258            %Priority,
2259        );
2260        $LayoutObject->Block(
2261            Name => 'Priority',
2262            Data => \%Param,
2263        );
2264    }
2265
2266    # Get Ticket type dynamic fields.
2267    for my $TicketTypeDynamicField ( @{ $Param{TicketTypeDynamicFields} } ) {
2268        $LayoutObject->Block(
2269            Name => 'TicketTypeDynamicField',
2270            Data => $TicketTypeDynamicField,
2271        );
2272
2273        # Output customization block too, if it exists.
2274        $LayoutObject->Block(
2275            Name => 'TicketTypeDynamicField_' . $TicketTypeDynamicField->{Name},
2276            Data => $TicketTypeDynamicField,
2277        );
2278    }
2279
2280    # End Widget Ticket Actions
2281
2282    # Widget Article
2283    if ( $Config->{Note} ) {
2284
2285        $Param{WidgetStatus} = 'Collapsed';
2286
2287        if (
2288            $Config->{NoteMandatory}
2289            || $ConfigObject->Get('Ticket::Frontend::NeedAccountedTime')
2290            || $Self->{ReplyToArticle}
2291            || $Param{CreateArticle}
2292            )
2293        {
2294            $Param{WidgetStatus} = 'Expanded';
2295        }
2296
2297        if (
2298            $Config->{NoteMandatory}
2299            || $ConfigObject->Get('Ticket::Frontend::NeedAccountedTime')
2300            )
2301        {
2302            $Param{SubjectRequired} = 'Validate_Required';
2303            $Param{BodyRequired}    = 'Validate_Required';
2304        }
2305        else {
2306            $Param{SubjectRequired} = 'Validate_DependingRequiredAND Validate_Depending_CreateArticle';
2307            $Param{BodyRequired}    = 'Validate_DependingRequiredAND Validate_Depending_CreateArticle';
2308        }
2309
2310        # set customer visibility of this note to the same value as the article for whom this is the reply
2311        if ( $Self->{ReplyToArticle} && !defined $Param{IsVisibleForCustomer} ) {
2312            $Param{IsVisibleForCustomer} = $Self->{ReplyToArticleContent}->{IsVisibleForCustomer};
2313        }
2314        elsif ( !defined $Param{IsVisibleForCustomer} ) {
2315            $Param{IsVisibleForCustomer} = $Config->{IsVisibleForCustomerDefault};
2316        }
2317
2318        # show attachments
2319        ATTACHMENT:
2320        for my $Attachment ( @{ $Param{Attachments} } ) {
2321            if (
2322                $Attachment->{ContentID}
2323                && $LayoutObject->{BrowserRichText}
2324                && ( $Attachment->{ContentType} =~ /image/i )
2325                && ( $Attachment->{Disposition} eq 'inline' )
2326                )
2327            {
2328                next ATTACHMENT;
2329            }
2330
2331            push @{ $Param{AttachmentList} }, $Attachment;
2332        }
2333
2334        $LayoutObject->Block(
2335            Name => 'WidgetArticle',
2336            Data => {%Param},
2337        );
2338
2339        # get all user ids of agents, that can be shown in this dialog
2340        # based on queue rights
2341        my %ShownUsers;
2342        my %AllGroupsMembers = $UserObject->UserList(
2343            Type  => 'Long',
2344            Valid => 1,
2345        );
2346        my $GID        = $QueueObject->GetQueueGroupID( QueueID => $Ticket{QueueID} );
2347        my %MemberList = $GroupObject->PermissionGroupGet(
2348            GroupID => $GID,
2349            Type    => 'note',
2350        );
2351        for my $UserID ( sort keys %MemberList ) {
2352            $ShownUsers{$UserID} = $AllGroupsMembers{$UserID};
2353        }
2354
2355        # create email parser object
2356        my $EmailParserObject = Kernel::System::EmailParser->new(
2357            Mode  => 'Standalone',
2358            Debug => 0,
2359        );
2360
2361        # check and retrieve involved and informed agents of ReplyTo Note
2362        my @ReplyToUsers;
2363        my %ReplyToUsersHash;
2364        my %ReplyToUserIDs;
2365        if ( $Self->{ReplyToArticle} ) {
2366            my @ReplyToParts = $EmailParserObject->SplitAddressLine(
2367                Line => $Self->{ReplyToArticleContent}->{To} || '',
2368            );
2369
2370            REPLYTOPART:
2371            for my $SingleReplyToPart (@ReplyToParts) {
2372                my $ReplyToAddress = $EmailParserObject->GetEmailAddress(
2373                    Email => $SingleReplyToPart,
2374                );
2375
2376                next REPLYTOPART if !$ReplyToAddress;
2377                push @ReplyToUsers, $ReplyToAddress;
2378            }
2379
2380            $ReplyToUsersHash{$_}++ for @ReplyToUsers;
2381
2382            # get user ids of available users
2383            for my $UserID ( sort keys %ShownUsers ) {
2384                my %UserData = $UserObject->GetUserData(
2385                    UserID => $UserID,
2386                );
2387
2388                my $UserEmail = $UserData{UserEmail};
2389                if ( $ReplyToUsersHash{$UserEmail} ) {
2390                    $ReplyToUserIDs{$UserID} = 1;
2391                }
2392            }
2393
2394            # add original note sender to list of user ids
2395            for my $UserID ( sort @{ $Self->{ReplyToSenderUserID} } ) {
2396
2397                # if sender replies to himself, do not include sender in list
2398                if ( $UserID ne $Self->{UserID} ) {
2399                    $ReplyToUserIDs{$UserID} = 1;
2400                }
2401            }
2402
2403            # remove user id of active user
2404            delete $ReplyToUserIDs{ $Self->{UserID} };
2405        }
2406
2407        if ( $Config->{InformAgent} || $Config->{InvolvedAgent} ) {
2408            $LayoutObject->Block(
2409                Name => 'InformAdditionalAgents',
2410            );
2411        }
2412
2413        # get param object
2414        my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
2415
2416        # get all agents for "involved agents"
2417        if ( $Config->{InvolvedAgent} ) {
2418
2419            my @UserIDs = $TicketObject->TicketInvolvedAgentsList(
2420                TicketID => $Self->{TicketID},
2421            );
2422
2423            my @InvolvedAgents;
2424            my $Counter = 1;
2425
2426            my @InvolvedUserID = $ParamObject->GetArray( Param => 'InvolvedUserID' );
2427
2428            my %AgentWithPermission = $GroupObject->PermissionGroupGet(
2429                GroupID => $GID,
2430                Type    => 'ro',
2431            );
2432
2433            USER:
2434            for my $User ( reverse @UserIDs ) {
2435
2436                next USER if !defined $AgentWithPermission{ $User->{UserID} };
2437
2438                my $Value = "$Counter: $User->{UserFullname}";
2439                if ( $User->{OutOfOfficeMessage} ) {
2440                    $Value .= " $User->{OutOfOfficeMessage}";
2441                }
2442
2443                push @InvolvedAgents, {
2444                    Key   => $User->{UserID},
2445                    Value => $Value,
2446                };
2447                $Counter++;
2448
2449                # add involved user as selected entries, if available in ReplyToAddresses list
2450                if ( $Self->{ReplyToArticle} && $ReplyToUserIDs{ $User->{UserID} } ) {
2451                    push @InvolvedUserID, $User->{UserID};
2452                    delete $ReplyToUserIDs{ $User->{UserID} };
2453                }
2454            }
2455
2456            my $InvolvedAgentSize = $ConfigObject->Get('Ticket::Frontend::InvolvedAgentMaxSize') || 3;
2457            $Param{InvolvedAgentStrg} = $LayoutObject->BuildSelection(
2458                Data       => \@InvolvedAgents,
2459                SelectedID => \@InvolvedUserID,
2460                Name       => 'InvolvedUserID',
2461                Class      => 'Modernize',
2462                Multiple   => 1,
2463                Size       => $InvolvedAgentSize,
2464            );
2465
2466            # block is called below "inform agents"
2467        }
2468
2469        # agent list
2470        if ( $Config->{InformAgent} ) {
2471
2472            # get inform user list
2473            my %InformAgents;
2474            my @InformUserID    = $ParamObject->GetArray( Param => 'InformUserID' );
2475            my %InformAgentList = $GroupObject->PermissionGroupGet(
2476                GroupID => $GID,
2477                Type    => 'ro',
2478            );
2479            for my $UserID ( sort keys %InformAgentList ) {
2480                $InformAgents{$UserID} = $AllGroupsMembers{$UserID};
2481            }
2482
2483            if ( $Self->{ReplyToArticle} ) {
2484
2485                # get email address of all users and compare to replyto-addresses
2486                for my $UserID ( sort keys %InformAgents ) {
2487                    if ( $ReplyToUserIDs{$UserID} ) {
2488                        push @InformUserID, $UserID;
2489                        delete $ReplyToUserIDs{$UserID};
2490                    }
2491                }
2492            }
2493
2494            my $InformAgentSize = $ConfigObject->Get('Ticket::Frontend::InformAgentMaxSize')
2495                || 3;
2496            $Param{OptionStrg} = $LayoutObject->BuildSelection(
2497                Data       => \%InformAgents,
2498                SelectedID => \@InformUserID,
2499                Name       => 'InformUserID',
2500                Class      => 'Modernize',
2501                Multiple   => 1,
2502                Size       => $InformAgentSize,
2503            );
2504            $LayoutObject->Block(
2505                Name => 'InformAgent',
2506                Data => \%Param,
2507            );
2508        }
2509
2510        # get involved
2511        if ( $Config->{InvolvedAgent} ) {
2512
2513            $LayoutObject->Block(
2514                Name => 'InvolvedAgent',
2515                Data => \%Param,
2516            );
2517        }
2518
2519        # show list of agents, that receive this note (ReplyToNote)
2520        # at least sender of original note and all recepients of the original note
2521        # that couldn't be selected with involved/inform agents
2522        if ( $Self->{ReplyToArticle} ) {
2523
2524            my $UsersHashSize = keys %ReplyToUserIDs;
2525            my $Counter       = 0;
2526            $Param{UserListWithoutSelection} = join( ',', keys %ReplyToUserIDs );
2527
2528            if ( $UsersHashSize > 0 ) {
2529                $LayoutObject->Block(
2530                    Name => 'InformAgentsWithoutSelection',
2531                    Data => \%Param,
2532                );
2533
2534                for my $UserID ( sort keys %ReplyToUserIDs ) {
2535                    $Counter++;
2536
2537                    my %UserData = $UserObject->GetUserData(
2538                        UserID => $UserID,
2539                    );
2540
2541                    $LayoutObject->Block(
2542                        Name => 'InformAgentsWithoutSelectionSingleUser',
2543                        Data => \%UserData,
2544                    );
2545
2546                    # output a separator (InformAgentsWithoutSelectionSingleUserSeparator),
2547                    # if not last entry
2548                    if ( $Counter < $UsersHashSize ) {
2549                        $LayoutObject->Block(
2550                            Name => 'InformAgentsWithoutSelectionSingleUserSeparator',
2551                            Data => \%UserData,
2552                        );
2553                    }
2554                }
2555            }
2556        }
2557
2558        # add rich text editor
2559        if ( $LayoutObject->{BrowserRichText} ) {
2560
2561            # use height/width defined for this screen
2562            $Param{RichTextHeight} = $Config->{RichTextHeight} || 0;
2563            $Param{RichTextWidth}  = $Config->{RichTextWidth}  || 0;
2564
2565            # set up rich text editor
2566            $LayoutObject->SetRichTextParameters(
2567                Data => \%Param,
2568            );
2569        }
2570
2571        if (
2572            $Config->{NoteMandatory}
2573            || $ConfigObject->Get('Ticket::Frontend::NeedAccountedTime')
2574            )
2575        {
2576            $LayoutObject->Block(
2577                Name => 'SubjectLabelMandatory',
2578            );
2579            $LayoutObject->Block(
2580                Name => 'RichTextLabelMandatory',
2581            );
2582        }
2583        else {
2584            $LayoutObject->Block(
2585                Name => 'SubjectLabel',
2586            );
2587            $LayoutObject->Block(
2588                Name => 'RichTextLabel',
2589            );
2590        }
2591
2592        # build text template string
2593        my %StandardTemplates = $Kernel::OM->Get('Kernel::System::StandardTemplate')->StandardTemplateList(
2594            Valid => 1,
2595            Type  => 'Note',
2596        );
2597
2598        my $QueueStandardTemplates = $Self->_GetStandardTemplates(
2599            %Param,
2600            TicketID => $Self->{TicketID} || '',
2601        );
2602
2603        if (
2604            IsHashRefWithData(
2605                $QueueStandardTemplates
2606                    || ( $Config->{Queue} && IsHashRefWithData( \%StandardTemplates ) )
2607            )
2608            )
2609        {
2610            $Param{StandardTemplateStrg} = $LayoutObject->BuildSelection(
2611                Data         => $QueueStandardTemplates || {},
2612                Name         => 'StandardTemplateID',
2613                SelectedID   => $Param{StandardTemplateID} || '',
2614                Class        => 'Modernize',
2615                PossibleNone => 1,
2616                Sort         => 'AlphanumericValue',
2617                Translation  => 1,
2618                Max          => 200,
2619            );
2620            $LayoutObject->Block(
2621                Name => 'StandardTemplate',
2622                Data => {%Param},
2623            );
2624        }
2625
2626        # show time accounting box
2627        if ( $ConfigObject->Get('Ticket::Frontend::AccountTime') ) {
2628            if ( $ConfigObject->Get('Ticket::Frontend::NeedAccountedTime') ) {
2629                $LayoutObject->Block(
2630                    Name => 'TimeUnitsLabelMandatory',
2631                    Data => \%Param,
2632                );
2633            }
2634            else {
2635                $LayoutObject->Block(
2636                    Name => 'TimeUnitsLabel',
2637                    Data => \%Param,
2638                );
2639            }
2640            $LayoutObject->Block(
2641                Name => 'TimeUnits',
2642                Data => \%Param,
2643            );
2644        }
2645
2646        # Get Article type dynamic fields.
2647        for my $ArticleTypeDynamicField ( @{ $Param{ArticleTypeDynamicFields} } ) {
2648            $LayoutObject->Block(
2649                Name => 'ArticleTypeDynamicField',
2650                Data => $ArticleTypeDynamicField,
2651            );
2652
2653            if ( $ArticleTypeDynamicField->{MandatoryTooltip} ) {
2654                $LayoutObject->Block(
2655                    Name => 'ArticleTypeDynamicFieldError',
2656                    Data => $ArticleTypeDynamicField,
2657                );
2658            }
2659
2660            # Output customization block too, if it exists.
2661            $LayoutObject->Block(
2662                Name => 'ArticleTypeDynamicField_' . $ArticleTypeDynamicField->{Name},
2663                Data => $ArticleTypeDynamicField,
2664            );
2665        }
2666    }
2667
2668    # End Widget Article
2669
2670    # get output back
2671    return $LayoutObject->Output(
2672        TemplateFile => $Self->{Action},
2673        Data         => \%Param
2674    );
2675}
2676
2677sub _GetNextStates {
2678    my ( $Self, %Param ) = @_;
2679
2680    my %NextStates = $Kernel::OM->Get('Kernel::System::Ticket')->TicketStateList(
2681        TicketID => $Self->{TicketID},
2682        Action   => $Self->{Action},
2683        UserID   => $Self->{UserID},
2684        %Param,
2685    );
2686
2687    return \%NextStates;
2688}
2689
2690sub _GetResponsible {
2691    my ( $Self, %Param ) = @_;
2692    my %ShownUsers;
2693    my %AllGroupsMembers = $Kernel::OM->Get('Kernel::System::User')->UserList(
2694        Type  => 'Long',
2695        Valid => 1,
2696    );
2697
2698    # show all users
2699    if ( $Kernel::OM->Get('Kernel::Config')->Get('Ticket::ChangeOwnerToEveryone') ) {
2700        %ShownUsers = %AllGroupsMembers;
2701    }
2702
2703    # show only users with responsible or rw pemissions in the queue
2704    elsif ( $Param{QueueID} && !$Param{AllUsers} ) {
2705        my $GID = $Kernel::OM->Get('Kernel::System::Queue')->GetQueueGroupID(
2706            QueueID => $Param{NewQueueID} || $Param{QueueID}
2707        );
2708        my %MemberList = $Kernel::OM->Get('Kernel::System::Group')->PermissionGroupGet(
2709            GroupID => $GID,
2710            Type    => 'responsible',
2711        );
2712        for my $UserID ( sort keys %MemberList ) {
2713            $ShownUsers{$UserID} = $AllGroupsMembers{$UserID};
2714        }
2715    }
2716
2717    # get ticket object
2718    my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
2719
2720    # workflow
2721    my $ACL = $TicketObject->TicketAcl(
2722        %Param,
2723        Action        => $Self->{Action},
2724        ReturnType    => 'Ticket',
2725        ReturnSubType => 'Responsible',
2726        Data          => \%ShownUsers,
2727        UserID        => $Self->{UserID},
2728    );
2729
2730    return { $TicketObject->TicketAclData() } if $ACL;
2731
2732    return \%ShownUsers;
2733}
2734
2735sub _GetOwners {
2736    my ( $Self, %Param ) = @_;
2737    my %ShownUsers;
2738    my %AllGroupsMembers = $Kernel::OM->Get('Kernel::System::User')->UserList(
2739        Type  => 'Long',
2740        Valid => 1,
2741    );
2742
2743    # show all users
2744    if ( $Kernel::OM->Get('Kernel::Config')->Get('Ticket::ChangeOwnerToEveryone') ) {
2745        %ShownUsers = %AllGroupsMembers;
2746    }
2747
2748    # show only users with owner or rw pemissions in the queue
2749    elsif ( $Param{QueueID} && !$Param{AllUsers} ) {
2750        my $GID = $Kernel::OM->Get('Kernel::System::Queue')->GetQueueGroupID(
2751            QueueID => $Param{NewQueueID} || $Param{QueueID}
2752        );
2753        my %MemberList = $Kernel::OM->Get('Kernel::System::Group')->PermissionGroupGet(
2754            GroupID => $GID,
2755            Type    => 'owner',
2756        );
2757        for my $UserID ( sort keys %MemberList ) {
2758            $ShownUsers{$UserID} = $AllGroupsMembers{$UserID};
2759        }
2760    }
2761
2762    # get ticket object
2763    my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
2764
2765    # workflow
2766    my $ACL = $TicketObject->TicketAcl(
2767        %Param,
2768        Action        => $Self->{Action},
2769        ReturnType    => 'Ticket',
2770        ReturnSubType => 'NewOwner',
2771        Data          => \%ShownUsers,
2772        UserID        => $Self->{UserID},
2773    );
2774
2775    return { $TicketObject->TicketAclData() } if $ACL;
2776
2777    return \%ShownUsers;
2778}
2779
2780sub _GetOldOwners {
2781    my ( $Self, %Param ) = @_;
2782
2783    # get ticket object
2784    my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
2785
2786    my @OldUserInfo = $TicketObject->TicketOwnerList( TicketID => $Self->{TicketID} );
2787    my %UserHash;
2788    if (@OldUserInfo) {
2789        my $Counter = 1;
2790        USER:
2791        for my $User ( reverse @OldUserInfo ) {
2792
2793            next USER if $UserHash{ $User->{UserID} };
2794
2795            $UserHash{ $User->{UserID} } = "$Counter: $User->{UserFullname}";
2796            $Counter++;
2797        }
2798    }
2799
2800    # workflow
2801    my $ACL = $TicketObject->TicketAcl(
2802        %Param,
2803        Action        => $Self->{Action},
2804        ReturnType    => 'Ticket',
2805        ReturnSubType => 'OldOwner',
2806        Data          => \%UserHash,
2807        UserID        => $Self->{UserID},
2808    );
2809
2810    return { $TicketObject->TicketAclData() } if $ACL;
2811
2812    return \%UserHash;
2813}
2814
2815sub _GetServices {
2816    my ( $Self, %Param ) = @_;
2817
2818    # get service
2819    my %Service;
2820
2821    # get options for default services for unknown customers
2822    my $DefaultServiceUnknownCustomer
2823        = $Kernel::OM->Get('Kernel::Config')->Get('Ticket::Service::Default::UnknownCustomer');
2824
2825    # check if no CustomerUserID is selected
2826    # if $DefaultServiceUnknownCustomer = 0 leave CustomerUserID empty, it will not get any services
2827    # if $DefaultServiceUnknownCustomer = 1 set CustomerUserID to get default services
2828    if ( !$Param{CustomerUserID} && $DefaultServiceUnknownCustomer ) {
2829        $Param{CustomerUserID} = '<DEFAULT>';
2830    }
2831
2832    # get service list
2833    if ( $Param{CustomerUserID} ) {
2834        %Service = $Kernel::OM->Get('Kernel::System::Ticket')->TicketServiceList(
2835            %Param,
2836            TicketID => $Self->{TicketID},
2837            Action   => $Self->{Action},
2838            UserID   => $Self->{UserID},
2839        );
2840    }
2841    return \%Service;
2842}
2843
2844sub _GetSLAs {
2845    my ( $Self, %Param ) = @_;
2846
2847    # if non set customers can get default services then they should also be able to get the SLAs
2848    #  for those services (this works during ticket creation).
2849    # if no CustomerUserID is set, TicketSLAList will complain during AJAX updates as UserID is not
2850    #  passed. See bug 11147.
2851
2852    # get options for default services for unknown customers
2853    my $DefaultServiceUnknownCustomer
2854        = $Kernel::OM->Get('Kernel::Config')->Get('Ticket::Service::Default::UnknownCustomer');
2855
2856    # check if no CustomerUserID is selected
2857    # if $DefaultServiceUnknownCustomer = 0 leave CustomerUserID empty, it will not get any services
2858    # if $DefaultServiceUnknownCustomer = 1 set CustomerUserID to get default services
2859    if ( !$Param{CustomerUserID} && $DefaultServiceUnknownCustomer ) {
2860        $Param{CustomerUserID} = '<DEFAULT>';
2861    }
2862
2863    my %SLA;
2864    if ( $Param{ServiceID} ) {
2865        %SLA = $Kernel::OM->Get('Kernel::System::Ticket')->TicketSLAList(
2866            %Param,
2867            TicketID => $Self->{TicketID},
2868            Action   => $Self->{Action},
2869            UserID   => $Self->{UserID},
2870        );
2871    }
2872    return \%SLA;
2873}
2874
2875sub _GetPriorities {
2876    my ( $Self, %Param ) = @_;
2877
2878    my %Priorities = $Kernel::OM->Get('Kernel::System::Ticket')->TicketPriorityList(
2879        %Param,
2880        Action   => $Self->{Action},
2881        UserID   => $Self->{UserID},
2882        TicketID => $Self->{TicketID},
2883    );
2884
2885    # get config of frontend module
2886    my $Config = $Kernel::OM->Get('Kernel::Config')->Get("Ticket::Frontend::$Self->{Action}");
2887
2888    if ( !$Config->{PriorityDefault} ) {
2889        $Priorities{''} = '-';
2890    }
2891    return \%Priorities;
2892}
2893
2894sub _GetFieldsToUpdate {
2895    my ( $Self, %Param ) = @_;
2896
2897    my @UpdatableFields;
2898
2899    # set the fields that can be updateable via AJAXUpdate
2900    if ( !$Param{OnlyDynamicFields} ) {
2901        @UpdatableFields = qw(
2902            TypeID ServiceID SLAID NewOwnerID NewResponsibleID NewStateID
2903            NewPriorityID
2904        );
2905    }
2906
2907    # define the dynamic fields to show based on the object type
2908    my $ObjectType = ['Ticket'];
2909
2910    # get config of frontend module
2911    my $Config = $Kernel::OM->Get('Kernel::Config')->Get("Ticket::Frontend::$Self->{Action}");
2912
2913    # only screens that add notes can modify Article dynamic fields
2914    if ( $Config->{Note} ) {
2915        $ObjectType = [ 'Ticket', 'Article' ];
2916    }
2917
2918    # get the dynamic fields for this screen
2919    my $DynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
2920        Valid       => 1,
2921        ObjectType  => $ObjectType,
2922        FieldFilter => $Config->{DynamicField} || {},
2923    );
2924
2925    # cycle through the activated Dynamic Fields for this screen
2926    DYNAMICFIELD:
2927    for my $DynamicFieldConfig ( @{$DynamicField} ) {
2928        next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
2929
2930        my $IsACLReducible = $Kernel::OM->Get('Kernel::System::DynamicField::Backend')->HasBehavior(
2931            DynamicFieldConfig => $DynamicFieldConfig,
2932            Behavior           => 'IsACLReducible',
2933        );
2934        next DYNAMICFIELD if !$IsACLReducible;
2935
2936        push @UpdatableFields, 'DynamicField_' . $DynamicFieldConfig->{Name};
2937    }
2938
2939    return \@UpdatableFields;
2940}
2941
2942sub _GetQuotedReplyBody {
2943    my ( $Self, %Param ) = @_;
2944
2945    # get needed objects
2946    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
2947    my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
2948
2949    if ( $LayoutObject->{BrowserRichText} ) {
2950
2951        # rewrap body if exists
2952        if ( $Param{Body} ) {
2953            $Param{Body} =~ s/\t/ /g;
2954            my $Quote = $LayoutObject->Ascii2Html(
2955                Text           => $ConfigObject->Get('Ticket::Frontend::Quote') || '',
2956                HTMLResultMode => 1,
2957            );
2958            if ($Quote) {
2959
2960                # quote text
2961                $Param{Body} = "<blockquote type=\"cite\">$Param{Body}</blockquote>\n";
2962
2963                # cleanup not compat. tags
2964                $Param{Body} = $LayoutObject->RichTextDocumentCleanup(
2965                    String => $Param{Body},
2966                );
2967
2968                my $ResponseFormat = $LayoutObject->{LanguageObject}
2969                    ->FormatTimeString( $Param{CreateTime}, 'DateFormat', 'NoSeconds' );
2970                $ResponseFormat .= ' - ' . $Param{From} . ' ';
2971                $ResponseFormat
2972                    .= $LayoutObject->{LanguageObject}->Translate('wrote') . ':';
2973
2974                $Param{Body} = $ResponseFormat . $Param{Body};
2975
2976            }
2977            else {
2978                $Param{Body} = "<br/>" . $Param{Body};
2979
2980                if ( $Param{CreateTime} ) {
2981                    $Param{Body} = $LayoutObject->{LanguageObject}->Translate('Date') .
2982                        ": $Param{CreateTime}<br/>" . $Param{Body};
2983                }
2984
2985                for (qw(Subject ReplyTo Reply-To Cc To From)) {
2986                    if ( $Param{$_} ) {
2987                        $Param{Body} = $LayoutObject->{LanguageObject}->Translate($_) .
2988                            ": $Param{$_}<br/>" . $Param{Body};
2989                    }
2990                }
2991
2992                my $From = $LayoutObject->Ascii2RichText(
2993                    String => $Param{From},
2994                );
2995
2996                my $MessageFrom = $LayoutObject->{LanguageObject}->Translate('Message from');
2997                my $EndMessage  = $LayoutObject->{LanguageObject}->Translate('End message');
2998
2999                $Param{Body} = "<br/>---- $MessageFrom $From ---<br/><br/>" . $Param{Body};
3000                $Param{Body} .= "<br/>---- $EndMessage ---<br/>";
3001            }
3002        }
3003    }
3004    else {
3005
3006        # prepare body, subject, ReplyTo ...
3007        # rewrap body if exists
3008        if ( $Param{Body} ) {
3009            $Param{Body} =~ s/\t/ /g;
3010            my $Quote = $ConfigObject->Get('Ticket::Frontend::Quote');
3011            if ($Quote) {
3012                $Param{Body} =~ s/\n/\n$Quote /g;
3013                $Param{Body} = "\n$Quote " . $Param{Body};
3014
3015                my $ResponseFormat = $LayoutObject->{LanguageObject}
3016                    ->FormatTimeString( $Param{CreateTime}, 'DateFormat', 'NoSeconds' );
3017                $ResponseFormat .= ' - ' . $Param{From} . ' ';
3018                $ResponseFormat
3019                    .= $LayoutObject->{LanguageObject}->Translate('wrote') . ":\n";
3020
3021                $Param{Body} = $ResponseFormat . $Param{Body};
3022            }
3023            else {
3024                $Param{Body} = "\n" . $Param{Body};
3025                if ( $Param{CreateTime} ) {
3026                    $Param{Body} = $LayoutObject->{LanguageObject}->Translate('Date') .
3027                        ": $Param{CreateTime}\n" . $Param{Body};
3028                }
3029
3030                for (qw(Subject ReplyTo Reply-To Cc To From)) {
3031                    if ( $Param{$_} ) {
3032                        $Param{Body} = $LayoutObject->{LanguageObject}->Translate($_) .
3033                            ": $Param{$_}\n" . $Param{Body};
3034                    }
3035                }
3036
3037                my $MessageFrom = $LayoutObject->{LanguageObject}->Translate('Message from');
3038                my $EndMessage  = $LayoutObject->{LanguageObject}->Translate('End message');
3039
3040                $Param{Body} = "\n---- $MessageFrom $Param{From} ---\n\n" . $Param{Body};
3041                $Param{Body} .= "\n---- $EndMessage ---\n";
3042            }
3043        }
3044    }
3045
3046    return $Param{Body};
3047}
3048
3049sub _GetStandardTemplates {
3050    my ( $Self, %Param ) = @_;
3051
3052    # get create templates
3053    my %Templates;
3054
3055    # check needed
3056    return \%Templates if !$Param{QueueID} && !$Param{TicketID};
3057
3058    my $QueueID = $Param{QueueID} || '';
3059    if ( !$Param{QueueID} && $Param{TicketID} ) {
3060
3061        # get QueueID from the ticket
3062        my %Ticket = $Kernel::OM->Get('Kernel::System::Ticket')->TicketGet(
3063            TicketID      => $Param{TicketID},
3064            DynamicFields => 0,
3065            UserID        => $Self->{UserID},
3066        );
3067        $QueueID = $Ticket{QueueID} || '';
3068    }
3069
3070    # fetch all std. templates
3071    my %StandardTemplates = $Kernel::OM->Get('Kernel::System::Queue')->QueueStandardTemplateMemberList(
3072        QueueID       => $QueueID,
3073        TemplateTypes => 1,
3074    );
3075
3076    # return empty hash if there are no templates for this screen
3077    return \%Templates if !IsHashRefWithData( $StandardTemplates{Note} );
3078
3079    # return just the templates for this screen
3080    return $StandardTemplates{Note};
3081}
3082
3083sub _GetTypes {
3084    my ( $Self, %Param ) = @_;
3085
3086    # get type
3087    my %Type;
3088    if ( $Param{QueueID} || $Param{TicketID} ) {
3089        %Type = $Kernel::OM->Get('Kernel::System::Ticket')->TicketTypeList(
3090            %Param,
3091            TicketID => $Self->{TicketID},
3092            Action   => $Self->{Action},
3093            UserID   => $Self->{UserID},
3094        );
3095    }
3096    return \%Type;
3097}
3098
3099sub _GetQueues {
3100    my ( $Self, %Param ) = @_;
3101
3102    # Get Queues.
3103    my %Queues = $Kernel::OM->Get('Kernel::System::Ticket')->TicketMoveList(
3104        %Param,
3105        TicketID => $Self->{TicketID},
3106        UserID   => $Self->{UserID},
3107        Action   => $Self->{Action},
3108        Type     => 'move_into',
3109    );
3110    return \%Queues;
3111}
3112
31131;
3114