1828 lines
64 KiB
Perl
1828 lines
64 KiB
Perl
# --
|
|
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
|
|
# --
|
|
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
|
|
# the enclosed file COPYING for license information (GPL). If you
|
|
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
|
|
# --
|
|
|
|
package Kernel::Modules::CustomerTicketZoom;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use Kernel::System::VariableCheck qw(:all);
|
|
use Kernel::Language qw(Translatable);
|
|
|
|
our $ObjectManagerDisabled = 1;
|
|
|
|
sub new {
|
|
my ( $Type, %Param ) = @_;
|
|
|
|
# allocate new hash for object
|
|
my $Self = {%Param};
|
|
bless( $Self, $Type );
|
|
|
|
# get form id
|
|
$Self->{FormID} = $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam( Param => 'FormID' );
|
|
|
|
# create form id
|
|
if ( !$Self->{FormID} ) {
|
|
$Self->{FormID} = $Kernel::OM->Get('Kernel::System::Web::UploadCache')->FormIDCreate();
|
|
}
|
|
|
|
return $Self;
|
|
}
|
|
|
|
sub Run {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
|
|
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
|
|
|
my $TicketNumber = $ParamObject->GetParam( Param => 'TicketNumber' );
|
|
|
|
# ticket id lookup
|
|
if ( !$Self->{TicketID} && $TicketNumber ) {
|
|
$Self->{TicketID} = $TicketObject->TicketIDLookup(
|
|
TicketNumber => $ParamObject->GetParam( Param => 'TicketNumber' ),
|
|
UserID => $Self->{UserID},
|
|
);
|
|
}
|
|
|
|
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
|
|
|
# customers should not get to know that whether an ticket exists or not
|
|
# if a ticket does not exist, show a "no permission" screen
|
|
if ( $TicketNumber && !$Self->{TicketID} ) {
|
|
return $LayoutObject->CustomerNoPermission( WithHeader => 'yes' );
|
|
}
|
|
|
|
# check needed stuff
|
|
if ( !$Self->{TicketID} ) {
|
|
my $Output = $LayoutObject->CustomerHeader(
|
|
Title => Translatable('Error'),
|
|
);
|
|
$Output .= $LayoutObject->CustomerError(
|
|
Message => Translatable('Need TicketID!'),
|
|
);
|
|
$Output .= $LayoutObject->CustomerFooter();
|
|
return $Output;
|
|
}
|
|
|
|
# check permissions
|
|
my $Access = $TicketObject->TicketCustomerPermission(
|
|
Type => 'ro',
|
|
TicketID => $Self->{TicketID},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
# error screen, don't show ticket
|
|
if ( !$Access ) {
|
|
return $LayoutObject->CustomerNoPermission( WithHeader => 'yes' );
|
|
}
|
|
|
|
# get ticket data
|
|
my %Ticket = $TicketObject->TicketGet(
|
|
TicketID => $Self->{TicketID},
|
|
DynamicFields => 1,
|
|
);
|
|
|
|
# get ACL restrictions
|
|
my %PossibleActions;
|
|
my $Counter = 0;
|
|
|
|
# get config object
|
|
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
|
|
|
# get all registered Actions
|
|
if ( ref $ConfigObject->Get('CustomerFrontend::Module') eq 'HASH' ) {
|
|
|
|
my %Actions = %{ $ConfigObject->Get('CustomerFrontend::Module') };
|
|
|
|
# only use those Actions that starts with Customer
|
|
%PossibleActions = map { ++$Counter => $_ }
|
|
grep { substr( $_, 0, length 'Customer' ) eq 'Customer' }
|
|
sort keys %Actions;
|
|
}
|
|
|
|
my $ACL = $TicketObject->TicketAcl(
|
|
Data => \%PossibleActions,
|
|
Action => $Self->{Action},
|
|
TicketID => $Self->{TicketID},
|
|
ReturnType => 'Action',
|
|
ReturnSubType => '-',
|
|
CustomerUserID => $Self->{UserID},
|
|
);
|
|
|
|
my %AclAction = %PossibleActions;
|
|
if ($ACL) {
|
|
%AclAction = $TicketObject->TicketAclActionData();
|
|
}
|
|
|
|
# check if ACL restrictions exist
|
|
my %AclActionLookup = reverse %AclAction;
|
|
|
|
# show error screen if ACL prohibits this action
|
|
if ( !$AclActionLookup{ $Self->{Action} } ) {
|
|
return $LayoutObject->NoPermission( WithHeader => 'yes' );
|
|
}
|
|
|
|
my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');
|
|
|
|
# get all articles of this ticket, that are visible for the customer
|
|
my @ArticleList = $ArticleObject->ArticleList(
|
|
TicketID => $Self->{TicketID},
|
|
IsVisibleForCustomer => 1,
|
|
DynamicFields => 0,
|
|
);
|
|
|
|
my @ArticleBox;
|
|
my $ArticleBackendObject;
|
|
|
|
ARTICLEMETADATA:
|
|
for my $ArticleMetaData (@ArticleList) {
|
|
|
|
next ARTICLEMETADATA if !$ArticleMetaData;
|
|
next ARTICLEMETADATA if !IsHashRefWithData($ArticleMetaData);
|
|
|
|
$ArticleBackendObject = $ArticleObject->BackendForArticle( %{$ArticleMetaData} );
|
|
|
|
my %ArticleData = $ArticleBackendObject->ArticleGet(
|
|
TicketID => $Self->{TicketID},
|
|
ArticleID => $ArticleMetaData->{ArticleID},
|
|
RealNames => 1,
|
|
);
|
|
|
|
# Get channel specific fields
|
|
my %ArticleFields = $LayoutObject->ArticleFields(
|
|
TicketID => $Self->{TicketID},
|
|
ArticleID => $ArticleMetaData->{ArticleID},
|
|
);
|
|
|
|
$ArticleData{Subject} = $ArticleFields{Subject};
|
|
$ArticleData{FromRealname} = $ArticleFields{Sender};
|
|
|
|
# Get attachment index.
|
|
my %AtmIndex = $ArticleBackendObject->ArticleAttachmentIndex(
|
|
ArticleID => $ArticleMetaData->{ArticleID},
|
|
ExcludePlainText => 1,
|
|
ExcludeHTMLBody => 1,
|
|
ExcludeInline => 1,
|
|
);
|
|
|
|
if ( IsHashRefWithData( \%AtmIndex ) ) {
|
|
$ArticleData{Attachment} = \%AtmIndex;
|
|
}
|
|
|
|
push @ArticleBox, \%ArticleData;
|
|
}
|
|
|
|
my %GetParam;
|
|
for my $Key (qw(Subject Body StateID PriorityID FromChatID FromChat)) {
|
|
$GetParam{$Key} = $ParamObject->GetParam( Param => $Key );
|
|
}
|
|
|
|
# get Dynamic fields from ParamObject
|
|
my %DynamicFieldValues;
|
|
|
|
my $Config = $ConfigObject->Get("Ticket::Frontend::$Self->{Action}");
|
|
my $FollowUpDynamicFieldFilter = $Config->{FollowUpDynamicField};
|
|
|
|
if ( $GetParam{FromChatID} ) {
|
|
if ( !$ConfigObject->Get('ChatEngine::Active') ) {
|
|
return $LayoutObject->FatalError(
|
|
Message => Translatable('Chat is not active.'),
|
|
);
|
|
}
|
|
|
|
# Check chat participant
|
|
my %ChatParticipant = $Kernel::OM->Get('Kernel::System::Chat')->ChatParticipantCheck(
|
|
ChatID => $GetParam{FromChatID},
|
|
ChatterType => 'Customer',
|
|
ChatterID => $Self->{UserID},
|
|
);
|
|
|
|
if ( !%ChatParticipant ) {
|
|
return $LayoutObject->FatalError(
|
|
Message => Translatable('No permission.'),
|
|
);
|
|
}
|
|
}
|
|
|
|
# get the dynamic fields for ticket object
|
|
my $FollowUpDynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
|
|
Valid => 1,
|
|
ObjectType => [ 'Ticket', 'Article' ],
|
|
FieldFilter => $FollowUpDynamicFieldFilter || {},
|
|
);
|
|
|
|
my $BackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
|
|
|
|
# cycle trough the activated Dynamic Fields for this screen
|
|
DYNAMICFIELD:
|
|
for my $DynamicFieldConfig ( @{$FollowUpDynamicField} ) {
|
|
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
|
|
|
|
# extract the dynamic field value from the web request
|
|
$DynamicFieldValues{ $DynamicFieldConfig->{Name} } = $BackendObject->EditFieldValueGet(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
ParamObject => $ParamObject,
|
|
LayoutObject => $LayoutObject,
|
|
);
|
|
}
|
|
|
|
# convert dynamic field values into a structure for ACLs
|
|
my %DynamicFieldACLParameters;
|
|
DYNAMICFIELD:
|
|
for my $DynamicField ( sort keys %DynamicFieldValues ) {
|
|
next DYNAMICFIELD if !$DynamicField;
|
|
next DYNAMICFIELD if !$DynamicFieldValues{$DynamicField};
|
|
|
|
$DynamicFieldACLParameters{ 'DynamicField_' . $DynamicField } = $DynamicFieldValues{$DynamicField};
|
|
}
|
|
$GetParam{DynamicField} = \%DynamicFieldACLParameters;
|
|
|
|
if ( $Self->{Subaction} eq 'AJAXUpdate' ) {
|
|
|
|
# get TicketID
|
|
if ( !$GetParam{TicketID} ) {
|
|
$GetParam{TicketID} =
|
|
$Self->{TicketID} ||
|
|
$ParamObject->GetParam( Param => 'TicketID' );
|
|
}
|
|
|
|
my $CustomerUser = $Self->{UserID};
|
|
|
|
my $Priorities = $Self->_GetPriorities(
|
|
%GetParam,
|
|
CustomerUserID => $CustomerUser || '',
|
|
TicketID => $Self->{TicketID},
|
|
);
|
|
my $NextStates = $Self->_GetNextStates(
|
|
%GetParam,
|
|
CustomerUserID => $CustomerUser || '',
|
|
TicketID => $Self->{TicketID},
|
|
);
|
|
|
|
# update Dynamic Fields Possible Values via AJAX
|
|
my @DynamicFieldAJAX;
|
|
|
|
# cycle trough the activated Dynamic Fields for this screen
|
|
DYNAMICFIELD:
|
|
for my $DynamicFieldConfig ( @{$FollowUpDynamicField} ) {
|
|
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
|
|
|
|
my $IsACLReducible = $BackendObject->HasBehavior(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
Behavior => 'IsACLReducible',
|
|
);
|
|
next DYNAMICFIELD if !$IsACLReducible;
|
|
|
|
my $PossibleValues = $BackendObject->PossibleValuesGet(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
);
|
|
|
|
# convert possible values key => value to key => key for ACLs using a Hash slice
|
|
my %AclData = %{$PossibleValues};
|
|
@AclData{ keys %AclData } = keys %AclData;
|
|
|
|
# set possible values filter from ACLs
|
|
my $ACL = $TicketObject->TicketAcl(
|
|
%GetParam,
|
|
Action => $Self->{Action},
|
|
ReturnType => 'Ticket',
|
|
ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name},
|
|
Data => \%AclData,
|
|
CustomerUserID => $Self->{UserID},
|
|
);
|
|
if ($ACL) {
|
|
my %Filter = $TicketObject->TicketAclData();
|
|
|
|
# convert Filer key => key back to key => value using map
|
|
%{$PossibleValues} = map { $_ => $PossibleValues->{$_} } keys %Filter;
|
|
}
|
|
|
|
my $DataValues = $BackendObject->BuildSelectionDataGet(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
PossibleValues => $PossibleValues,
|
|
Value => $DynamicFieldValues{ $DynamicFieldConfig->{Name} },
|
|
) || $PossibleValues;
|
|
|
|
# add dynamic field to the list of fields to update
|
|
push @DynamicFieldAJAX, {
|
|
Name => 'DynamicField_' . $DynamicFieldConfig->{Name},
|
|
Data => $DataValues,
|
|
SelectedID => $DynamicFieldValues{ $DynamicFieldConfig->{Name} },
|
|
Translation => $DynamicFieldConfig->{Config}->{TranslatableValues} || 0,
|
|
Max => 100,
|
|
};
|
|
}
|
|
|
|
my $JSON = $LayoutObject->BuildSelectionJSON(
|
|
[
|
|
{
|
|
Name => 'PriorityID',
|
|
Data => $Priorities,
|
|
SelectedID => $GetParam{PriorityID},
|
|
Translation => 1,
|
|
Max => 100,
|
|
},
|
|
{
|
|
Name => 'StateID',
|
|
Data => $NextStates,
|
|
SelectedID => $GetParam{StateID},
|
|
Translation => 1,
|
|
Max => 100,
|
|
},
|
|
@DynamicFieldAJAX,
|
|
],
|
|
);
|
|
return $LayoutObject->Attachment(
|
|
ContentType => 'application/json; charset=' . $LayoutObject->{Charset},
|
|
Content => $JSON,
|
|
Type => 'inline',
|
|
NoCache => 1,
|
|
);
|
|
}
|
|
|
|
# end AJAX Update
|
|
|
|
# save, if browser link message was closed
|
|
elsif ( $Self->{Subaction} eq 'BrowserLinkMessage' ) {
|
|
|
|
$Kernel::OM->Get('Kernel::System::CustomerUser')->SetPreferences(
|
|
UserID => $Self->{UserID},
|
|
Key => 'UserCustomerDoNotShowBrowserLinkMessage',
|
|
Value => 1,
|
|
);
|
|
|
|
return $LayoutObject->Attachment(
|
|
ContentType => 'text/html',
|
|
Content => 1,
|
|
Type => 'inline',
|
|
NoCache => 1,
|
|
);
|
|
}
|
|
|
|
# check follow up
|
|
elsif ( $Self->{Subaction} eq 'Store' ) {
|
|
|
|
# challenge token check for write action
|
|
$LayoutObject->ChallengeTokenCheck( Type => 'Customer' );
|
|
|
|
my $NextScreen = $Self->{NextScreen} || $Config->{NextScreenAfterFollowUp};
|
|
my %Error;
|
|
|
|
# get follow up option (possible or not)
|
|
my $QueueObject = $Kernel::OM->Get('Kernel::System::Queue');
|
|
my $FollowUpPossible = $QueueObject->GetFollowUpOption(
|
|
QueueID => $Ticket{QueueID},
|
|
);
|
|
|
|
# get lock option (should be the ticket locked - if closed - after the follow up)
|
|
my $Lock = $QueueObject->GetFollowUpLockOption(
|
|
QueueID => $Ticket{QueueID},
|
|
);
|
|
|
|
my $StateObject = $Kernel::OM->Get('Kernel::System::State');
|
|
|
|
# get ticket state details
|
|
my %State = $StateObject->StateGet(
|
|
ID => $Ticket{StateID},
|
|
);
|
|
if ( $FollowUpPossible =~ /(new ticket|reject)/i && $State{TypeName} =~ /^close/i ) {
|
|
my $Output = $LayoutObject->CustomerHeader(
|
|
Title => Translatable('Error'),
|
|
);
|
|
$Output .= $LayoutObject->CustomerWarning(
|
|
Message => Translatable('Can\'t reopen ticket, not possible in this queue!'),
|
|
Comment => Translatable('Create a new ticket!'),
|
|
);
|
|
$Output .= $LayoutObject->CustomerFooter();
|
|
return $Output;
|
|
}
|
|
|
|
# rewrap body if no rich text is used
|
|
if ( $GetParam{Body} && !$LayoutObject->{BrowserRichText} ) {
|
|
$GetParam{Body} = $LayoutObject->WrapPlainText(
|
|
MaxCharacters => $ConfigObject->Get('Ticket::Frontend::TextAreaNote'),
|
|
PlainText => $GetParam{Body},
|
|
);
|
|
}
|
|
|
|
my $UploadCacheObject = $Kernel::OM->Get('Kernel::System::Web::UploadCache');
|
|
|
|
if ( $GetParam{FromChat} ) {
|
|
$Error{FromChat} = 1;
|
|
$GetParam{FollowUpVisible} = 'Visible';
|
|
if ( $GetParam{FromChatID} ) {
|
|
my @ChatMessages = $Kernel::OM->Get('Kernel::System::Chat')->ChatMessageList(
|
|
ChatID => $GetParam{FromChatID},
|
|
);
|
|
if (@ChatMessages) {
|
|
for my $Message (@ChatMessages) {
|
|
$Message->{MessageText} = $LayoutObject->Ascii2Html(
|
|
Text => $Message->{MessageText},
|
|
LinkFeature => 1,
|
|
);
|
|
}
|
|
$GetParam{ChatMessages} = \@ChatMessages;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !$GetParam{FromChat} ) {
|
|
if ( !$GetParam{Body} || $GetParam{Body} eq '<br />' ) {
|
|
$Error{RichTextInvalid} = 'ServerError';
|
|
$GetParam{FollowUpVisible} = 'Visible';
|
|
}
|
|
}
|
|
|
|
# create html strings for all dynamic fields
|
|
my %DynamicFieldHTML;
|
|
|
|
# cycle trough the activated Dynamic Fields for this screen
|
|
DYNAMICFIELD:
|
|
for my $DynamicFieldConfig ( @{$FollowUpDynamicField} ) {
|
|
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
|
|
|
|
my $PossibleValuesFilter;
|
|
|
|
my $IsACLReducible = $BackendObject->HasBehavior(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
Behavior => 'IsACLReducible',
|
|
);
|
|
|
|
if ($IsACLReducible) {
|
|
|
|
# get PossibleValues
|
|
my $PossibleValues = $BackendObject->PossibleValuesGet(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
);
|
|
|
|
# check if field has PossibleValues property in its configuration
|
|
if ( IsHashRefWithData($PossibleValues) ) {
|
|
|
|
# convert possible values key => value to key => key for ACLs using a Hash slice
|
|
my %AclData = %{$PossibleValues};
|
|
@AclData{ keys %AclData } = keys %AclData;
|
|
|
|
# set possible values filter from ACLs
|
|
my $ACL = $TicketObject->TicketAcl(
|
|
%GetParam,
|
|
Action => $Self->{Action},
|
|
TicketID => $Self->{TicketID},
|
|
ReturnType => 'Ticket',
|
|
ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name},
|
|
Data => \%AclData,
|
|
CustomerUserID => $Self->{UserID},
|
|
);
|
|
if ($ACL) {
|
|
my %Filter = $TicketObject->TicketAclData();
|
|
|
|
# convert Filer key => key back to key => value using map
|
|
%{$PossibleValuesFilter} = map { $_ => $PossibleValues->{$_} }
|
|
keys %Filter;
|
|
}
|
|
}
|
|
}
|
|
|
|
my $ValidationResult = $BackendObject->EditFieldValueValidate(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
PossibleValuesFilter => $PossibleValuesFilter,
|
|
ParamObject => $ParamObject,
|
|
Mandatory =>
|
|
$Config->{FollowUpDynamicField}->{ $DynamicFieldConfig->{Name} }
|
|
== 2,
|
|
);
|
|
|
|
if ( !IsHashRefWithData($ValidationResult) ) {
|
|
my $Output = $LayoutObject->CustomerHeader(
|
|
Title => Translatable('Error'),
|
|
);
|
|
$Output .= $LayoutObject->CustomerError(
|
|
Message => $LayoutObject->{LanguageObject}
|
|
->Translate( 'Could not perform validation on field %s!', $DynamicFieldConfig->{Label} ),
|
|
Comment => Translatable('Please contact the administrator.'),
|
|
);
|
|
$Output .= $LayoutObject->CustomerFooter();
|
|
return $Output;
|
|
}
|
|
|
|
# propagate validation error to the Error variable to be detected by the frontend
|
|
if ( $ValidationResult->{ServerError} ) {
|
|
$Error{ $DynamicFieldConfig->{Name} } = ' ServerError';
|
|
|
|
# make FollowUp visible to correctly show the error
|
|
$GetParam{FollowUpVisible} = 'Visible';
|
|
}
|
|
|
|
# get field html
|
|
$DynamicFieldHTML{ $DynamicFieldConfig->{Name} } = $BackendObject->EditFieldRender(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
PossibleValuesFilter => $PossibleValuesFilter,
|
|
Mandatory =>
|
|
$Config->{FollowUpDynamicField}->{ $DynamicFieldConfig->{Name} } == 2,
|
|
ServerError => $ValidationResult->{ServerError} || '',
|
|
ErrorMessage => $ValidationResult->{ErrorMessage} || '',
|
|
LayoutObject => $LayoutObject,
|
|
ParamObject => $ParamObject,
|
|
AJAXUpdate => 1,
|
|
UpdatableFields => $Self->_GetFieldsToUpdate(),
|
|
);
|
|
}
|
|
|
|
# show edit again
|
|
if (%Error) {
|
|
|
|
# generate output
|
|
my $Output = $LayoutObject->CustomerHeader( Value => $Ticket{TicketNumber} );
|
|
$Output .= $LayoutObject->CustomerNavigationBar();
|
|
$Output .= $Self->_Mask(
|
|
TicketID => $Self->{TicketID},
|
|
ArticleBox => \@ArticleBox,
|
|
Errors => \%Error,
|
|
%Ticket,
|
|
TicketState => $Ticket{State},
|
|
TicketStateID => $Ticket{StateID},
|
|
%GetParam,
|
|
DynamicFieldHTML => \%DynamicFieldHTML,
|
|
);
|
|
$Output .= $LayoutObject->CustomerFooter();
|
|
return $Output;
|
|
}
|
|
|
|
# unlock ticket if agent is on vacation or invalid
|
|
my $LockAction;
|
|
if ( $Ticket{OwnerID} ) {
|
|
my %User = $Kernel::OM->Get('Kernel::System::User')->GetUserData(
|
|
UserID => $Ticket{OwnerID},
|
|
);
|
|
if ( %User && ( $User{OutOfOfficeMessage} || $User{ValidID} ne '1' ) ) {
|
|
$LockAction = 'unlock';
|
|
}
|
|
}
|
|
|
|
# set lock if ticket was closed
|
|
if (
|
|
!$LockAction
|
|
&& $Lock
|
|
&& $State{TypeName} =~ /^close/i && $Ticket{OwnerID} ne '1'
|
|
)
|
|
{
|
|
|
|
$LockAction = 'lock';
|
|
}
|
|
|
|
if ($LockAction) {
|
|
$TicketObject->TicketLockSet(
|
|
TicketID => $Self->{TicketID},
|
|
Lock => $LockAction,
|
|
UserID => $ConfigObject->Get('CustomerPanelUserID'),
|
|
);
|
|
}
|
|
|
|
my $From = "\"$Self->{UserFullname}\" <$Self->{UserEmail}>";
|
|
my $MimeType = 'text/plain';
|
|
if ( $LayoutObject->{BrowserRichText} ) {
|
|
$MimeType = 'text/html';
|
|
|
|
# verify html document
|
|
$GetParam{Body} = $LayoutObject->RichTextDocumentComplete(
|
|
String => $GetParam{Body},
|
|
);
|
|
}
|
|
|
|
# set state
|
|
my $NextState = $Config->{StateDefault} || 'open';
|
|
if ( $GetParam{StateID} && $Config->{State} ) {
|
|
my %NextStateData = $StateObject->StateGet( ID => $GetParam{StateID} );
|
|
$NextState = $NextStateData{Name};
|
|
}
|
|
|
|
# change state if
|
|
# customer set another state
|
|
# or the ticket is not new
|
|
if ( $Ticket{StateType} !~ /^new/ || $GetParam{StateID} ) {
|
|
$TicketObject->StateSet(
|
|
TicketID => $Self->{TicketID},
|
|
State => $NextState,
|
|
UserID => $ConfigObject->Get('CustomerPanelUserID'),
|
|
);
|
|
|
|
# set unlock on close state
|
|
if ( $NextState =~ /^close/i ) {
|
|
$TicketObject->TicketLockSet(
|
|
TicketID => $Self->{TicketID},
|
|
Lock => 'unlock',
|
|
UserID => $ConfigObject->Get('CustomerPanelUserID'),
|
|
);
|
|
}
|
|
}
|
|
|
|
# set priority
|
|
if ( $Config->{Priority} && $GetParam{PriorityID} ) {
|
|
$TicketObject->TicketPrioritySet(
|
|
TicketID => $Self->{TicketID},
|
|
PriorityID => $GetParam{PriorityID},
|
|
UserID => $ConfigObject->Get('CustomerPanelUserID'),
|
|
);
|
|
}
|
|
|
|
my $ArticleID = $Kernel::OM->Get('Kernel::System::Ticket::Article::Backend::Internal')->ArticleCreate(
|
|
TicketID => $Self->{TicketID},
|
|
IsVisibleForCustomer => 1,
|
|
SenderType => $Config->{SenderType},
|
|
From => $From,
|
|
Subject => $GetParam{Subject},
|
|
Body => $GetParam{Body},
|
|
MimeType => $MimeType,
|
|
Charset => $LayoutObject->{UserCharset},
|
|
UserID => $ConfigObject->Get('CustomerPanelUserID'),
|
|
OrigHeader => {
|
|
From => $From,
|
|
To => 'System',
|
|
Subject => $GetParam{Subject},
|
|
Body => $LayoutObject->RichText2Ascii( String => $GetParam{Body} ),
|
|
},
|
|
HistoryType => $Config->{HistoryType},
|
|
HistoryComment => $Config->{HistoryComment} || '%%',
|
|
AutoResponseType => ( $ConfigObject->Get('AutoResponseForWebTickets') ) ? 'auto follow up' : '',
|
|
);
|
|
if ( !$ArticleID ) {
|
|
my $Output = $LayoutObject->CustomerHeader(
|
|
Title => Translatable('Error'),
|
|
);
|
|
$Output .= $LayoutObject->CustomerError();
|
|
$Output .= $LayoutObject->CustomerFooter();
|
|
return $Output;
|
|
}
|
|
|
|
# get pre loaded attachment
|
|
my @AttachmentData = $UploadCacheObject->FormIDGetAllFilesData(
|
|
FormID => $Self->{FormID}
|
|
);
|
|
|
|
# get submit attachment
|
|
my %UploadStuff = $ParamObject->GetUploadAll(
|
|
Param => 'file_upload',
|
|
);
|
|
if (%UploadStuff) {
|
|
push @AttachmentData, \%UploadStuff;
|
|
}
|
|
|
|
# write attachments
|
|
ATTACHMENT:
|
|
for my $Attachment (@AttachmentData) {
|
|
|
|
my $ContentID = $Attachment->{ContentID};
|
|
if (
|
|
$ContentID
|
|
&& ( $Attachment->{ContentType} =~ /image/i )
|
|
&& ( $Attachment->{Disposition} eq 'inline' )
|
|
)
|
|
{
|
|
my $ContentIDHTMLQuote = $LayoutObject->Ascii2Html(
|
|
Text => $ContentID,
|
|
);
|
|
|
|
# workaround for link encode of rich text editor, see bug#5053
|
|
my $ContentIDLinkEncode = $LayoutObject->LinkEncode($ContentID);
|
|
$GetParam{Body} =~ s/(ContentID=)$ContentIDLinkEncode/$1$ContentID/g;
|
|
|
|
# ignore attachment if not linked in body
|
|
if ( $GetParam{Body} !~ /(\Q$ContentIDHTMLQuote\E|\Q$ContentID\E)/i ) {
|
|
next ATTACHMENT;
|
|
}
|
|
}
|
|
|
|
# write existing file to backend
|
|
$ArticleBackendObject->ArticleWriteAttachment(
|
|
%{$Attachment},
|
|
ArticleID => $ArticleID,
|
|
UserID => $ConfigObject->Get('CustomerPanelUserID'),
|
|
);
|
|
}
|
|
|
|
# set ticket dynamic fields
|
|
# cycle trough the activated Dynamic Fields for this screen
|
|
DYNAMICFIELD:
|
|
for my $DynamicFieldConfig ( @{$FollowUpDynamicField} ) {
|
|
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
|
|
next DYNAMICFIELD if $DynamicFieldConfig->{ObjectType} ne 'Ticket';
|
|
|
|
# set the value
|
|
my $Success = $BackendObject->ValueSet(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
ObjectID => $Self->{TicketID},
|
|
Value => $DynamicFieldValues{ $DynamicFieldConfig->{Name} },
|
|
UserID => $ConfigObject->Get('CustomerPanelUserID'),
|
|
);
|
|
}
|
|
|
|
# set article dynamic fields
|
|
# cycle trough the activated Dynamic Fields for this screen
|
|
DYNAMICFIELD:
|
|
for my $DynamicFieldConfig ( @{$FollowUpDynamicField} ) {
|
|
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
|
|
next DYNAMICFIELD if $DynamicFieldConfig->{ObjectType} ne 'Article';
|
|
|
|
# set the value
|
|
my $Success = $BackendObject->ValueSet(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
ObjectID => $ArticleID,
|
|
Value => $DynamicFieldValues{ $DynamicFieldConfig->{Name} },
|
|
UserID => $ConfigObject->Get('CustomerPanelUserID'),
|
|
);
|
|
}
|
|
|
|
# if user clicked submit on the main screen
|
|
# store also chat protocol
|
|
if ( !$GetParam{FromChat} && $GetParam{FromChatID} ) {
|
|
my $ChatObject = $Kernel::OM->Get('Kernel::System::Chat');
|
|
my %Chat = $ChatObject->ChatGet(
|
|
ChatID => $GetParam{FromChatID},
|
|
);
|
|
my @ChatMessageList = $ChatObject->ChatMessageList(
|
|
ChatID => $GetParam{FromChatID},
|
|
);
|
|
my $ChatArticleID;
|
|
|
|
if (@ChatMessageList) {
|
|
for my $Message (@ChatMessageList) {
|
|
$Message->{MessageText} = $LayoutObject->Ascii2Html(
|
|
Text => $Message->{MessageText},
|
|
LinkFeature => 1,
|
|
);
|
|
}
|
|
|
|
my $ArticleChatBackend = $ArticleObject->BackendForChannel( ChannelName => 'Chat' );
|
|
|
|
$ChatArticleID = $ArticleChatBackend->ArticleCreate(
|
|
TicketID => $Self->{TicketID},
|
|
SenderType => $Config->{SenderType},
|
|
ChatMessageList => \@ChatMessageList,
|
|
IsVisibleForCustomer => 1,
|
|
UserID => $ConfigObject->Get('CustomerPanelUserID'),
|
|
HistoryType => $Config->{HistoryType},
|
|
HistoryComment => $Config->{HistoryComment} || '%%',
|
|
);
|
|
}
|
|
if ($ChatArticleID) {
|
|
$ChatObject->ChatDelete(
|
|
ChatID => $GetParam{FromChatID},
|
|
);
|
|
}
|
|
}
|
|
|
|
# remove pre submited attachments
|
|
$UploadCacheObject->FormIDRemove( FormID => $Self->{FormID} );
|
|
|
|
# redirect to zoom view
|
|
return $LayoutObject->Redirect(
|
|
OP => "Action=$NextScreen;TicketID=$Self->{TicketID}",
|
|
);
|
|
}
|
|
|
|
$Ticket{TmpCounter} = 0;
|
|
$Ticket{TicketTimeUnits} = $TicketObject->TicketAccountedTimeGet(
|
|
TicketID => $Ticket{TicketID},
|
|
);
|
|
|
|
# set priority from ticket as fallback
|
|
$GetParam{PriorityID} ||= $Ticket{PriorityID};
|
|
|
|
# create html strings for all dynamic fields
|
|
my %DynamicFieldHTML;
|
|
|
|
# cycle trough the activated Dynamic Fields for this screen
|
|
DYNAMICFIELD:
|
|
for my $DynamicFieldConfig ( @{$FollowUpDynamicField} ) {
|
|
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
|
|
|
|
my $PossibleValuesFilter;
|
|
|
|
my $IsACLReducible = $BackendObject->HasBehavior(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
Behavior => 'IsACLReducible',
|
|
);
|
|
|
|
if ($IsACLReducible) {
|
|
|
|
# get PossibleValues
|
|
my $PossibleValues = $BackendObject->PossibleValuesGet(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
);
|
|
|
|
# check if field has PossibleValues property in its configuration
|
|
if ( IsHashRefWithData($PossibleValues) ) {
|
|
|
|
# convert possible values key => value to key => key for ACLs using a Hash slice
|
|
my %AclData = %{$PossibleValues};
|
|
@AclData{ keys %AclData } = keys %AclData;
|
|
|
|
# set possible values filter from ACLs
|
|
my $ACL = $TicketObject->TicketAcl(
|
|
%GetParam,
|
|
Action => $Self->{Action},
|
|
TicketID => $Self->{TicketID},
|
|
ReturnType => 'Ticket',
|
|
ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name},
|
|
Data => \%AclData,
|
|
CustomerUserID => $Self->{UserID},
|
|
);
|
|
if ($ACL) {
|
|
my %Filter = $TicketObject->TicketAclData();
|
|
|
|
# convert Filer key => key back to key => value using map
|
|
%{$PossibleValuesFilter} = map { $_ => $PossibleValues->{$_} }
|
|
keys %Filter;
|
|
}
|
|
}
|
|
}
|
|
|
|
# get field html
|
|
$DynamicFieldHTML{ $DynamicFieldConfig->{Name} } = $BackendObject->EditFieldRender(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
PossibleValuesFilter => $PossibleValuesFilter,
|
|
Mandatory =>
|
|
$Config->{FollowUpDynamicField}->{ $DynamicFieldConfig->{Name} } == 2,
|
|
LayoutObject => $LayoutObject,
|
|
ParamObject => $ParamObject,
|
|
AJAXUpdate => 1,
|
|
UpdatableFields => $Self->_GetFieldsToUpdate(),
|
|
Value => $Ticket{ 'DynamicField_' . $DynamicFieldConfig->{Name} },
|
|
);
|
|
}
|
|
|
|
# generate output
|
|
my $Output = $LayoutObject->CustomerHeader( Value => $Ticket{TicketNumber} );
|
|
$Output .= $LayoutObject->CustomerNavigationBar();
|
|
|
|
# show ticket
|
|
if ( $Self->{Subaction} eq 'ShowHTMLeMail' ) {
|
|
|
|
# if it is a html email, drop normal header
|
|
$Ticket{ShowHTMLeMail} = 1;
|
|
$Output = '';
|
|
}
|
|
$Output .= $Self->_Mask(
|
|
TicketID => $Self->{TicketID},
|
|
ArticleBox => \@ArticleBox,
|
|
%Ticket,
|
|
TicketState => $Ticket{State},
|
|
TicketStateID => $Ticket{StateID},
|
|
%GetParam,
|
|
AclAction => \%AclAction,
|
|
DynamicFieldHTML => \%DynamicFieldHTML,
|
|
);
|
|
|
|
# return if HTML email
|
|
if ( $Self->{Subaction} eq 'ShowHTMLeMail' ) {
|
|
return $Output;
|
|
}
|
|
|
|
# add footer
|
|
$Output .= $LayoutObject->CustomerFooter();
|
|
|
|
# return output
|
|
return $Output;
|
|
}
|
|
|
|
sub _GetNextStates {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my %NextStates;
|
|
if ( $Param{TicketID} ) {
|
|
%NextStates = $Kernel::OM->Get('Kernel::System::Ticket')->TicketStateList(
|
|
%Param,
|
|
Action => $Self->{Action},
|
|
CustomerUserID => $Self->{UserID},
|
|
|
|
# %Param could contain Ticket Type as only Type, it should not be sent
|
|
Type => undef,
|
|
);
|
|
}
|
|
return \%NextStates;
|
|
}
|
|
|
|
sub _GetPriorities {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# get priority
|
|
my %Priorities;
|
|
if ( $Param{TicketID} ) {
|
|
%Priorities = $Kernel::OM->Get('Kernel::System::Ticket')->TicketPriorityList(
|
|
%Param,
|
|
Action => $Self->{Action},
|
|
CustomerUserID => $Self->{UserID},
|
|
);
|
|
}
|
|
return \%Priorities;
|
|
}
|
|
|
|
sub _Mask {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
|
|
my $UploadCacheObject = $Kernel::OM->Get('Kernel::System::Web::UploadCache');
|
|
|
|
my %AclActionLookup;
|
|
if ( $Param{AclAction} ) {
|
|
%AclActionLookup = reverse %{ $Param{AclAction} };
|
|
}
|
|
|
|
$Param{FormID} = $Self->{FormID};
|
|
|
|
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
|
|
|
# show back link
|
|
if ( $Self->{LastScreenOverview} ) {
|
|
$LayoutObject->Block(
|
|
Name => 'Back',
|
|
Data => \%Param,
|
|
);
|
|
}
|
|
|
|
# build article stuff
|
|
my $SelectedArticleID = $ParamObject->GetParam( Param => 'ArticleID' ) || '';
|
|
my $BaseLink = $LayoutObject->{Baselink} . "TicketID=$Self->{TicketID}&";
|
|
my @ArticleBox = @{ $Param{ArticleBox} };
|
|
|
|
# prepare errors!
|
|
if ( $Param{Errors} ) {
|
|
for my $KeyError ( sort keys %{ $Param{Errors} } ) {
|
|
$Param{$KeyError} = $LayoutObject->Ascii2Html( Text => $Param{Errors}->{$KeyError} );
|
|
}
|
|
}
|
|
|
|
my $ArticleID = '';
|
|
my $LastCustomerArticle = '';
|
|
if (@ArticleBox) {
|
|
|
|
# get last customer article
|
|
my $CounterArray = 0;
|
|
my $LastCustomerArticleID;
|
|
$LastCustomerArticle = $#ArticleBox;
|
|
|
|
for my $ArticleTmp (@ArticleBox) {
|
|
my %Article = %{$ArticleTmp};
|
|
|
|
# if it is a customer article
|
|
if ( $Article{SenderType} eq 'customer' ) {
|
|
$LastCustomerArticleID = $Article{ArticleID};
|
|
$LastCustomerArticle = $CounterArray;
|
|
}
|
|
$CounterArray++;
|
|
if ( ($SelectedArticleID) && ( $SelectedArticleID eq $Article{ArticleID} ) ) {
|
|
$ArticleID = $Article{ArticleID};
|
|
}
|
|
}
|
|
|
|
# try to use the latest non internal agent article
|
|
if ( !$ArticleID ) {
|
|
$ArticleID = $ArticleBox[-1]->{ArticleID};
|
|
$SelectedArticleID = $ArticleID;
|
|
}
|
|
|
|
# try to use the latest customer article
|
|
if ( !$ArticleID && $LastCustomerArticleID ) {
|
|
$ArticleID = $LastCustomerArticleID;
|
|
$SelectedArticleID = $ArticleID;
|
|
}
|
|
}
|
|
|
|
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
|
|
|
# set display options
|
|
$Param{Hook} = $ConfigObject->Get('Ticket::Hook') || 'Ticket#';
|
|
|
|
my $Config = $ConfigObject->Get("Ticket::Frontend::$Self->{Action}");
|
|
|
|
# ticket accounted time
|
|
if ( $Config->{ZoomTimeDisplay} ) {
|
|
$LayoutObject->Block(
|
|
Name => 'TicketTimeUnits',
|
|
Data => \%Param,
|
|
);
|
|
}
|
|
|
|
# ticket priority flag
|
|
if ( $Config->{AttributesView}->{Priority} ) {
|
|
$LayoutObject->Block(
|
|
Name => 'PriorityFlag',
|
|
Data => \%Param,
|
|
);
|
|
}
|
|
|
|
# ticket type
|
|
if ( $ConfigObject->Get('Ticket::Type') && $Config->{AttributesView}->{Type} ) {
|
|
|
|
my %Type = $Kernel::OM->Get('Kernel::System::Type')->TypeGet(
|
|
Name => $Param{Type},
|
|
);
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'Type',
|
|
Data => {
|
|
Valid => $Type{ValidID},
|
|
%Param,
|
|
}
|
|
);
|
|
}
|
|
|
|
# ticket service
|
|
if (
|
|
$Param{Service}
|
|
&&
|
|
$ConfigObject->Get('Ticket::Service')
|
|
&& $Config->{AttributesView}->{Service}
|
|
)
|
|
{
|
|
$LayoutObject->Block(
|
|
Name => 'Service',
|
|
Data => \%Param,
|
|
);
|
|
if (
|
|
$Param{SLA}
|
|
&& $ConfigObject->Get('Ticket::Service')
|
|
&& $Config->{AttributesView}->{SLA}
|
|
)
|
|
{
|
|
$LayoutObject->Block(
|
|
Name => 'SLA',
|
|
Data => \%Param,
|
|
);
|
|
}
|
|
}
|
|
|
|
# ticket state
|
|
if ( $Config->{AttributesView}->{State} ) {
|
|
$LayoutObject->Block(
|
|
Name => 'State',
|
|
Data => \%Param,
|
|
);
|
|
}
|
|
|
|
# ticket priority
|
|
if ( $Config->{AttributesView}->{Priority} ) {
|
|
$LayoutObject->Block(
|
|
Name => 'Priority',
|
|
Data => \%Param,
|
|
);
|
|
}
|
|
|
|
# ticket queue
|
|
if ( $Config->{AttributesView}->{Queue} ) {
|
|
$LayoutObject->Block(
|
|
Name => 'Queue',
|
|
Data => \%Param,
|
|
);
|
|
}
|
|
|
|
my $AgentUserObject = $Kernel::OM->Get('Kernel::System::User');
|
|
|
|
# ticket owner
|
|
if ( $Config->{AttributesView}->{Owner} ) {
|
|
my $OwnerName = $AgentUserObject->UserName(
|
|
UserID => $Param{OwnerID},
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'Owner',
|
|
Data => { OwnerName => $OwnerName },
|
|
);
|
|
}
|
|
|
|
# ticket responsible
|
|
if (
|
|
$ConfigObject->Get('Ticket::Responsible')
|
|
&&
|
|
$Config->{AttributesView}->{Responsible}
|
|
)
|
|
{
|
|
my $ResponsibleName = $AgentUserObject->UserName(
|
|
UserID => $Param{ResponsibleID},
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'Responsible',
|
|
Data => {
|
|
ResponsibleName => $ResponsibleName,
|
|
},
|
|
);
|
|
}
|
|
|
|
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
|
|
|
# check if ticket is normal or process ticket
|
|
my $IsProcessTicket = $TicketObject->TicketCheckForProcessType(
|
|
'TicketID' => $Self->{TicketID},
|
|
);
|
|
|
|
# show process widget and activity dialogs on process tickets
|
|
if ($IsProcessTicket) {
|
|
|
|
# get the DF where the ProcessEntityID is stored
|
|
my $ProcessEntityIDField = 'DynamicField_'
|
|
. $ConfigObject->Get("Process::DynamicFieldProcessManagementProcessID");
|
|
|
|
# get the DF where the AtivityEntityID is stored
|
|
my $ActivityEntityIDField = 'DynamicField_'
|
|
. $ConfigObject->Get("Process::DynamicFieldProcessManagementActivityID");
|
|
|
|
# create additional objects for process management
|
|
my $ActivityObject = $Kernel::OM->Get('Kernel::System::ProcessManagement::Activity');
|
|
my $ActivityDialogObject = $Kernel::OM->Get('Kernel::System::ProcessManagement::ActivityDialog');
|
|
my $ProcessObject = $Kernel::OM->Get('Kernel::System::ProcessManagement::Process');
|
|
my $ProcessData = $ProcessObject->ProcessGet(
|
|
ProcessEntityID => $Param{$ProcessEntityIDField},
|
|
);
|
|
|
|
my $ActivityData = $ActivityObject->ActivityGet(
|
|
Interface => 'CustomerInterface',
|
|
ActivityEntityID => $Param{$ActivityEntityIDField},
|
|
);
|
|
|
|
# output process information in the sidebar
|
|
$LayoutObject->Block(
|
|
Name => 'ProcessData',
|
|
Data => {
|
|
Process => $ProcessData->{Name} || '',
|
|
Activity => $ActivityData->{Name} || '',
|
|
},
|
|
);
|
|
|
|
# output the process widget the the main screen
|
|
$LayoutObject->Block(
|
|
Name => 'ProcessWidget',
|
|
Data => {
|
|
WidgetTitle => $Param{WidgetTitle},
|
|
},
|
|
);
|
|
|
|
# get next activity dialogs
|
|
my $NextActivityDialogs;
|
|
if ( $Param{$ActivityEntityIDField} ) {
|
|
$NextActivityDialogs = $ActivityData;
|
|
}
|
|
|
|
if ( IsHashRefWithData($NextActivityDialogs) ) {
|
|
|
|
# we don't need the whole Activity config,
|
|
# just the Activity Dialogs of the current Activity
|
|
if ( IsHashRefWithData( $NextActivityDialogs->{ActivityDialog} ) ) {
|
|
%{$NextActivityDialogs} = %{ $NextActivityDialogs->{ActivityDialog} };
|
|
}
|
|
else {
|
|
$NextActivityDialogs = {};
|
|
}
|
|
|
|
# we have to check if the current user has the needed permissions to view the
|
|
# different activity dialogs, so we loop over every activity dialog and check if there
|
|
# is a permission configured. If there is a permission configured we check this
|
|
# and display/hide the activity dialog link
|
|
my %PermissionRights;
|
|
my %PermissionActivityDialogList;
|
|
ACTIVITYDIALOGPERMISSION:
|
|
for my $Index ( sort { $a <=> $b } keys %{$NextActivityDialogs} ) {
|
|
my $CurrentActivityDialogEntityID = $NextActivityDialogs->{$Index};
|
|
my $CurrentActivityDialog = $ActivityDialogObject->ActivityDialogGet(
|
|
ActivityDialogEntityID => $CurrentActivityDialogEntityID,
|
|
Interface => 'CustomerInterface',
|
|
);
|
|
|
|
# create an interface lookup-list
|
|
my %InterfaceLookup = map { $_ => 1 } @{ $CurrentActivityDialog->{Interface} };
|
|
|
|
next ACTIVITYDIALOGPERMISSION if !$InterfaceLookup{CustomerInterface};
|
|
|
|
if ( $CurrentActivityDialog->{Permission} ) {
|
|
|
|
# performance-boost/cache
|
|
if ( !defined $PermissionRights{ $CurrentActivityDialog->{Permission} } ) {
|
|
$PermissionRights{ $CurrentActivityDialog->{Permission} }
|
|
= $TicketObject->TicketCustomerPermission(
|
|
Type => $CurrentActivityDialog->{Permission},
|
|
TicketID => $Param{TicketID},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
}
|
|
|
|
if ( !$PermissionRights{ $CurrentActivityDialog->{Permission} } ) {
|
|
next ACTIVITYDIALOGPERMISSION;
|
|
}
|
|
}
|
|
|
|
$PermissionActivityDialogList{$Index} = $CurrentActivityDialogEntityID;
|
|
}
|
|
|
|
# reduce next activity dialogs to the ones that have permissions
|
|
$NextActivityDialogs = \%PermissionActivityDialogList;
|
|
|
|
# get ACL restrictions
|
|
my $ACL = $TicketObject->TicketAcl(
|
|
Data => \%PermissionActivityDialogList,
|
|
TicketID => $Param{TicketID},
|
|
Action => $Self->{Action},
|
|
ReturnType => 'ActivityDialog',
|
|
ReturnSubType => '-',
|
|
CustomerUserID => $Self->{UserID},
|
|
);
|
|
|
|
if ($ACL) {
|
|
%{$NextActivityDialogs} = $TicketObject->TicketAclData();
|
|
}
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'NextActivities',
|
|
);
|
|
|
|
for my $NextActivityDialogKey ( sort { $a <=> $b } keys %{$NextActivityDialogs} ) {
|
|
my $ActivityDialogData = $ActivityDialogObject->ActivityDialogGet(
|
|
ActivityDialogEntityID => $NextActivityDialogs->{$NextActivityDialogKey},
|
|
Interface => 'CustomerInterface',
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'ActivityDialog',
|
|
Data => {
|
|
ActivityDialogEntityID => $NextActivityDialogs->{$NextActivityDialogKey},
|
|
Name => $ActivityDialogData->{Name},
|
|
ProcessEntityID => $Param{$ProcessEntityIDField},
|
|
TicketID => $Param{TicketID},
|
|
},
|
|
);
|
|
}
|
|
|
|
if ( !IsHashRefWithData($NextActivityDialogs) ) {
|
|
$LayoutObject->Block(
|
|
Name => 'NoActivityDialog',
|
|
Data => {},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
# get dynamic field config for frontend module
|
|
my $DynamicFieldFilter = $Config->{DynamicField};
|
|
my $FollowUpDynamicFieldFilter = $Config->{FollowUpDynamicField};
|
|
|
|
my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField');
|
|
|
|
# get the dynamic fields for ticket object
|
|
my $FollowUpDynamicField = $DynamicFieldObject->DynamicFieldListGet(
|
|
Valid => 1,
|
|
ObjectType => [ 'Ticket', 'Article' ],
|
|
FieldFilter => $FollowUpDynamicFieldFilter || {},
|
|
);
|
|
|
|
my $BackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
|
|
|
|
# reduce the dynamic fields to only the ones that are designed for customer interface
|
|
my @CustomerDynamicFields;
|
|
DYNAMICFIELD:
|
|
for my $DynamicFieldConfig ( @{$FollowUpDynamicField} ) {
|
|
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
|
|
|
|
my $IsCustomerInterfaceCapable = $BackendObject->HasBehavior(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
Behavior => 'IsCustomerInterfaceCapable',
|
|
);
|
|
next DYNAMICFIELD if !$IsCustomerInterfaceCapable;
|
|
|
|
push @CustomerDynamicFields, $DynamicFieldConfig;
|
|
}
|
|
$FollowUpDynamicField = \@CustomerDynamicFields;
|
|
|
|
# get the dynamic fields for ticket object
|
|
my $DynamicField = $DynamicFieldObject->DynamicFieldListGet(
|
|
Valid => 1,
|
|
ObjectType => ['Ticket'],
|
|
FieldFilter => $DynamicFieldFilter || {},
|
|
);
|
|
|
|
# cycle trough the activated Dynamic Fields for ticket object
|
|
DYNAMICFIELD:
|
|
for my $DynamicFieldConfig ( @{$DynamicField} ) {
|
|
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
|
|
|
|
# skip the dynamic field if is not designed for customer interface
|
|
my $IsCustomerInterfaceCapable = $BackendObject->HasBehavior(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
Behavior => 'IsCustomerInterfaceCapable',
|
|
);
|
|
next DYNAMICFIELD if !$IsCustomerInterfaceCapable;
|
|
|
|
my $Value = $BackendObject->ValueGet(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
ObjectID => $Param{TicketID},
|
|
);
|
|
|
|
next DYNAMICFIELD if !defined $Value;
|
|
next DYNAMICFIELD if $Value eq "";
|
|
|
|
# get print string for this dynamic field
|
|
my $ValueStrg = $BackendObject->DisplayValueRender(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
Value => $Value,
|
|
ValueMaxChars => 25,
|
|
LayoutObject => $LayoutObject,
|
|
);
|
|
|
|
my $Label = $DynamicFieldConfig->{Label};
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'TicketDynamicField',
|
|
Data => {
|
|
Label => $Label,
|
|
},
|
|
);
|
|
|
|
if ( $DynamicFieldConfig->{Config}->{Link} ) {
|
|
$LayoutObject->Block(
|
|
Name => 'TicketDynamicFieldLink',
|
|
Data => {
|
|
Value => $ValueStrg->{Value},
|
|
Title => $ValueStrg->{Title},
|
|
Link => $DynamicFieldConfig->{Config}->{Link},
|
|
LinkPreview => $DynamicFieldConfig->{Config}->{LinkPreview},
|
|
$DynamicFieldConfig->{Name} => $ValueStrg->{Value},
|
|
},
|
|
);
|
|
}
|
|
else {
|
|
$LayoutObject->Block(
|
|
Name => 'TicketDynamicFieldPlain',
|
|
Data => {
|
|
Value => $ValueStrg->{Value},
|
|
Title => $ValueStrg->{Title},
|
|
},
|
|
);
|
|
}
|
|
|
|
# example of dynamic fields order customization
|
|
$LayoutObject->Block(
|
|
Name => 'TicketDynamicField_' . $DynamicFieldConfig->{Name},
|
|
Data => {
|
|
Label => $Label,
|
|
Value => $ValueStrg->{Value},
|
|
Title => $ValueStrg->{Title},
|
|
},
|
|
);
|
|
}
|
|
|
|
# check is chat available and is starting a chat from ticket zoom available
|
|
my $ChatConfig = $ConfigObject->Get('Ticket::Customer::StartChatFromTicket');
|
|
if (
|
|
$ChatConfig->{Allowed}
|
|
&& $ConfigObject->Get('ChatEngine::Active')
|
|
)
|
|
{
|
|
# get all queues to tickets relations
|
|
my %QueueChatChannelRelations = $Kernel::OM->Get('Kernel::System::ChatChannel')->ChatChannelQueuesGet(
|
|
CustomerInterface => 1,
|
|
);
|
|
|
|
# if a support chat channel is set for this queue
|
|
if ( $QueueChatChannelRelations{ $Param{QueueID} } ) {
|
|
|
|
# check is starting a chat from ticket zoom allowed to all user or only to ticket customer user_agent
|
|
if (
|
|
!$ChatConfig->{Permissions}
|
|
|| ( $Param{CustomerUserID} eq $Self->{UserID} )
|
|
)
|
|
{
|
|
# add chat channelID to Param
|
|
$Param{ChatChannelID} = $QueueChatChannelRelations{ $Param{QueueID} };
|
|
|
|
if ( $Param{ChatChannelID} ) {
|
|
|
|
# check should chat be available only if there are available agents in this chat channelID
|
|
if ( !$ChatConfig->{AllowChatOnlyIfAgentsAvailable} ) {
|
|
|
|
# show start a chat icon
|
|
$LayoutObject->Block(
|
|
Name => 'Chat',
|
|
Data => {
|
|
%Param,
|
|
},
|
|
);
|
|
}
|
|
else {
|
|
# Get channels data
|
|
my %ChatChannelData = $Kernel::OM->Get('Kernel::System::ChatChannel')->ChatChannelGet(
|
|
ChatChannelID => $Param{ChatChannelID},
|
|
);
|
|
|
|
# Get all online users
|
|
my @OnlineUsers = $Kernel::OM->Get('Kernel::System::Chat')->OnlineUserList(
|
|
UserType => 'User',
|
|
);
|
|
my $AvailabilityCheck
|
|
= $Kernel::OM->Get('Kernel::Config')->Get("ChatEngine::CustomerFrontend::AvailabilityCheck")
|
|
|| 0;
|
|
my %AvailableUsers;
|
|
if ($AvailabilityCheck) {
|
|
%AvailableUsers = $Kernel::OM->Get('Kernel::System::Chat')->AvailableUsersGet(
|
|
Key => 'ExternalChannels',
|
|
);
|
|
}
|
|
|
|
# Rename hash key: ChatChannelID => Key
|
|
$ChatChannelData{Key} = delete $ChatChannelData{ChatChannelID};
|
|
|
|
if ($AvailabilityCheck) {
|
|
my $UserAvailable = 0;
|
|
|
|
AVAILABLE_USER:
|
|
for my $AvailableUser ( sort keys %AvailableUsers ) {
|
|
if ( grep {/^$ChatChannelData{Key}$/} @{ $AvailableUsers{$AvailableUser} } ) {
|
|
$UserAvailable = 1;
|
|
last AVAILABLE_USER;
|
|
}
|
|
}
|
|
|
|
if ($UserAvailable) {
|
|
$LayoutObject->Block(
|
|
Name => 'Chat',
|
|
Data => {
|
|
%Param,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# print option
|
|
if (
|
|
$ConfigObject->Get('CustomerFrontend::Module')->{CustomerTicketPrint}
|
|
&& $AclActionLookup{CustomerTicketPrint}
|
|
)
|
|
{
|
|
$LayoutObject->Block(
|
|
Name => 'Print',
|
|
Data => \%Param,
|
|
);
|
|
}
|
|
|
|
# get params
|
|
my $ZoomExpand = $ParamObject->GetParam( Param => 'ZoomExpand' );
|
|
if ( !defined $ZoomExpand ) {
|
|
$ZoomExpand = $ConfigObject->Get('Ticket::Frontend::CustomerTicketZoom')->{CustomerZoomExpand} || '';
|
|
}
|
|
|
|
# Expand option
|
|
my $ExpandOption = ( $ZoomExpand ? 'One' : 'All' );
|
|
my $ExpandText = ( $ZoomExpand ? 'Show one article' : 'Show all articles' );
|
|
$LayoutObject->Block(
|
|
Name => 'Expand',
|
|
Data => {
|
|
ZoomExpand => !$ZoomExpand,
|
|
ExpandOption => $ExpandOption,
|
|
ExpandText => $ExpandText,
|
|
%Param,
|
|
},
|
|
);
|
|
|
|
my %Ticket = $TicketObject->TicketGet(
|
|
TicketID => $Self->{TicketID},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
my $CommunicationChannelObject = $Kernel::OM->Get('Kernel::System::CommunicationChannel');
|
|
|
|
my $ShownArticles;
|
|
my $LastSenderType = '';
|
|
my $ArticleHTML = '';
|
|
|
|
for my $ArticleTmp (@ArticleBox) {
|
|
my %Article = %$ArticleTmp;
|
|
|
|
# check if article should be expanded (visible)
|
|
if ( $SelectedArticleID eq $Article{ArticleID} || $ZoomExpand ) {
|
|
$Article{Class} = 'Visible';
|
|
$ShownArticles++;
|
|
}
|
|
|
|
# Calculate difference between article create time and now in seconds.
|
|
my $ArticleCreateTimeObject = $Kernel::OM->Create(
|
|
'Kernel::System::DateTime',
|
|
ObjectParams => {
|
|
String => $Article{CreateTime},
|
|
},
|
|
);
|
|
my $Delta = $ArticleCreateTimeObject->Delta(
|
|
DateTimeObject => $Kernel::OM->Create('Kernel::System::DateTime'),
|
|
);
|
|
|
|
# do some html quoting
|
|
$Article{Age} = $LayoutObject->CustomerAge(
|
|
Age => $Delta->{AbsoluteSeconds},
|
|
Space => ' ',
|
|
);
|
|
|
|
$Article{Subject} = $TicketObject->TicketSubjectClean(
|
|
TicketNumber => $Ticket{TicketNumber},
|
|
Subject => $Article{Subject} || '',
|
|
Size => 150,
|
|
);
|
|
|
|
$LastSenderType = $Article{SenderType};
|
|
|
|
if ( !defined $Self->{ShowBrowserLinkMessage} ) {
|
|
my %UserPreferences = $Kernel::OM->Get('Kernel::System::CustomerUser')->GetPreferences(
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
if ( $UserPreferences{UserCustomerDoNotShowBrowserLinkMessage} ) {
|
|
$Self->{ShowBrowserLinkMessage} = 0;
|
|
}
|
|
else {
|
|
$Self->{ShowBrowserLinkMessage} = 1;
|
|
}
|
|
}
|
|
|
|
my $ArticleBackendObject = $Kernel::OM->Get('Kernel::System::Ticket::Article')->BackendForArticle(
|
|
TicketID => $Param{TicketID},
|
|
ArticleID => $Article{ArticleID},
|
|
);
|
|
|
|
my $ChannelName = $ArticleBackendObject->ChannelNameGet();
|
|
|
|
$ArticleHTML .= $Kernel::OM->Get("Kernel::Output::HTML::TicketZoom::Customer::$ChannelName")->ArticleRender(
|
|
TicketID => $Param{TicketID},
|
|
ArticleID => $Article{ArticleID},
|
|
Class => $Article{Class},
|
|
UserID => $Self->{UserID},
|
|
ShowBrowserLinkMessage => $Self->{ShowBrowserLinkMessage},
|
|
ArticleExpanded => $SelectedArticleID eq $Article{ArticleID} || $ZoomExpand,
|
|
ArticleAge => $Article{Age},
|
|
);
|
|
}
|
|
|
|
# TODO: Refactor
|
|
# if there are no viewable articles show NoArticles message
|
|
if ( !@ArticleBox ) {
|
|
$Param{NoArticles} = 1;
|
|
}
|
|
|
|
my %Article;
|
|
if (@ArticleBox) {
|
|
|
|
my $ArticleOB = {};
|
|
if ($LastCustomerArticle) {
|
|
$ArticleOB = $ArticleBox[$LastCustomerArticle];
|
|
}
|
|
|
|
%Article = %$ArticleOB;
|
|
|
|
# if no customer articles found use ticket values
|
|
if ( !IsHashRefWithData( \%Article ) ) {
|
|
%Article = %Param;
|
|
if ( !$Article{StateID} ) {
|
|
$Article{StateID} = $Param{TicketStateID};
|
|
}
|
|
}
|
|
|
|
my $ArticleArray = 0;
|
|
for my $ArticleTmp (@ArticleBox) {
|
|
my %ArticleTmp1 = %$ArticleTmp;
|
|
if ( $ArticleID eq $ArticleTmp1{ArticleID} ) {
|
|
%Article = %ArticleTmp1;
|
|
}
|
|
}
|
|
|
|
# just body if html email
|
|
if ( $Param{ShowHTMLeMail} ) {
|
|
|
|
# generate output
|
|
return $LayoutObject->Attachment(
|
|
Filename => $ConfigObject->Get('Ticket::Hook')
|
|
. "-$Ticket{TicketNumber}-$Self->{TicketID}-$Article{ArticleID}",
|
|
Type => 'inline',
|
|
ContentType => "$Article{MimeType}; charset=$Article{Charset}",
|
|
Content => $Article{Body},
|
|
);
|
|
}
|
|
}
|
|
|
|
# fallback to ticket info if there is no article
|
|
if ( !IsHashRefWithData( \%Article ) ) {
|
|
%Article = %Param;
|
|
if ( !$Article{StateID} ) {
|
|
$Article{StateID} = $Param{TicketStateID};
|
|
}
|
|
}
|
|
|
|
# check follow up permissions
|
|
my $FollowUpPossible = $Kernel::OM->Get('Kernel::System::Queue')->GetFollowUpOption(
|
|
QueueID => $Ticket{QueueID},
|
|
);
|
|
my %State = $Kernel::OM->Get('Kernel::System::State')->StateGet(
|
|
ID => $Ticket{StateID},
|
|
);
|
|
if (
|
|
$TicketObject->TicketCustomerPermission(
|
|
Type => 'update',
|
|
TicketID => $Self->{TicketID},
|
|
UserID => $Self->{UserID},
|
|
)
|
|
&& (
|
|
( $FollowUpPossible !~ /(new ticket|reject)/i && $State{TypeName} =~ /^close/i )
|
|
|| $State{TypeName} !~ /^close|merged/i
|
|
)
|
|
)
|
|
{
|
|
|
|
my $DynamicFieldNames = $Self->_GetFieldsToUpdate(
|
|
OnlyDynamicFields => 1,
|
|
);
|
|
|
|
# send data to JS
|
|
$LayoutObject->AddJSData(
|
|
Key => 'DynamicFieldNames',
|
|
Value => $DynamicFieldNames,
|
|
);
|
|
|
|
# check subject
|
|
if ( !$Param{Subject} ) {
|
|
$Param{Subject} = "Re: " . ( $Param{Title} // '' );
|
|
}
|
|
$LayoutObject->Block(
|
|
Name => 'FollowUp',
|
|
Data => \%Param,
|
|
);
|
|
|
|
# add rich text editor
|
|
if ( $LayoutObject->{BrowserRichText} ) {
|
|
|
|
# use height/width defined for this screen
|
|
$Param{RichTextHeight} = $Config->{RichTextHeight} || 0;
|
|
$Param{RichTextWidth} = $Config->{RichTextWidth} || 0;
|
|
|
|
# set up customer rich text editor
|
|
$LayoutObject->CustomerSetRichTextParameters(
|
|
Data => \%Param,
|
|
);
|
|
}
|
|
|
|
# build next states string
|
|
if ( $Config->{State} ) {
|
|
my $NextStates = $Self->_GetNextStates(
|
|
%Param,
|
|
TicketID => $Self->{TicketID},
|
|
);
|
|
my %StateSelected;
|
|
if ( $Param{StateID} ) {
|
|
$StateSelected{SelectedID} = $Param{StateID};
|
|
}
|
|
else {
|
|
$StateSelected{SelectedValue} = $Config->{StateDefault};
|
|
}
|
|
$Param{NextStatesStrg} = $LayoutObject->BuildSelection(
|
|
Data => $NextStates,
|
|
Name => 'StateID',
|
|
%StateSelected,
|
|
Class => 'Modernize',
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'FollowUpState',
|
|
Data => \%Param,
|
|
);
|
|
}
|
|
|
|
# get priority
|
|
if ( $Config->{Priority} ) {
|
|
my $Priorities = $Self->_GetPriorities(
|
|
%Param,
|
|
TicketID => $Self->{TicketID},
|
|
);
|
|
my %PrioritySelected;
|
|
if ( $Param{PriorityID} ) {
|
|
$PrioritySelected{SelectedID} = $Param{PriorityID};
|
|
}
|
|
else {
|
|
$PrioritySelected{SelectedValue} = $Config->{PriorityDefault}
|
|
|| '3 normal';
|
|
}
|
|
$Param{PriorityStrg} = $LayoutObject->BuildSelection(
|
|
Data => $Priorities,
|
|
Name => 'PriorityID',
|
|
%PrioritySelected,
|
|
Class => 'Modernize',
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'FollowUpPriority',
|
|
Data => \%Param,
|
|
);
|
|
}
|
|
|
|
# Dynamic fields
|
|
# cycle trough the activated Dynamic Fields for this screen
|
|
DYNAMICFIELD:
|
|
for my $DynamicFieldConfig ( @{$FollowUpDynamicField} ) {
|
|
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
|
|
|
|
# skip fields that HTML could not be retrieved
|
|
next DYNAMICFIELD if !IsHashRefWithData(
|
|
$Param{DynamicFieldHTML}->{ $DynamicFieldConfig->{Name} }
|
|
);
|
|
|
|
# get the html strings form $Param
|
|
my $DynamicFieldHTML = $Param{DynamicFieldHTML}->{ $DynamicFieldConfig->{Name} };
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'FollowUpDynamicField',
|
|
Data => {
|
|
Name => $DynamicFieldConfig->{Name},
|
|
Label => $DynamicFieldHTML->{Label},
|
|
Field => $DynamicFieldHTML->{Field},
|
|
},
|
|
);
|
|
|
|
# example of dynamic fields order customization
|
|
$LayoutObject->Block(
|
|
Name => 'FollowUpDynamicField_' . $DynamicFieldConfig->{Name},
|
|
Data => {
|
|
Name => $DynamicFieldConfig->{Name},
|
|
Label => $DynamicFieldHTML->{Label},
|
|
Field => $DynamicFieldHTML->{Field},
|
|
},
|
|
);
|
|
}
|
|
|
|
# show attachments
|
|
# get all attachments meta data
|
|
my @Attachments = $UploadCacheObject->FormIDGetAllFilesMeta(
|
|
FormID => $Self->{FormID},
|
|
);
|
|
|
|
ATTACHMENT:
|
|
for my $Attachment (@Attachments) {
|
|
if (
|
|
$Attachment->{ContentID}
|
|
&& $LayoutObject->{BrowserRichText}
|
|
&& ( $Attachment->{ContentType} =~ /image/i )
|
|
&& ( $Attachment->{Disposition} eq 'inline' )
|
|
)
|
|
{
|
|
next ATTACHMENT;
|
|
}
|
|
|
|
push @{ $Param{AttachmentList} }, $Attachment;
|
|
}
|
|
}
|
|
|
|
# select the output template
|
|
return $LayoutObject->Output(
|
|
TemplateFile => 'CustomerTicketZoom',
|
|
Data => {
|
|
%Article,
|
|
%Param,
|
|
Articles => $ArticleHTML,
|
|
},
|
|
);
|
|
}
|
|
|
|
sub _GetFieldsToUpdate {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my @UpdatableFields;
|
|
|
|
# set the fields that can be updatable via AJAXUpdate
|
|
if ( !$Param{OnlyDynamicFields} ) {
|
|
@UpdatableFields = qw( ServiceID SLAID PriorityID StateID );
|
|
}
|
|
|
|
my $Config = $Kernel::OM->Get('Kernel::Config')->Get("Ticket::Frontend::$Self->{Action}");
|
|
my $FollowUpDynamicFieldFilter = $Config->{FollowUpDynamicField};
|
|
|
|
# get the dynamic fields for ticket object
|
|
my $FollowUpDynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
|
|
Valid => 1,
|
|
ObjectType => [ 'Ticket', 'Article' ],
|
|
FieldFilter => $FollowUpDynamicFieldFilter || {},
|
|
);
|
|
|
|
# cycle trough the activated Dynamic Fields for this screen
|
|
DYNAMICFIELD:
|
|
for my $DynamicFieldConfig ( @{$FollowUpDynamicField} ) {
|
|
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
|
|
|
|
my $IsACLReducible = $Kernel::OM->Get('Kernel::System::DynamicField::Backend')->HasBehavior(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
Behavior => 'IsACLReducible',
|
|
);
|
|
next DYNAMICFIELD if !$IsACLReducible;
|
|
|
|
push @UpdatableFields, 'DynamicField_' . $DynamicFieldConfig->{Name};
|
|
}
|
|
|
|
return \@UpdatableFields;
|
|
}
|
|
|
|
1;
|