2683 lines
104 KiB
Perl
2683 lines
104 KiB
Perl
|
# --
|
||
|
# OTOBO is a web-based ticketing system for service organisations.
|
||
|
# --
|
||
|
# Copyright (C) 2001-2020 OTRS AG, https://otrs.com/
|
||
|
# Copyright (C) 2019-2023 Rother OSS GmbH, https://otobo.de/
|
||
|
# --
|
||
|
# $origin: otobo - a077e914380d1a13d5aa31472ea687353b614622 - Kernel/Modules/AgentTicketSearch.pm
|
||
|
# --
|
||
|
# This program is free software: you can redistribute it and/or modify it under
|
||
|
# the terms of the GNU General Public License as published by the Free Software
|
||
|
# Foundation, either version 3 of the License, or (at your option) any later version.
|
||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||
|
# You should have received a copy of the GNU General Public License
|
||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
|
# --
|
||
|
|
||
|
package Kernel::Modules::AgentTicketSearch;
|
||
|
|
||
|
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 ) = @_;
|
||
|
|
||
|
my $Output;
|
||
|
|
||
|
# get needed objects
|
||
|
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
|
||
|
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
||
|
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
||
|
|
||
|
my $Config = $ConfigObject->Get("Ticket::Frontend::$Self->{Action}");
|
||
|
|
||
|
# get config data
|
||
|
$Self->{StartHit} = int( $ParamObject->GetParam( Param => 'StartHit' ) || 1 );
|
||
|
$Self->{SearchLimit} = $Config->{SearchLimit} || 500;
|
||
|
$Self->{SortBy} = $ParamObject->GetParam( Param => 'SortBy' )
|
||
|
|| $Config->{'SortBy::Default'}
|
||
|
|| 'Age';
|
||
|
$Self->{OrderBy} = $ParamObject->GetParam( Param => 'OrderBy' )
|
||
|
|| $Config->{'Order::Default'}
|
||
|
|| 'Down';
|
||
|
$Self->{Profile} = $ParamObject->GetParam( Param => 'Profile' ) || '';
|
||
|
$Self->{SaveProfile} = $ParamObject->GetParam( Param => 'SaveProfile' ) || '';
|
||
|
$Self->{TakeLastSearch} = $ParamObject->GetParam( Param => 'TakeLastSearch' ) || '';
|
||
|
$Self->{SelectTemplate} = $ParamObject->GetParam( Param => 'SelectTemplate' ) || '';
|
||
|
$Self->{EraseTemplate} = $ParamObject->GetParam( Param => 'EraseTemplate' ) || '';
|
||
|
|
||
|
# get list type
|
||
|
my $TreeView = 0;
|
||
|
if ( $ConfigObject->Get('Ticket::Frontend::ListType') eq 'tree' ) {
|
||
|
$TreeView = 1;
|
||
|
}
|
||
|
|
||
|
# get layout object
|
||
|
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
||
|
|
||
|
# check request
|
||
|
if ( $Self->{Subaction} eq 'OpenSearchDescriptionTicketNumber' ) {
|
||
|
my $Output = $LayoutObject->Output(
|
||
|
TemplateFile => 'AgentTicketSearchOpenSearchDescriptionTicketNumber',
|
||
|
Data => \%Param,
|
||
|
);
|
||
|
|
||
|
# TODO: maybe declare as UTF-8
|
||
|
return $LayoutObject->Attachment(
|
||
|
Filename => 'OpenSearchDescriptionTicketNumber.xml',
|
||
|
ContentType => 'application/opensearchdescription+xml',
|
||
|
Content => $Output,
|
||
|
Type => 'inline',
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if ( $Self->{Subaction} eq 'OpenSearchDescriptionFulltext' ) {
|
||
|
my $Output = $LayoutObject->Output(
|
||
|
TemplateFile => 'AgentTicketSearchOpenSearchDescriptionFulltext',
|
||
|
Data => \%Param,
|
||
|
);
|
||
|
|
||
|
# TODO: maybe declare as UTF-8
|
||
|
return $LayoutObject->Attachment(
|
||
|
Filename => 'OpenSearchDescriptionFulltext.xml',
|
||
|
ContentType => 'application/opensearchdescription+xml',
|
||
|
Content => $Output,
|
||
|
Type => 'inline',
|
||
|
);
|
||
|
}
|
||
|
|
||
|
# Autocomplete is executed via AJAX request.
|
||
|
if ( $Self->{Subaction} eq 'AJAXAutocomplete' ) {
|
||
|
$LayoutObject->ChallengeTokenCheck();
|
||
|
|
||
|
my $Skip = $ParamObject->GetParam( Param => 'Skip' ) || '';
|
||
|
my $Search = $ParamObject->GetParam( Param => 'Term' ) || '';
|
||
|
my $Filter = $ParamObject->GetParam( Param => 'Filter' ) || '{}';
|
||
|
my $MaxResults = int( $ParamObject->GetParam( Param => 'MaxResults' ) || 20 );
|
||
|
|
||
|
# Remove leading and trailing spaces from search term.
|
||
|
$Search =~ s{ \A \s* ( [^\s]+ ) \s* \z }{$1}xms;
|
||
|
|
||
|
# Parse passed search filter.
|
||
|
my $SearchFilter = $Kernel::OM->Get('Kernel::System::JSON')->Decode(
|
||
|
Data => $Filter,
|
||
|
);
|
||
|
|
||
|
# Workaround, all auto completion requests get posted by UTF8 anyway.
|
||
|
# Convert any to 8bit string if application is not running in UTF8.
|
||
|
$Search = $Kernel::OM->Get('Kernel::System::Encode')->Convert2CharsetInternal(
|
||
|
Text => $Search,
|
||
|
From => 'utf-8',
|
||
|
);
|
||
|
|
||
|
my @TicketIDs;
|
||
|
|
||
|
# Search for tickets by:
|
||
|
# - Ticket Number
|
||
|
# - Ticket Title
|
||
|
if ($Search) {
|
||
|
|
||
|
@TicketIDs = $TicketObject->TicketSearch(
|
||
|
%{$SearchFilter},
|
||
|
TicketNumber => '%' . $Search . '%',
|
||
|
Limit => $MaxResults,
|
||
|
Result => 'ARRAY',
|
||
|
ArchiveFlags => ['n'],
|
||
|
UserID => $Self->{UserID},
|
||
|
);
|
||
|
|
||
|
if ( !@TicketIDs ) {
|
||
|
@TicketIDs = $TicketObject->TicketSearch(
|
||
|
%{$SearchFilter},
|
||
|
Title => '%' . $Search . '%',
|
||
|
Limit => $MaxResults,
|
||
|
Result => 'ARRAY',
|
||
|
ArchiveFlags => ['n'],
|
||
|
UserID => $Self->{UserID},
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
my @Results;
|
||
|
|
||
|
# Include additional ticket information in results.
|
||
|
TICKET:
|
||
|
for my $TicketID (@TicketIDs) {
|
||
|
next TICKET if !$TicketID;
|
||
|
next TICKET if $TicketID eq $Skip;
|
||
|
|
||
|
my %Ticket = $TicketObject->TicketGet(
|
||
|
TicketID => $TicketID,
|
||
|
DynamicFields => 0,
|
||
|
UserID => $Self->{UserID},
|
||
|
);
|
||
|
|
||
|
next TICKET if !%Ticket;
|
||
|
|
||
|
push @Results, {
|
||
|
Key => $Ticket{TicketNumber},
|
||
|
Value => $Ticket{TicketNumber} . ' ' . $Ticket{Title},
|
||
|
};
|
||
|
}
|
||
|
|
||
|
my $JSON = $LayoutObject->JSONEncode(
|
||
|
Data => \@Results || [],
|
||
|
);
|
||
|
|
||
|
# Send JSON response.
|
||
|
return $LayoutObject->Attachment(
|
||
|
ContentType => 'application/json; charset=' . $LayoutObject->{Charset},
|
||
|
Content => $JSON || '',
|
||
|
Type => 'inline',
|
||
|
NoCache => 1,
|
||
|
);
|
||
|
}
|
||
|
|
||
|
# check request
|
||
|
if ( $ParamObject->GetParam( Param => 'SearchTemplate' ) && $Self->{Profile} ) {
|
||
|
my $Profile = $LayoutObject->LinkEncode( $Self->{Profile} );
|
||
|
return $LayoutObject->Redirect(
|
||
|
OP =>
|
||
|
"Action=AgentTicketSearch;Subaction=Search;TakeLastSearch=1;SaveProfile=1;Profile=$Profile"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
# get single params
|
||
|
my %GetParam;
|
||
|
|
||
|
# get needed objects
|
||
|
my $SearchProfileObject = $Kernel::OM->Get('Kernel::System::SearchProfile');
|
||
|
my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField');
|
||
|
my $BackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
|
||
|
|
||
|
# get dynamic field config for frontend module
|
||
|
my $DynamicFieldFilter = $Config->{DynamicField};
|
||
|
|
||
|
# get the dynamic fields for ticket object
|
||
|
my $DynamicField = $DynamicFieldObject->DynamicFieldListGet(
|
||
|
Valid => 1,
|
||
|
ObjectType => [ 'Ticket', 'Article' ],
|
||
|
FieldFilter => $DynamicFieldFilter || {},
|
||
|
);
|
||
|
|
||
|
# collect all searchable article field definitions and add the fields to the attributes array
|
||
|
my %ArticleSearchableFields = $Kernel::OM->Get('Kernel::System::Ticket::Article')->ArticleSearchableFieldsList();
|
||
|
|
||
|
# load profiles string params (press load profile)
|
||
|
if ( ( $Self->{Subaction} eq 'LoadProfile' && $Self->{Profile} ) || $Self->{TakeLastSearch} ) {
|
||
|
%GetParam = $SearchProfileObject->SearchProfileGet(
|
||
|
Base => 'TicketSearch',
|
||
|
Name => $Self->{Profile},
|
||
|
UserLogin => $Self->{UserLogin},
|
||
|
);
|
||
|
|
||
|
# convert attributes
|
||
|
if ( $GetParam{ShownAttributes} && ref $GetParam{ShownAttributes} eq 'ARRAY' ) {
|
||
|
$GetParam{ShownAttributes} = join ';', @{ $GetParam{ShownAttributes} };
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# get search string params (get submitted params)
|
||
|
else {
|
||
|
for my $Key (
|
||
|
sort keys %ArticleSearchableFields,
|
||
|
qw(
|
||
|
TicketNumber Title CustomerID CustomerIDRaw CustomerUserLogin CustomerUserLoginRaw
|
||
|
CustomerUserID StateType Agent ResultForm TimeSearchType ChangeTimeSearchType
|
||
|
CloseTimeSearchType LastChangeTimeSearchType EscalationTimeSearchType PendingTimeSearchType
|
||
|
UseSubQueues ArticleTimeSearchType SearchInArchive Fulltext ShownAttributes
|
||
|
ArticleCreateTimePointFormat ArticleCreateTimePoint ArticleCreateTimePointStart
|
||
|
ArticleCreateTimeStart ArticleCreateTimeStartDay ArticleCreateTimeStartMonth
|
||
|
ArticleCreateTimeStartYear ArticleCreateTimeStop ArticleCreateTimeStopDay
|
||
|
ArticleCreateTimeStopMonth ArticleCreateTimeStopYear
|
||
|
TicketCreateTimePointFormat TicketCreateTimePoint
|
||
|
TicketCreateTimePointStart
|
||
|
TicketCreateTimeStart TicketCreateTimeStartDay TicketCreateTimeStartMonth
|
||
|
TicketCreateTimeStartYear
|
||
|
TicketCreateTimeStop TicketCreateTimeStopDay TicketCreateTimeStopMonth
|
||
|
TicketCreateTimeStopYear
|
||
|
TicketChangeTimePointFormat TicketChangeTimePoint
|
||
|
TicketChangeTimePointStart
|
||
|
TicketChangeTimeStart TicketChangeTimeStartDay TicketChangeTimeStartMonth
|
||
|
TicketChangeTimeStartYear
|
||
|
TicketChangeTimeStop TicketChangeTimeStopDay TicketChangeTimeStopMonth
|
||
|
TicketChangeTimeStopYear
|
||
|
TicketLastChangeTimePointFormat TicketLastChangeTimePoint
|
||
|
TicketLastChangeTimePointStart
|
||
|
TicketLastChangeTimeStart TicketLastChangeTimeStartDay TicketLastChangeTimeStartMonth
|
||
|
TicketLastChangeTimeStartYear
|
||
|
TicketLastChangeTimeStop TicketLastChangeTimeStopDay TicketLastChangeTimeStopMonth
|
||
|
TicketLastChangeTimeStopYear
|
||
|
TicketCloseTimePointFormat TicketCloseTimePoint
|
||
|
TicketCloseTimePointStart
|
||
|
TicketCloseTimeStart TicketCloseTimeStartDay TicketCloseTimeStartMonth
|
||
|
TicketCloseTimeStartYear
|
||
|
TicketCloseTimeStop TicketCloseTimeStopDay TicketCloseTimeStopMonth
|
||
|
TicketCloseTimeStopYear
|
||
|
TicketPendingTimePointFormat TicketPendingTimePoint
|
||
|
TicketPendingTimePointStart
|
||
|
TicketPendingTimeStart TicketPendingTimeStartDay TicketPendingTimeStartMonth
|
||
|
TicketPendingTimeStartYear
|
||
|
TicketPendingTimeStop TicketPendingTimeStopDay TicketPendingTimeStopMonth
|
||
|
TicketPendingTimeStopYear
|
||
|
TicketEscalationTimePointFormat TicketEscalationTimePoint
|
||
|
TicketEscalationTimePointStart
|
||
|
TicketEscalationTimeStart TicketEscalationTimeStartDay TicketEscalationTimeStartMonth
|
||
|
TicketEscalationTimeStartYear
|
||
|
TicketEscalationTimeStop TicketEscalationTimeStopDay TicketEscalationTimeStopMonth
|
||
|
TicketEscalationTimeStopYear
|
||
|
TicketCloseTimeNewerDate TicketCloseTimeOlderDate
|
||
|
FulltextES
|
||
|
)
|
||
|
)
|
||
|
{
|
||
|
# get search string params (get submitted params)
|
||
|
$GetParam{$Key} = $ParamObject->GetParam( Param => $Key );
|
||
|
|
||
|
# remove white space on the start and end
|
||
|
if ( $GetParam{$Key} ) {
|
||
|
$GetParam{$Key} =~ s/\s+$//g;
|
||
|
$GetParam{$Key} =~ s/^\s+//g;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# get array params
|
||
|
for my $Key (
|
||
|
qw(StateIDs States StateTypeIDs QueueIDs Queues PriorityIDs Priorities OwnerIDs
|
||
|
CreatedQueueIDs CreatedUserIDs WatchUserIDs ResponsibleIDs
|
||
|
TypeIDs Types ServiceIDs Services SLAIDs SLAs LockIDs Locks)
|
||
|
)
|
||
|
{
|
||
|
|
||
|
# get search array params (get submitted params)
|
||
|
my @Array = $ParamObject->GetArray( Param => $Key );
|
||
|
if (@Array) {
|
||
|
$GetParam{$Key} = \@Array;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# get Dynamic fields from param object
|
||
|
# cycle trough the activated Dynamic Fields for this screen
|
||
|
DYNAMICFIELD:
|
||
|
for my $DynamicFieldConfig ( @{$DynamicField} ) {
|
||
|
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
|
||
|
|
||
|
# get search field preferences
|
||
|
my $SearchFieldPreferences = $BackendObject->SearchFieldPreferences(
|
||
|
DynamicFieldConfig => $DynamicFieldConfig,
|
||
|
);
|
||
|
|
||
|
next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences);
|
||
|
|
||
|
PREFERENCE:
|
||
|
for my $Preference ( @{$SearchFieldPreferences} ) {
|
||
|
|
||
|
# extract the dynamic field value from the web request
|
||
|
my $DynamicFieldValue = $BackendObject->SearchFieldValueGet(
|
||
|
DynamicFieldConfig => $DynamicFieldConfig,
|
||
|
ParamObject => $ParamObject,
|
||
|
ReturnProfileStructure => 1,
|
||
|
LayoutObject => $LayoutObject,
|
||
|
Type => $Preference->{Type},
|
||
|
);
|
||
|
|
||
|
# set the complete value structure in GetParam to store it later in the search profile
|
||
|
if ( IsHashRefWithData($DynamicFieldValue) ) {
|
||
|
%GetParam = ( %GetParam, %{$DynamicFieldValue} );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# get article create time option
|
||
|
if ( !$GetParam{ArticleTimeSearchType} ) {
|
||
|
$GetParam{'ArticleTimeSearchType::None'} = 1;
|
||
|
}
|
||
|
elsif ( $GetParam{ArticleTimeSearchType} eq 'TimePoint' ) {
|
||
|
$GetParam{'ArticleTimeSearchType::TimePoint'} = 1;
|
||
|
}
|
||
|
elsif ( $GetParam{ArticleTimeSearchType} eq 'TimeSlot' ) {
|
||
|
$GetParam{'ArticleTimeSearchType::TimeSlot'} = 1;
|
||
|
}
|
||
|
|
||
|
# get create time option
|
||
|
if ( !$GetParam{TimeSearchType} ) {
|
||
|
$GetParam{'TimeSearchType::None'} = 1;
|
||
|
}
|
||
|
elsif ( $GetParam{TimeSearchType} eq 'TimePoint' ) {
|
||
|
$GetParam{'TimeSearchType::TimePoint'} = 1;
|
||
|
}
|
||
|
elsif ( $GetParam{TimeSearchType} eq 'TimeSlot' ) {
|
||
|
$GetParam{'TimeSearchType::TimeSlot'} = 1;
|
||
|
}
|
||
|
|
||
|
# get change time option
|
||
|
if ( !$GetParam{ChangeTimeSearchType} ) {
|
||
|
$GetParam{'ChangeTimeSearchType::None'} = 1;
|
||
|
}
|
||
|
elsif ( $GetParam{ChangeTimeSearchType} eq 'TimePoint' ) {
|
||
|
$GetParam{'ChangeTimeSearchType::TimePoint'} = 1;
|
||
|
}
|
||
|
elsif ( $GetParam{ChangeTimeSearchType} eq 'TimeSlot' ) {
|
||
|
$GetParam{'ChangeTimeSearchType::TimeSlot'} = 1;
|
||
|
}
|
||
|
|
||
|
# get last change time option
|
||
|
if ( !$GetParam{LastChangeTimeSearchType} ) {
|
||
|
$GetParam{'LastChangeTimeSearchType::None'} = 1;
|
||
|
}
|
||
|
elsif ( $GetParam{LastChangeTimeSearchType} eq 'TimePoint' ) {
|
||
|
$GetParam{'LastChangeTimeSearchType::TimePoint'} = 1;
|
||
|
}
|
||
|
elsif ( $GetParam{LastChangeTimeSearchType} eq 'TimeSlot' ) {
|
||
|
$GetParam{'LastChangeTimeSearchType::TimeSlot'} = 1;
|
||
|
}
|
||
|
|
||
|
# get close time option
|
||
|
if ( !$GetParam{PendingTimeSearchType} ) {
|
||
|
$GetParam{'PendingTimeSearchType::None'} = 1;
|
||
|
}
|
||
|
elsif ( $GetParam{PendingTimeSearchType} eq 'TimePoint' ) {
|
||
|
$GetParam{'PendingTimeSearchType::TimePoint'} = 1;
|
||
|
}
|
||
|
elsif ( $GetParam{PendingTimeSearchType} eq 'TimeSlot' ) {
|
||
|
$GetParam{'PendingTimeSearchType::TimeSlot'} = 1;
|
||
|
}
|
||
|
|
||
|
# get close time option
|
||
|
if ( !$GetParam{CloseTimeSearchType} ) {
|
||
|
$GetParam{'CloseTimeSearchType::None'} = 1;
|
||
|
}
|
||
|
elsif ( $GetParam{CloseTimeSearchType} eq 'TimePoint' ) {
|
||
|
$GetParam{'CloseTimeSearchType::TimePoint'} = 1;
|
||
|
}
|
||
|
elsif ( $GetParam{CloseTimeSearchType} eq 'TimeSlot' ) {
|
||
|
$GetParam{'CloseTimeSearchType::TimeSlot'} = 1;
|
||
|
}
|
||
|
|
||
|
# get escalation time option
|
||
|
if ( !$GetParam{EscalationTimeSearchType} ) {
|
||
|
$GetParam{'EscalationTimeSearchType::None'} = 1;
|
||
|
}
|
||
|
elsif ( $GetParam{EscalationTimeSearchType} eq 'TimePoint' ) {
|
||
|
$GetParam{'EscalationTimeSearchType::TimePoint'} = 1;
|
||
|
}
|
||
|
elsif ( $GetParam{EscalationTimeSearchType} eq 'TimeSlot' ) {
|
||
|
$GetParam{'EscalationTimeSearchType::TimeSlot'} = 1;
|
||
|
}
|
||
|
|
||
|
# set result form env
|
||
|
if ( !$GetParam{ResultForm} ) {
|
||
|
$GetParam{ResultForm} = '';
|
||
|
}
|
||
|
|
||
|
# get needed objects
|
||
|
my $UserObject = $Kernel::OM->Get('Kernel::System::User');
|
||
|
my $StateObject = $Kernel::OM->Get('Kernel::System::State');
|
||
|
|
||
|
# show result site
|
||
|
if ( $Self->{Subaction} eq 'Search' && !$Self->{EraseTemplate} ) {
|
||
|
|
||
|
$Self->{ProfileName} = '';
|
||
|
if ( $Self->{Profile} ) {
|
||
|
$Self->{ProfileName} = "($Self->{Profile})";
|
||
|
}
|
||
|
|
||
|
# fill up profile name (e.g. with last-search)
|
||
|
if ( !$Self->{Profile} || !$Self->{SaveProfile} ) {
|
||
|
$Self->{Profile} = Translatable('last-search');
|
||
|
}
|
||
|
|
||
|
if ( !$Self->{ProfileName} ) {
|
||
|
$Self->{ProfileName} = "($Self->{Profile})";
|
||
|
}
|
||
|
|
||
|
if ( $Self->{ProfileName} eq '(last-search)' ) {
|
||
|
$Self->{ProfileName} = '(' . $LayoutObject->{LanguageObject}->Translate('last-search') . ')';
|
||
|
}
|
||
|
|
||
|
# save search profile (under last-search or real profile name)
|
||
|
$Self->{SaveProfile} = 1;
|
||
|
|
||
|
# remember last search values
|
||
|
if ( $Self->{SaveProfile} && $Self->{Profile} ) {
|
||
|
|
||
|
# remove old profile stuff
|
||
|
$SearchProfileObject->SearchProfileDelete(
|
||
|
Base => 'TicketSearch',
|
||
|
Name => $Self->{Profile},
|
||
|
UserLogin => $Self->{UserLogin},
|
||
|
);
|
||
|
|
||
|
# convert attributes
|
||
|
if ( $GetParam{ShownAttributes} && ref $GetParam{ShownAttributes} eq '' ) {
|
||
|
$GetParam{ShownAttributes} = [ split /;/, $GetParam{ShownAttributes} ];
|
||
|
}
|
||
|
|
||
|
# replace StateType to StateIDs
|
||
|
if ( $GetParam{StateType} ) {
|
||
|
my @StateIDs;
|
||
|
|
||
|
if ( $GetParam{StateType} eq 'Open' ) {
|
||
|
@StateIDs = $StateObject->StateGetStatesByType(
|
||
|
Type => 'Viewable',
|
||
|
Result => 'ID',
|
||
|
);
|
||
|
}
|
||
|
elsif ( $GetParam{StateType} eq 'Closed' ) {
|
||
|
my %ViewableStateOpenLookup = $StateObject->StateGetStatesByType(
|
||
|
Type => 'Viewable',
|
||
|
Result => 'HASH',
|
||
|
);
|
||
|
|
||
|
my %StateList = $StateObject->StateList( UserID => $Self->{UserID} );
|
||
|
for my $Item ( sort keys %StateList ) {
|
||
|
if ( !$ViewableStateOpenLookup{$Item} ) {
|
||
|
push @StateIDs, $Item;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# current ticket state type
|
||
|
else {
|
||
|
@StateIDs = $StateObject->StateGetStatesByType(
|
||
|
StateType => $GetParam{StateType},
|
||
|
Result => 'ID',
|
||
|
);
|
||
|
}
|
||
|
|
||
|
# merge with StateIDs
|
||
|
if ( @StateIDs && IsArrayRefWithData( $GetParam{StateIDs} ) ) {
|
||
|
my %StateIDs = map { $_ => 1 } @StateIDs;
|
||
|
@StateIDs = grep { exists $StateIDs{$_} } @{ $GetParam{StateIDs} };
|
||
|
}
|
||
|
|
||
|
if (@StateIDs) {
|
||
|
$GetParam{StateIDs} = \@StateIDs;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# insert new profile params
|
||
|
KEY:
|
||
|
for my $Key ( sort keys %GetParam ) {
|
||
|
next KEY if !defined $GetParam{$Key};
|
||
|
next KEY if $Key eq 'StateType';
|
||
|
$SearchProfileObject->SearchProfileAdd(
|
||
|
Base => 'TicketSearch',
|
||
|
Name => $Self->{Profile},
|
||
|
Key => $Key,
|
||
|
Value => $GetParam{$Key},
|
||
|
UserLogin => $Self->{UserLogin},
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
my %TimeMap = (
|
||
|
ArticleCreate => 'ArticleTime',
|
||
|
TicketCreate => 'Time',
|
||
|
TicketChange => 'ChangeTime',
|
||
|
TicketLastChange => 'LastChangeTime',
|
||
|
TicketClose => 'CloseTime',
|
||
|
TicketPending => 'PendingTime',
|
||
|
TicketEscalation => 'EscalationTime',
|
||
|
);
|
||
|
|
||
|
for my $TimeType ( sort keys %TimeMap ) {
|
||
|
|
||
|
# get create time settings
|
||
|
if ( !$GetParam{ $TimeMap{$TimeType} . 'SearchType' } ) {
|
||
|
|
||
|
# do nothing with time stuff
|
||
|
}
|
||
|
elsif ( $GetParam{ $TimeMap{$TimeType} . 'SearchType' } eq 'TimeSlot' ) {
|
||
|
for my $Key (qw(Month Day)) {
|
||
|
$GetParam{ $TimeType . 'TimeStart' . $Key } = sprintf( "%02d", $GetParam{ $TimeType . 'TimeStart' . $Key } );
|
||
|
$GetParam{ $TimeType . 'TimeStop' . $Key } = sprintf( "%02d", $GetParam{ $TimeType . 'TimeStop' . $Key } );
|
||
|
}
|
||
|
if (
|
||
|
$GetParam{ $TimeType . 'TimeStartDay' }
|
||
|
&& $GetParam{ $TimeType . 'TimeStartMonth' }
|
||
|
&& $GetParam{ $TimeType . 'TimeStartYear' }
|
||
|
)
|
||
|
{
|
||
|
my $DateTimeObject = $Kernel::OM->Create(
|
||
|
'Kernel::System::DateTime',
|
||
|
ObjectParams => {
|
||
|
Year => $GetParam{ $TimeType . 'TimeStartYear' },
|
||
|
Month => $GetParam{ $TimeType . 'TimeStartMonth' },
|
||
|
Day => $GetParam{ $TimeType . 'TimeStartDay' },
|
||
|
Hour => 0, # midnight
|
||
|
Minute => 0,
|
||
|
Second => 0,
|
||
|
TimeZone => $Self->{UserTimeZone} || Kernel::System::DateTime->UserDefaultTimeZoneGet(),
|
||
|
},
|
||
|
);
|
||
|
|
||
|
# Convert start time to local system time zone.
|
||
|
$DateTimeObject->ToOTOBOTimeZone();
|
||
|
$GetParam{ $TimeType . 'TimeNewerDate' } = $DateTimeObject->ToString();
|
||
|
}
|
||
|
if (
|
||
|
$GetParam{ $TimeType . 'TimeStopDay' }
|
||
|
&& $GetParam{ $TimeType . 'TimeStopMonth' }
|
||
|
&& $GetParam{ $TimeType . 'TimeStopYear' }
|
||
|
)
|
||
|
{
|
||
|
my $DateTimeObject = $Kernel::OM->Create(
|
||
|
'Kernel::System::DateTime',
|
||
|
ObjectParams => {
|
||
|
Year => $GetParam{ $TimeType . 'TimeStopYear' },
|
||
|
Month => $GetParam{ $TimeType . 'TimeStopMonth' },
|
||
|
Day => $GetParam{ $TimeType . 'TimeStopDay' },
|
||
|
Hour => 23, # just before midnight
|
||
|
Minute => 59,
|
||
|
Second => 59,
|
||
|
TimeZone => $Self->{UserTimeZone} || Kernel::System::DateTime->UserDefaultTimeZoneGet(),
|
||
|
},
|
||
|
);
|
||
|
|
||
|
# Convert stop time to local system time zone.
|
||
|
$DateTimeObject->ToOTOBOTimeZone();
|
||
|
$GetParam{ $TimeType . 'TimeOlderDate' } = $DateTimeObject->ToString();
|
||
|
}
|
||
|
}
|
||
|
elsif ( $GetParam{ $TimeMap{$TimeType} . 'SearchType' } eq 'TimePoint' ) {
|
||
|
if (
|
||
|
$GetParam{ $TimeType . 'TimePoint' }
|
||
|
&& $GetParam{ $TimeType . 'TimePointStart' }
|
||
|
&& $GetParam{ $TimeType . 'TimePointFormat' }
|
||
|
)
|
||
|
{
|
||
|
my $Time = 0;
|
||
|
if ( $GetParam{ $TimeType . 'TimePointFormat' } eq 'minute' ) {
|
||
|
$Time = $GetParam{ $TimeType . 'TimePoint' };
|
||
|
}
|
||
|
elsif ( $GetParam{ $TimeType . 'TimePointFormat' } eq 'hour' ) {
|
||
|
$Time = $GetParam{ $TimeType . 'TimePoint' } * 60;
|
||
|
}
|
||
|
elsif ( $GetParam{ $TimeType . 'TimePointFormat' } eq 'day' ) {
|
||
|
$Time = $GetParam{ $TimeType . 'TimePoint' } * 60 * 24;
|
||
|
}
|
||
|
elsif ( $GetParam{ $TimeType . 'TimePointFormat' } eq 'week' ) {
|
||
|
$Time = $GetParam{ $TimeType . 'TimePoint' } * 60 * 24 * 7;
|
||
|
}
|
||
|
elsif ( $GetParam{ $TimeType . 'TimePointFormat' } eq 'month' ) {
|
||
|
$Time = $GetParam{ $TimeType . 'TimePoint' } * 60 * 24 * 30;
|
||
|
}
|
||
|
elsif ( $GetParam{ $TimeType . 'TimePointFormat' } eq 'year' ) {
|
||
|
$Time = $GetParam{ $TimeType . 'TimePoint' } * 60 * 24 * 365;
|
||
|
}
|
||
|
if ( $GetParam{ $TimeType . 'TimePointStart' } eq 'Before' ) {
|
||
|
|
||
|
# more than ... ago
|
||
|
$GetParam{ $TimeType . 'TimeOlderMinutes' } = $Time;
|
||
|
}
|
||
|
elsif ( $GetParam{ $TimeType . 'TimePointStart' } eq 'Next' ) {
|
||
|
|
||
|
# within next
|
||
|
$GetParam{ $TimeType . 'TimeNewerMinutes' } = 0;
|
||
|
$GetParam{ $TimeType . 'TimeOlderMinutes' } = -$Time;
|
||
|
}
|
||
|
elsif ( $GetParam{ $TimeType . 'TimePointStart' } eq 'After' ) {
|
||
|
|
||
|
# in more then ...
|
||
|
$GetParam{ $TimeType . 'TimeNewerMinutes' } = -$Time;
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
# within last ...
|
||
|
$GetParam{ $TimeType . 'TimeOlderMinutes' } = 0;
|
||
|
$GetParam{ $TimeType . 'TimeNewerMinutes' } = $Time;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');
|
||
|
|
||
|
# Special behavior for the fulltext search toolbar module:
|
||
|
# - Check full text string to see if contents is a ticket number.
|
||
|
# - If exists and not in print or CSV mode, redirect to the ticket.
|
||
|
# See http://bugs.otrs.org/show_bug.cgi?id=4238 for details.
|
||
|
# The original problem was that tickets with customer reply will be
|
||
|
# found by a fulltext search (ticket number is in the subjects), but
|
||
|
# 'new' tickets will not be found.
|
||
|
if (
|
||
|
$GetParam{Fulltext}
|
||
|
&& $ParamObject->GetParam( Param => 'CheckTicketNumberAndRedirect' )
|
||
|
&& $GetParam{ResultForm} ne 'Normal'
|
||
|
&& $GetParam{ResultForm} ne 'Print'
|
||
|
)
|
||
|
{
|
||
|
my $TicketID = $TicketObject->TicketIDLookup(
|
||
|
TicketNumber => $GetParam{Fulltext},
|
||
|
UserID => $Self->{UserID},
|
||
|
);
|
||
|
if ($TicketID) {
|
||
|
return $LayoutObject->Redirect(
|
||
|
OP => "Action=AgentTicketZoom;TicketID=$TicketID",
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# prepare archive flag
|
||
|
if ( $ConfigObject->Get('Ticket::ArchiveSystem') ) {
|
||
|
|
||
|
$GetParam{SearchInArchive} ||= '';
|
||
|
if ( $GetParam{SearchInArchive} eq 'AllTickets' ) {
|
||
|
$GetParam{ArchiveFlags} = [ 'y', 'n' ];
|
||
|
}
|
||
|
elsif ( $GetParam{SearchInArchive} eq 'ArchivedTickets' ) {
|
||
|
$GetParam{ArchiveFlags} = ['y'];
|
||
|
}
|
||
|
else {
|
||
|
$GetParam{ArchiveFlags} = ['n'];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
my %AttributeLookup;
|
||
|
|
||
|
# create attribute lookup table
|
||
|
for my $Attribute ( @{ $GetParam{ShownAttributes} || [] } ) {
|
||
|
$AttributeLookup{$Attribute} = 1;
|
||
|
}
|
||
|
|
||
|
# dynamic fields search parameters for ticket search
|
||
|
my %DynamicFieldSearchParameters;
|
||
|
|
||
|
# cycle trough the activated Dynamic Fields for this screen
|
||
|
DYNAMICFIELD:
|
||
|
for my $DynamicFieldConfig ( @{$DynamicField} ) {
|
||
|
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
|
||
|
|
||
|
# get search field preferences
|
||
|
my $SearchFieldPreferences = $BackendObject->SearchFieldPreferences(
|
||
|
DynamicFieldConfig => $DynamicFieldConfig,
|
||
|
);
|
||
|
|
||
|
next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences);
|
||
|
|
||
|
PREFERENCE:
|
||
|
for my $Preference ( @{$SearchFieldPreferences} ) {
|
||
|
|
||
|
if (
|
||
|
!$AttributeLookup{
|
||
|
'LabelSearch_DynamicField_'
|
||
|
. $DynamicFieldConfig->{Name}
|
||
|
. $Preference->{Type}
|
||
|
}
|
||
|
)
|
||
|
{
|
||
|
next PREFERENCE;
|
||
|
}
|
||
|
|
||
|
# extract the dynamic field value from the profile
|
||
|
my $SearchParameter = $BackendObject->SearchFieldParameterBuild(
|
||
|
DynamicFieldConfig => $DynamicFieldConfig,
|
||
|
Profile => \%GetParam,
|
||
|
LayoutObject => $LayoutObject,
|
||
|
Type => $Preference->{Type},
|
||
|
);
|
||
|
|
||
|
# set search parameter
|
||
|
if ( defined $SearchParameter ) {
|
||
|
$DynamicFieldSearchParameters{ 'DynamicField_' . $DynamicFieldConfig->{Name} } = $SearchParameter->{Parameter};
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
my @ViewableTicketIDs;
|
||
|
|
||
|
my $ESActive = $Kernel::OM->Get('Kernel::Config')->Get('Elasticsearch::Active') || 0;
|
||
|
|
||
|
# check whether we want to perform a search via Elasticsearch or not
|
||
|
# use normal search for sorting, or if ES is not activated
|
||
|
if ( $GetParam{FulltextES} && ( !$ESActive || $Self->{TakeLastSearch} ) ) {
|
||
|
$GetParam{Fulltext} = $GetParam{Fulltext} || $GetParam{FulltextES};
|
||
|
delete $GetParam{FulltextES};
|
||
|
}
|
||
|
|
||
|
# search via DB if FulltextES is empty
|
||
|
if ( !$GetParam{FulltextES} ) {
|
||
|
local $Kernel::System::DB::UseSlaveDB = 1;
|
||
|
|
||
|
# perform ticket search
|
||
|
@ViewableTicketIDs = $TicketObject->TicketSearch(
|
||
|
Result => 'ARRAY',
|
||
|
SortBy => $Self->{SortBy},
|
||
|
OrderBy => $Self->{OrderBy},
|
||
|
Limit => $Self->{SearchLimit},
|
||
|
UserID => $Self->{UserID},
|
||
|
ConditionInline => $Config->{ExtendedSearchCondition},
|
||
|
ContentSearchPrefix => '*',
|
||
|
ContentSearchSuffix => '*',
|
||
|
FullTextIndex => 1,
|
||
|
%GetParam,
|
||
|
%DynamicFieldSearchParameters,
|
||
|
);
|
||
|
}
|
||
|
|
||
|
# if ES is activated
|
||
|
else {
|
||
|
# get data from cache
|
||
|
my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
|
||
|
my $CacheData = $CacheObject->Get(
|
||
|
Type => 'TicketSearch',
|
||
|
Key => ( $GetParam{FulltextES} || '' ) . '_SortBy_' . $Self->{SortBy} . '_OrderBy_' . $Self->{OrderBy},
|
||
|
);
|
||
|
|
||
|
# take ticketids from cache if available, else perform ES search
|
||
|
if ( defined $CacheData ) {
|
||
|
@ViewableTicketIDs = @{$CacheData};
|
||
|
}
|
||
|
else {
|
||
|
my $ESObject = $Kernel::OM->Get('Kernel::System::Elasticsearch');
|
||
|
my $Count = $ConfigObject->Get('Elasticsearch::MaxArticleSearch');
|
||
|
|
||
|
# TODO please consider sorting via ES. For fields with type text, mapping for sorting must be the keyword version, e.g. Title.keyword!
|
||
|
#my $Field = $Mapping->{ticket}->{mappings}->{properties}->{ $Param{Sort} }->{fields};
|
||
|
#$Param{Sort} .= ( defined $Field ) ? '.' . ( %{$Field} )[0] : '';
|
||
|
if ( ( defined $GetParam{FulltextES} ) && ( !defined $GetParam{Fulltext} ) ) {
|
||
|
@ViewableTicketIDs = $ESObject->TicketSearch(
|
||
|
Fulltext => $GetParam{FulltextES},
|
||
|
UserID => $Self->{UserID},
|
||
|
UserLogin => $Self->{UserLogin},
|
||
|
CustomerUserID => $Self->{CustomerUserIDLogin} || '',
|
||
|
SortBy => $Self->{SortBy},
|
||
|
OrderBy => $Self->{OrderBy},
|
||
|
Limit => $Count,
|
||
|
Result => 'ARRAY',
|
||
|
);
|
||
|
|
||
|
# set cache for ES results
|
||
|
if ($CacheObject) {
|
||
|
$CacheObject->Set(
|
||
|
Type => 'TicketSearch',
|
||
|
Key => $GetParam{FulltextES}
|
||
|
. '_SortBy_'
|
||
|
. $Self->{SortBy}
|
||
|
. '_OrderBy_'
|
||
|
. $Self->{OrderBy},
|
||
|
Value => \@ViewableTicketIDs,
|
||
|
TTL => $GetParam{CacheTTL} || 60 * 4,
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# get needed objects
|
||
|
my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser');
|
||
|
|
||
|
# get the ticket dynamic fields for CSV display
|
||
|
my $CSVDynamicField = $DynamicFieldObject->DynamicFieldListGet(
|
||
|
Valid => 1,
|
||
|
ObjectType => ['Ticket'],
|
||
|
FieldFilter => $Config->{SearchCSVDynamicField} || {},
|
||
|
);
|
||
|
|
||
|
# CSV and Excel output
|
||
|
if (
|
||
|
$GetParam{ResultForm} eq 'CSV'
|
||
|
||
|
||
|
$GetParam{ResultForm} eq 'Excel'
|
||
|
)
|
||
|
{
|
||
|
|
||
|
# create head (actual head and head for data fill)
|
||
|
my @TmpCSVHead = @{ $Config->{SearchCSVData} };
|
||
|
my @CSVHead = @{ $Config->{SearchCSVData} };
|
||
|
|
||
|
# include the selected dynamic fields in CVS results
|
||
|
DYNAMICFIELD:
|
||
|
for my $DynamicFieldConfig ( @{$CSVDynamicField} ) {
|
||
|
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
|
||
|
next DYNAMICFIELD if !$DynamicFieldConfig->{Name};
|
||
|
next DYNAMICFIELD if $DynamicFieldConfig->{Name} eq '';
|
||
|
|
||
|
push @TmpCSVHead, 'DynamicField_' . $DynamicFieldConfig->{Name};
|
||
|
push @CSVHead, $DynamicFieldConfig->{Label};
|
||
|
}
|
||
|
|
||
|
my @CSVData;
|
||
|
for my $TicketID (@ViewableTicketIDs) {
|
||
|
|
||
|
# Get ticket data.
|
||
|
my %Ticket = $TicketObject->TicketGet(
|
||
|
TicketID => $TicketID,
|
||
|
DynamicFields => 1,
|
||
|
Extended => 1,
|
||
|
UserID => $Self->{UserID},
|
||
|
);
|
||
|
|
||
|
# 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 = $ArticleObject->BackendForArticle( %{ $Articles[0] } )->ArticleGet(
|
||
|
%{ $Articles[0] },
|
||
|
DynamicFields => 1,
|
||
|
);
|
||
|
|
||
|
my %Data;
|
||
|
|
||
|
if ( !%Article ) {
|
||
|
|
||
|
%Data = %Ticket;
|
||
|
|
||
|
# Set missing information.
|
||
|
$Data{Subject} = $Ticket{Title} || $LayoutObject->{LanguageObject}->Translate('Untitled');
|
||
|
$Data{Body} = $LayoutObject->{LanguageObject}->Translate('This item has no articles yet.');
|
||
|
$Data{From} = '--';
|
||
|
}
|
||
|
else {
|
||
|
%Data = ( %Ticket, %Article );
|
||
|
}
|
||
|
|
||
|
for my $Key (qw(State Lock)) {
|
||
|
$Data{$Key} = $LayoutObject->{LanguageObject}->Translate( $Data{$Key} );
|
||
|
}
|
||
|
|
||
|
$Data{Age} = $LayoutObject->CustomerAge(
|
||
|
Age => $Data{Age},
|
||
|
Space => ' ',
|
||
|
);
|
||
|
|
||
|
# get whole article (if configured!)
|
||
|
if ( $Config->{SearchArticleCSVTree} ) {
|
||
|
my @Articles = $ArticleObject->ArticleList(
|
||
|
TicketID => $TicketID,
|
||
|
);
|
||
|
|
||
|
if (@Articles) {
|
||
|
for my $Article (@Articles) {
|
||
|
my %ArticleData = $ArticleObject->BackendForArticle( %{$Article} )->ArticleGet(
|
||
|
TicketID => $TicketID,
|
||
|
ArticleID => $Article->{ArticleID},
|
||
|
DynamicFields => 0,
|
||
|
);
|
||
|
if ( $ArticleData{Body} ) {
|
||
|
$Data{ArticleTree}
|
||
|
.= "\n-->"
|
||
|
. "||$ArticleData{SenderType}"
|
||
|
. "||$ArticleData{From}"
|
||
|
. "||$ArticleData{CreateTime}"
|
||
|
. "||<--------------\n"
|
||
|
. $ArticleData{Body};
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
$Data{ArticleTree} .= $LayoutObject->{LanguageObject}->Translate(
|
||
|
'This item has no articles yet.'
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# customer info (customer name)
|
||
|
if ( $Data{CustomerUserID} ) {
|
||
|
$Data{CustomerName} = $CustomerUserObject->CustomerName(
|
||
|
UserLogin => $Data{CustomerUserID},
|
||
|
);
|
||
|
}
|
||
|
|
||
|
# user info
|
||
|
my %UserInfo = $UserObject->GetUserData(
|
||
|
User => $Data{Owner},
|
||
|
);
|
||
|
|
||
|
# merge row data
|
||
|
my %Info = (
|
||
|
%Data,
|
||
|
%UserInfo,
|
||
|
AccountedTime =>
|
||
|
$TicketObject->TicketAccountedTimeGet( TicketID => $TicketID ),
|
||
|
);
|
||
|
|
||
|
# Transform EscalationTime and EscalationTimeWorkingTime to a human readable format.
|
||
|
# See bug#13088 (https://bugs.otrs.org/show_bug.cgi?id=13088).
|
||
|
$Info{EscalationTime} = $LayoutObject->CustomerAgeInHours(
|
||
|
Age => $Info{EscalationTime},
|
||
|
Space => ' ',
|
||
|
);
|
||
|
$Info{EscalationTimeWorkingTime} = $LayoutObject->CustomerAgeInHours(
|
||
|
Age => $Info{EscalationTimeWorkingTime},
|
||
|
Space => ' ',
|
||
|
);
|
||
|
|
||
|
my @Data;
|
||
|
for my $Header (@TmpCSVHead) {
|
||
|
|
||
|
# check if header is a dynamic field and get the value from dynamic field
|
||
|
# backend
|
||
|
if ( $Header =~ m{\A DynamicField_ ( [a-zA-Z\d]+ ) \z}xms ) {
|
||
|
|
||
|
# loop over the dynamic fields configured for CSV output
|
||
|
DYNAMICFIELD:
|
||
|
for my $DynamicFieldConfig ( @{$CSVDynamicField} ) {
|
||
|
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
|
||
|
next DYNAMICFIELD if !$DynamicFieldConfig->{Name};
|
||
|
|
||
|
# skip all fields that does not match with current field name ($1)
|
||
|
# with out the 'DynamicField_' prefix
|
||
|
next DYNAMICFIELD if $DynamicFieldConfig->{Name} ne $1;
|
||
|
|
||
|
# get the value as for print (to correctly display)
|
||
|
my $ValueStrg = $BackendObject->DisplayValueRender(
|
||
|
DynamicFieldConfig => $DynamicFieldConfig,
|
||
|
Value => $Info{$Header},
|
||
|
HTMLOutput => 0,
|
||
|
LayoutObject => $LayoutObject,
|
||
|
);
|
||
|
push @Data, $ValueStrg->{Value};
|
||
|
|
||
|
# terminate the DYNAMICFIELD loop
|
||
|
last DYNAMICFIELD;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# otherwise retrieve data from article
|
||
|
else {
|
||
|
push @Data, $Info{$Header};
|
||
|
}
|
||
|
}
|
||
|
push @CSVData, \@Data;
|
||
|
}
|
||
|
|
||
|
# get Separator from language file
|
||
|
my $UserCSVSeparator = $LayoutObject->{LanguageObject}->{Separator};
|
||
|
|
||
|
if ( $ConfigObject->Get('PreferencesGroups')->{CSVSeparator}->{Active} ) {
|
||
|
my %UserData = $UserObject->GetUserData( UserID => $Self->{UserID} );
|
||
|
$UserCSVSeparator = $UserData{UserCSVSeparator} if $UserData{UserCSVSeparator};
|
||
|
}
|
||
|
|
||
|
my %HeaderMap = (
|
||
|
TicketNumber => Translatable('Ticket Number'),
|
||
|
CustomerName => Translatable('Customer Name'),
|
||
|
);
|
||
|
|
||
|
my @CSVHeadTranslated = map { $LayoutObject->{LanguageObject}->Translate( $HeaderMap{$_} || $_ ); }
|
||
|
@CSVHead;
|
||
|
|
||
|
my $CurDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
|
||
|
my $FileName = sprintf(
|
||
|
'ticket_search_%s',
|
||
|
$CurDateTimeObject->Format(
|
||
|
Format => '%Y-%m-%d_%H-%M'
|
||
|
)
|
||
|
);
|
||
|
|
||
|
# get CSV object
|
||
|
my $CSVObject = $Kernel::OM->Get('Kernel::System::CSV');
|
||
|
|
||
|
# generate CSV output
|
||
|
if ( $GetParam{ResultForm} eq 'CSV' ) {
|
||
|
my $CSV = $CSVObject->Array2CSV(
|
||
|
Head => \@CSVHeadTranslated,
|
||
|
Data => \@CSVData,
|
||
|
Separator => $UserCSVSeparator,
|
||
|
);
|
||
|
|
||
|
# return csv to download
|
||
|
return $LayoutObject->Attachment(
|
||
|
Filename => $FileName . '.csv',
|
||
|
ContentType => "text/csv; charset=" . $LayoutObject->{UserCharset},
|
||
|
Content => $CSV,
|
||
|
);
|
||
|
}
|
||
|
|
||
|
# generate Excel output
|
||
|
elsif ( $GetParam{ResultForm} eq 'Excel' ) {
|
||
|
my $Excel = $CSVObject->Array2CSV(
|
||
|
Head => \@CSVHeadTranslated,
|
||
|
Data => \@CSVData,
|
||
|
Format => 'Excel',
|
||
|
);
|
||
|
|
||
|
# return Excel to download
|
||
|
return $LayoutObject->Attachment(
|
||
|
Filename => $FileName . '.xlsx',
|
||
|
ContentType =>
|
||
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||
|
Content => $Excel,
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# PDF output
|
||
|
elsif ( $GetParam{ResultForm} eq 'Print' ) {
|
||
|
|
||
|
# get PDF object
|
||
|
my $PDFObject = $Kernel::OM->Get('Kernel::System::PDF');
|
||
|
|
||
|
my @PDFData;
|
||
|
for my $TicketID (@ViewableTicketIDs) {
|
||
|
|
||
|
# Get ticket data.
|
||
|
my %Ticket = $TicketObject->TicketGet(
|
||
|
TicketID => $TicketID,
|
||
|
DynamicFields => 1,
|
||
|
UserID => $Self->{UserID},
|
||
|
);
|
||
|
|
||
|
# 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 = $ArticleObject->BackendForArticle( %{ $Articles[0] } )->ArticleGet(
|
||
|
%{ $Articles[0] },
|
||
|
DynamicFields => 1,
|
||
|
);
|
||
|
|
||
|
my %Data;
|
||
|
|
||
|
if ( !%Article ) {
|
||
|
|
||
|
%Data = %Ticket;
|
||
|
|
||
|
# Set missing information.
|
||
|
$Data{Subject} = $Data{Title} || Translatable('Untitled');
|
||
|
$Data{From} = '--';
|
||
|
}
|
||
|
else {
|
||
|
%Data = ( %Ticket, %Article );
|
||
|
}
|
||
|
|
||
|
# customer info
|
||
|
my %CustomerData;
|
||
|
if ( $Data{CustomerUserID} ) {
|
||
|
%CustomerData = $CustomerUserObject->CustomerUserDataGet(
|
||
|
User => $Data{CustomerUserID},
|
||
|
);
|
||
|
}
|
||
|
elsif ( $Data{CustomerID} ) {
|
||
|
%CustomerData = $CustomerUserObject->CustomerUserDataGet(
|
||
|
CustomerID => $Data{CustomerID},
|
||
|
);
|
||
|
}
|
||
|
|
||
|
# customer info (customer name)
|
||
|
if ( $CustomerData{UserLogin} ) {
|
||
|
$Data{CustomerName} = $CustomerUserObject->CustomerName(
|
||
|
UserLogin => $CustomerData{UserLogin},
|
||
|
);
|
||
|
}
|
||
|
|
||
|
# user info
|
||
|
my %UserInfo = $UserObject->GetUserData(
|
||
|
User => $Data{Owner},
|
||
|
);
|
||
|
|
||
|
# customer info string
|
||
|
$UserInfo{CustomerName} = '(' . $UserInfo{CustomerName} . ')'
|
||
|
if ( $UserInfo{CustomerName} );
|
||
|
|
||
|
my %Info = ( %Data, %UserInfo );
|
||
|
my $Created = $LayoutObject->{LanguageObject}->FormatTimeString(
|
||
|
$Data{CreateTime} // $Data{Created},
|
||
|
'DateFormat',
|
||
|
);
|
||
|
my $Owner = "$Info{Owner} ($Info{UserFullname})";
|
||
|
my $Customer = "$Data{CustomerID} $Data{CustomerName}";
|
||
|
|
||
|
my @PDFRow;
|
||
|
push @PDFRow, $Data{TicketNumber};
|
||
|
push @PDFRow, $Created;
|
||
|
push @PDFRow, $Data{From};
|
||
|
push @PDFRow, $Data{Subject};
|
||
|
push @PDFRow, $Data{State};
|
||
|
push @PDFRow, $Data{Queue};
|
||
|
push @PDFRow, $Owner;
|
||
|
push @PDFRow, $Customer;
|
||
|
push @PDFData, \@PDFRow;
|
||
|
|
||
|
}
|
||
|
|
||
|
my $Title = $LayoutObject->{LanguageObject}->Translate('Ticket') . ' '
|
||
|
. $LayoutObject->{LanguageObject}->Translate('Search');
|
||
|
my $PrintedBy = $LayoutObject->{LanguageObject}->Translate('printed by');
|
||
|
my $Page = $LayoutObject->{LanguageObject}->Translate('Page');
|
||
|
my $DateTimeString = $Kernel::OM->Create('Kernel::System::DateTime')->ToString();
|
||
|
my $Time = $LayoutObject->{LanguageObject}->FormatTimeString(
|
||
|
$DateTimeString,
|
||
|
'DateFormat',
|
||
|
);
|
||
|
|
||
|
# get maximum number of pages
|
||
|
my $MaxPages = $ConfigObject->Get('PDF::MaxPages');
|
||
|
if ( !$MaxPages || $MaxPages < 1 || $MaxPages > 1000 ) {
|
||
|
$MaxPages = 100;
|
||
|
}
|
||
|
|
||
|
my $CellData;
|
||
|
|
||
|
# verify if there are tickets to show
|
||
|
if (@PDFData) {
|
||
|
|
||
|
# create the header
|
||
|
$CellData->[0]->[0]->{Content} = $ConfigObject->Get('Ticket::Hook');
|
||
|
$CellData->[0]->[0]->{Font} = 'ProportionalBold';
|
||
|
$CellData->[0]->[1]->{Content} = $LayoutObject->{LanguageObject}->Translate('Created');
|
||
|
$CellData->[0]->[1]->{Font} = 'ProportionalBold';
|
||
|
$CellData->[0]->[2]->{Content} = $LayoutObject->{LanguageObject}->Translate('From');
|
||
|
$CellData->[0]->[2]->{Font} = 'ProportionalBold';
|
||
|
$CellData->[0]->[3]->{Content} = $LayoutObject->{LanguageObject}->Translate('Subject');
|
||
|
$CellData->[0]->[3]->{Font} = 'ProportionalBold';
|
||
|
$CellData->[0]->[4]->{Content} = $LayoutObject->{LanguageObject}->Translate('State');
|
||
|
$CellData->[0]->[4]->{Font} = 'ProportionalBold';
|
||
|
$CellData->[0]->[5]->{Content} = $LayoutObject->{LanguageObject}->Translate('Queue');
|
||
|
$CellData->[0]->[5]->{Font} = 'ProportionalBold';
|
||
|
$CellData->[0]->[6]->{Content} = $LayoutObject->{LanguageObject}->Translate('Owner');
|
||
|
$CellData->[0]->[6]->{Font} = 'ProportionalBold';
|
||
|
$CellData->[0]->[7]->{Content} = $LayoutObject->{LanguageObject}->Translate('CustomerID');
|
||
|
$CellData->[0]->[7]->{Font} = 'ProportionalBold';
|
||
|
|
||
|
# create the content array
|
||
|
my $CounterRow = 1;
|
||
|
for my $Row (@PDFData) {
|
||
|
my $CounterColumn = 0;
|
||
|
for my $Content ( @{$Row} ) {
|
||
|
$CellData->[$CounterRow]->[$CounterColumn]->{Content} = $Content;
|
||
|
$CounterColumn++;
|
||
|
}
|
||
|
$CounterRow++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# otherwise, show 'No ticket data found' message
|
||
|
else {
|
||
|
$CellData->[0]->[0]->{Content} = $LayoutObject->{LanguageObject}->Translate('No ticket data found.');
|
||
|
}
|
||
|
|
||
|
# page params
|
||
|
my %PageParam;
|
||
|
$PageParam{PageOrientation} = 'landscape';
|
||
|
$PageParam{MarginTop} = 30;
|
||
|
$PageParam{MarginRight} = 40;
|
||
|
$PageParam{MarginBottom} = 40;
|
||
|
$PageParam{MarginLeft} = 40;
|
||
|
$PageParam{HeaderRight} = $Title;
|
||
|
$PageParam{HeadlineLeft} = $Title;
|
||
|
|
||
|
# table params
|
||
|
my %TableParam;
|
||
|
$TableParam{CellData} = $CellData;
|
||
|
$TableParam{Type} = 'Cut';
|
||
|
$TableParam{FontSize} = 6;
|
||
|
$TableParam{Border} = 0;
|
||
|
$TableParam{BackgroundColorEven} = '#DDDDDD';
|
||
|
$TableParam{Padding} = 1;
|
||
|
$TableParam{PaddingTop} = 3;
|
||
|
$TableParam{PaddingBottom} = 3;
|
||
|
|
||
|
# create new pdf document
|
||
|
$PDFObject->DocumentNew(
|
||
|
Title => $ConfigObject->Get('Product') . ': ' . $Title,
|
||
|
Encode => $LayoutObject->{UserCharset},
|
||
|
);
|
||
|
|
||
|
# start table output
|
||
|
$PDFObject->PageNew(
|
||
|
%PageParam,
|
||
|
FooterRight => $Page . ' 1',
|
||
|
);
|
||
|
|
||
|
$PDFObject->PositionSet(
|
||
|
Move => 'relativ',
|
||
|
Y => -6,
|
||
|
);
|
||
|
|
||
|
# output title
|
||
|
$PDFObject->Text(
|
||
|
Text => $Title,
|
||
|
FontSize => 13,
|
||
|
);
|
||
|
|
||
|
$PDFObject->PositionSet(
|
||
|
Move => 'relativ',
|
||
|
Y => -6,
|
||
|
);
|
||
|
|
||
|
# output "printed by"
|
||
|
$PDFObject->Text(
|
||
|
Text => $PrintedBy . ' '
|
||
|
. $Self->{UserFullname} . ' ('
|
||
|
. $Self->{UserEmail} . ')'
|
||
|
. ', ' . $Time,
|
||
|
FontSize => 9,
|
||
|
);
|
||
|
|
||
|
$PDFObject->PositionSet(
|
||
|
Move => 'relativ',
|
||
|
Y => -14,
|
||
|
);
|
||
|
|
||
|
PAGE:
|
||
|
for my $PageNumber ( 2 .. $MaxPages ) {
|
||
|
|
||
|
# output table (or a fragment of it)
|
||
|
%TableParam = $PDFObject->Table(%TableParam);
|
||
|
|
||
|
# stop output or another page
|
||
|
if ( $TableParam{State} ) {
|
||
|
last PAGE;
|
||
|
}
|
||
|
else {
|
||
|
$PDFObject->PageNew(
|
||
|
%PageParam,
|
||
|
FooterRight => $Page . ' ' . $PageNumber,
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# return the pdf document
|
||
|
my $CurDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
|
||
|
my $Filename = sprintf(
|
||
|
'ticket_search_%s.pdf',
|
||
|
$CurDateTimeObject->Format(
|
||
|
Format => '%Y-%m-%d_%H-%M'
|
||
|
)
|
||
|
);
|
||
|
|
||
|
my $PDFString = $PDFObject->DocumentOutput();
|
||
|
|
||
|
return $LayoutObject->Attachment(
|
||
|
Filename => $Filename,
|
||
|
ContentType => "application/pdf",
|
||
|
Content => $PDFString,
|
||
|
Type => 'inline',
|
||
|
);
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
# redirect to the ticketzoom if result of the search is only one
|
||
|
if ( scalar @ViewableTicketIDs eq 1 && !$Self->{TakeLastSearch} ) {
|
||
|
return $LayoutObject->Redirect(
|
||
|
OP => "Action=AgentTicketZoom;TicketID=$ViewableTicketIDs[0]",
|
||
|
);
|
||
|
}
|
||
|
|
||
|
# store last overview screen
|
||
|
my $URL = "Action=AgentTicketSearch;Subaction=Search"
|
||
|
. ";Profile=" . $LayoutObject->LinkEncode( $Self->{Profile} )
|
||
|
. ";SortBy=" . $LayoutObject->LinkEncode( $Self->{SortBy} )
|
||
|
. ";OrderBy=" . $LayoutObject->LinkEncode( $Self->{OrderBy} )
|
||
|
. ";TakeLastSearch=1;StartHit="
|
||
|
. $LayoutObject->LinkEncode( $Self->{StartHit} );
|
||
|
|
||
|
$Kernel::OM->Get('Kernel::System::AuthSession')->UpdateSessionID(
|
||
|
SessionID => $Self->{SessionID},
|
||
|
Key => 'LastScreenOverview',
|
||
|
Value => $URL,
|
||
|
);
|
||
|
|
||
|
# start HTML page
|
||
|
my $Output = $LayoutObject->Header();
|
||
|
$Output .= $LayoutObject->NavigationBar();
|
||
|
|
||
|
# Notify if there are tickets which are not updated.
|
||
|
$Output .= $LayoutObject->NotifyNonUpdatedTickets() // '';
|
||
|
|
||
|
$Self->{Filter} = $ParamObject->GetParam( Param => 'Filter' ) || '';
|
||
|
$Self->{View} = $ParamObject->GetParam( Param => 'View' ) || '';
|
||
|
|
||
|
# show tickets
|
||
|
my $LinkPage = 'Filter='
|
||
|
. $LayoutObject->LinkEncode( $Self->{Filter} )
|
||
|
. ';View=' . $LayoutObject->LinkEncode( $Self->{View} )
|
||
|
. ';SortBy=' . $LayoutObject->LinkEncode( $Self->{SortBy} )
|
||
|
. ';OrderBy=' . $LayoutObject->LinkEncode( $Self->{OrderBy} )
|
||
|
. ';Profile='
|
||
|
. $LayoutObject->LinkEncode( $Self->{Profile} )
|
||
|
. ';TakeLastSearch=1;Subaction=Search'
|
||
|
. ';';
|
||
|
my $LinkSort = 'Filter='
|
||
|
. $LayoutObject->LinkEncode( $Self->{Filter} )
|
||
|
. ';View=' . $LayoutObject->LinkEncode( $Self->{View} )
|
||
|
. ';Profile='
|
||
|
. $LayoutObject->LinkEncode( $Self->{Profile} )
|
||
|
. ';TakeLastSearch=1;Subaction=Search'
|
||
|
. ';';
|
||
|
my $LinkFilter = 'TakeLastSearch=1;Subaction=Search;Profile='
|
||
|
. $LayoutObject->LinkEncode( $Self->{Profile} )
|
||
|
. ';';
|
||
|
my $LinkBack = 'Subaction=LoadProfile;Profile='
|
||
|
. $LayoutObject->LinkEncode( $Self->{Profile} )
|
||
|
. ';TakeLastSearch=1&';
|
||
|
|
||
|
my $FilterLink = 'SortBy=' . $LayoutObject->LinkEncode( $Self->{SortBy} )
|
||
|
. ';OrderBy=' . $LayoutObject->LinkEncode( $Self->{OrderBy} )
|
||
|
. ';View=' . $LayoutObject->LinkEncode( $Self->{View} )
|
||
|
. ';Profile='
|
||
|
. $LayoutObject->LinkEncode( $Self->{Profile} )
|
||
|
. ';TakeLastSearch=1;Subaction=Search'
|
||
|
. ';';
|
||
|
$Output .= $LayoutObject->TicketListShow(
|
||
|
TicketIDs => \@ViewableTicketIDs,
|
||
|
Total => scalar @ViewableTicketIDs,
|
||
|
|
||
|
View => $Self->{View},
|
||
|
|
||
|
Env => $Self,
|
||
|
LinkPage => $LinkPage,
|
||
|
LinkSort => $LinkSort,
|
||
|
LinkFilter => $LinkFilter,
|
||
|
LinkBack => $LinkBack,
|
||
|
Profile => $Self->{Profile},
|
||
|
ProfileName => $Self->{ProfileName},
|
||
|
|
||
|
TitleName => Translatable('Search Results'),
|
||
|
Bulk => 1,
|
||
|
Limit => $Self->{SearchLimit},
|
||
|
|
||
|
Filter => $Self->{Filter},
|
||
|
|
||
|
OrderBy => $Self->{OrderBy},
|
||
|
SortBy => $Self->{SortBy},
|
||
|
RequestedURL => 'Action=' . $Self->{Action} . ';' . $LinkPage,
|
||
|
|
||
|
# do not print the result earlier, but return complete content
|
||
|
Output => 1,
|
||
|
);
|
||
|
|
||
|
# build footer
|
||
|
$Output .= $LayoutObject->Footer();
|
||
|
return $Output;
|
||
|
}
|
||
|
}
|
||
|
elsif ( $Self->{Subaction} eq 'AJAXProfileDelete' ) {
|
||
|
my $Profile = $ParamObject->GetParam( Param => 'Profile' );
|
||
|
|
||
|
# remove old profile stuff
|
||
|
$SearchProfileObject->SearchProfileDelete(
|
||
|
Base => 'TicketSearch',
|
||
|
Name => $Profile,
|
||
|
UserLogin => $Self->{UserLogin},
|
||
|
);
|
||
|
my $Output = $LayoutObject->JSONEncode(
|
||
|
Data => 1,
|
||
|
);
|
||
|
|
||
|
# TODO: why not application/json
|
||
|
return $LayoutObject->Attachment(
|
||
|
NoCache => 1,
|
||
|
ContentType => 'text/html',
|
||
|
Content => $Output,
|
||
|
Type => 'inline'
|
||
|
);
|
||
|
}
|
||
|
elsif ( $Self->{Subaction} eq 'AJAXStopWordCheck' ) {
|
||
|
|
||
|
my $StopWordCheckResult = {
|
||
|
FoundStopWords => [],
|
||
|
};
|
||
|
|
||
|
if ( $Kernel::OM->Get('Kernel::System::Ticket::Article')->SearchStringStopWordsUsageWarningActive() ) {
|
||
|
my @ParamNames = $ParamObject->GetParamNames();
|
||
|
my %SearchStrings;
|
||
|
SEARCHSTRINGPARAMNAME:
|
||
|
for my $SearchStringParamName ( sort @ParamNames ) {
|
||
|
next SEARCHSTRINGPARAMNAME if $SearchStringParamName !~ m{\ASearchStrings\[(.*)\]\z}sm;
|
||
|
$SearchStrings{$1} = $ParamObject->GetParam( Param => $SearchStringParamName );
|
||
|
}
|
||
|
|
||
|
$StopWordCheckResult->{FoundStopWords} = $Kernel::OM->Get('Kernel::System::Ticket::Article')->SearchStringStopWordsFind(
|
||
|
SearchStrings => \%SearchStrings,
|
||
|
);
|
||
|
}
|
||
|
|
||
|
my $Output = $LayoutObject->JSONEncode(
|
||
|
Data => $StopWordCheckResult,
|
||
|
);
|
||
|
|
||
|
# TODO: why not application/json
|
||
|
return $LayoutObject->Attachment(
|
||
|
NoCache => 1,
|
||
|
ContentType => 'text/html',
|
||
|
Charset => $LayoutObject->{UserCharset},
|
||
|
Content => $Output,
|
||
|
Type => 'inline'
|
||
|
);
|
||
|
}
|
||
|
elsif ( $Self->{Subaction} eq 'AJAX' ) {
|
||
|
my $Profile = $ParamObject->GetParam( Param => 'Profile' ) || '';
|
||
|
my $EmptySearch = $ParamObject->GetParam( Param => 'EmptySearch' );
|
||
|
if ( !$Profile ) {
|
||
|
$EmptySearch = 1;
|
||
|
}
|
||
|
my %GetParam = $SearchProfileObject->SearchProfileGet(
|
||
|
Base => 'TicketSearch',
|
||
|
Name => $Profile,
|
||
|
UserLogin => $Self->{UserLogin},
|
||
|
);
|
||
|
|
||
|
# convert attributes
|
||
|
if ( IsArrayRefWithData( $GetParam{ShownAttributes} ) ) {
|
||
|
my @ShowAttributes = grep {defined} @{ $GetParam{ShownAttributes} };
|
||
|
$GetParam{ShownAttributes} = join ';', @ShowAttributes;
|
||
|
}
|
||
|
|
||
|
# if no profile is used, set default params of default attributes
|
||
|
if ( !$Profile ) {
|
||
|
if ( $Config->{Defaults} ) {
|
||
|
KEY:
|
||
|
for my $Key ( sort keys %{ $Config->{Defaults} } ) {
|
||
|
next KEY if !$Config->{Defaults}->{$Key};
|
||
|
next KEY if $Key eq 'DynamicField';
|
||
|
|
||
|
if ( $Key =~ /^(Ticket|Article)(Create|Change|Close|Escalation)/ ) {
|
||
|
my @Items = split /;/, $Config->{Defaults}->{$Key};
|
||
|
for my $Item (@Items) {
|
||
|
my ( $Key, $Value ) = split /=/, $Item;
|
||
|
$GetParam{$Key} = $Value;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
$GetParam{$Key} = $Config->{Defaults}->{$Key};
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
my @Attributes = (
|
||
|
|
||
|
# Main fields
|
||
|
{
|
||
|
Key => 'TicketNumber',
|
||
|
Value => Translatable('Ticket Number'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'Fulltext',
|
||
|
Value => Translatable('Fulltext'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'Title',
|
||
|
Value => Translatable('Title'),
|
||
|
},
|
||
|
{
|
||
|
Key => '',
|
||
|
Value => '-',
|
||
|
Disabled => 1,
|
||
|
},
|
||
|
);
|
||
|
|
||
|
for my $ArticleFieldKey ( sort keys %ArticleSearchableFields ) {
|
||
|
push @Attributes, (
|
||
|
{
|
||
|
Key => $ArticleSearchableFields{$ArticleFieldKey}->{Key},
|
||
|
Value => Translatable( $ArticleSearchableFields{$ArticleFieldKey}->{Label} ),
|
||
|
},
|
||
|
);
|
||
|
}
|
||
|
|
||
|
# Ticket fields
|
||
|
push @Attributes, (
|
||
|
{
|
||
|
Key => '',
|
||
|
Value => '-',
|
||
|
Disabled => 1,
|
||
|
},
|
||
|
{
|
||
|
Key => 'CustomerID',
|
||
|
Value => Translatable('CustomerID (complex search)'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'CustomerIDRaw',
|
||
|
Value => Translatable('CustomerID (exact match)'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'CustomerUserLogin',
|
||
|
Value => Translatable('Assigned to Customer User Login (complex search)'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'CustomerUserLoginRaw',
|
||
|
Value => Translatable('Assigned to Customer User Login (exact match)'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'CustomerUserID',
|
||
|
Value => Translatable('Accessible to Customer User Login (exact match)'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'StateIDs',
|
||
|
Value => Translatable('State'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'PriorityIDs',
|
||
|
Value => Translatable('Priority'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'LockIDs',
|
||
|
Value => Translatable('Lock'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'QueueIDs',
|
||
|
Value => Translatable('Queue'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'CreatedQueueIDs',
|
||
|
Value => Translatable('Created in Queue'),
|
||
|
},
|
||
|
);
|
||
|
|
||
|
if ( $ConfigObject->Get('Ticket::Type') ) {
|
||
|
push @Attributes, (
|
||
|
{
|
||
|
Key => 'TypeIDs',
|
||
|
Value => Translatable('Type'),
|
||
|
},
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if ( $ConfigObject->Get('Ticket::Service') ) {
|
||
|
push @Attributes, (
|
||
|
{
|
||
|
Key => 'ServiceIDs',
|
||
|
Value => Translatable('Service'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'SLAIDs',
|
||
|
Value => Translatable('SLA'),
|
||
|
},
|
||
|
);
|
||
|
}
|
||
|
|
||
|
push @Attributes, (
|
||
|
{
|
||
|
Key => 'OwnerIDs',
|
||
|
Value => Translatable('Owner'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'CreatedUserIDs',
|
||
|
Value => Translatable('Created by'),
|
||
|
},
|
||
|
);
|
||
|
if ( $ConfigObject->Get('Ticket::Watcher') ) {
|
||
|
push @Attributes, (
|
||
|
{
|
||
|
Key => 'WatchUserIDs',
|
||
|
Value => Translatable('Watcher'),
|
||
|
},
|
||
|
);
|
||
|
}
|
||
|
if ( $ConfigObject->Get('Ticket::Responsible') ) {
|
||
|
push @Attributes, (
|
||
|
{
|
||
|
Key => 'ResponsibleIDs',
|
||
|
Value => Translatable('Responsible'),
|
||
|
},
|
||
|
);
|
||
|
}
|
||
|
|
||
|
# Time fields
|
||
|
push @Attributes, (
|
||
|
{
|
||
|
Key => '',
|
||
|
Value => '-',
|
||
|
Disabled => 1,
|
||
|
},
|
||
|
{
|
||
|
Key => 'TicketLastChangeTimePoint',
|
||
|
Value => Translatable('Ticket Last Change Time (before/after)'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'TicketLastChangeTimeSlot',
|
||
|
Value => Translatable('Ticket Last Change Time (between)'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'TicketChangeTimePoint',
|
||
|
Value => Translatable('Ticket Change Time (before/after)'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'TicketChangeTimeSlot',
|
||
|
Value => Translatable('Ticket Change Time (between)'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'TicketCloseTimePoint',
|
||
|
Value => Translatable('Ticket Close Time (before/after)'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'TicketCloseTimeSlot',
|
||
|
Value => Translatable('Ticket Close Time (between)'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'TicketCreateTimePoint',
|
||
|
Value => Translatable('Ticket Create Time (before/after)'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'TicketCreateTimeSlot',
|
||
|
Value => Translatable('Ticket Create Time (between)'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'TicketPendingTimePoint',
|
||
|
Value => Translatable('Ticket Pending Until Time (before/after)'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'TicketPendingTimeSlot',
|
||
|
Value => Translatable('Ticket Pending Until Time (between)'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'TicketEscalationTimePoint',
|
||
|
Value => Translatable('Ticket Escalation Time (before/after)'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'TicketEscalationTimeSlot',
|
||
|
Value => Translatable('Ticket Escalation Time (between)'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'ArticleCreateTimePoint',
|
||
|
Value => Translatable('Article Create Time (before/after)'),
|
||
|
},
|
||
|
{
|
||
|
Key => 'ArticleCreateTimeSlot',
|
||
|
Value => Translatable('Article Create Time (between)'),
|
||
|
},
|
||
|
);
|
||
|
|
||
|
if ( $ConfigObject->Get('Ticket::ArchiveSystem') ) {
|
||
|
push @Attributes, (
|
||
|
{
|
||
|
Key => 'SearchInArchive',
|
||
|
Value => Translatable('Archive Search'),
|
||
|
},
|
||
|
);
|
||
|
}
|
||
|
|
||
|
# Dynamic fields
|
||
|
my $DynamicFieldSeparator = 1;
|
||
|
|
||
|
# create dynamic fields search options for attribute select
|
||
|
# cycle trough the activated Dynamic Fields for this screen
|
||
|
DYNAMICFIELD:
|
||
|
for my $DynamicFieldConfig ( @{$DynamicField} ) {
|
||
|
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
|
||
|
next DYNAMICFIELD if !$DynamicFieldConfig->{Name};
|
||
|
next DYNAMICFIELD if $DynamicFieldConfig->{Name} eq '';
|
||
|
|
||
|
# create a separator for dynamic fields attributes
|
||
|
if ($DynamicFieldSeparator) {
|
||
|
push @Attributes, (
|
||
|
{
|
||
|
Key => '',
|
||
|
Value => '-',
|
||
|
Disabled => 1,
|
||
|
},
|
||
|
);
|
||
|
$DynamicFieldSeparator = 0;
|
||
|
}
|
||
|
|
||
|
# get search field preferences
|
||
|
my $SearchFieldPreferences = $BackendObject->SearchFieldPreferences(
|
||
|
DynamicFieldConfig => $DynamicFieldConfig,
|
||
|
);
|
||
|
|
||
|
next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences);
|
||
|
|
||
|
# translate the dynamic field label
|
||
|
my $TranslatedDynamicFieldLabel = $LayoutObject->{LanguageObject}->Translate(
|
||
|
$DynamicFieldConfig->{Label},
|
||
|
);
|
||
|
|
||
|
PREFERENCE:
|
||
|
for my $Preference ( @{$SearchFieldPreferences} ) {
|
||
|
|
||
|
# translate the suffix
|
||
|
my $TranslatedSuffix = $LayoutObject->{LanguageObject}->Translate(
|
||
|
$Preference->{LabelSuffix},
|
||
|
) || '';
|
||
|
|
||
|
if ($TranslatedSuffix) {
|
||
|
$TranslatedSuffix = ' (' . $TranslatedSuffix . ')';
|
||
|
}
|
||
|
|
||
|
push @Attributes, (
|
||
|
{
|
||
|
Key => 'Search_DynamicField_'
|
||
|
. $DynamicFieldConfig->{Name}
|
||
|
. $Preference->{Type},
|
||
|
Value => $TranslatedDynamicFieldLabel . $TranslatedSuffix,
|
||
|
},
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# create HTML strings for all dynamic fields
|
||
|
my %DynamicFieldHTML;
|
||
|
|
||
|
# cycle trough the activated Dynamic Fields for this screen
|
||
|
DYNAMICFIELD:
|
||
|
for my $DynamicFieldConfig ( @{$DynamicField} ) {
|
||
|
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) ) {
|
||
|
|
||
|
# get historical values from database
|
||
|
my $HistoricalValues = $BackendObject->HistoricalValuesGet(
|
||
|
DynamicFieldConfig => $DynamicFieldConfig,
|
||
|
);
|
||
|
|
||
|
my $Data = $PossibleValues;
|
||
|
|
||
|
# add historic values to current values (if they don't exist anymore)
|
||
|
if ( IsHashRefWithData($HistoricalValues) ) {
|
||
|
for my $Key ( sort keys %{$HistoricalValues} ) {
|
||
|
if ( !$Data->{$Key} ) {
|
||
|
$Data->{$Key} = $HistoricalValues->{$Key};
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# convert possible values key => value to key => key for ACLs using a Hash slice
|
||
|
my %AclData = %{$Data};
|
||
|
@AclData{ keys %AclData } = keys %AclData;
|
||
|
|
||
|
# set possible values filter from ACLs
|
||
|
my $ACL = $TicketObject->TicketAcl(
|
||
|
Action => $Self->{Action},
|
||
|
ReturnType => 'Ticket',
|
||
|
ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name},
|
||
|
Data => \%AclData,
|
||
|
UserID => $Self->{UserID},
|
||
|
);
|
||
|
if ($ACL) {
|
||
|
my %Filter = $TicketObject->TicketAclData();
|
||
|
|
||
|
# convert Filer key => key back to key => value using map
|
||
|
%{$PossibleValuesFilter} = map { $_ => $Data->{$_} } keys %Filter;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# get search field preferences
|
||
|
my $SearchFieldPreferences = $BackendObject->SearchFieldPreferences(
|
||
|
DynamicFieldConfig => $DynamicFieldConfig,
|
||
|
);
|
||
|
|
||
|
next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences);
|
||
|
|
||
|
PREFERENCE:
|
||
|
for my $Preference ( @{$SearchFieldPreferences} ) {
|
||
|
|
||
|
# get field HTML
|
||
|
$DynamicFieldHTML{ $DynamicFieldConfig->{Name} . $Preference->{Type} } = $BackendObject->SearchFieldRender(
|
||
|
DynamicFieldConfig => $DynamicFieldConfig,
|
||
|
Profile => \%GetParam,
|
||
|
PossibleValuesFilter => $PossibleValuesFilter,
|
||
|
DefaultValue =>
|
||
|
$Config->{Defaults}->{DynamicField}
|
||
|
->{ $DynamicFieldConfig->{Name} },
|
||
|
LayoutObject => $LayoutObject,
|
||
|
Type => $Preference->{Type},
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$Param{AttributesStrg} = $LayoutObject->BuildSelection(
|
||
|
PossibleNone => 1,
|
||
|
Data => \@Attributes,
|
||
|
Name => 'Attribute',
|
||
|
Multiple => 0,
|
||
|
Class => 'Modernize',
|
||
|
);
|
||
|
$Param{AttributesOrigStrg} = $LayoutObject->BuildSelection(
|
||
|
PossibleNone => 1,
|
||
|
Data => \@Attributes,
|
||
|
Name => 'AttributeOrig',
|
||
|
Multiple => 0,
|
||
|
Class => 'Modernize',
|
||
|
);
|
||
|
|
||
|
# get all users of own groups
|
||
|
my %AllUsers = $UserObject->UserList(
|
||
|
Type => 'Long',
|
||
|
Valid => 0,
|
||
|
);
|
||
|
# Rother OSS / TicketSearch-IncludeInvalidAgents
|
||
|
# if ( !$ConfigObject->Get('Ticket::ChangeOwnerToEveryone') ) {
|
||
|
if ( !$ConfigObject->Get('Ticket::ChangeOwnerToEveryone') && !$ConfigObject->Get('Ticket::Search')->{IncludeInvalidAgents} ) {
|
||
|
# EO TicketSearch-IncludeInvalidAgents
|
||
|
my %Involved = $Kernel::OM->Get('Kernel::System::Group')->PermissionUserInvolvedGet(
|
||
|
UserID => $Self->{UserID},
|
||
|
Type => 'ro',
|
||
|
);
|
||
|
for my $UserID ( sort keys %AllUsers ) {
|
||
|
if ( !$Involved{$UserID} ) {
|
||
|
delete $AllUsers{$UserID};
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
my @ShownUsers;
|
||
|
my %UsersInvalid;
|
||
|
|
||
|
# get valid users of own groups
|
||
|
my %ValidUsers = $UserObject->UserList(
|
||
|
Type => 'Long',
|
||
|
Valid => 1,
|
||
|
);
|
||
|
|
||
|
USERID:
|
||
|
for my $UserID ( sort { $AllUsers{$a} cmp $AllUsers{$b} } keys %AllUsers ) {
|
||
|
|
||
|
if ( !$ValidUsers{$UserID} ) {
|
||
|
$UsersInvalid{$UserID} = $AllUsers{$UserID};
|
||
|
next USERID;
|
||
|
}
|
||
|
|
||
|
push @ShownUsers, {
|
||
|
Key => $UserID,
|
||
|
Value => $AllUsers{$UserID},
|
||
|
};
|
||
|
}
|
||
|
|
||
|
# also show invalid agents (if any)
|
||
|
if ( scalar %UsersInvalid ) {
|
||
|
push @ShownUsers, {
|
||
|
Key => '-',
|
||
|
Value => '_____________________',
|
||
|
Disabled => 1,
|
||
|
};
|
||
|
push @ShownUsers, {
|
||
|
Key => '-',
|
||
|
Value => $LayoutObject->{LanguageObject}->Translate('Invalid Users'),
|
||
|
Disabled => 1,
|
||
|
};
|
||
|
push @ShownUsers, {
|
||
|
Key => '-',
|
||
|
Value => '',
|
||
|
Disabled => 1,
|
||
|
};
|
||
|
for my $UserID ( sort { $UsersInvalid{$a} cmp $UsersInvalid{$b} } keys %UsersInvalid ) {
|
||
|
push @ShownUsers, {
|
||
|
Key => $UserID,
|
||
|
Value => $UsersInvalid{$UserID},
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$Param{UserStrg} = $LayoutObject->BuildSelection(
|
||
|
Data => \@ShownUsers,
|
||
|
Name => 'OwnerIDs',
|
||
|
Multiple => 1,
|
||
|
Size => 5,
|
||
|
SelectedID => $GetParam{OwnerIDs},
|
||
|
Class => 'Modernize',
|
||
|
);
|
||
|
$Param{CreatedUserStrg} = $LayoutObject->BuildSelection(
|
||
|
Data => \@ShownUsers,
|
||
|
Name => 'CreatedUserIDs',
|
||
|
Multiple => 1,
|
||
|
Size => 5,
|
||
|
SelectedID => $GetParam{CreatedUserIDs},
|
||
|
Class => 'Modernize',
|
||
|
);
|
||
|
if ( $ConfigObject->Get('Ticket::Watcher') ) {
|
||
|
$Param{WatchUserStrg} = $LayoutObject->BuildSelection(
|
||
|
Data => \@ShownUsers,
|
||
|
Name => 'WatchUserIDs',
|
||
|
Multiple => 1,
|
||
|
Size => 5,
|
||
|
SelectedID => $GetParam{WatchUserIDs},
|
||
|
Class => 'Modernize',
|
||
|
);
|
||
|
}
|
||
|
if ( $ConfigObject->Get('Ticket::Responsible') ) {
|
||
|
$Param{ResponsibleStrg} = $LayoutObject->BuildSelection(
|
||
|
Data => \@ShownUsers,
|
||
|
Name => 'ResponsibleIDs',
|
||
|
Multiple => 1,
|
||
|
Size => 5,
|
||
|
SelectedID => $GetParam{ResponsibleIDs},
|
||
|
Class => 'Modernize',
|
||
|
);
|
||
|
}
|
||
|
|
||
|
# build service string
|
||
|
if ( $ConfigObject->Get('Ticket::Service') ) {
|
||
|
|
||
|
my %Service = $Kernel::OM->Get('Kernel::System::Service')->ServiceList(
|
||
|
UserID => $Self->{UserID},
|
||
|
KeepChildren => $ConfigObject->Get('Ticket::Service::KeepChildren'),
|
||
|
);
|
||
|
$Param{ServicesStrg} = $LayoutObject->BuildSelection(
|
||
|
Data => \%Service,
|
||
|
Name => 'ServiceIDs',
|
||
|
SelectedID => $GetParam{ServiceIDs},
|
||
|
TreeView => $TreeView,
|
||
|
Sort => 'TreeView',
|
||
|
Size => 5,
|
||
|
Multiple => 1,
|
||
|
Translation => 0,
|
||
|
Max => 200,
|
||
|
Class => 'Modernize',
|
||
|
);
|
||
|
my %SLA = $Kernel::OM->Get('Kernel::System::SLA')->SLAList(
|
||
|
UserID => $Self->{UserID},
|
||
|
);
|
||
|
$Param{SLAsStrg} = $LayoutObject->BuildSelection(
|
||
|
Data => \%SLA,
|
||
|
Name => 'SLAIDs',
|
||
|
SelectedID => $GetParam{SLAIDs},
|
||
|
Sort => 'AlphanumericValue',
|
||
|
Size => 5,
|
||
|
Multiple => 1,
|
||
|
Translation => 0,
|
||
|
Max => 200,
|
||
|
Class => 'Modernize',
|
||
|
);
|
||
|
}
|
||
|
|
||
|
$Param{ResultFormStrg} = $LayoutObject->BuildSelection(
|
||
|
Data => {
|
||
|
Normal => Translatable('Normal'),
|
||
|
Print => Translatable('Print'),
|
||
|
CSV => Translatable('CSV'),
|
||
|
Excel => Translatable('Excel'),
|
||
|
},
|
||
|
Name => 'ResultForm',
|
||
|
SelectedID => $GetParam{ResultForm} || 'Normal',
|
||
|
Class => 'Modernize',
|
||
|
);
|
||
|
|
||
|
if ( $ConfigObject->Get('Ticket::ArchiveSystem') ) {
|
||
|
|
||
|
$Param{SearchInArchiveStrg} = $LayoutObject->BuildSelection(
|
||
|
Data => {
|
||
|
ArchivedTickets => Translatable('Archived tickets'),
|
||
|
NotArchivedTickets => Translatable('Unarchived tickets'),
|
||
|
AllTickets => Translatable('All tickets'),
|
||
|
},
|
||
|
Name => 'SearchInArchive',
|
||
|
SelectedID => $GetParam{SearchInArchive} || 'NotArchivedTickets',
|
||
|
Class => 'Modernize',
|
||
|
);
|
||
|
}
|
||
|
|
||
|
my %Profiles = $SearchProfileObject->SearchProfileList(
|
||
|
Base => 'TicketSearch',
|
||
|
UserLogin => $Self->{UserLogin},
|
||
|
);
|
||
|
|
||
|
if ( $Profiles{'last-search'} ) {
|
||
|
$Profiles{'last-search'} = $LayoutObject->{LanguageObject}->Translate('last-search');
|
||
|
}
|
||
|
|
||
|
$Param{ProfilesStrg} = $LayoutObject->BuildSelection(
|
||
|
Data => \%Profiles,
|
||
|
Name => 'Profile',
|
||
|
ID => 'SearchProfile',
|
||
|
SelectedID => $Profile,
|
||
|
Class => 'Modernize',
|
||
|
Translation => 0,
|
||
|
PossibleNone => 1,
|
||
|
);
|
||
|
|
||
|
$Param{StatesStrg} = $LayoutObject->BuildSelection(
|
||
|
Data => {
|
||
|
$StateObject->StateList(
|
||
|
UserID => $Self->{UserID},
|
||
|
Action => $Self->{Action},
|
||
|
),
|
||
|
},
|
||
|
Name => 'StateIDs',
|
||
|
Multiple => 1,
|
||
|
Size => 5,
|
||
|
SelectedID => $GetParam{StateIDs},
|
||
|
Class => 'Modernize',
|
||
|
);
|
||
|
my %AllQueues = $Kernel::OM->Get('Kernel::System::Queue')->GetAllQueues(
|
||
|
UserID => $Self->{UserID},
|
||
|
Type => 'ro',
|
||
|
);
|
||
|
$Param{QueuesStrg} = $LayoutObject->AgentQueueListOption(
|
||
|
Data => \%AllQueues,
|
||
|
Size => 5,
|
||
|
Multiple => 1,
|
||
|
Name => 'QueueIDs',
|
||
|
TreeView => $TreeView,
|
||
|
SelectedIDRefArray => $GetParam{QueueIDs},
|
||
|
OnChangeSubmit => 0,
|
||
|
Class => 'Modernize',
|
||
|
);
|
||
|
$Param{CreatedQueuesStrg} = $LayoutObject->AgentQueueListOption(
|
||
|
Data => \%AllQueues,
|
||
|
Size => 5,
|
||
|
Multiple => 1,
|
||
|
Name => 'CreatedQueueIDs',
|
||
|
TreeView => $TreeView,
|
||
|
SelectedIDRefArray => $GetParam{CreatedQueueIDs},
|
||
|
OnChangeSubmit => 0,
|
||
|
Class => 'Modernize',
|
||
|
);
|
||
|
$Param{PrioritiesStrg} = $LayoutObject->BuildSelection(
|
||
|
Data => {
|
||
|
$TicketObject->TicketPriorityList(
|
||
|
UserID => $Self->{UserID},
|
||
|
Action => $Self->{Action},
|
||
|
),
|
||
|
},
|
||
|
Name => 'PriorityIDs',
|
||
|
Multiple => 1,
|
||
|
Size => 5,
|
||
|
SelectedID => $GetParam{PriorityIDs},
|
||
|
Class => 'Modernize',
|
||
|
);
|
||
|
$Param{LocksStrg} = $LayoutObject->BuildSelection(
|
||
|
Data => {
|
||
|
$Kernel::OM->Get('Kernel::System::Lock')->LockList(
|
||
|
UserID => $Self->{UserID},
|
||
|
Action => $Self->{Action},
|
||
|
),
|
||
|
},
|
||
|
Name => 'LockIDs',
|
||
|
Multiple => 1,
|
||
|
Size => 5,
|
||
|
SelectedID => $GetParam{LockIDs},
|
||
|
Class => 'Modernize',
|
||
|
);
|
||
|
|
||
|
$Param{ArticleCreateTimePoint} = $LayoutObject->BuildSelection(
|
||
|
Data => [ 1 .. 59 ],
|
||
|
Name => 'ArticleCreateTimePoint',
|
||
|
SelectedID => $GetParam{ArticleCreateTimePoint},
|
||
|
);
|
||
|
$Param{ArticleCreateTimePointStart} = $LayoutObject->BuildSelection(
|
||
|
Data => {
|
||
|
'Last' => Translatable('within the last ...'),
|
||
|
'Before' => Translatable('more than ... ago'),
|
||
|
},
|
||
|
Name => 'ArticleCreateTimePointStart',
|
||
|
SelectedID => $GetParam{ArticleCreateTimePointStart} || 'Last',
|
||
|
);
|
||
|
$Param{ArticleCreateTimePointFormat} = $LayoutObject->BuildSelection(
|
||
|
Data => {
|
||
|
minute => Translatable('minute(s)'),
|
||
|
hour => Translatable('hour(s)'),
|
||
|
day => Translatable('day(s)'),
|
||
|
week => Translatable('week(s)'),
|
||
|
month => Translatable('month(s)'),
|
||
|
year => Translatable('year(s)'),
|
||
|
},
|
||
|
Name => 'ArticleCreateTimePointFormat',
|
||
|
SelectedID => $GetParam{ArticleCreateTimePointFormat},
|
||
|
);
|
||
|
$Param{ArticleCreateTimeStart} = $LayoutObject->BuildDateSelection(
|
||
|
%GetParam,
|
||
|
Prefix => 'ArticleCreateTimeStart',
|
||
|
Format => 'DateInputFormat',
|
||
|
DiffTime => -( ( 60 * 60 * 24 ) * 30 ),
|
||
|
Validate => 1,
|
||
|
ValidateDateBeforePrefix => 'ArticleCreateTimeStop',
|
||
|
);
|
||
|
$Param{ArticleCreateTimeStop} = $LayoutObject->BuildDateSelection(
|
||
|
%GetParam,
|
||
|
Prefix => 'ArticleCreateTimeStop',
|
||
|
Format => 'DateInputFormat',
|
||
|
Validate => 1,
|
||
|
ValidateDateAfterPrefix => 'ArticleCreateTimeStart',
|
||
|
);
|
||
|
$Param{TicketCreateTimePoint} = $LayoutObject->BuildSelection(
|
||
|
Data => [ 1 .. 59 ],
|
||
|
Name => 'TicketCreateTimePoint',
|
||
|
SelectedID => $GetParam{TicketCreateTimePoint},
|
||
|
);
|
||
|
$Param{TicketCreateTimePointStart} = $LayoutObject->BuildSelection(
|
||
|
Data => {
|
||
|
'Last' => Translatable('within the last ...'),
|
||
|
'Before' => Translatable('more than ... ago'),
|
||
|
},
|
||
|
Name => 'TicketCreateTimePointStart',
|
||
|
SelectedID => $GetParam{TicketCreateTimePointStart} || 'Last',
|
||
|
);
|
||
|
$Param{TicketCreateTimePointFormat} = $LayoutObject->BuildSelection(
|
||
|
Data => {
|
||
|
minute => Translatable('minute(s)'),
|
||
|
hour => Translatable('hour(s)'),
|
||
|
day => Translatable('day(s)'),
|
||
|
week => Translatable('week(s)'),
|
||
|
month => Translatable('month(s)'),
|
||
|
year => Translatable('year(s)'),
|
||
|
},
|
||
|
Name => 'TicketCreateTimePointFormat',
|
||
|
SelectedID => $GetParam{TicketCreateTimePointFormat},
|
||
|
);
|
||
|
$Param{TicketCreateTimeStart} = $LayoutObject->BuildDateSelection(
|
||
|
%GetParam,
|
||
|
Prefix => 'TicketCreateTimeStart',
|
||
|
Format => 'DateInputFormat',
|
||
|
DiffTime => -( ( 60 * 60 * 24 ) * 30 ),
|
||
|
Validate => 1,
|
||
|
ValidateDateBeforePrefix => 'TicketCreateTimeStop',
|
||
|
);
|
||
|
$Param{TicketCreateTimeStop} = $LayoutObject->BuildDateSelection(
|
||
|
%GetParam,
|
||
|
Prefix => 'TicketCreateTimeStop',
|
||
|
Format => 'DateInputFormat',
|
||
|
Validate => 1,
|
||
|
ValidateDateAfterPrefix => 'TicketCreateTimeStart',
|
||
|
);
|
||
|
|
||
|
$Param{TicketPendingTimePoint} = $LayoutObject->BuildSelection(
|
||
|
Data => [ 1 .. 59 ],
|
||
|
Name => 'TicketPendingTimePoint',
|
||
|
SelectedID => $GetParam{TicketPendingTimePoint},
|
||
|
);
|
||
|
$Param{TicketPendingTimePointStart} = $LayoutObject->BuildSelection(
|
||
|
Data => {
|
||
|
'Last' => Translatable('within the last ...'),
|
||
|
'Next' => Translatable('within the next ...'),
|
||
|
'Before' => Translatable('more than ... ago'),
|
||
|
'After' => Translatable('in more than ...'),
|
||
|
},
|
||
|
Name => 'TicketPendingTimePointStart',
|
||
|
SelectedID => $GetParam{TicketPendingTimePointStart} || 'Next',
|
||
|
);
|
||
|
$Param{TicketPendingTimePointFormat} = $LayoutObject->BuildSelection(
|
||
|
Data => {
|
||
|
minute => Translatable('minute(s)'),
|
||
|
hour => Translatable('hour(s)'),
|
||
|
day => Translatable('day(s)'),
|
||
|
week => Translatable('week(s)'),
|
||
|
month => Translatable('month(s)'),
|
||
|
year => Translatable('year(s)'),
|
||
|
},
|
||
|
Name => 'TicketPendingTimePointFormat',
|
||
|
SelectedID => $GetParam{TicketPendingTimePointFormat},
|
||
|
);
|
||
|
$Param{TicketPendingTimeStart} = $LayoutObject->BuildDateSelection(
|
||
|
%GetParam,
|
||
|
Prefix => 'TicketPendingTimeStart',
|
||
|
Format => 'DateInputFormat',
|
||
|
DiffTime => -( ( 60 * 60 * 24 ) * 30 ),
|
||
|
Validate => 1,
|
||
|
ValidateDateBeforePrefix => 'TicketPendingTimeStop',
|
||
|
);
|
||
|
$Param{TicketPendingTimeStop} = $LayoutObject->BuildDateSelection(
|
||
|
%GetParam,
|
||
|
Prefix => 'TicketPendingTimeStop',
|
||
|
Format => 'DateInputFormat',
|
||
|
Validate => 1,
|
||
|
ValidateDateAfterPrefix => 'TicketPendingTimeStart',
|
||
|
);
|
||
|
|
||
|
$Param{TicketChangeTimePoint} = $LayoutObject->BuildSelection(
|
||
|
Data => [ 1 .. 59 ],
|
||
|
Name => 'TicketChangeTimePoint',
|
||
|
SelectedID => $GetParam{TicketChangeTimePoint},
|
||
|
);
|
||
|
$Param{TicketChangeTimePointStart} = $LayoutObject->BuildSelection(
|
||
|
Data => {
|
||
|
'Last' => 'within the last ...',
|
||
|
'Before' => 'more than ... ago',
|
||
|
},
|
||
|
Name => 'TicketChangeTimePointStart',
|
||
|
SelectedID => $GetParam{TicketChangeTimePointStart} || 'Last',
|
||
|
);
|
||
|
$Param{TicketChangeTimePointFormat} = $LayoutObject->BuildSelection(
|
||
|
Data => {
|
||
|
minute => Translatable('minute(s)'),
|
||
|
hour => Translatable('hour(s)'),
|
||
|
day => Translatable('day(s)'),
|
||
|
week => Translatable('week(s)'),
|
||
|
month => Translatable('month(s)'),
|
||
|
year => Translatable('year(s)'),
|
||
|
},
|
||
|
Name => 'TicketChangeTimePointFormat',
|
||
|
SelectedID => $GetParam{TicketChangeTimePointFormat},
|
||
|
);
|
||
|
$Param{TicketChangeTimeStart} = $LayoutObject->BuildDateSelection(
|
||
|
%GetParam,
|
||
|
Prefix => 'TicketChangeTimeStart',
|
||
|
Format => 'DateInputFormat',
|
||
|
DiffTime => -( ( 60 * 60 * 24 ) * 30 ),
|
||
|
Validate => 1,
|
||
|
ValidateDateBeforePrefix => 'TicketChangeTimeStop',
|
||
|
);
|
||
|
$Param{TicketChangeTimeStop} = $LayoutObject->BuildDateSelection(
|
||
|
%GetParam,
|
||
|
Prefix => 'TicketChangeTimeStop',
|
||
|
Format => 'DateInputFormat',
|
||
|
Validate => 1,
|
||
|
ValidateDateAfterPrefix => 'TicketChangeTimeStart',
|
||
|
);
|
||
|
|
||
|
$Param{TicketCloseTimePoint} = $LayoutObject->BuildSelection(
|
||
|
Data => [ 1 .. 59 ],
|
||
|
Name => 'TicketCloseTimePoint',
|
||
|
SelectedID => $GetParam{TicketCloseTimePoint},
|
||
|
);
|
||
|
$Param{TicketCloseTimePointStart} = $LayoutObject->BuildSelection(
|
||
|
Data => {
|
||
|
'Last' => Translatable('within the last ...'),
|
||
|
'Before' => Translatable('more than ... ago'),
|
||
|
},
|
||
|
Name => 'TicketCloseTimePointStart',
|
||
|
SelectedID => $GetParam{TicketCloseTimePointStart} || 'Last',
|
||
|
);
|
||
|
$Param{TicketCloseTimePointFormat} = $LayoutObject->BuildSelection(
|
||
|
Data => {
|
||
|
minute => Translatable('minute(s)'),
|
||
|
hour => Translatable('hour(s)'),
|
||
|
day => Translatable('day(s)'),
|
||
|
week => Translatable('week(s)'),
|
||
|
month => Translatable('month(s)'),
|
||
|
year => Translatable('year(s)'),
|
||
|
},
|
||
|
Name => 'TicketCloseTimePointFormat',
|
||
|
SelectedID => $GetParam{TicketCloseTimePointFormat},
|
||
|
);
|
||
|
$Param{TicketCloseTimeStart} = $LayoutObject->BuildDateSelection(
|
||
|
%GetParam,
|
||
|
Prefix => 'TicketCloseTimeStart',
|
||
|
Format => 'DateInputFormat',
|
||
|
DiffTime => -( ( 60 * 60 * 24 ) * 30 ),
|
||
|
Validate => 1,
|
||
|
ValidateDateBeforePrefix => 'TicketCloseTimeStop',
|
||
|
);
|
||
|
$Param{TicketCloseTimeStop} = $LayoutObject->BuildDateSelection(
|
||
|
%GetParam,
|
||
|
Prefix => 'TicketCloseTimeStop',
|
||
|
Format => 'DateInputFormat',
|
||
|
Validate => 1,
|
||
|
ValidateDateAfterPrefix => 'TicketCloseTimeStart',
|
||
|
);
|
||
|
|
||
|
$Param{TicketLastChangeTimePoint} = $LayoutObject->BuildSelection(
|
||
|
Data => [ 1 .. 59 ],
|
||
|
Name => 'TicketLastChangeTimePoint',
|
||
|
SelectedID => $GetParam{TicketLastChangeTimePoint},
|
||
|
);
|
||
|
$Param{TicketLastChangeTimePointStart} = $LayoutObject->BuildSelection(
|
||
|
Data => {
|
||
|
'Last' => Translatable('within the last ...'),
|
||
|
'Before' => Translatable('more than ... ago'),
|
||
|
},
|
||
|
Name => 'TicketLastChangeTimePointStart',
|
||
|
SelectedID => $GetParam{TicketLastChangeTimePointStart} || 'Last',
|
||
|
);
|
||
|
$Param{TicketLastChangeTimePointFormat} = $LayoutObject->BuildSelection(
|
||
|
Data => {
|
||
|
minute => Translatable('minute(s)'),
|
||
|
hour => Translatable('hour(s)'),
|
||
|
day => Translatable('day(s)'),
|
||
|
week => Translatable('week(s)'),
|
||
|
month => Translatable('month(s)'),
|
||
|
year => Translatable('year(s)'),
|
||
|
},
|
||
|
Name => 'TicketLastChangeTimePointFormat',
|
||
|
SelectedID => $GetParam{TicketLastChangeTimePointFormat},
|
||
|
);
|
||
|
$Param{TicketLastChangeTimeStart} = $LayoutObject->BuildDateSelection(
|
||
|
%GetParam,
|
||
|
Prefix => 'TicketLastChangeTimeStart',
|
||
|
Format => 'DateInputFormat',
|
||
|
DiffTime => -( ( 60 * 60 * 24 ) * 30 ),
|
||
|
Validate => 1,
|
||
|
ValidateDateBeforePrefix => 'TicketLastChangeTimeStop',
|
||
|
);
|
||
|
$Param{TicketLastChangeTimeStop} = $LayoutObject->BuildDateSelection(
|
||
|
%GetParam,
|
||
|
Prefix => 'TicketLastChangeTimeStop',
|
||
|
Format => 'DateInputFormat',
|
||
|
Validate => 1,
|
||
|
ValidateDateAfterPrefix => 'TicketLastChangeTimeStart',
|
||
|
);
|
||
|
|
||
|
$Param{TicketEscalationTimePoint} = $LayoutObject->BuildSelection(
|
||
|
Data => [ 1 .. 59 ],
|
||
|
Name => 'TicketEscalationTimePoint',
|
||
|
SelectedID => $GetParam{TicketEscalationTimePoint},
|
||
|
);
|
||
|
$Param{TicketEscalationTimePointStart} = $LayoutObject->BuildSelection(
|
||
|
Data => {
|
||
|
'Last' => Translatable('within the last ...'),
|
||
|
'Next' => Translatable('within the next ...'),
|
||
|
'Before' => Translatable('more than ... ago'),
|
||
|
},
|
||
|
Name => 'TicketEscalationTimePointStart',
|
||
|
SelectedID => $GetParam{TicketEscalationTimePointStart} || 'Last',
|
||
|
);
|
||
|
$Param{TicketEscalationTimePointFormat} = $LayoutObject->BuildSelection(
|
||
|
Data => {
|
||
|
minute => Translatable('minute(s)'),
|
||
|
hour => Translatable('hour(s)'),
|
||
|
day => Translatable('day(s)'),
|
||
|
week => Translatable('week(s)'),
|
||
|
month => Translatable('month(s)'),
|
||
|
year => Translatable('year(s)'),
|
||
|
},
|
||
|
Name => 'TicketEscalationTimePointFormat',
|
||
|
SelectedID => $GetParam{TicketEscalationTimePointFormat},
|
||
|
);
|
||
|
$Param{TicketEscalationTimeStart} = $LayoutObject->BuildDateSelection(
|
||
|
%GetParam,
|
||
|
Prefix => 'TicketEscalationTimeStart',
|
||
|
Format => 'DateInputFormat',
|
||
|
DiffTime => -( ( 60 * 60 * 24 ) * 30 ),
|
||
|
Validate => 1,
|
||
|
ValidateDateBeforePrefix => 'TicketEscalationTimeStop',
|
||
|
);
|
||
|
$Param{TicketEscalationTimeStop} = $LayoutObject->BuildDateSelection(
|
||
|
%GetParam,
|
||
|
Prefix => 'TicketEscalationTimeStop',
|
||
|
Format => 'DateInputFormat',
|
||
|
Validate => 1,
|
||
|
ValidateDateAfterPrefix => 'TicketEscalationTimeStart',
|
||
|
);
|
||
|
|
||
|
my %GetParamBackup = %GetParam;
|
||
|
for my $Key (
|
||
|
qw(TicketEscalation TicketClose TicketChange TicketLastChange TicketPending TicketCreate ArticleCreate)
|
||
|
)
|
||
|
{
|
||
|
for my $SubKey (qw(TimeStart TimeStop TimePoint TimePointStart TimePointFormat)) {
|
||
|
delete $GetParam{ $Key . $SubKey };
|
||
|
delete $GetParamBackup{ $Key . $SubKey };
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# build type string
|
||
|
if ( $ConfigObject->Get('Ticket::Type') ) {
|
||
|
|
||
|
# get ticket object
|
||
|
my %Type = $TicketObject->TicketTypeList(
|
||
|
UserID => $Self->{UserID},
|
||
|
Action => $Self->{Action},
|
||
|
);
|
||
|
$Param{TypesStrg} = $LayoutObject->BuildSelection(
|
||
|
Data => \%Type,
|
||
|
Name => 'TypeIDs',
|
||
|
SelectedID => $GetParam{TypeIDs},
|
||
|
Sort => 'AlphanumericValue',
|
||
|
Size => 3,
|
||
|
Multiple => 1,
|
||
|
Translation => 0,
|
||
|
Class => 'Modernize',
|
||
|
);
|
||
|
}
|
||
|
|
||
|
# html search mask output
|
||
|
$LayoutObject->Block(
|
||
|
Name => 'SearchAJAX',
|
||
|
Data => {
|
||
|
%Param,
|
||
|
%GetParam,
|
||
|
EmptySearch => $EmptySearch,
|
||
|
},
|
||
|
);
|
||
|
|
||
|
# create the field entries to be displayed in the modal dialog
|
||
|
for my $ArticleFieldKey ( sort keys %ArticleSearchableFields ) {
|
||
|
$LayoutObject->Block(
|
||
|
Name => 'SearchableArticleField',
|
||
|
Data => {
|
||
|
ArticleFieldLabel => $ArticleSearchableFields{$ArticleFieldKey}->{Label},
|
||
|
ArticleFieldKey => $ArticleSearchableFields{$ArticleFieldKey}->{Key},
|
||
|
ArticleFieldValue => $GetParam{$ArticleFieldKey} // '',
|
||
|
},
|
||
|
);
|
||
|
}
|
||
|
|
||
|
# output Dynamic fields blocks
|
||
|
# cycle trough the activated Dynamic Fields for this screen
|
||
|
DYNAMICFIELD:
|
||
|
for my $DynamicFieldConfig ( @{$DynamicField} ) {
|
||
|
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
|
||
|
|
||
|
# get search field preferences
|
||
|
my $SearchFieldPreferences = $BackendObject->SearchFieldPreferences(
|
||
|
DynamicFieldConfig => $DynamicFieldConfig,
|
||
|
);
|
||
|
|
||
|
next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences);
|
||
|
|
||
|
PREFERENCE:
|
||
|
for my $Preference ( @{$SearchFieldPreferences} ) {
|
||
|
|
||
|
# skip fields that HTML could not be retrieved
|
||
|
next PREFERENCE if !IsHashRefWithData(
|
||
|
$DynamicFieldHTML{ $DynamicFieldConfig->{Name} . $Preference->{Type} }
|
||
|
);
|
||
|
|
||
|
$LayoutObject->Block(
|
||
|
Name => 'DynamicField',
|
||
|
Data => {
|
||
|
Label =>
|
||
|
$DynamicFieldHTML{ $DynamicFieldConfig->{Name} . $Preference->{Type} }
|
||
|
->{Label},
|
||
|
Field =>
|
||
|
$DynamicFieldHTML{ $DynamicFieldConfig->{Name} . $Preference->{Type} }
|
||
|
->{Field},
|
||
|
},
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# compat. map for attributes
|
||
|
my %Map = (
|
||
|
TimeSearchType => 'TicketCreate',
|
||
|
ChangeTimeSearchType => 'TicketChange',
|
||
|
CloseTimeSearchType => 'TicketClose',
|
||
|
LastChangeTimeSearchType => 'TicketLastChange',
|
||
|
PendingTimeSearchType => 'TicketPending',
|
||
|
EscalationTimeSearchType => 'TicketEscalation',
|
||
|
ArticleTimeSearchType => 'ArticleCreate',
|
||
|
);
|
||
|
KEY:
|
||
|
for my $Key ( sort keys %Map ) {
|
||
|
next KEY if !defined $GetParamBackup{$Key};
|
||
|
if ( $GetParamBackup{$Key} eq 'TimePoint' ) {
|
||
|
$GetParamBackup{ $Map{$Key} . 'TimePoint' } = 1;
|
||
|
}
|
||
|
elsif ( $GetParamBackup{$Key} eq 'TimeSlot' ) {
|
||
|
$GetParamBackup{ $Map{$Key} . 'TimeSlot' } = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# attributes for search
|
||
|
my @SearchAttributes;
|
||
|
|
||
|
# show attributes
|
||
|
my @ShownAttributes;
|
||
|
if ( $GetParamBackup{ShownAttributes} ) {
|
||
|
@ShownAttributes = split /;/, $GetParamBackup{ShownAttributes};
|
||
|
}
|
||
|
my %AlreadyShown;
|
||
|
|
||
|
if ($Profile) {
|
||
|
ITEM:
|
||
|
for my $Item (@Attributes) {
|
||
|
my $Key = $Item->{Key};
|
||
|
next ITEM if !$Key;
|
||
|
|
||
|
# check if shown
|
||
|
if (@ShownAttributes) {
|
||
|
my $Show = 0;
|
||
|
SHOWN_ATTRIBUTE:
|
||
|
for my $ShownAttribute (@ShownAttributes) {
|
||
|
if ( 'Label' . $Key eq $ShownAttribute ) {
|
||
|
$Show = 1;
|
||
|
last SHOWN_ATTRIBUTE;
|
||
|
}
|
||
|
}
|
||
|
next ITEM if !$Show;
|
||
|
}
|
||
|
else {
|
||
|
# Skip undefined
|
||
|
next ITEM if !defined $GetParamBackup{$Key};
|
||
|
|
||
|
# Skip empty strings
|
||
|
next ITEM if $GetParamBackup{$Key} eq '';
|
||
|
|
||
|
# Skip empty arrays
|
||
|
if ( ref $GetParamBackup{$Key} eq 'ARRAY' && !@{ $GetParamBackup{$Key} } ) {
|
||
|
next ITEM;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# show attribute
|
||
|
next ITEM if $AlreadyShown{$Key};
|
||
|
$AlreadyShown{$Key} = 1;
|
||
|
|
||
|
push @SearchAttributes, $Key;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# No profile, show default screen
|
||
|
else {
|
||
|
|
||
|
# Merge regular show/hide settings and the settings for the dynamic fields
|
||
|
my %Defaults = %{ $Config->{Defaults} || {} };
|
||
|
for my $DynamicFields ( sort keys %{ $Config->{DynamicField} || {} } ) {
|
||
|
if ( $Config->{DynamicField}->{$DynamicFields} == 2 ) {
|
||
|
$Defaults{"Search_DynamicField_$DynamicFields"} = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
my @OrderedDefaults;
|
||
|
if (%Defaults) {
|
||
|
|
||
|
# ordering attributes on the same order like in Attributes
|
||
|
for my $Item (@Attributes) {
|
||
|
my $KeyAtr = $Item->{Key};
|
||
|
for my $Key ( sort keys %Defaults ) {
|
||
|
if ( $Key eq $KeyAtr ) {
|
||
|
push @OrderedDefaults, $Key;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
KEY:
|
||
|
for my $Key (@OrderedDefaults) {
|
||
|
next KEY if $Key eq 'DynamicField'; # Ignore entry for DF config
|
||
|
next KEY if $AlreadyShown{$Key};
|
||
|
$AlreadyShown{$Key} = 1;
|
||
|
|
||
|
push @SearchAttributes, $Key;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# If no attribute is shown, show fulltext search.
|
||
|
if ( !keys %AlreadyShown ) {
|
||
|
push @SearchAttributes, 'Fulltext';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$LayoutObject->AddJSData(
|
||
|
Key => 'SearchAttributes',
|
||
|
Value => \@SearchAttributes,
|
||
|
);
|
||
|
|
||
|
my $Output = $LayoutObject->Output(
|
||
|
TemplateFile => 'AgentTicketSearch',
|
||
|
Data => \%Param,
|
||
|
AJAX => 1,
|
||
|
);
|
||
|
|
||
|
return $LayoutObject->Attachment(
|
||
|
NoCache => 1,
|
||
|
ContentType => 'text/html',
|
||
|
Charset => $LayoutObject->{UserCharset},
|
||
|
Content => $Output,
|
||
|
Type => 'inline',
|
||
|
);
|
||
|
}
|
||
|
|
||
|
# show default search screen
|
||
|
$Output = $LayoutObject->Header();
|
||
|
$Output .= $LayoutObject->NavigationBar();
|
||
|
|
||
|
# Notify if there are tickets which are not updated.
|
||
|
$Output .= $LayoutObject->NotifyNonUpdatedTickets() // '';
|
||
|
|
||
|
$LayoutObject->AddJSData(
|
||
|
Key => 'NonAJAXSearch',
|
||
|
Value => 1,
|
||
|
);
|
||
|
if ( $Self->{Profile} ) {
|
||
|
$LayoutObject->AddJSData(
|
||
|
Key => 'Profile',
|
||
|
Value => $Self->{Profile},
|
||
|
);
|
||
|
}
|
||
|
$Output .= $LayoutObject->Output(
|
||
|
TemplateFile => 'AgentTicketSearch',
|
||
|
Data => \%Param,
|
||
|
);
|
||
|
$Output .= $LayoutObject->Footer();
|
||
|
return $Output;
|
||
|
}
|
||
|
|
||
|
1;
|