1809 lines
62 KiB
Perl
1809 lines
62 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::AgentTicketBulk;
|
|
|
|
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 );
|
|
|
|
return $Self;
|
|
}
|
|
|
|
sub Run {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# get needed objects
|
|
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
|
|
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
|
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
|
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
|
|
|
if ( $Self->{Subaction} eq 'CancelAndUnlockTickets' ) {
|
|
|
|
my @TicketIDs = grep {$_}
|
|
$ParamObject->GetArray( Param => 'LockedTicketID' );
|
|
|
|
# challenge token check for write action
|
|
$LayoutObject->ChallengeTokenCheck();
|
|
|
|
# check needed stuff
|
|
if ( !@TicketIDs ) {
|
|
return $LayoutObject->ErrorScreen(
|
|
Message => Translatable('Can\'t lock Tickets, no TicketIDs are given!'),
|
|
Comment => Translatable('Please contact the administrator.'),
|
|
);
|
|
}
|
|
|
|
my $Message = '';
|
|
|
|
TICKET_ID:
|
|
for my $TicketID (@TicketIDs) {
|
|
|
|
my $Access = $TicketObject->TicketPermission(
|
|
Type => 'lock',
|
|
TicketID => $TicketID,
|
|
UserID => $Self->{UserID}
|
|
);
|
|
|
|
# error screen, don't show ticket
|
|
if ( !$Access ) {
|
|
return $LayoutObject->NoPermission( WithHeader => 'yes' );
|
|
}
|
|
|
|
# set unlock
|
|
my $Lock = $TicketObject->TicketLockSet(
|
|
TicketID => $TicketID,
|
|
Lock => 'unlock',
|
|
UserID => $Self->{UserID},
|
|
);
|
|
if ( !$Lock ) {
|
|
$Message .= "$TicketID,";
|
|
}
|
|
}
|
|
|
|
if ( $Message ne '' ) {
|
|
return $LayoutObject->ErrorScreen(
|
|
Message => $LayoutObject->{LanguageObject}->Translate( "Ticket (%s) is not unlocked!", $Message ),
|
|
);
|
|
}
|
|
|
|
return $LayoutObject->Redirect(
|
|
OP => $Self->{LastScreenOverview},
|
|
);
|
|
|
|
}
|
|
|
|
elsif ( $Self->{Subaction} eq 'AJAXUpdate' ) {
|
|
|
|
my $Config = $ConfigObject->Get("Ticket::Frontend::$Self->{Action}");
|
|
|
|
# Get List type.
|
|
my $TreeView = 0;
|
|
if ( $ConfigObject->Get('Ticket::Frontend::ListType') eq 'tree' ) {
|
|
$TreeView = 1;
|
|
}
|
|
|
|
my %GetParam;
|
|
for my $Key (qw(OwnerID ResponsibleID PriorityID QueueID Queue TypeID StateID)) {
|
|
$GetParam{$Key} = $ParamObject->GetParam( Param => $Key ) || '';
|
|
}
|
|
|
|
my %QueueList = $Self->_GetQueues(
|
|
%GetParam,
|
|
Type => 'move_into',
|
|
UserID => $Self->{UserID},
|
|
Action => $Self->{Action},
|
|
);
|
|
my @JSONData = (
|
|
{
|
|
Name => 'QueueID',
|
|
Data => \%QueueList,
|
|
SelectedID => $GetParam{QueueID},
|
|
TreeView => $TreeView,
|
|
PossibleNone => 1,
|
|
},
|
|
);
|
|
|
|
if ( $Config->{State} ) {
|
|
my %State;
|
|
my %StateList = $Self->_GetStates(
|
|
%GetParam,
|
|
StateType => $Config->{StateType},
|
|
Action => $Self->{Action},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
if ( !$Config->{StateDefault} ) {
|
|
$StateList{''} = '-';
|
|
}
|
|
|
|
push @JSONData, {
|
|
Name => 'StateID',
|
|
Data => \%StateList,
|
|
SelectedID => $GetParam{StateID},
|
|
};
|
|
}
|
|
|
|
if ( $ConfigObject->Get('Ticket::Type') && $Config->{TicketType} ) {
|
|
|
|
my %TypeList = $Self->_GetTypes(
|
|
%GetParam,
|
|
Action => $Self->{Action},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
push @JSONData, {
|
|
Name => 'TypeID',
|
|
Data => \%TypeList,
|
|
SelectedID => $GetParam{TypeID},
|
|
PossibleNone => 1,
|
|
Translation => 0,
|
|
};
|
|
}
|
|
|
|
if ( $Config->{Owner} ) {
|
|
my %OwnerList = $Self->_GetOwners(
|
|
%GetParam,
|
|
Action => $Self->{Action},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
push @JSONData, {
|
|
Name => 'OwnerID',
|
|
Data => \%OwnerList,
|
|
SelectedID => $GetParam{OwnerID},
|
|
PossibleNone => 1,
|
|
};
|
|
}
|
|
|
|
if ( $ConfigObject->Get('Ticket::Responsible') && $Config->{Responsible} ) {
|
|
my %ResponsibleList = $Self->_GetResponsibles(
|
|
%GetParam,
|
|
Action => $Self->{Action},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
push @JSONData, {
|
|
Name => 'ResponsibleID',
|
|
Data => \%ResponsibleList,
|
|
SelectedID => $GetParam{ResponsibleID},
|
|
PossibleNone => 1,
|
|
};
|
|
}
|
|
|
|
if ( $Config->{Priority} ) {
|
|
my %PriorityList = $Self->_GetPriorities(
|
|
%GetParam,
|
|
UserID => $Self->{UserID},
|
|
Action => $Self->{Action},
|
|
);
|
|
if ( !$Config->{PriorityDefault} ) {
|
|
$PriorityList{''} = '-';
|
|
}
|
|
|
|
push @JSONData, {
|
|
Name => 'PriorityID',
|
|
Data => \%PriorityList,
|
|
SelectedID => $GetParam{PriorityID},
|
|
};
|
|
}
|
|
|
|
# get bulk modules from SysConfig
|
|
my $BulkModuleConfig = $Kernel::OM->Get('Kernel::Config')->Get('Ticket::Frontend::BulkModule') || {};
|
|
|
|
# create bulk module objects
|
|
my @BulkModules;
|
|
MODULECONFIG:
|
|
for my $ModuleConfig ( sort keys %{$BulkModuleConfig} ) {
|
|
|
|
next MODULECONFIG if !$ModuleConfig;
|
|
next MODULECONFIG if !$BulkModuleConfig->{$ModuleConfig};
|
|
next MODULECONFIG if ref $BulkModuleConfig->{$ModuleConfig} ne 'HASH';
|
|
next MODULECONFIG if !$BulkModuleConfig->{$ModuleConfig}->{Module};
|
|
|
|
my $Module = $BulkModuleConfig->{$ModuleConfig}->{Module};
|
|
|
|
my $ModuleObject;
|
|
eval {
|
|
$ModuleObject = $Kernel::OM->Get($Module);
|
|
};
|
|
|
|
if ( !$ModuleObject ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Could not create a new object for $Module!",
|
|
);
|
|
next MODULECONFIG;
|
|
}
|
|
|
|
if ( ref $ModuleObject ne $Module ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Object for $Module is invalid!",
|
|
);
|
|
next MODULECONFIG;
|
|
}
|
|
|
|
push @BulkModules, $ModuleObject;
|
|
}
|
|
|
|
# call AJAXUpdate() in all ticket bulk modules
|
|
if (@BulkModules) {
|
|
|
|
MODULEOBJECT:
|
|
for my $ModuleObject (@BulkModules) {
|
|
next MODULEOBJECT if !$ModuleObject->can('AJAXUpdate');
|
|
|
|
my $ModuleAjaxUpdate = $ModuleObject->AJAXUpdate(
|
|
%GetParam,
|
|
Action => $Self->{Action},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
next MODULEOBJECT if !IsHashRefWithData($ModuleAjaxUpdate);
|
|
|
|
push @JSONData, $ModuleAjaxUpdate;
|
|
}
|
|
}
|
|
|
|
my $JSON = $LayoutObject->BuildSelectionJSON( [@JSONData] );
|
|
|
|
return $LayoutObject->Attachment(
|
|
ContentType => 'application/json; charset=' . $LayoutObject->{Charset},
|
|
Content => $JSON,
|
|
Type => 'inline',
|
|
NoCache => 1,
|
|
);
|
|
}
|
|
|
|
elsif ( $Self->{Subaction} eq 'AJAXRecipientList' ) {
|
|
|
|
# get ticket IDs
|
|
my @Recipients;
|
|
my $TicketIDs = $ParamObject->GetParam( Param => 'TicketIDs' );
|
|
if ($TicketIDs) {
|
|
$TicketIDs = $Kernel::OM->Get('Kernel::System::JSON')->Decode(
|
|
Data => $TicketIDs
|
|
);
|
|
@Recipients = $Self->_GetRecipientList( TicketIDs => $TicketIDs );
|
|
}
|
|
|
|
return $LayoutObject->Attachment(
|
|
ContentType => 'application/json; charset=' . $LayoutObject->{Charset},
|
|
Content => $Kernel::OM->Get('Kernel::System::JSON')->Encode( Data => \@Recipients ),
|
|
Type => 'inline',
|
|
NoCache => 1,
|
|
);
|
|
}
|
|
|
|
elsif ( $Self->{Subaction} eq 'AJAXIgnoreLockedTicketIDs' ) {
|
|
|
|
my @ValidTicketIDs;
|
|
my @IgnoreLockedTicketIDs;
|
|
my $TicketIDs = $ParamObject->GetParam( Param => 'TicketIDs' );
|
|
if ($TicketIDs) {
|
|
$TicketIDs = $Kernel::OM->Get('Kernel::System::JSON')->Decode(
|
|
Data => $TicketIDs
|
|
);
|
|
|
|
}
|
|
|
|
my $Config = $ConfigObject->Get("Ticket::Frontend::$Self->{Action}");
|
|
my @TicketIDArray = split( /;/, $TicketIDs );
|
|
|
|
if ( $Config->{RequiredLock} ) {
|
|
for my $TicketID (@TicketIDArray) {
|
|
if ( $TicketObject->TicketLockGet( TicketID => $TicketID ) ) {
|
|
my $AccessOk = $TicketObject->OwnerCheck(
|
|
TicketID => $TicketID,
|
|
OwnerID => $Self->{UserID},
|
|
);
|
|
if ($AccessOk) {
|
|
push @ValidTicketIDs, $TicketID;
|
|
}
|
|
else {
|
|
push @IgnoreLockedTicketIDs, $TicketID;
|
|
}
|
|
}
|
|
else {
|
|
push @ValidTicketIDs, $TicketID;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
@ValidTicketIDs = @TicketIDArray;
|
|
}
|
|
|
|
my %DialogWarning;
|
|
my @IgnoreLockedTicketNumber;
|
|
if ( @IgnoreLockedTicketIDs && !@ValidTicketIDs ) {
|
|
for my $TicketID (@IgnoreLockedTicketIDs) {
|
|
my $TicketNumber = $TicketObject->TicketNumberLookup(
|
|
TicketID => $TicketID,
|
|
);
|
|
push @IgnoreLockedTicketNumber, $TicketNumber;
|
|
|
|
}
|
|
|
|
if ( $Config->{RequiredLock} ) {
|
|
if ( scalar @IgnoreLockedTicketNumber > 1 ) {
|
|
%DialogWarning = (
|
|
Message => $LayoutObject->{LanguageObject}->Translate(
|
|
"The following tickets were ignored because they are locked by another agent or you don't have write access to tickets: %s.",
|
|
join( ", ", @IgnoreLockedTicketNumber ),
|
|
)
|
|
);
|
|
}
|
|
else {
|
|
%DialogWarning = (
|
|
Message => $LayoutObject->{LanguageObject}->Translate(
|
|
"The following ticket was ignored because it is locked by another agent or you don't have write access to ticket: %s.",
|
|
join( ", ", @IgnoreLockedTicketNumber ),
|
|
)
|
|
);
|
|
}
|
|
}
|
|
else {
|
|
%DialogWarning = (
|
|
Message => Translatable('You need to select at least one ticket.'),
|
|
);
|
|
}
|
|
}
|
|
|
|
return $LayoutObject->Attachment(
|
|
ContentType => 'application/json; charset=' . $LayoutObject->{Charset},
|
|
Content => $Kernel::OM->Get('Kernel::System::JSON')->Encode(
|
|
Data => {
|
|
Message => $DialogWarning{Message} || '',
|
|
}
|
|
),
|
|
Type => 'inline',
|
|
NoCache => 1,
|
|
);
|
|
|
|
}
|
|
|
|
if ( !$Self->{Subaction} || $Self->{Subaction} ne 'AJAXRecipientList' ) {
|
|
|
|
# check if bulk feature is enabled
|
|
if ( !$ConfigObject->Get('Ticket::Frontend::BulkFeature') ) {
|
|
return $LayoutObject->ErrorScreen(
|
|
Message => Translatable('Bulk feature is not enabled!'),
|
|
);
|
|
}
|
|
|
|
# get involved tickets, filtering empty TicketIDs
|
|
my @ValidTicketIDs;
|
|
my @IgnoreLockedTicketIDs;
|
|
my @TicketIDs = sort grep {$_}
|
|
$ParamObject->GetArray( Param => 'TicketID' );
|
|
|
|
my $Config = $ConfigObject->Get("Ticket::Frontend::$Self->{Action}");
|
|
|
|
# check if only locked tickets have been selected
|
|
if ( $Config->{RequiredLock} ) {
|
|
for my $TicketID (@TicketIDs) {
|
|
if ( $TicketObject->TicketLockGet( TicketID => $TicketID ) ) {
|
|
my $AccessOk = $TicketObject->OwnerCheck(
|
|
TicketID => $TicketID,
|
|
OwnerID => $Self->{UserID},
|
|
);
|
|
if ($AccessOk) {
|
|
push @ValidTicketIDs, $TicketID;
|
|
}
|
|
else {
|
|
push @IgnoreLockedTicketIDs, $TicketID;
|
|
}
|
|
}
|
|
else {
|
|
push @ValidTicketIDs, $TicketID;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
@ValidTicketIDs = @TicketIDs;
|
|
}
|
|
|
|
# check needed stuff
|
|
if ( !@ValidTicketIDs ) {
|
|
if ( $Config->{RequiredLock} ) {
|
|
return $LayoutObject->ErrorScreen(
|
|
Message => Translatable('No selectable TicketID is given!'),
|
|
Comment =>
|
|
Translatable('You either selected no ticket or only tickets which are locked by other agents.'),
|
|
);
|
|
}
|
|
else {
|
|
return $LayoutObject->ErrorScreen(
|
|
Message => Translatable('No TicketID is given!'),
|
|
Comment => Translatable('You need to select at least one ticket.'),
|
|
);
|
|
}
|
|
}
|
|
|
|
my $Output = $LayoutObject->Header(
|
|
Type => 'Small',
|
|
);
|
|
|
|
# make the ticket IDs available in javascript
|
|
$LayoutObject->AddJSData(
|
|
Key => 'ValidTicketIDs',
|
|
Value => \@ValidTicketIDs,
|
|
);
|
|
|
|
# declare the variables for all the parameters
|
|
my %Error;
|
|
my %Time;
|
|
my %GetParam;
|
|
|
|
# get bulk modules from SysConfig
|
|
my $BulkModuleConfig = $Kernel::OM->Get('Kernel::Config')->Get('Ticket::Frontend::BulkModule') || {};
|
|
|
|
# create bulk module objects
|
|
my @BulkModules;
|
|
MODULECONFIG:
|
|
for my $ModuleConfig ( sort keys %{$BulkModuleConfig} ) {
|
|
|
|
next MODULECONFIG if !$ModuleConfig;
|
|
next MODULECONFIG if !$BulkModuleConfig->{$ModuleConfig};
|
|
next MODULECONFIG if ref $BulkModuleConfig->{$ModuleConfig} ne 'HASH';
|
|
next MODULECONFIG if !$BulkModuleConfig->{$ModuleConfig}->{Module};
|
|
|
|
my $Module = $BulkModuleConfig->{$ModuleConfig}->{Module};
|
|
|
|
my $ModuleObject;
|
|
eval {
|
|
$ModuleObject = $Kernel::OM->Get($Module);
|
|
};
|
|
|
|
if ( !$ModuleObject ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Could not create a new object for $Module!",
|
|
);
|
|
next MODULECONFIG;
|
|
}
|
|
|
|
if ( ref $ModuleObject ne $Module ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Object for $Module is invalid!",
|
|
);
|
|
next MODULECONFIG;
|
|
}
|
|
|
|
push @BulkModules, $ModuleObject;
|
|
}
|
|
|
|
# get needed objects
|
|
my $StateObject = $Kernel::OM->Get('Kernel::System::State');
|
|
|
|
# get all parameters and check for errors
|
|
if ( $Self->{Subaction} eq 'Do' ) {
|
|
|
|
# challenge token check for write action
|
|
$LayoutObject->ChallengeTokenCheck();
|
|
|
|
# get all parameters
|
|
for my $Key (
|
|
qw(OwnerID Owner ResponsibleID Responsible PriorityID Priority QueueID Queue Subject
|
|
Body IsVisibleForCustomer TypeID StateID State MergeToSelection MergeTo LinkTogether
|
|
EmailSubject EmailBody EmailTimeUnits
|
|
LinkTogetherParent Unlock MergeToChecked MergeToOldestChecked)
|
|
)
|
|
{
|
|
$GetParam{$Key} = $ParamObject->GetParam( Param => $Key ) || '';
|
|
}
|
|
|
|
for my $Key (qw(TimeUnits)) {
|
|
$GetParam{$Key} = $ParamObject->GetParam( Param => $Key );
|
|
}
|
|
|
|
# get time stamp based on user time zone
|
|
%Time = $LayoutObject->TransformDateSelection(
|
|
Year => $ParamObject->GetParam( Param => 'Year' ),
|
|
Month => $ParamObject->GetParam( Param => 'Month' ),
|
|
Day => $ParamObject->GetParam( Param => 'Day' ),
|
|
Hour => $ParamObject->GetParam( Param => 'Hour' ),
|
|
Minute => $ParamObject->GetParam( Param => 'Minute' ),
|
|
);
|
|
|
|
if ( $GetParam{'MergeToSelection'} eq 'OptionMergeTo' ) {
|
|
$GetParam{'MergeToChecked'} = 'checked';
|
|
}
|
|
elsif ( $GetParam{'MergeToSelection'} eq 'OptionMergeToOldest' ) {
|
|
$GetParam{'MergeToOldestChecked'} = 'checked';
|
|
}
|
|
|
|
# check some stuff
|
|
if (
|
|
$GetParam{Subject}
|
|
&& $ConfigObject->Get('Ticket::Frontend::AccountTime')
|
|
&& $ConfigObject->Get('Ticket::Frontend::NeedAccountedTime')
|
|
&& $GetParam{TimeUnits} eq ''
|
|
)
|
|
{
|
|
$Error{'TimeUnitsInvalid'} = 'ServerError';
|
|
}
|
|
|
|
if (
|
|
$GetParam{EmailSubject}
|
|
&& $ConfigObject->Get('Ticket::Frontend::AccountTime')
|
|
&& $ConfigObject->Get('Ticket::Frontend::NeedAccountedTime')
|
|
&& $GetParam{EmailTimeUnits} eq ''
|
|
)
|
|
{
|
|
$Error{'EmailTimeUnitsInvalid'} = 'ServerError';
|
|
}
|
|
|
|
# Body and Subject must both be filled in or both be empty
|
|
if ( $GetParam{Subject} eq '' && $GetParam{Body} ne '' ) {
|
|
$Error{'SubjectInvalid'} = 'ServerError';
|
|
}
|
|
if ( $GetParam{Subject} ne '' && $GetParam{Body} eq '' ) {
|
|
$Error{'BodyInvalid'} = 'ServerError';
|
|
}
|
|
|
|
# Email Body and Email Subject must both be filled in or both be empty
|
|
if ( $GetParam{EmailSubject} eq '' && $GetParam{EmailBody} ne '' ) {
|
|
$Error{'EmailSubjectInvalid'} = 'ServerError';
|
|
}
|
|
if ( $GetParam{EmailSubject} ne '' && $GetParam{EmailBody} eq '' ) {
|
|
$Error{'EmailBodyInvalid'} = 'ServerError';
|
|
}
|
|
|
|
# check if pending date must be validated
|
|
if ( $GetParam{StateID} || $GetParam{State} ) {
|
|
my %StateData;
|
|
if ( $GetParam{StateID} ) {
|
|
%StateData = $StateObject->StateGet(
|
|
ID => $GetParam{StateID},
|
|
);
|
|
}
|
|
else {
|
|
%StateData = $StateObject->StateGet(
|
|
Name => $GetParam{State},
|
|
);
|
|
}
|
|
|
|
if ( $StateData{TypeName} =~ /^pending/i ) {
|
|
|
|
# create datetime object
|
|
my $PendingDateTimeObject = $Kernel::OM->Create(
|
|
'Kernel::System::DateTime',
|
|
ObjectParams => {
|
|
%Time,
|
|
Second => 0,
|
|
},
|
|
);
|
|
|
|
# get current system epoch
|
|
my $CurSystemDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
|
|
if (
|
|
!$PendingDateTimeObject
|
|
|| $PendingDateTimeObject < $CurSystemDateTimeObject
|
|
)
|
|
{
|
|
$Error{'DateInvalid'} = 'ServerError';
|
|
}
|
|
}
|
|
}
|
|
|
|
# get check item object
|
|
my $CheckItemObject = $Kernel::OM->Get('Kernel::System::CheckItem');
|
|
|
|
if ( $GetParam{'MergeToSelection'} eq 'OptionMergeTo' && $GetParam{'MergeTo'} ) {
|
|
$CheckItemObject->StringClean(
|
|
StringRef => \$GetParam{'MergeTo'},
|
|
TrimLeft => 1,
|
|
TrimRight => 1,
|
|
);
|
|
my $TicketID = $TicketObject->TicketCheckNumber(
|
|
Tn => $GetParam{'MergeTo'},
|
|
);
|
|
if ( !$TicketID ) {
|
|
$Error{'MergeToInvalid'} = 'ServerError';
|
|
}
|
|
}
|
|
if ( $GetParam{'LinkTogetherParent'} ) {
|
|
$CheckItemObject->StringClean(
|
|
StringRef => \$GetParam{'LinkTogetherParent'},
|
|
TrimLeft => 1,
|
|
TrimRight => 1,
|
|
);
|
|
my $TicketID = $TicketObject->TicketCheckNumber(
|
|
Tn => $GetParam{'LinkTogetherParent'},
|
|
);
|
|
if ( !$TicketID ) {
|
|
$Error{'LinkTogetherParentInvalid'} = 'ServerError';
|
|
}
|
|
}
|
|
|
|
# call Validate() in all ticket bulk modules
|
|
if (@BulkModules) {
|
|
MODULEOBJECT:
|
|
for my $ModuleObject (@BulkModules) {
|
|
next MODULEOBJECT if !$ModuleObject->can('Validate');
|
|
|
|
my @Result = $ModuleObject->Validate(
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
next MODULEOBJECT if !@Result;
|
|
|
|
# include all validation errors in the common error hash
|
|
for my $ValidationError (@Result) {
|
|
$Error{ $ValidationError->{ErrorKey} } = $ValidationError->{ErrorValue};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# process tickets
|
|
my @TicketIDSelected;
|
|
my $LockedTickets = '';
|
|
my $ActionFlag = 0;
|
|
my $Counter = 1;
|
|
$Param{TicketsWereLocked} = 0;
|
|
|
|
# if the tickets are to merged, precompute the ticket to merge to.
|
|
# (it's the same for all tickets, so do it only once):
|
|
my $MainTicketID;
|
|
|
|
if ( ( $Self->{Subaction} eq 'Do' ) && ( !%Error ) ) {
|
|
|
|
# merge to
|
|
if ( $GetParam{'MergeToSelection'} eq 'OptionMergeTo' && $GetParam{'MergeTo'} ) {
|
|
$MainTicketID = $TicketObject->TicketIDLookup(
|
|
TicketNumber => $GetParam{'MergeTo'},
|
|
);
|
|
}
|
|
|
|
# merge to oldest
|
|
elsif ( $GetParam{'MergeToSelection'} eq 'OptionMergeToOldest' ) {
|
|
|
|
# find oldest
|
|
my $OldestCreateTime;
|
|
my $OldestTicketID;
|
|
for my $TicketIDCheck (@TicketIDs) {
|
|
my %Ticket = $TicketObject->TicketGet(
|
|
TicketID => $TicketIDCheck,
|
|
DynamicFields => 0,
|
|
);
|
|
if ( !defined $OldestCreateTime || $OldestCreateTime gt $Ticket{Created} ) {
|
|
$OldestCreateTime = $Ticket{Created};
|
|
$OldestTicketID = $TicketIDCheck;
|
|
}
|
|
}
|
|
$MainTicketID = $OldestTicketID;
|
|
}
|
|
}
|
|
|
|
my @TicketsWithError;
|
|
my @TicketsWithLockNotice;
|
|
my $Result;
|
|
my @NonUpdatedTickets;
|
|
|
|
my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');
|
|
|
|
TICKET_ID:
|
|
for my $TicketID (@TicketIDs) {
|
|
my %Ticket = $TicketObject->TicketGet(
|
|
TicketID => $TicketID,
|
|
DynamicFields => 0,
|
|
);
|
|
|
|
# check permissions
|
|
my $Access = $TicketObject->TicketPermission(
|
|
Type => 'rw',
|
|
TicketID => $TicketID,
|
|
UserID => $Self->{UserID}
|
|
);
|
|
if ( !$Access ) {
|
|
|
|
# error screen, don't show ticket
|
|
push @TicketsWithError, $Ticket{TicketNumber};
|
|
next TICKET_ID;
|
|
}
|
|
|
|
# check if it's already locked by somebody else
|
|
if ( $Config->{RequiredLock} ) {
|
|
if ( grep ( { $_ eq $TicketID } @IgnoreLockedTicketIDs ) ) {
|
|
push @TicketsWithError, $Ticket{TicketNumber};
|
|
next TICKET_ID;
|
|
}
|
|
elsif ( $Ticket{Lock} eq 'unlock' ) {
|
|
$LockedTickets .= "LockedTicketID=" . $TicketID . ';';
|
|
$Param{TicketsWereLocked} = 1;
|
|
|
|
# set lock
|
|
$TicketObject->TicketLockSet(
|
|
TicketID => $TicketID,
|
|
Lock => 'lock',
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
# set user id
|
|
$Result = $TicketObject->TicketOwnerSet(
|
|
TicketID => $TicketID,
|
|
UserID => $Self->{UserID},
|
|
NewUserID => $Self->{UserID},
|
|
);
|
|
|
|
push @TicketsWithLockNotice, $Ticket{TicketNumber};
|
|
|
|
if ( !$Result ) {
|
|
push @NonUpdatedTickets, $Ticket{TicketNumber};
|
|
}
|
|
}
|
|
}
|
|
|
|
# remember selected ticket ids
|
|
push @TicketIDSelected, $TicketID;
|
|
|
|
# do some actions on tickets
|
|
if ( ( $Self->{Subaction} eq 'Do' ) && ( !%Error ) ) {
|
|
|
|
# challenge token check for write action
|
|
$LayoutObject->ChallengeTokenCheck();
|
|
|
|
# Clean up 'TicketSearch' cache type if Bulk screen is reached from ticket search.
|
|
if ( $Self->{LastScreenOverview} =~ /Action=AgentTicketSearch/ ) {
|
|
$Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
|
|
Type => 'TicketSearch',
|
|
);
|
|
}
|
|
|
|
# set queue
|
|
if ( $GetParam{'QueueID'} || $GetParam{'Queue'} ) {
|
|
$Result = $TicketObject->TicketQueueSet(
|
|
QueueID => $GetParam{'QueueID'},
|
|
Queue => $GetParam{'Queue'},
|
|
TicketID => $TicketID,
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
if ( !$Result ) {
|
|
push @NonUpdatedTickets, $Ticket{TicketNumber};
|
|
}
|
|
}
|
|
|
|
# set owner
|
|
if ( $Config->{Owner} && ( $GetParam{'OwnerID'} || $GetParam{'Owner'} ) ) {
|
|
$Result = $TicketObject->TicketOwnerSet(
|
|
TicketID => $TicketID,
|
|
UserID => $Self->{UserID},
|
|
NewUser => $GetParam{'Owner'},
|
|
NewUserID => $GetParam{'OwnerID'},
|
|
);
|
|
|
|
if ( !$Result ) {
|
|
push @NonUpdatedTickets, $Ticket{TicketNumber};
|
|
}
|
|
|
|
if ( !$Config->{RequiredLock} && $Ticket{StateType} !~ /^close/i ) {
|
|
$TicketObject->TicketLockSet(
|
|
TicketID => $TicketID,
|
|
Lock => 'lock',
|
|
UserID => $Self->{UserID},
|
|
);
|
|
}
|
|
}
|
|
|
|
# set responsible
|
|
if (
|
|
$ConfigObject->Get('Ticket::Responsible')
|
|
&& $Config->{Responsible}
|
|
&& ( $GetParam{'ResponsibleID'} || $GetParam{'Responsible'} )
|
|
)
|
|
{
|
|
$Result = $TicketObject->TicketResponsibleSet(
|
|
TicketID => $TicketID,
|
|
UserID => $Self->{UserID},
|
|
NewUser => $GetParam{'Responsible'},
|
|
NewUserID => $GetParam{'ResponsibleID'},
|
|
);
|
|
|
|
if ( !$Result ) {
|
|
push @NonUpdatedTickets, $Ticket{TicketNumber};
|
|
}
|
|
}
|
|
|
|
# set priority
|
|
if (
|
|
$Config->{Priority}
|
|
&& ( $GetParam{'PriorityID'} || $GetParam{'Priority'} )
|
|
)
|
|
{
|
|
$Result = $TicketObject->TicketPrioritySet(
|
|
TicketID => $TicketID,
|
|
UserID => $Self->{UserID},
|
|
Priority => $GetParam{'Priority'},
|
|
PriorityID => $GetParam{'PriorityID'},
|
|
);
|
|
|
|
if ( !$Result ) {
|
|
push @NonUpdatedTickets, $Ticket{TicketNumber};
|
|
}
|
|
}
|
|
|
|
# set type
|
|
if ( $ConfigObject->Get('Ticket::Type') && $Config->{TicketType} ) {
|
|
if ( $GetParam{'TypeID'} ) {
|
|
$Result = $TicketObject->TicketTypeSet(
|
|
TypeID => $GetParam{'TypeID'},
|
|
TicketID => $TicketID,
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
if ( !$Result ) {
|
|
push @NonUpdatedTickets, $Ticket{TicketNumber};
|
|
}
|
|
}
|
|
}
|
|
|
|
# send email
|
|
my $EmailArticleID;
|
|
if (
|
|
$GetParam{'EmailSubject'}
|
|
&& $GetParam{'EmailBody'}
|
|
)
|
|
{
|
|
my $MimeType = 'text/plain';
|
|
if ( $LayoutObject->{BrowserRichText} ) {
|
|
$MimeType = 'text/html';
|
|
|
|
# verify html document
|
|
$GetParam{'EmailBody'} = $LayoutObject->RichTextDocumentComplete(
|
|
String => $GetParam{'EmailBody'},
|
|
);
|
|
}
|
|
|
|
my @Recipients = $Self->_GetRecipientList( TicketIDs => [ $Ticket{TicketID} ] );
|
|
my $Customer = $Recipients[0];
|
|
|
|
# get template generator object
|
|
my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser');
|
|
my $TemplateGeneratorObject = $Kernel::OM->ObjectParamAdd(
|
|
'Kernel::System::TemplateGenerator' => {
|
|
CustomerUserObject => $CustomerUserObject,
|
|
},
|
|
);
|
|
|
|
$TemplateGeneratorObject = $Kernel::OM->Get('Kernel::System::TemplateGenerator');
|
|
|
|
# generate sender name
|
|
my $From = $TemplateGeneratorObject->Sender(
|
|
QueueID => $Ticket{QueueID},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
# generate subject
|
|
my $TicketNumber = $TicketObject->TicketNumberLookup( TicketID => $TicketID );
|
|
|
|
my $EmailSubject = $TicketObject->TicketSubjectBuild(
|
|
TicketNumber => $TicketNumber,
|
|
Subject => $GetParam{EmailSubject} || '',
|
|
);
|
|
|
|
my $EmailArticleBackendObject = $ArticleObject->BackendForChannel( ChannelName => 'Email' );
|
|
|
|
$EmailArticleID = $EmailArticleBackendObject->ArticleSend(
|
|
TicketID => $TicketID,
|
|
SenderType => 'agent',
|
|
IsVisibleForCustomer => 1,
|
|
From => $From,
|
|
To => $Customer,
|
|
Subject => $EmailSubject,
|
|
Body => $GetParam{EmailBody},
|
|
MimeType => $MimeType,
|
|
Charset => $LayoutObject->{UserCharset},
|
|
UserID => $Self->{UserID},
|
|
HistoryType => 'SendAnswer',
|
|
HistoryComment => '%%' . $Customer,
|
|
);
|
|
}
|
|
|
|
# add note
|
|
my $ArticleID;
|
|
if (
|
|
$GetParam{'Subject'}
|
|
&& $GetParam{'Body'}
|
|
)
|
|
{
|
|
my $MimeType = 'text/plain';
|
|
if ( $LayoutObject->{BrowserRichText} ) {
|
|
$MimeType = 'text/html';
|
|
|
|
# verify html document
|
|
$GetParam{'Body'} = $LayoutObject->RichTextDocumentComplete(
|
|
String => $GetParam{'Body'},
|
|
);
|
|
}
|
|
my $InternalArticleBackendObject = $ArticleObject->BackendForChannel( ChannelName => 'Internal' );
|
|
|
|
$ArticleID = $InternalArticleBackendObject->ArticleCreate(
|
|
TicketID => $TicketID,
|
|
SenderType => 'agent',
|
|
IsVisibleForCustomer => $GetParam{IsVisibleForCustomer},
|
|
From => "$Self->{UserFullname} <$Self->{UserEmail}>",
|
|
Subject => $GetParam{'Subject'},
|
|
Body => $GetParam{'Body'},
|
|
MimeType => $MimeType,
|
|
Charset => $LayoutObject->{UserCharset},
|
|
UserID => $Self->{UserID},
|
|
HistoryType => 'AddNote',
|
|
HistoryComment => '%%Bulk',
|
|
);
|
|
}
|
|
|
|
# set state
|
|
if ( $Config->{State} && ( $GetParam{'StateID'} || $GetParam{'State'} ) ) {
|
|
$Result = $TicketObject->TicketStateSet(
|
|
TicketID => $TicketID,
|
|
StateID => $GetParam{'StateID'},
|
|
State => $GetParam{'State'},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
if ( !$Result ) {
|
|
push @NonUpdatedTickets, $Ticket{TicketNumber};
|
|
}
|
|
|
|
my %Ticket = $TicketObject->TicketGet(
|
|
TicketID => $TicketID,
|
|
DynamicFields => 0,
|
|
);
|
|
my %StateData = $StateObject->StateGet(
|
|
ID => $Ticket{StateID},
|
|
);
|
|
|
|
# should i set the pending date?
|
|
if ( $Ticket{StateType} =~ /^pending/i ) {
|
|
|
|
# set pending time
|
|
$TicketObject->TicketPendingTimeSet(
|
|
%Time,
|
|
TicketID => $TicketID,
|
|
UserID => $Self->{UserID},
|
|
);
|
|
}
|
|
|
|
# should I set an unlock?
|
|
if ( $Ticket{StateType} =~ /^close/i ) {
|
|
$TicketObject->TicketLockSet(
|
|
TicketID => $TicketID,
|
|
Lock => 'unlock',
|
|
UserID => $Self->{UserID},
|
|
);
|
|
}
|
|
}
|
|
|
|
# time units for note
|
|
if ( $GetParam{TimeUnits} && $ArticleID ) {
|
|
if ( $ConfigObject->Get('Ticket::Frontend::BulkAccountedTime') ) {
|
|
$TicketObject->TicketAccountTime(
|
|
TicketID => $TicketID,
|
|
ArticleID => $ArticleID,
|
|
TimeUnit => $GetParam{'TimeUnits'},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
}
|
|
elsif (
|
|
!$ConfigObject->Get('Ticket::Frontend::BulkAccountedTime')
|
|
&& $Counter == 1
|
|
)
|
|
{
|
|
$TicketObject->TicketAccountTime(
|
|
TicketID => $TicketID,
|
|
ArticleID => $ArticleID,
|
|
TimeUnit => $GetParam{'TimeUnits'},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
}
|
|
}
|
|
|
|
# time units for email
|
|
if ( $GetParam{EmailTimeUnits} && $EmailArticleID ) {
|
|
if ( $ConfigObject->Get('Ticket::Frontend::BulkAccountedTime') ) {
|
|
$TicketObject->TicketAccountTime(
|
|
TicketID => $TicketID,
|
|
ArticleID => $EmailArticleID,
|
|
TimeUnit => $GetParam{'EmailTimeUnits'},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
}
|
|
elsif (
|
|
!$ConfigObject->Get('Ticket::Frontend::BulkAccountedTime')
|
|
&& $Counter == 1
|
|
)
|
|
{
|
|
$TicketObject->TicketAccountTime(
|
|
TicketID => $TicketID,
|
|
ArticleID => $EmailArticleID,
|
|
TimeUnit => $GetParam{'EmailTimeUnits'},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
}
|
|
}
|
|
|
|
# merge
|
|
if ( $MainTicketID && $MainTicketID ne $TicketID ) {
|
|
$TicketObject->TicketMerge(
|
|
MainTicketID => $MainTicketID,
|
|
MergeTicketID => $TicketID,
|
|
UserID => $Self->{UserID},
|
|
);
|
|
}
|
|
|
|
# get link object
|
|
my $LinkObject = $Kernel::OM->Get('Kernel::System::LinkObject');
|
|
|
|
# link all tickets to a parent
|
|
if ( $GetParam{'LinkTogetherParent'} ) {
|
|
my $MainTicketID = $TicketObject->TicketIDLookup(
|
|
TicketNumber => $GetParam{'LinkTogetherParent'},
|
|
);
|
|
|
|
for my $TicketIDPartner (@TicketIDs) {
|
|
if ( $MainTicketID ne $TicketID ) {
|
|
$LinkObject->LinkAdd(
|
|
SourceObject => 'Ticket',
|
|
SourceKey => $MainTicketID,
|
|
TargetObject => 'Ticket',
|
|
TargetKey => $TicketID,
|
|
Type => 'ParentChild',
|
|
State => 'Valid',
|
|
UserID => $Self->{UserID},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
# link together
|
|
if ( $GetParam{'LinkTogether'} ) {
|
|
for my $TicketIDPartner (@TicketIDs) {
|
|
if ( $TicketID ne $TicketIDPartner ) {
|
|
$LinkObject->LinkAdd(
|
|
SourceObject => 'Ticket',
|
|
SourceKey => $TicketID,
|
|
TargetObject => 'Ticket',
|
|
TargetKey => $TicketIDPartner,
|
|
Type => 'Normal',
|
|
State => 'Valid',
|
|
UserID => $Self->{UserID},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
# should I unlock tickets at user request?
|
|
if ( $GetParam{'Unlock'} ) {
|
|
$Result = $TicketObject->TicketLockSet(
|
|
TicketID => $TicketID,
|
|
Lock => 'unlock',
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
if ( !$Result ) {
|
|
push @NonUpdatedTickets, $Ticket{TicketNumber};
|
|
}
|
|
}
|
|
|
|
# call Store() in all ticket bulk modules
|
|
if (@BulkModules) {
|
|
|
|
MODULEOBJECT:
|
|
for my $ModuleObject (@BulkModules) {
|
|
next MODULEOBJECT if !$ModuleObject->can('Store');
|
|
|
|
$ModuleObject->Store(
|
|
TicketID => $TicketID,
|
|
UserID => $Self->{UserID},
|
|
);
|
|
}
|
|
}
|
|
|
|
$ActionFlag = 1;
|
|
}
|
|
$Counter++;
|
|
}
|
|
|
|
# notify user about actions (errors)
|
|
if (@TicketsWithError) {
|
|
my $NotificationError = $LayoutObject->{LanguageObject}->Translate(
|
|
"The following tickets were ignored because they are locked by another agent or you don't have write access to these tickets: %s.",
|
|
join( ", ", @TicketsWithError ),
|
|
);
|
|
|
|
$Output .= $LayoutObject->Notify(
|
|
Priority => 'Error',
|
|
Data => $NotificationError,
|
|
);
|
|
}
|
|
|
|
# notify user about actions (notices)
|
|
if (@TicketsWithLockNotice) {
|
|
my $NotificationNotice = $LayoutObject->{LanguageObject}->Translate(
|
|
"The following tickets were locked: %s.",
|
|
join( ", ", @TicketsWithLockNotice ),
|
|
);
|
|
|
|
$Output .= $LayoutObject->Notify(
|
|
Priority => 'Notice',
|
|
Data => $NotificationNotice,
|
|
);
|
|
}
|
|
|
|
# redirect
|
|
if ($ActionFlag) {
|
|
|
|
if ( IsArrayRefWithData( \@NonUpdatedTickets ) ) {
|
|
my $NonUpdatedTicketsString = join ', ', @NonUpdatedTickets;
|
|
$Kernel::OM->Get('Kernel::System::Cache')->Set(
|
|
Type => 'Ticket',
|
|
TTL => 60,
|
|
Key => 'NonUpdatedTicketsString-' . $Self->{UserID},
|
|
Value => $NonUpdatedTicketsString,
|
|
);
|
|
}
|
|
|
|
my $DestURL = defined $MainTicketID
|
|
? "Action=AgentTicketZoom;TicketID=$MainTicketID"
|
|
: ( $Self->{LastScreenOverview} || 'Action=AgentDashboard' );
|
|
|
|
return $LayoutObject->PopupClose(
|
|
URL => $DestURL,
|
|
);
|
|
}
|
|
|
|
$Output .= $Self->_Mask(
|
|
%Param,
|
|
%GetParam,
|
|
%Time,
|
|
TicketIDs => \@TicketIDSelected,
|
|
LockedTickets => $LockedTickets,
|
|
Errors => \%Error,
|
|
BulkModules => \@BulkModules,
|
|
);
|
|
$Output .= $LayoutObject->Footer(
|
|
Type => 'Small',
|
|
);
|
|
return $Output;
|
|
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub _GetRecipientList {
|
|
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
|
|
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
|
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
|
my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');
|
|
|
|
my @Recipients;
|
|
|
|
TICKETID:
|
|
for my $TicketID ( @{ $Param{TicketIDs} } ) {
|
|
|
|
my %Ticket = $TicketObject->TicketGet(
|
|
TicketID => $TicketID,
|
|
DynamicFields => 0,
|
|
);
|
|
|
|
my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser');
|
|
|
|
# Get customer email address.
|
|
my $Customer;
|
|
if ( $Ticket{CustomerUserID} ) {
|
|
my %Customer = $CustomerUserObject->CustomerUserDataGet(
|
|
User => $Ticket{CustomerUserID}
|
|
);
|
|
if ( $Customer{UserEmail} ) {
|
|
$Customer = $Customer{UserEmail};
|
|
}
|
|
}
|
|
|
|
# Check if we have an address, otherwise deduce it from the article.
|
|
if ( !$Customer ) {
|
|
|
|
# Get last customer article.
|
|
my @Articles = $ArticleObject->ArticleList(
|
|
TicketID => $TicketID,
|
|
SenderType => 'customer',
|
|
OnlyLast => 1,
|
|
);
|
|
|
|
# If the ticket has no customer article, get the last agent article.
|
|
if ( !@Articles ) {
|
|
@Articles = $ArticleObject->ArticleList(
|
|
TicketID => $TicketID,
|
|
SenderType => 'agent',
|
|
OnlyLast => 1,
|
|
);
|
|
}
|
|
|
|
# Finally, if everything failed, get latest article.
|
|
if ( !@Articles ) {
|
|
@Articles = $ArticleObject->ArticleList(
|
|
TicketID => $TicketID,
|
|
OnlyLast => 1,
|
|
);
|
|
}
|
|
|
|
my %Article;
|
|
for my $Article (@Articles) {
|
|
%Article = $ArticleObject->BackendForArticle( %{$Article} )->ArticleGet(
|
|
%{$Article},
|
|
DynamicFields => 0,
|
|
);
|
|
}
|
|
|
|
# Use ReplyTo if set, otherwise use From.
|
|
$Customer = $Article{ReplyTo} ? $Article{ReplyTo} : $Article{From};
|
|
|
|
# Check article sender type and replace From with To (in case sender is not customer).
|
|
if ( $Article{SenderType} !~ /customer/ ) {
|
|
$Customer = $Article{To};
|
|
}
|
|
}
|
|
|
|
# Skip to next ticket if no customer recipient was found.
|
|
next TICKETID if !$Customer;
|
|
|
|
# Customer recipients are unique.
|
|
push @Recipients, $Customer if !grep { $_ eq $Customer } @Recipients;
|
|
}
|
|
|
|
return @Recipients;
|
|
}
|
|
|
|
sub _Mask {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# get layout object
|
|
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
|
|
|
# prepare errors!
|
|
if ( $Param{Errors} ) {
|
|
for my $KeyError ( sort keys %{ $Param{Errors} } ) {
|
|
$Param{$KeyError} = $LayoutObject->Ascii2Html( Text => $Param{Errors}->{$KeyError} );
|
|
}
|
|
}
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'BulkAction',
|
|
Data => \%Param,
|
|
);
|
|
|
|
# remember ticket ids
|
|
if ( $Param{TicketIDs} ) {
|
|
for my $TicketID ( @{ $Param{TicketIDs} } ) {
|
|
$LayoutObject->Block(
|
|
Name => 'UsedTicketID',
|
|
Data => {
|
|
TicketID => $TicketID,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
|
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
|
|
|
my $Config = $ConfigObject->Get("Ticket::Frontend::$Self->{Action}");
|
|
|
|
$Param{IsVisibleForCustomer} //= $Config->{IsVisibleForCustomerDefault} // 0;
|
|
|
|
# build next states string
|
|
if ( $Config->{State} ) {
|
|
my %State;
|
|
|
|
my %StateList = $Self->_GetStates(
|
|
%Param,
|
|
StateType => $Config->{StateType},
|
|
Action => $Self->{Action},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
if ( !$Config->{StateDefault} ) {
|
|
$StateList{''} = '-';
|
|
}
|
|
if ( !$Param{StateID} ) {
|
|
if ( $Config->{StateDefault} ) {
|
|
$State{SelectedValue} = $Config->{StateDefault};
|
|
}
|
|
}
|
|
else {
|
|
$State{SelectedID} = $Param{StateID};
|
|
}
|
|
|
|
$Param{NextStatesStrg} = $LayoutObject->BuildSelection(
|
|
Data => \%StateList,
|
|
Name => 'StateID',
|
|
%State,
|
|
Class => 'Modernize',
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'State',
|
|
Data => \%Param,
|
|
);
|
|
|
|
my $StateObject = $Kernel::OM->Get('Kernel::System::State');
|
|
|
|
STATE_ID:
|
|
for my $StateID ( sort keys %StateList ) {
|
|
next STATE_ID if !$StateID;
|
|
my %StateData = $StateObject->StateGet( ID => $StateID );
|
|
next STATE_ID if $StateData{TypeName} !~ /pending/i;
|
|
$Param{DateString} = $LayoutObject->BuildDateSelection(
|
|
%Param,
|
|
Format => 'DateInputFormatLong',
|
|
DiffTime => $ConfigObject->Get('Ticket::Frontend::PendingDiffTime') || 0,
|
|
Class => $Param{Errors}->{DateInvalid} || '',
|
|
Validate => 1,
|
|
ValidateDateInFuture => 1,
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'StatePending',
|
|
Data => \%Param,
|
|
);
|
|
last STATE_ID;
|
|
}
|
|
}
|
|
|
|
# types
|
|
if ( $ConfigObject->Get('Ticket::Type') && $Config->{TicketType} ) {
|
|
my %TypeList = $Self->_GetTypes(
|
|
%Param,
|
|
Action => $Self->{Action},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
$Param{TypeStrg} = $LayoutObject->BuildSelection(
|
|
Data => \%TypeList,
|
|
PossibleNone => 1,
|
|
Name => 'TypeID',
|
|
SelectedID => $Param{TypeID},
|
|
Sort => 'AlphanumericValue',
|
|
Translation => 0,
|
|
Class => 'Modernize',
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'Type',
|
|
Data => {%Param},
|
|
);
|
|
}
|
|
|
|
# owner list
|
|
if ( $Config->{Owner} ) {
|
|
my %OwnerList = $Self->_GetOwners(
|
|
%Param,
|
|
Action => $Self->{Action},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
$Param{OwnerStrg} = $LayoutObject->BuildSelection(
|
|
Data => \%OwnerList,
|
|
Name => 'OwnerID',
|
|
Translation => 0,
|
|
SelectedID => $Param{OwnerID},
|
|
PossibleNone => 1,
|
|
Class => 'Modernize',
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'Owner',
|
|
Data => \%Param,
|
|
);
|
|
}
|
|
|
|
# responsible list
|
|
if ( $ConfigObject->Get('Ticket::Responsible') && $Config->{Responsible} ) {
|
|
my %ResponsibleList = $Self->_GetResponsibles(
|
|
%Param,
|
|
Action => $Self->{Action},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
$Param{ResponsibleStrg} = $LayoutObject->BuildSelection(
|
|
Data => \%ResponsibleList,
|
|
PossibleNone => 1,
|
|
Name => 'ResponsibleID',
|
|
Translation => 0,
|
|
SelectedID => $Param{ResponsibleID},
|
|
Class => 'Modernize',
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'Responsible',
|
|
Data => \%Param,
|
|
);
|
|
}
|
|
|
|
# build move queue string
|
|
my %QueueList = $Self->_GetQueues(
|
|
%Param,
|
|
Type => 'move_into',
|
|
UserID => $Self->{UserID},
|
|
Action => $Self->{Action},
|
|
);
|
|
$Param{MoveQueuesStrg} = $LayoutObject->AgentQueueListOption(
|
|
Data => { %QueueList, '' => '-' },
|
|
Multiple => 0,
|
|
Size => 0,
|
|
Name => 'QueueID',
|
|
OnChangeSubmit => 0,
|
|
Class => 'Modernize',
|
|
);
|
|
|
|
# get priority
|
|
if ( $Config->{Priority} ) {
|
|
my %Priority;
|
|
my %PriorityList = $Self->_GetPriorities(
|
|
%Param,
|
|
UserID => $Self->{UserID},
|
|
Action => $Self->{Action},
|
|
);
|
|
if ( !$Config->{PriorityDefault} ) {
|
|
$PriorityList{''} = '-';
|
|
}
|
|
if ( !$Param{PriorityID} ) {
|
|
if ( $Config->{PriorityDefault} ) {
|
|
$Priority{SelectedValue} = $Config->{PriorityDefault};
|
|
}
|
|
}
|
|
else {
|
|
$Priority{SelectedID} = $Param{PriorityID};
|
|
}
|
|
$Param{PriorityStrg} = $LayoutObject->BuildSelection(
|
|
Data => \%PriorityList,
|
|
Name => 'PriorityID',
|
|
%Priority,
|
|
Class => 'Modernize',
|
|
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'Priority',
|
|
Data => \%Param,
|
|
);
|
|
}
|
|
|
|
# show time accounting box
|
|
if ( $ConfigObject->Get('Ticket::Frontend::AccountTime') ) {
|
|
$Param{TimeUnitsRequired} = (
|
|
$ConfigObject->Get('Ticket::Frontend::NeedAccountedTime')
|
|
? 'Validate_DependingRequiredAND Validate_Depending_Subject'
|
|
: ''
|
|
);
|
|
$Param{TimeUnitsRequiredEmail} = (
|
|
$ConfigObject->Get('Ticket::Frontend::NeedAccountedTime')
|
|
? 'Validate_DependingRequiredAND Validate_Depending_EmailSubject'
|
|
: ''
|
|
);
|
|
|
|
if ( $ConfigObject->Get('Ticket::Frontend::NeedAccountedTime') ) {
|
|
$LayoutObject->Block(
|
|
Name => 'TimeUnitsLabelMandatory',
|
|
Data => { TimeUnitsRequired => $Param{TimeUnitsRequired} },
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'TimeUnitsLabelMandatoryEmail',
|
|
Data => { TimeUnitsRequired => $Param{TimeUnitsRequiredEmail} },
|
|
);
|
|
}
|
|
else {
|
|
$LayoutObject->Block(
|
|
Name => 'TimeUnitsLabel',
|
|
Data => \%Param,
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'TimeUnitsLabelEmail',
|
|
Data => \%Param,
|
|
);
|
|
}
|
|
$LayoutObject->Block(
|
|
Name => 'TimeUnits',
|
|
Data => \%Param,
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'TimeUnitsEmail',
|
|
Data => \%Param,
|
|
);
|
|
}
|
|
|
|
$Param{LinkTogetherYesNoOption} = $LayoutObject->BuildSelection(
|
|
Data => $ConfigObject->Get('YesNoOptions'),
|
|
Name => 'LinkTogether',
|
|
SelectedID => $Param{LinkTogether} // 0,
|
|
Class => 'Modernize',
|
|
);
|
|
|
|
$Param{UnlockYesNoOption} = $LayoutObject->BuildSelection(
|
|
Data => $ConfigObject->Get('YesNoOptions'),
|
|
Name => 'Unlock',
|
|
SelectedID => $Param{Unlock} // 1,
|
|
Class => 'Modernize',
|
|
);
|
|
|
|
# add rich text editor for note & email
|
|
if ( $LayoutObject->{BrowserRichText} ) {
|
|
|
|
# use height/width defined for this screen
|
|
$Param{RichTextHeight} = $Config->{RichTextHeight} || 0;
|
|
$Param{RichTextWidth} = $Config->{RichTextWidth} || 0;
|
|
|
|
# set up rich text editor
|
|
$LayoutObject->SetRichTextParameters(
|
|
Data => \%Param,
|
|
);
|
|
}
|
|
|
|
# reload parent window
|
|
if ( $Param{TicketsWereLocked} ) {
|
|
|
|
my $URL = $Self->{LastScreenOverview};
|
|
|
|
# add session if no cookies are enabled
|
|
if ( $Self->{SessionID} && !$Self->{SessionIDCookie} ) {
|
|
$URL .= ';' . $Self->{SessionName} . '=' . $Self->{SessionID};
|
|
}
|
|
|
|
$LayoutObject->AddJSData(
|
|
Key => 'TicketBulkURL',
|
|
Value => $LayoutObject->{Baselink} . $URL,
|
|
);
|
|
|
|
# show undo&close link
|
|
$LayoutObject->Block(
|
|
Name => 'UndoClosePopup',
|
|
Data => {%Param},
|
|
);
|
|
}
|
|
else {
|
|
|
|
# show cancel&close link
|
|
$LayoutObject->Block(
|
|
Name => 'CancelClosePopup',
|
|
Data => {%Param},
|
|
);
|
|
}
|
|
|
|
my @BulkModules = @{ $Param{BulkModules} };
|
|
|
|
# call Display() in all ticket bulk modules
|
|
if (@BulkModules) {
|
|
|
|
my @BulkModuleContent;
|
|
|
|
MODULEOBJECT:
|
|
for my $ModuleObject (@BulkModules) {
|
|
next MODULEOBJECT if !$ModuleObject->can('Display');
|
|
|
|
my $ModuleContent = $ModuleObject->Display(
|
|
Errors => $Param{Errors},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
push @BulkModuleContent, $ModuleContent;
|
|
}
|
|
|
|
$Param{BulkModuleContent} = \@BulkModuleContent;
|
|
}
|
|
|
|
# get output back
|
|
return $LayoutObject->Output(
|
|
TemplateFile => 'AgentTicketBulk',
|
|
Data => \%Param,
|
|
);
|
|
}
|
|
|
|
sub _GetStates {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# TicketStateList() can not be used here as it might not be a queue selected
|
|
my %StateList = $Kernel::OM->Get('Kernel::System::State')->StateGetStatesByType(
|
|
StateType => $Param{StateType},
|
|
Result => 'HASH',
|
|
Action => $Self->{Action},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
|
|
|
# Execute ACLs.
|
|
my $ACL = $TicketObject->TicketAcl(
|
|
%Param,
|
|
ReturnType => 'Ticket',
|
|
ReturnSubType => 'State',
|
|
Data => \%StateList,
|
|
);
|
|
|
|
return $TicketObject->TicketAclData() if $ACL;
|
|
return %StateList;
|
|
}
|
|
|
|
sub _GetTypes {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my %TypeList = $Kernel::OM->Get('Kernel::System::Ticket')->TicketTypeList(
|
|
%Param,
|
|
Action => $Self->{Action},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
return %TypeList;
|
|
}
|
|
|
|
sub _GetOwners {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# Get all users.
|
|
my %AllGroupsMembers = $Kernel::OM->Get('Kernel::System::User')->UserList(
|
|
Type => 'Long',
|
|
Valid => 1
|
|
);
|
|
|
|
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
|
|
|
# Put only possible 'owner' and 'rw' agents to owner list.
|
|
my %OwnerList;
|
|
if ( !$Kernel::OM->Get('Kernel::Config')->Get('Ticket::ChangeOwnerToEveryone') ) {
|
|
my %AllGroupsMembersNew;
|
|
my @QueueIDs;
|
|
|
|
if ( $Param{QueueID} ) {
|
|
push @QueueIDs, $Param{QueueID};
|
|
}
|
|
else {
|
|
my @TicketIDs = grep {$_} $Kernel::OM->Get('Kernel::System::Web::Request')->GetArray( Param => 'TicketID' );
|
|
for my $TicketID (@TicketIDs) {
|
|
my %Ticket = $TicketObject->TicketGet(
|
|
TicketID => $TicketID,
|
|
DynamicFields => 0,
|
|
);
|
|
push @QueueIDs, $Ticket{QueueID};
|
|
}
|
|
}
|
|
|
|
my $QueueObject = $Kernel::OM->Get('Kernel::System::Queue');
|
|
my $GroupObject = $Kernel::OM->Get('Kernel::System::Group');
|
|
|
|
for my $QueueID (@QueueIDs) {
|
|
my $GroupID = $QueueObject->GetQueueGroupID( QueueID => $QueueID );
|
|
my %GroupMember = $GroupObject->PermissionGroupGet(
|
|
GroupID => $GroupID,
|
|
Type => 'owner',
|
|
);
|
|
USER_ID:
|
|
for my $UserID ( sort keys %GroupMember ) {
|
|
next USER_ID if !$AllGroupsMembers{$UserID};
|
|
$AllGroupsMembersNew{$UserID} = $AllGroupsMembers{$UserID};
|
|
}
|
|
%OwnerList = %AllGroupsMembersNew;
|
|
}
|
|
}
|
|
else {
|
|
%OwnerList = %AllGroupsMembers;
|
|
}
|
|
|
|
# Execute ACLs.
|
|
my $ACL = $TicketObject->TicketAcl(
|
|
%Param,
|
|
Action => $Self->{Action},
|
|
ReturnType => 'Ticket',
|
|
ReturnSubType => 'Owner',
|
|
Data => \%OwnerList,
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
return $TicketObject->TicketAclData() if $ACL;
|
|
return %OwnerList;
|
|
}
|
|
|
|
sub _GetResponsibles {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# Get all users.
|
|
my %AllGroupsMembers = $Kernel::OM->Get('Kernel::System::User')->UserList(
|
|
Type => 'Long',
|
|
Valid => 1
|
|
);
|
|
|
|
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
|
|
|
# Put only possible 'responsible' and 'rw' agents to responsible list.
|
|
my %ResponsibleList;
|
|
if ( !$Kernel::OM->Get('Kernel::Config')->Get('Ticket::ChangeOwnerToEveryone') ) {
|
|
my %AllGroupsMembersNew;
|
|
my @QueueIDs;
|
|
|
|
if ( $Param{QueueID} ) {
|
|
push @QueueIDs, $Param{QueueID};
|
|
}
|
|
else {
|
|
my @TicketIDs = grep {$_} $Kernel::OM->Get('Kernel::System::Web::Request')->GetArray( Param => 'TicketID' );
|
|
for my $TicketID (@TicketIDs) {
|
|
my %Ticket = $TicketObject->TicketGet(
|
|
TicketID => $TicketID,
|
|
DynamicFields => 0,
|
|
);
|
|
push @QueueIDs, $Ticket{QueueID};
|
|
}
|
|
}
|
|
|
|
my $QueueObject = $Kernel::OM->Get('Kernel::System::Queue');
|
|
my $GroupObject = $Kernel::OM->Get('Kernel::System::Group');
|
|
|
|
for my $QueueID (@QueueIDs) {
|
|
my $GroupID = $QueueObject->GetQueueGroupID( QueueID => $QueueID );
|
|
my %GroupMember = $GroupObject->PermissionGroupGet(
|
|
GroupID => $GroupID,
|
|
Type => 'responsible',
|
|
);
|
|
USER_ID:
|
|
for my $UserID ( sort keys %GroupMember ) {
|
|
next USER_ID if !$AllGroupsMembers{$UserID};
|
|
$AllGroupsMembersNew{$UserID} = $AllGroupsMembers{$UserID};
|
|
}
|
|
%ResponsibleList = %AllGroupsMembersNew;
|
|
}
|
|
}
|
|
else {
|
|
%ResponsibleList = %AllGroupsMembers;
|
|
}
|
|
|
|
# Execute ACLs.
|
|
my $ACL = $TicketObject->TicketAcl(
|
|
%Param,
|
|
Action => $Self->{Action},
|
|
ReturnType => 'Ticket',
|
|
ReturnSubType => 'Responsible',
|
|
Data => \%ResponsibleList,
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
return $TicketObject->TicketAclData() if $ACL;
|
|
return %ResponsibleList;
|
|
}
|
|
|
|
sub _GetQueues {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my %QueueList = $Kernel::OM->Get('Kernel::System::Ticket')->MoveList(
|
|
%Param,
|
|
UserID => $Self->{UserID},
|
|
Action => $Self->{Action},
|
|
Type => 'move_into',
|
|
);
|
|
|
|
return %QueueList;
|
|
}
|
|
|
|
sub _GetPriorities {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my %PriorityList = $Kernel::OM->Get('Kernel::System::Ticket')->TicketPriorityList(
|
|
%Param,
|
|
Action => $Self->{Action},
|
|
UserID => $Self->{UserID},
|
|
);
|
|
return %PriorityList;
|
|
}
|
|
|
|
1;
|