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