Files
scripts/Perl OTRS/Kernel/Modules/CustomerTicketSearch.pm
2024-10-14 00:08:40 +02:00

2061 lines
76 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::CustomerTicketSearch;
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 $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my $Config = $ConfigObject->Get("Ticket::Frontend::$Self->{Action}");
# get dynamic field config for frontend module
my $DynamicFieldFilter = $Config->{DynamicField};
# get dynamic field object
my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField');
# get the dynamic fields for ticket object
my $DynamicField = $DynamicFieldObject->DynamicFieldListGet(
Valid => 1,
ObjectType => ['Ticket'],
FieldFilter => $DynamicFieldFilter || {},
);
# get backend object
my $BackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
# reduce the dynamic fields to only the ones that are desinged for customer interface
my @CustomerDynamicFields;
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{$DynamicField} ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
my $IsCustomerInterfaceCapable = $BackendObject->HasBehavior(
DynamicFieldConfig => $DynamicFieldConfig,
Behavior => 'IsCustomerInterfaceCapable',
);
next DYNAMICFIELD if !$IsCustomerInterfaceCapable;
push @CustomerDynamicFields, $DynamicFieldConfig;
}
$DynamicField = \@CustomerDynamicFields;
my $Profile = $ParamObject->GetParam( Param => 'Profile' ) || '';
# check request
if ( $ParamObject->GetParam( Param => 'SearchTemplate' ) && $Profile ) {
return $LayoutObject->Redirect(
OP =>
"Action=CustomerTicketSearch;Subaction=Search;TakeLastSearch=1;SaveProfile=1;Profile=$Profile",
);
}
# remember exclude attributes
my @Excludes = $ParamObject->GetArray( Param => 'Exclude' );
# get single params
my %GetParam;
# get config data
my $StartHit = int( $ParamObject->GetParam( Param => 'StartHit' ) || 1 );
my $SearchLimit = $ConfigObject->Get('Ticket::CustomerTicketSearch::SearchLimit')
|| 200;
my $SearchPageShown = $ConfigObject->Get('Ticket::CustomerTicketSearch::SearchPageShown') || 40;
my $SortBy = $ParamObject->GetParam( Param => 'SortBy' )
|| $ConfigObject->Get('Ticket::CustomerTicketSearch::SortBy::Default')
|| 'Age';
my $CurrentOrder = $ParamObject->GetParam( Param => 'Order' )
|| $ConfigObject->Get('Ticket::CustomerTicketSearch::Order::Default')
|| 'Down';
# get search profile object
my $SearchProfileObject = $Kernel::OM->Get('Kernel::System::SearchProfile');
my $TakeLastSearch = $ParamObject->GetParam( Param => 'TakeLastSearch' ) || '';
# 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' && $Profile ) || $TakeLastSearch ) {
%GetParam = $SearchProfileObject->SearchProfileGet(
Base => 'CustomerTicketSearch',
Name => $Profile,
UserLogin => $Self->{UserLogin},
);
}
# get search string params (get submitted params)
else {
KEY:
for my $Key (
sort keys %ArticleSearchableFields,
qw(TicketNumber ResultForm TimeSearchType StateType SearchInArchive
TicketCreateTimePointFormat TicketCreateTimePoint TicketCreateTimePointStart
TicketCreateTimeStart TicketCreateTimeStartDay TicketCreateTimeStartMonth
TicketCreateTimeStartYear TicketCreateTimeStop TicketCreateTimeStopDay
TicketCreateTimeStopMonth TicketCreateTimeStopYear
)
)
{
next KEY if $ArticleSearchableFields{$Key} && $ArticleSearchableFields{$Key}->{HideInCustomerInterface};
# 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(CustomerID StateIDs StateTypeIDs PriorityIDs OwnerIDs ResponsibleIDs ServiceIDs
TypeIDs
)
)
{
# get search array params (get submitted params)
my @Array = $ParamObject->GetArray( Param => $Key );
if (@Array) {
$GetParam{$Key} = \@Array;
}
}
# get Dynamic fields form param object
# cycle through 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} );
}
}
}
}
# check if item need to get excluded
for my $Exclude (@Excludes) {
if ( $GetParam{$Exclude} ) {
delete $GetParam{$Exclude};
}
}
# get time option
if ( !$GetParam{TimeSearchType} ) {
$GetParam{'TimeSearchType::None'} = 'checked="checked"';
}
elsif ( $GetParam{TimeSearchType} eq 'TimePoint' ) {
$GetParam{'TimeSearchType::TimePoint'} = 'checked="checked"';
}
elsif ( $GetParam{TimeSearchType} eq 'TimeSlot' ) {
$GetParam{'TimeSearchType::TimeSlot'} = 'checked="checked"';
}
# set result form env
if ( !$GetParam{ResultForm} ) {
$GetParam{ResultForm} = '';
}
if ( $GetParam{ResultForm} eq 'Print' ) {
$SearchPageShown = $SearchLimit;
}
# check request
if ( $Self->{Subaction} eq 'OpenSearchDescription' ) {
my $Output = $LayoutObject->Output(
TemplateFile => 'CustomerTicketSearchOpenSearchDescription',
Data => {%Param},
);
return $LayoutObject->Attachment(
Filename => 'OpenSearchDescription.xml',
ContentType => 'text/xml',
Content => $Output,
Type => 'inline',
);
}
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');
# Get profile search and template data.
my $SaveProfile = $ParamObject->GetParam( Param => 'SaveProfile' ) || '';
my $SelectTemplate = $ParamObject->GetParam( Param => 'SelectTemplate' ) || '';
my $EraseTemplate = $ParamObject->GetParam( Param => 'EraseTemplate' ) || '';
# check for server errors
my %ServerErrors;
if (
$Self->{Subaction} eq 'Search'
&& !$Self->{EraseTemplate}
)
{
# check for stop word errors
my %StopWordsServerErrors = $Self->_StopWordsServerErrorsGet(
From => $GetParam{From},
To => $GetParam{To},
Cc => $GetParam{Cc},
Subject => $GetParam{Subject},
Body => $GetParam{Body},
);
%ServerErrors = ( %ServerErrors, %StopWordsServerErrors );
}
# show result page
if ( !%ServerErrors && $Self->{Subaction} eq 'Search' && !$EraseTemplate ) {
my $ProfileName = '';
if ($Profile) {
$ProfileName = "($Profile)";
}
# fill up profile name (e.g. with last-search)
if ( !$Profile || !$SaveProfile ) {
$Profile = 'last-search';
}
if ( !$ProfileName ) {
$ProfileName = "($Profile)";
}
if ( $ProfileName eq '(last-search)' ) {
$ProfileName = '(' . $LayoutObject->{LanguageObject}->Translate('last-search') . ')';
}
# store search URL in LastScreenOverview to make sure the
# customer can use the "back" link as expected
my $URL = "Action=CustomerTicketSearch;Subaction=Search;Profile=$Profile;"
. "SortBy=$SortBy;Order=$CurrentOrder;TakeLastSearch=1;"
. "StartHit=$StartHit";
$Kernel::OM->Get('Kernel::System::AuthSession')->UpdateSessionID(
SessionID => $Self->{SessionID},
Key => 'LastScreenOverview',
Value => $URL,
);
# save search profile (under last-search or real profile name)
$SaveProfile = 1;
# remember last search values
if ( $SaveProfile && $Profile ) {
# remove old profile stuff
$SearchProfileObject->SearchProfileDelete(
Base => 'CustomerTicketSearch',
Name => $Profile,
UserLogin => $Self->{UserLogin},
);
# insert new profile params
for my $Key ( sort keys %GetParam ) {
if ( $GetParam{$Key} ) {
$SearchProfileObject->SearchProfileAdd(
Base => 'CustomerTicketSearch',
Name => $Profile,
Key => $Key,
Value => $GetParam{$Key},
UserLogin => $Self->{UserLogin},
);
}
}
}
my %TimeMap = (
TicketCreate => 'Time',
);
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->ToOTRSTimeZone();
$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->ToOTRSTimeZone();
$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;
}
else {
# within last ...
$GetParam{ $TimeType . 'TimeOlderMinutes' } = 0;
$GetParam{ $TimeType . 'TimeNewerMinutes' } = $Time;
}
}
}
}
# prepare archive flag
if (
$ConfigObject->Get('Ticket::ArchiveSystem')
&& $ConfigObject->Get('Ticket::CustomerArchiveSystem')
&& $ConfigObject->Get('Ticket::CustomerArchiveSystem') eq 1
)
{
$GetParam{SearchInArchive} ||= '';
if ( $GetParam{SearchInArchive} eq 'AllTickets' ) {
$GetParam{ArchiveFlags} = [ 'y', 'n' ];
}
elsif ( $GetParam{SearchInArchive} eq 'ArchivedTickets' ) {
$GetParam{ArchiveFlags} = ['y'];
}
else {
$GetParam{ArchiveFlags} = ['n'];
}
}
elsif (
$ConfigObject->Get('Ticket::ArchiveSystem')
&& $ConfigObject->Get('Ticket::CustomerArchiveSystem')
&& $ConfigObject->Get('Ticket::CustomerArchiveSystem') eq 2
)
{
$GetParam{ArchiveFlags} = ['n'];
}
else {
$GetParam{ArchiveFlags} = [ 'y', 'n' ];
}
# dynamic fields search parameters for ticket search
my %DynamicFieldSearchParameters;
my %DynamicFieldSearchDisplay;
# cycle through 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} ) {
my $DynamicFieldValue = $BackendObject->SearchFieldValueGet(
DynamicFieldConfig => $DynamicFieldConfig,
ParamObject => $ParamObject,
Type => $Preference->{Type},
ReturnProfileStructure => 1,
);
# set the complete value structure in %DynamicFieldValues to discard those where the
# value will not be possible to get
next PREFERENCE if !IsHashRefWithData($DynamicFieldValue);
# 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};
# set value to display
$DynamicFieldSearchDisplay{ 'DynamicField_' . $DynamicFieldConfig->{Name} }
= $SearchParameter->{Display};
}
}
}
# disable output of customer company tickets
my $DisableCompanyTickets = $ConfigObject->Get('Ticket::Frontend::CustomerDisableCompanyTicketAccess');
if ($DisableCompanyTickets) {
$GetParam{CustomerUserLoginRaw} = $Self->{UserID};
}
# perform ticket search
my @ViewableTicketIDs = $TicketObject->TicketSearch(
Result => 'ARRAY',
SortBy => $SortBy,
OrderBy => $CurrentOrder,
Limit => $SearchLimit,
CustomerUserID => $Self->{UserID},
ConditionInline => $Config->{ExtendedSearchCondition},
ContentSearchPrefix => '*',
ContentSearchSuffix => '*',
FullTextIndex => 1,
%GetParam,
%DynamicFieldSearchParameters,
);
# get needed objects
my $UserObject = $Kernel::OM->Get('Kernel::System::User');
my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser');
# 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} };
# get the ticket dynamic fields for CSV display
my $CSVDynamicField = $DynamicFieldObject->DynamicFieldListGet(
Valid => 1,
ObjectType => ['Ticket'],
FieldFilter => $Config->{SearchCSVDynamicField} || {},
);
# reduce the dynamic fields to only the ones that are desinged for customer interface
my @CSVCustomerDynamicFields;
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{$CSVDynamicField} ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
my $IsCustomerInterfaceCapable = $BackendObject->HasBehavior(
DynamicFieldConfig => $DynamicFieldConfig,
Behavior => 'IsCustomerInterfaceCapable',
);
next DYNAMICFIELD if !$IsCustomerInterfaceCapable;
push @CSVCustomerDynamicFields, $DynamicFieldConfig;
}
$CSVDynamicField = \@CSVCustomerDynamicFields;
# 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 first article data.
my @Articles = $ArticleObject->ArticleList(
TicketID => $TicketID,
IsVisibleForCustomer => 1,
OnlyFirst => 1,
);
my %Article;
for my $Article (@Articles) {
%Article = $ArticleObject->BackendForArticle( %{$Article} )->ArticleGet(
TicketID => $TicketID,
ArticleID => $Article->{ArticleID},
DynamicFields => 1,
);
}
my %Data;
# If no article was found, set some defaults.
if ( !%Article ) {
%Data = %Ticket;
$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} && $GetParam{ResultForm} eq 'CSV' ) {
my @Articles = $ArticleObject->ArticleList(
TicketID => $TicketID,
IsVisibleForCustomer => 1,
);
if (@Articles) {
for my $Article (@Articles) {
my %Article = $ArticleObject->BackendForArticle( %{$Article} )->ArticleGet(
TicketID => $TicketID,
ArticleID => $Article->{ArticleID},
DynamicFields => 0,
);
if ( $Article{Body} ) {
$Data{ArticleTree}
.= "\n-->"
. "||$Article{SenderType}"
. "||$Article{From}"
. "||$Article{CreateTime}"
. "||<--------------\n"
. $Article{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 ),
);
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 do not match the current field name ($1)
# with out the 'DynamicField_' prefix
next DYNAMICFIELD if $DynamicFieldConfig->{Name} ne $1;
# get the value for print
my $ValueStrg = $BackendObject->DisplayValueRender(
DynamicFieldConfig => $DynamicFieldConfig,
Value => $Info{$Header},
HTMLOutput => 0,
LayoutObject => $LayoutObject,
);
push @Data, $ValueStrg->{Value};
# terminate the 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 Realname'),
);
my @CSVHeadTranslated = map { $LayoutObject->{LanguageObject}->Translate( $HeaderMap{$_} || $_ ); }
@CSVHead;
# return csv to download
my $CurSystemDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
my $FileName = sprintf(
'ticket_search_%s',
$CurSystemDateTimeObject->Format(
Format => '%Y-%m-%d_%H-%M'
)
);
# get CSV object
my $CSVObject = $Kernel::OM->Get('Kernel::System::CSV');
if ( $GetParam{ResultForm} eq 'CSV' ) {
my $CSV = $CSVObject->Array2CSV(
Head => \@CSVHeadTranslated,
Data => \@CSVData,
);
return $LayoutObject->Attachment(
Filename => $FileName . '.csv',
ContentType => "text/csv; charset=" . $LayoutObject->{UserCharset},
Content => $CSV,
);
}
# return Excel to download
elsif ( $GetParam{ResultForm} eq 'Excel' ) {
my $Excel = $CSVObject->Array2CSV(
Head => \@CSVHeadTranslated,
Data => \@CSVData,
Format => "Excel",
);
return $LayoutObject->Attachment(
Filename => $FileName . '.xlsx',
ContentType =>
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
Content => $Excel,
);
}
}
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,
Extended => 1,
UserID => $Self->{UserID},
);
# Get last customer article.
my @Articles = $ArticleObject->ArticleList(
TicketID => $TicketID,
SenderType => 'customer',
IsVisibleForCustomer => 1,
OnlyLast => 1,
);
# If the ticket has no customer article, get the last agent article.
if ( !@Articles ) {
@Articles = $ArticleObject->ArticleList(
TicketID => $TicketID,
SenderType => 'agent',
IsVisibleForCustomer => 1,
OnlyLast => 1,
);
}
# Finally, if everything failed, get latest article.
if ( !@Articles ) {
@Articles = $ArticleObject->ArticleList(
TicketID => $TicketID,
IsVisibleForCustomer => 1,
OnlyLast => 1,
);
}
my %Article;
for my $Article (@Articles) {
%Article = $ArticleObject->BackendForArticle( %{$Article} )->ArticleGet(
%{$Article},
DynamicFields => 0,
);
}
my %Data;
# If no article was found, set some defaults.
if ( !%Article ) {
%Data = %Ticket;
$Data{Subject} = $Ticket{Title} || $LayoutObject->{LanguageObject}->Translate('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 $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, $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('CustomerID');
$CellData->[0]->[6]->{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 $CurSystemDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
my $Filename = sprintf(
'ticket_search_%s.pdf',
$CurSystemDateTimeObject->Format( Format => '%Y-%m-%d_%H-%M' ),
);
my $PDFString = $PDFObject->DocumentOutput();
return $LayoutObject->Attachment(
Filename => $Filename,
ContentType => "application/pdf",
Content => $PDFString,
Type => 'inline',
);
}
my $Counter = 0;
# get the ticket dynamic fields for overview display
my $OverviewDynamicField = $DynamicFieldObject->DynamicFieldListGet(
Valid => 1,
ObjectType => ['Ticket'],
FieldFilter => $Config->{SearchOverviewDynamicField} || {},
);
# reduce the dynamic fields to only the ones that are desinged for customer interface
my @OverviewCustomerDynamicFields;
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{$OverviewDynamicField} ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
my $IsCustomerInterfaceCapable = $BackendObject->HasBehavior(
DynamicFieldConfig => $DynamicFieldConfig,
Behavior => 'IsCustomerInterfaceCapable',
);
next DYNAMICFIELD if !$IsCustomerInterfaceCapable;
push @OverviewCustomerDynamicFields, $DynamicFieldConfig;
}
$OverviewDynamicField = \@OverviewCustomerDynamicFields;
# if there are results to show
if (@ViewableTicketIDs) {
# Dynamic fields table headers
# cycle through the activated Dynamic Fields for this screen
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{$OverviewDynamicField} ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
my $Label = $DynamicFieldConfig->{Label};
# get field sortable condition
my $IsSortable = $BackendObject->HasBehavior(
DynamicFieldConfig => $DynamicFieldConfig,
Behavior => 'IsSortable',
);
if ($IsSortable) {
my $CSS = '';
my $Order = 'Down';
if (
$SortBy
&& (
$SortBy eq
( 'DynamicField_' . $DynamicFieldConfig->{Name} )
)
)
{
if ( $CurrentOrder && ( $CurrentOrder eq 'Up' ) ) {
$Order = 'Down';
$CSS .= ' SortAscending';
}
else {
$Order = 'Up';
$CSS .= ' SortDescending';
}
}
$LayoutObject->Block(
Name => 'HeaderDynamicField',
Data => {
%Param,
CSS => $CSS,
},
);
$LayoutObject->Block(
Name => 'HeaderDynamicFieldSortable',
Data => {
%Param,
Order => $Order,
Label => $Label,
DynamicFieldName => $DynamicFieldConfig->{Name},
},
);
}
else {
$LayoutObject->Block(
Name => 'HeaderDynamicField',
Data => {
%Param,
},
);
$LayoutObject->Block(
Name => 'HeaderDynamicFieldNotSortable',
Data => {
%Param,
Label => $Label,
},
);
}
}
for my $TicketID (@ViewableTicketIDs) {
$Counter++;
# build search result
if (
$Counter >= $StartHit
&& $Counter < ( $SearchPageShown + $StartHit )
)
{
# 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',
IsVisibleForCustomer => 1,
OnlyLast => 1,
);
# If the ticket has no customer article, get the last agent article.
if ( !@Articles ) {
@Articles = $ArticleObject->ArticleList(
TicketID => $TicketID,
SenderType => 'agent',
IsVisibleForCustomer => 1,
OnlyLast => 1,
);
}
# Finally, if everything failed, get latest article.
if ( !@Articles ) {
@Articles = $ArticleObject->ArticleList(
TicketID => $TicketID,
IsVisibleForCustomer => 1,
OnlyLast => 1,
);
}
my %Article;
for my $Article (@Articles) {
%Article = $ArticleObject->BackendForArticle( %{$Article} )->ArticleGet(
%{$Article},
DynamicFields => 1,
);
}
my %Data;
# If no article was found, set some defaults.
if ( !%Article ) {
%Data = %Ticket;
$Data{Subject} = $Ticket{Title} || $LayoutObject->{LanguageObject}->Translate('Untitled');
$Data{Body} = $LayoutObject->{LanguageObject}->Translate(
'This item has no articles yet.'
);
}
else {
%Data = ( %Ticket, %Article );
}
# customer info
my %CustomerData;
if ( $Data{CustomerUserID} ) {
%CustomerData = $CustomerUserObject->CustomerUserDataGet(
User => $Data{CustomerUserID},
);
}
elsif ( $Data{CustomerID} ) {
%CustomerData = $CustomerUserObject->CustomerUserDataGet(
User => $Data{CustomerID},
);
}
# customer info (customer name)
if ( $CustomerData{UserLogin} ) {
$Data{CustomerName} = $CustomerUserObject->CustomerName(
UserLogin => $CustomerData{UserLogin},
);
}
# user info
my %Owner = $UserObject->GetUserData(
User => $Data{Owner},
);
# Condense down the subject
my $Subject = $TicketObject->TicketSubjectClean(
TicketNumber => $Data{TicketNumber},
Subject => $Data{Subject} || '',
);
$Data{CustomerAge} = $LayoutObject->CustomerAge(
Age => $Data{Age},
Space => ' ',
);
# customer info string
if ( $Data{CustomerName} ) {
$Data{CustomerName} = '(' . $Data{CustomerName} . ')';
}
# add blocks to template
$LayoutObject->Block(
Name => 'Record',
Data => {
%Data,
Subject => $Subject,
%Owner,
},
);
# Dynamic fields
# cycle through the activated Dynamic Fields for this screen
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{$OverviewDynamicField} ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
# get field value
my $ValueStrg = $BackendObject->DisplayValueRender(
DynamicFieldConfig => $DynamicFieldConfig,
Value => $Data{ 'DynamicField_' . $DynamicFieldConfig->{Name} },
ValueMaxChars => 20,
LayoutObject => $LayoutObject,
);
$LayoutObject->Block(
Name => 'RecordDynamicField',
Data => {
Value => $ValueStrg->{Value},
Title => $ValueStrg->{Title},
},
);
}
}
}
}
# otherwise show a no data found msg
else {
$LayoutObject->Block( Name => 'NoDataFoundMsg' );
}
# show attributes used for search
my %IDMap = (
StateIDs => {
Name => 'State',
Object => 'State',
Method => 'StateLookup',
Key => 'StateID',
Translation => 1,
},
StateTypeIDs => {
Name => 'StateType',
Object => 'State',
Method => 'StateTypeLookup',
Key => 'StateTypeID',
Translation => 1,
},
PriorityIDs => {
Name => 'Priority',
Object => 'Priority',
Method => 'PriorityLookup',
Key => 'PriorityID',
Translation => 1,
},
QueueIDs => {
Name => 'Queue',
Object => 'Queue',
Method => 'QueueLookup',
Key => 'QueueID',
Translation => 0,
},
OwnerIDs => {
Name => 'Owner',
Object => 'User',
Method => 'UserLookup',
Key => 'UserID',
Translation => 0,
},
ResponsibleIDs => {
Name => 'Responsible',
Object => 'User',
Method => 'UserLookup',
Key => 'UserID',
Translation => 0,
},
ResponsibleIDs => {
Name => 'Responsible',
Object => 'User',
Method => 'UserLookup',
Key => 'UserID',
Translation => 0,
},
);
KEY:
for my $Key (
qw(TicketNumber From To Cc Subject Body CustomerID TimeSearchType StateType
StateIDs StateTypeIDs PriorityIDs OwnerIDs ResponsibleIDs
)
)
{
next KEY if !$GetParam{$Key};
my $Attribute = $IDMap{$Key}->{Name} || $Key;
my $Object = $IDMap{$Key}->{Object} || '';
my $Method = $IDMap{$Key}->{Method};
my $MethodKey = $IDMap{$Key}->{Key};
my $Translation = $IDMap{$Key}->{Translation};
my $Value;
# get appropriate object
my $LookupObject;
if ( $IDMap{$Key}->{Name} ) {
$LookupObject = $Kernel::OM->Get( 'Kernel::System::' . $Object );
}
if ( ref $GetParam{$Key} eq 'ARRAY' ) {
for my $ItemRaw ( @{ $GetParam{$Key} } ) {
my $Item = $ItemRaw;
if ($Value) {
$Value .= '+';
}
if ($LookupObject) {
$Item = $LookupObject->$Method( $MethodKey => $Item );
if ($Translation) {
$Item = $LayoutObject->{LanguageObject}->Translate($Item);
}
}
$Value .= $Item;
}
}
else {
my $Item = $GetParam{$Key};
if ($LookupObject) {
$Item = $LookupObject->$Method( $MethodKey => $Item );
if ($Translation) {
$Item = $LayoutObject->{LanguageObject}->Translate($Item);
}
}
$Value = $Item;
}
if ( $Key eq 'TimeSearchType' ) {
if ( $GetParam{TimeSearchType} eq 'TimeSlot' ) {
my $StartDate = $LayoutObject->{LanguageObject}->FormatTimeString(
$GetParam{TicketCreateTimeStartYear}
. '-' . $GetParam{TicketCreateTimeStartMonth}
. '-' . $GetParam{TicketCreateTimeStartDay}
. ' 00:00:00', 'DateFormatShort'
);
my $StopDate = $LayoutObject->{LanguageObject}->FormatTimeString(
$GetParam{TicketCreateTimeStopYear}
. '-' . $GetParam{TicketCreateTimeStopMonth}
. '-' . $GetParam{TicketCreateTimeStopDay}
. ' 00:00:00', 'DateFormatShort'
);
$Attribute = 'Created between';
$Value = $StartDate . ' '
. $LayoutObject->{LanguageObject}->Translate('and') . ' '
. $StopDate;
}
else {
my $Mapping = {
'Last' => Translatable('Created within the last'),
'Before' => Translatable('Created more than ... ago'),
};
$Attribute = $Mapping->{ $GetParam{TicketCreateTimePointStart} };
$Value = $GetParam{TicketCreateTimePoint} . ' '
. $LayoutObject->{LanguageObject}->Translate( $GetParam{TicketCreateTimePointFormat} . '(s)' );
}
}
$LayoutObject->Block(
Name => 'SearchTerms',
Data => {
%Param,
Attribute => $Attribute,
Key => $Key,
Value => $Value,
},
);
}
# cycle through the activated Dynamic Fields for this screen
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{$DynamicField} ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
next DYNAMICFIELD
if !$DynamicFieldSearchDisplay{ 'DynamicField_' . $DynamicFieldConfig->{Name} };
$LayoutObject->Block(
Name => 'SearchTerms',
Data => {
Attribute => $DynamicFieldConfig->{Label},
Value =>
$DynamicFieldSearchDisplay{ 'DynamicField_' . $DynamicFieldConfig->{Name} },
},
);
}
my $Link = 'Profile=' . $LayoutObject->LinkEncode($Profile) . ';';
$Link .= 'SortBy=' . $LayoutObject->LinkEncode($SortBy) . ';';
$Link .= 'Order=' . $LayoutObject->LinkEncode($CurrentOrder) . ';';
$Link .= 'TakeLastSearch=1;';
# build search navigation bar
my %PageNav = $LayoutObject->PageNavBar(
Limit => $SearchLimit,
StartHit => $StartHit,
PageShown => $SearchPageShown,
AllHits => $Counter,
Action => "Action=CustomerTicketSearch;Subaction=Search",
Link => $Link,
IDPrefix => "CustomerTicketSearch",
);
# show footer filter - show only if more the one page is available
if ( $SearchLimit && ( $SearchLimit > $SearchPageShown ) ) {
$LayoutObject->Block(
Name => 'Pagination',
Data => {
%Param,
%PageNav,
},
);
}
my $Order = 'Down';
if ( $CurrentOrder eq 'Down' ) {
$Order = 'Up';
}
my $Sort = '';
my $StateSort = '';
my $TicketSort = '';
my $AgeSort = '';
# define sort order
if ( $Order eq 'Down' ) {
$Sort = 'SortAscending';
}
if ( $Order eq 'Up' ) {
$Sort = 'SortDescending';
}
if ( $SortBy eq 'State' ) {
$StateSort = $Sort;
}
if ( $SortBy eq 'Ticket' ) {
$TicketSort = $Sort;
}
if ( $SortBy eq 'Age' ) {
$AgeSort = $Sort;
}
# start html page
my $Output = $LayoutObject->CustomerHeader();
$Output .= $LayoutObject->CustomerNavigationBar();
$Output .= $LayoutObject->Output(
TemplateFile => 'CustomerTicketSearchResultShort',
Data => {
%Param,
%PageNav,
Order => $Order,
StateSort => $StateSort,
TicketSort => $TicketSort,
AgeSort => $AgeSort,
Profile => $Profile,
ProfileName => $ProfileName,
},
);
# build footer
$Output .= $LayoutObject->CustomerFooter();
return $Output;
}
# empty search site
else {
# delete profile
if ( $EraseTemplate && $Profile ) {
# remove old profile stuff
$SearchProfileObject->SearchProfileDelete(
Base => 'CustomerTicketSearch',
Name => $Profile,
UserLogin => $Self->{UserLogin},
);
%GetParam = ();
$Profile = '';
}
# create HTML strings for all dynamic fields
my %DynamicFieldHTML;
# cycle through 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,
CustomerUserID => $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,
ConfirmationCheckboxes => 1,
Type => $Preference->{Type},
);
}
}
# generate search mask
my $Output = $LayoutObject->CustomerHeader();
$Output .= $LayoutObject->CustomerNavigationBar();
$Output .= $Self->MaskForm(
%GetParam,
Profile => $Profile,
Area => 'Customer',
DynamicFieldHTML => \%DynamicFieldHTML,
%ServerErrors,
);
$Output .= $LayoutObject->CustomerFooter();
return $Output;
}
}
sub MaskForm {
my ( $Self, %Param ) = @_;
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# 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');
$Param{ResultFormStrg} = $LayoutObject->BuildSelection(
Data => {
Normal => 'Normal',
Print => 'Print',
CSV => 'CSV',
Excel => 'Excel',
},
Name => 'ResultForm',
SelectedID => $Param{ResultForm} || 'Normal',
Class => 'Modernize',
);
my %Profiles = $Kernel::OM->Get('Kernel::System::SearchProfile')->SearchProfileList(
Base => 'CustomerTicketSearch',
UserLogin => $Self->{UserLogin},
);
if ( $Profiles{'last-search'} ) {
$Profiles{'last-search'} = $LayoutObject->{LanguageObject}->Translate('last-search');
}
$Param{ProfilesStrg} = $LayoutObject->BuildSelection(
Data => \%Profiles,
Translation => 0,
Name => 'Profile',
SelectedID => $Param{Profile},
Class => 'Modernize',
PossibleNone => 1,
);
my %Customers = $Kernel::OM->Get('Kernel::System::CustomerGroup')->GroupContextCustomers(
CustomerUserID => $Self->{UserID},
);
$Param{CustomerIDStrg} = $LayoutObject->BuildSelection(
Data => \%Customers,
Name => 'CustomerID',
Multiple => 1,
Size => 5,
SelectedID => $Param{CustomerID},
Class => 'Modernize',
);
# get service object
my $ServiceObject = $Kernel::OM->Get('Kernel::System::Service');
my %ServiceList;
if ( $ConfigObject->Get('Customer::TicketSearch::AllServices') ) {
%ServiceList = $ServiceObject->ServiceList(
UserID => $Self->{UserID},
);
}
else {
%ServiceList = $ServiceObject->CustomerUserServiceMemberList(
CustomerUserLogin => $Self->{UserID},
Result => 'HASH',
);
}
$Param{ServicesStrg} = $LayoutObject->BuildSelection(
Data => \%ServiceList,
Name => 'ServiceIDs',
Multiple => 1,
Size => 5,
SelectedID => $Param{ServiceIDs},
TreeView => $TreeView,
Class => 'Modernize',
);
$Param{TypesStrg} = $LayoutObject->BuildSelection(
Data => {
$Kernel::OM->Get('Kernel::System::Type')->TypeList(
UserID => $Self->{UserID},
),
},
Name => 'TypeIDs',
SelectedID => $Param{TypeIDs},
Multiple => 1,
Size => 5,
SelectedID => $Param{TypeIDs},
Class => 'Modernize',
);
$Param{StatesStrg} = $LayoutObject->BuildSelection(
Data => {
$Kernel::OM->Get('Kernel::System::State')->StateList(
UserID => $Self->{UserID},
Action => $Self->{Action},
),
},
Name => 'StateIDs',
Multiple => 1,
Size => 5,
SelectedID => $Param{StateIDs},
Class => 'Modernize',
);
$Param{StateTypeStrg} = $LayoutObject->BuildSelection(
Data => {
Open => 'open',
Closed => 'closed',
},
Name => 'StateType',
Size => 5,
SelectedID => $Param{StateType},
Class => 'Modernize',
);
$Param{PrioritiesStrg} = $LayoutObject->BuildSelection(
Data => {
$Kernel::OM->Get('Kernel::System::Priority')->PriorityList(
UserID => $Self->{UserID},
Action => $Self->{Action},
),
},
Name => 'PriorityIDs',
Multiple => 1,
Size => 5,
SelectedID => $Param{PriorityIDs},
Class => 'Modernize',
);
$Param{TicketCreateTimePoint} = $LayoutObject->BuildSelection(
Data => {
1 => ' 1',
2 => ' 2',
3 => ' 3',
4 => ' 4',
5 => ' 5',
6 => ' 6',
7 => ' 7',
8 => ' 8',
9 => ' 9',
10 => '10',
11 => '11',
12 => '12',
13 => '13',
14 => '14',
15 => '15',
16 => '16',
17 => '17',
18 => '18',
19 => '19',
20 => '20',
21 => '21',
22 => '22',
23 => '23',
24 => '24',
25 => '25',
26 => '26',
27 => '27',
28 => '28',
29 => '29',
30 => '30',
31 => '31',
32 => '32',
33 => '33',
34 => '34',
35 => '35',
36 => '36',
37 => '37',
38 => '38',
39 => '39',
40 => '40',
41 => '41',
42 => '42',
43 => '43',
44 => '44',
45 => '45',
46 => '46',
47 => '47',
48 => '48',
49 => '49',
50 => '50',
51 => '51',
52 => '52',
53 => '53',
54 => '54',
55 => '55',
56 => '56',
57 => '57',
58 => '58',
59 => '59',
},
Translation => 0,
Name => 'TicketCreateTimePoint',
SelectedID => $Param{TicketCreateTimePoint},
);
$Param{TicketCreateTimePointStart} = $LayoutObject->BuildSelection(
Data => {
Last => Translatable('within the last ...'),
Before => Translatable('more than ... ago'),
},
Translation => 1,
Name => 'TicketCreateTimePointStart',
SelectedID => $Param{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)'),
},
Translation => 1,
Name => 'TicketCreateTimePointFormat',
SelectedID => $Param{TicketCreateTimePointFormat},
);
$Param{TicketCreateTimeStart} = $LayoutObject->BuildDateSelection(
%Param,
Prefix => 'TicketCreateTimeStart',
TicketCreateTimeStartClass => 'DateSelection',
Format => 'DateInputFormat',
DiffTime => -( ( 60 * 60 * 24 ) * 30 ),
);
$Param{TicketCreateTimeStop} = $LayoutObject->BuildDateSelection(
%Param,
Prefix => 'TicketCreateTimeStop',
TicketCreateTimeStopClass => 'DateSelection',
Format => 'DateInputFormat',
);
# html search mask output
$LayoutObject->Block(
Name => 'Search',
Data => { %Param, },
);
# create the fulltext field entries to be displayed
my %ArticleSearchableFields = $Kernel::OM->Get('Kernel::System::Ticket::Article')->ArticleSearchableFieldsList();
FIELD:
for my $ArticleFieldKey (
sort { $ArticleSearchableFields{$a}->{Label} cmp $ArticleSearchableFields{$b}->{Label} }
keys %ArticleSearchableFields
)
{
next FIELD if $ArticleSearchableFields{$ArticleFieldKey}->{HideInCustomerInterface};
$LayoutObject->Block(
Name => 'SearchableArticleField',
Data => {
ArticleFieldLabel => $ArticleSearchableFields{$ArticleFieldKey}->{Label},
ArticleFieldKey => $ArticleSearchableFields{$ArticleFieldKey}->{Key},
ArticleFieldValue => $Param{$ArticleFieldKey} // '',
},
);
}
# enable archive search
if (
$ConfigObject->Get('Ticket::ArchiveSystem')
&& $ConfigObject->Get('Ticket::CustomerArchiveSystem')
&& $ConfigObject->Get('Ticket::CustomerArchiveSystem') eq 1
)
{
$Param{SearchInArchiveStrg} = $LayoutObject->BuildSelection(
Data => {
ArchivedTickets => Translatable('Archived tickets'),
NotArchivedTickets => Translatable('Unarchived tickets'),
AllTickets => Translatable('All tickets'),
},
Name => 'SearchInArchive',
SelectedID => $Param{SearchInArchive} || 'NotArchivedTickets',
Class => 'Modernize',
);
$LayoutObject->Block(
Name => 'SearchInArchive',
Data => {
SearchInArchiveStrg => $Param{SearchInArchiveStrg},
},
);
}
my $Config = $ConfigObject->Get("Ticket::Frontend::$Self->{Action}");
# get dynamic field config for frontend module
my $DynamicFieldFilter = $Config->{DynamicField};
# get the dynamic fields for ticket object
my $DynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
Valid => 1,
ObjectType => ['Ticket'],
FieldFilter => $DynamicFieldFilter || {},
);
# get backend object
my $BackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
# reduce the dynamic fields to only the ones that are desinged for customer interface
my @CustomerDynamicFields;
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{$DynamicField} ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
my $IsCustomerInterfaceCapable = $BackendObject->HasBehavior(
DynamicFieldConfig => $DynamicFieldConfig,
Behavior => 'IsCustomerInterfaceCapable',
);
next DYNAMICFIELD if !$IsCustomerInterfaceCapable;
push @CustomerDynamicFields, $DynamicFieldConfig;
}
$DynamicField = \@CustomerDynamicFields;
# output Dynamic fields blocks
# cycle through 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(
$Param{DynamicFieldHTML}->{ $DynamicFieldConfig->{Name} . $Preference->{Type} }
);
$LayoutObject->Block(
Name => 'DynamicField',
Data => {
Label => $Param{DynamicFieldHTML}
->{ $DynamicFieldConfig->{Name} . $Preference->{Type} }->{Label},
Field => $Param{DynamicFieldHTML}
->{ $DynamicFieldConfig->{Name} . $Preference->{Type} }->{Field},
},
);
}
}
# html search mask output
return $LayoutObject->Output(
TemplateFile => 'CustomerTicketSearch',
Data => \%Param,
);
}
sub _StopWordsServerErrorsGet {
my ( $Self, %Param ) = @_;
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');
if ( !%Param ) {
$LayoutObject->FatalError(
Message => Translatable('Got no values to check.'),
);
}
my %StopWordsServerErrors;
if ( !$ArticleObject->SearchStringStopWordsUsageWarningActive() ) {
return %StopWordsServerErrors;
}
my %SearchStrings;
FIELD:
for my $Field ( sort keys %Param ) {
next FIELD if !defined $Param{$Field};
next FIELD if !length $Param{$Field};
$SearchStrings{$Field} = $Param{$Field};
}
if (%SearchStrings) {
my $StopWords = $ArticleObject->SearchStringStopWordsFind(
SearchStrings => \%SearchStrings,
);
FIELD:
for my $Field ( sort keys %{$StopWords} ) {
next FIELD if !defined $StopWords->{$Field};
next FIELD if ref $StopWords->{$Field} ne 'ARRAY';
next FIELD if !@{ $StopWords->{$Field} };
$StopWordsServerErrors{ $Field . 'Invalid' } = 'ServerError';
$StopWordsServerErrors{ $Field . 'InvalidTooltip' } = $LayoutObject->{LanguageObject}
->Translate('Please remove the following words because they cannot be used for the search:')
. ' '
. join( ',', sort @{ $StopWords->{$Field} } );
}
}
return %StopWordsServerErrors;
}
1;