# --
# 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::Output::HTML::Statistics::View;
## nofilter(TidyAll::Plugin::OTRS::Perl::PodChecker)
use strict;
use warnings;
use List::Util qw( first );
use Kernel::System::VariableCheck qw(:all);
use Kernel::System::DateTime;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::Language',
'Kernel::Output::HTML::Layout',
'Kernel::Output::PDF::Statistics',
'Kernel::System::CSV',
'Kernel::System::DateTime',
'Kernel::System::Group',
'Kernel::System::Log',
'Kernel::System::Main',
'Kernel::System::PDF',
'Kernel::System::Stats',
'Kernel::System::Ticket::Article',
'Kernel::System::User',
'Kernel::System::Web::Request',
);
use Kernel::Language qw(Translatable);
=head1 NAME
Kernel::Output::HTML::Statistics::View - View object for statistics
=head1 DESCRIPTION
Provides several functions to generate statistics GUI elements.
=head1 PUBLIC INTERFACE
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
return $Self;
}
=head2 StatsParamsWidget()
generate HTML for statistics run widget.
my $HTML = $StatsViewObject->StatsParamsWidget(
StatID => $StatID,
Formats => { # optional, limit the available formats
Print => 'Print',
}
OutputCounter => 1, # optional, counter to append to ElementIDs
# This is needed if there is more than one stat on the page.
AJAX => 0, # optional, keep script tags for AJAX responses
);
=cut
sub StatsParamsWidget {
my ( $Self, %Param ) = @_;
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
for my $Needed (qw(Stat)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => "error",
Message => "Need $Needed!"
);
return;
}
}
# Don't allow to run an invalid stat.
return if !$Param{Stat}->{Valid};
$Param{OutputCounter} ||= 1;
# Check if there are any configuration errors that must be corrected by the stats admin
my $StatsConfigurationValid = $Self->StatsConfigurationValidate(
Stat => $Param{Stat},
Errors => {},
);
if ( !$StatsConfigurationValid ) {
return;
}
my $HasUserGetParam = ref $Param{UserGetParam} eq 'HASH';
my %UserGetParam = %{ $Param{UserGetParam} // {} };
my $Format = $Param{Formats} || $ConfigObject->Get('Stats::Format');
my $LocalGetParam = sub {
my (%Param) = @_;
my $Param = $Param{Param};
return $HasUserGetParam ? $UserGetParam{$Param} : $ParamObject->GetParam( Param => $Param );
};
my $LocalGetArray = sub {
my (%Param) = @_;
my $Param = $Param{Param};
if ($HasUserGetParam) {
if ( $UserGetParam{$Param} && ref $UserGetParam{$Param} eq 'ARRAY' ) {
return @{ $UserGetParam{$Param} };
}
return;
}
return $ParamObject->GetArray( Param => $Param );
};
my $Stat = $Param{Stat};
my $StatID = $Stat->{StatID};
my $Output;
# get the object name
if ( $Stat->{StatType} eq 'static' ) {
$Stat->{ObjectName} = $Stat->{File};
}
# if no object name is defined use an empty string
$Stat->{ObjectName} ||= '';
# create format select box
my %SelectFormat;
VALUE:
for my $Value ( @{ $Stat->{Format} } ) {
next VALUE if !defined $Format->{$Value};
$SelectFormat{$Value} = $Format->{$Value};
}
if ( keys %SelectFormat > 1 ) {
my %Frontend;
$Frontend{SelectFormat} = $LayoutObject->BuildSelection(
Data => \%SelectFormat,
SelectedID => $LocalGetParam->( Param => 'Format' ),
Name => 'Format',
Class => 'Modernize',
);
$LayoutObject->Block(
Name => 'Format',
Data => \%Frontend,
);
}
elsif ( keys %SelectFormat == 1 ) {
$LayoutObject->Block(
Name => 'FormatFixed',
Data => {
Format => ( values %SelectFormat )[0],
FormatKey => ( keys %SelectFormat )[0],
},
);
}
else {
return; # no possible output format
}
# provide the time zone field only for dynamic statistics
if ( $Stat->{StatType} eq 'dynamic' ) {
my $SelectedTimeZone = $Self->_GetValidTimeZone( TimeZone => $LocalGetParam->( Param => 'TimeZone' ) )
// $Stat->{TimeZone}
// Kernel::System::DateTime->OTRSTimeZoneGet();
my %TimeZoneBuildSelection = $Self->_TimeZoneBuildSelection();
my %Frontend;
$Frontend{SelectTimeZone} = $LayoutObject->BuildSelection(
%TimeZoneBuildSelection,
Name => 'TimeZone',
Class => 'Modernize',
SelectedID => $SelectedTimeZone,
);
$LayoutObject->Block(
Name => 'TimeZone',
Data => \%Frontend,
);
}
if ( $ConfigObject->Get('Stats::ExchangeAxis') ) {
my $ExchangeAxis = $LayoutObject->BuildSelection(
Data => {
1 => Translatable('Yes'),
0 => Translatable('No')
},
Name => 'ExchangeAxis',
SelectedID => $LocalGetParam->( Param => 'ExchangeAxis' ) // 0,
Class => 'Modernize',
);
$LayoutObject->Block(
Name => 'ExchangeAxis',
Data => { ExchangeAxis => $ExchangeAxis }
);
}
# get static attributes
if ( $Stat->{StatType} eq 'static' ) {
# load static module
my $Params = $Kernel::OM->Get('Kernel::System::Stats')->GetParams( StatID => $StatID );
return if !$Params;
$LayoutObject->Block(
Name => 'Static',
);
PARAMITEM:
for my $ParamItem ( @{$Params} ) {
$LayoutObject->Block(
Name => 'ItemParam',
Data => {
Param => $ParamItem->{Frontend},
Name => $ParamItem->{Name},
Field => $LayoutObject->BuildSelection(
Data => $ParamItem->{Data},
Name => $ParamItem->{Name},
SelectedID => $LocalGetParam->( Param => $ParamItem->{Name} ) // $ParamItem->{SelectedID} || '',
Multiple => $ParamItem->{Multiple} || 0,
Size => $ParamItem->{Size} || '',
Class => 'Modernize',
),
},
);
}
}
# get dynamic attributes
elsif ( $Stat->{StatType} eq 'dynamic' ) {
my %Name = (
UseAsXvalue => Translatable('X-axis'),
UseAsValueSeries => Translatable('Y-axis'),
UseAsRestriction => Translatable('Filter'),
);
for my $Use (qw(UseAsXvalue UseAsValueSeries UseAsRestriction)) {
my $Flag = 0;
$LayoutObject->Block(
Name => 'Dynamic',
Data => { Name => $Name{$Use} },
);
OBJECTATTRIBUTE:
for my $ObjectAttribute ( @{ $Stat->{$Use} } ) {
next OBJECTATTRIBUTE if !$ObjectAttribute->{Selected};
my $ElementName = $Use . $ObjectAttribute->{Element};
my %ValueHash;
$Flag = 1;
# Select All function
if ( !$ObjectAttribute->{SelectedValues}[0] ) {
if (
$ObjectAttribute->{Values} && ref $ObjectAttribute->{Values} ne 'HASH'
)
{
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Values needs to be a hash reference!'
);
next OBJECTATTRIBUTE;
}
my @Values = keys( %{ $ObjectAttribute->{Values} } );
$ObjectAttribute->{SelectedValues} = \@Values;
}
VALUE:
for my $Value ( @{ $ObjectAttribute->{SelectedValues} } ) {
if ( $ObjectAttribute->{Values} ) {
next VALUE if !defined $ObjectAttribute->{Values}->{$Value};
$ValueHash{$Value} = $ObjectAttribute->{Values}->{$Value};
}
else {
$ValueHash{Value} = $Value;
}
}
$LayoutObject->Block(
Name => 'Element',
Data => { Name => $ObjectAttribute->{Name} },
);
# show fixed elements
if ( $ObjectAttribute->{Fixed} ) {
if ( $ObjectAttribute->{Block} eq 'Time' ) {
if ( $Use eq 'UseAsRestriction' ) {
delete $ObjectAttribute->{SelectedValues};
}
my $TimeScale = $Self->_TimeScale();
if ( $ObjectAttribute->{TimeStart} ) {
$LayoutObject->Block(
Name => 'TimePeriodFixed',
Data => {
TimeStart => $ObjectAttribute->{TimeStart},
TimeStop => $ObjectAttribute->{TimeStop},
},
);
}
elsif ( $ObjectAttribute->{TimeRelativeUnit} ) {
$LayoutObject->Block(
Name => 'TimeRelativeFixed',
Data => {
TimeRelativeUnit => $TimeScale->{ $ObjectAttribute->{TimeRelativeUnit} }->{Value},
TimeRelativeCount => $ObjectAttribute->{TimeRelativeCount},
TimeRelativeUpcomingCount => $ObjectAttribute->{TimeRelativeUpcomingCount},
},
);
}
if ( $ObjectAttribute->{SelectedValues}[0] ) {
$LayoutObject->Block(
Name => 'TimeScaleFixed',
Data => {
Scale => $TimeScale->{ $ObjectAttribute->{SelectedValues}[0] }->{Value},
Count => $ObjectAttribute->{TimeScaleCount},
},
);
}
}
else {
# find out which sort mechanism is used
my @Sorted;
if ( $ObjectAttribute->{SortIndividual} ) {
@Sorted = grep { $ValueHash{$_} } @{ $ObjectAttribute->{SortIndividual} };
}
else {
@Sorted = sort { $ValueHash{$a} cmp $ValueHash{$b} } keys %ValueHash;
}
my @FixedAttributes;
ELEMENT:
for my $Element (@Sorted) {
my $Value = $ValueHash{$Element};
if ( $ObjectAttribute->{Translation} ) {
$Value = $LayoutObject->{LanguageObject}->Translate( $ValueHash{$Element} );
}
next ELEMENT if !defined $Value;
push @FixedAttributes, $Value;
}
$LayoutObject->Block(
Name => 'Fixed',
Data => {
Value => join( ', ', @FixedAttributes ),
Key => $_,
Use => $Use,
Element => $ObjectAttribute->{Element},
},
);
}
}
# show unfixed elements
else {
my %BlockData;
$BlockData{Name} = $ObjectAttribute->{Name};
$BlockData{Element} = $ObjectAttribute->{Element};
$BlockData{Value} = $ObjectAttribute->{SelectedValues}->[0];
my @SelectedIDs = $LocalGetArray->( Param => $ElementName );
if ( $ObjectAttribute->{Block} eq 'MultiSelectField' ) {
$BlockData{SelectField} = $LayoutObject->BuildSelection(
Data => \%ValueHash,
Name => $ElementName,
Multiple => 1,
Size => 5,
SelectedID => @SelectedIDs ? [@SelectedIDs] : $ObjectAttribute->{SelectedValues},
Translation => $ObjectAttribute->{Translation},
TreeView => $ObjectAttribute->{TreeView} || 0,
Sort => scalar $ObjectAttribute->{Sort},
SortIndividual => scalar $ObjectAttribute->{SortIndividual},
Class => 'Modernize',
);
$LayoutObject->Block(
Name => 'MultiSelectField',
Data => \%BlockData,
);
}
elsif ( $ObjectAttribute->{Block} eq 'SelectField' ) {
$BlockData{SelectField} = $LayoutObject->BuildSelection(
Data => \%ValueHash,
Name => $ElementName,
Translation => $ObjectAttribute->{Translation},
TreeView => $ObjectAttribute->{TreeView} || 0,
Sort => scalar $ObjectAttribute->{Sort},
SortIndividual => scalar $ObjectAttribute->{SortIndividual},
SelectedID => $LocalGetParam->( Param => $ElementName ),
Class => 'Modernize',
);
$LayoutObject->Block(
Name => 'SelectField',
Data => \%BlockData,
);
}
elsif ( $ObjectAttribute->{Block} eq 'InputField' ) {
$LayoutObject->Block(
Name => 'InputField',
Data => {
Key => $ElementName,
Value => $LocalGetParam->( Param => $ElementName )
// $ObjectAttribute->{SelectedValues}[0],
CSSClass => $ObjectAttribute->{CSSClass},
HTMLDataAttributes => $ObjectAttribute->{HTMLDataAttributes},
},
);
}
elsif ( $ObjectAttribute->{Block} eq 'Time' ) {
$ObjectAttribute->{Element} = $Use . $ObjectAttribute->{Element};
my %Time;
if ( $ObjectAttribute->{TimeStart} ) {
if ( $LocalGetParam->( Param => $ElementName . 'StartYear' ) ) {
for my $Limit (qw(Start Stop)) {
for my $Unit (qw(Year Month Day Hour Minute Second)) {
if ( defined( $LocalGetParam->( Param => "$ElementName$Limit$Unit" ) ) ) {
$Time{ $Limit . $Unit } = $LocalGetParam->(
Param => $ElementName . "$Limit$Unit",
);
}
}
if ( !defined( $Time{ $Limit . 'Hour' } ) ) {
if ( $Limit eq 'Start' ) {
$Time{StartHour} = 0;
$Time{StartMinute} = 0;
$Time{StartSecond} = 0;
}
elsif ( $Limit eq 'Stop' ) {
$Time{StopHour} = 23;
$Time{StopMinute} = 59;
$Time{StopSecond} = 59;
}
}
elsif ( !defined( $Time{ $Limit . 'Second' } ) ) {
if ( $Limit eq 'Start' ) {
$Time{StartSecond} = 0;
}
elsif ( $Limit eq 'Stop' ) {
$Time{StopSecond} = 59;
}
}
$Time{"Time$Limit"} = sprintf(
"%04d-%02d-%02d %02d:%02d:%02d",
$Time{ $Limit . 'Year' },
$Time{ $Limit . 'Month' },
$Time{ $Limit . 'Day' },
$Time{ $Limit . 'Hour' },
$Time{ $Limit . 'Minute' },
$Time{ $Limit . 'Second' },
);
}
}
}
elsif ( $ObjectAttribute->{TimeRelativeUnit} ) {
$Time{TimeRelativeCount} = $LocalGetParam->(
Param => $ObjectAttribute->{Element} . 'TimeRelativeCount',
) // $ObjectAttribute->{TimeRelativeCount};
$Time{TimeRelativeUpcomingCount} = $LocalGetParam->(
Param => $ObjectAttribute->{Element} . 'TimeRelativeUpcomingCount',
) // $ObjectAttribute->{TimeRelativeUpcomingCount};
$Time{TimeScaleCount} = $LocalGetParam->(
Param => $ObjectAttribute->{Element} . 'TimeScaleCount',
) || $ObjectAttribute->{TimeScaleCount};
$Time{TimeRelativeUnitLocalSelectedValue} = $LocalGetParam->(
Param => $ObjectAttribute->{Element} . 'TimeRelativeUnit'
);
}
if ( $Use ne 'UseAsRestriction' ) {
$Time{TimeScaleUnitLocalSelectedValue} = $LocalGetParam->(
Param => $ObjectAttribute->{Element},
);
# get the selected x axis time scale value for value series
if ( $Use eq 'UseAsValueSeries' ) {
# get the name for the x axis element
my $XAxisElementName = $ObjectAttribute->{Element};
$XAxisElementName =~ s{ \A UseAsValueSeries }{UseAsXvalue}xms;
# get the current x axis value
my $XAxisLocalSelectedValue = $LocalGetParam->(
Param => $XAxisElementName,
);
$Time{SelectedXAxisValue} = $XAxisLocalSelectedValue
|| $Self->_GetSelectedXAxisTimeScaleValue( Stat => $Stat );
# save the x axis time scale element id for the output
$BlockData{XAxisTimeScaleElementID}
= $XAxisElementName . '-' . $StatID . '-' . $Param{OutputCounter};
}
}
my %TimeData = $Self->_TimeOutput(
StatID => $StatID,
OutputCounter => $Param{OutputCounter},
Output => 'View',
Use => $Use,
%{$ObjectAttribute},
%Time,
);
%BlockData = ( %BlockData, %TimeData );
if ( $ObjectAttribute->{TimeStart} ) {
$LayoutObject->Block(
Name => 'TimePeriod',
Data => \%BlockData,
);
}
elsif ( $ObjectAttribute->{TimeRelativeUnit} ) {
$LayoutObject->Block(
Name => 'TimePeriodRelative',
Data => \%BlockData,
);
}
# build the Timescale output
if ( $Use ne 'UseAsRestriction' ) {
$LayoutObject->Block(
Name => 'TimeScale',
Data => {
%BlockData,
},
);
# send data to JS
$LayoutObject->AddJSData(
Key => 'StatsParamData',
Value => {
%BlockData
},
);
}
# end of build timescale output
}
}
}
# Show this Block if no value series or restrictions are selected
if ( !$Flag ) {
$LayoutObject->Block(
Name => 'NoElement',
);
}
}
}
my %YesNo = (
0 => Translatable('No'),
1 => Translatable('Yes')
);
my %ValidInvalid = (
0 => Translatable('invalid'),
1 => Translatable('valid')
);
$Stat->{SumRowValue} = $YesNo{ $Stat->{SumRow} };
$Stat->{SumColValue} = $YesNo{ $Stat->{SumCol} };
$Stat->{CacheValue} = $YesNo{ $Stat->{Cache} };
$Stat->{ShowAsDashboardWidgetValue} = $YesNo{ $Stat->{ShowAsDashboardWidget} // 0 };
$Stat->{ValidValue} = $ValidInvalid{ $Stat->{Valid} };
for my $Field (qw(CreatedBy ChangedBy)) {
$Stat->{$Field} = $Kernel::OM->Get('Kernel::System::User')->UserName( UserID => $Stat->{$Field} );
}
if ( $Param{AJAX} ) {
# send data to JS
$LayoutObject->AddJSData(
Key => 'StatsWidgetAJAX',
Value => $Param{AJAX}
);
}
$Output .= $LayoutObject->Output(
TemplateFile => 'Statistics/StatsParamsWidget',
Data => {
%{$Stat},
AJAX => $Param{AJAX},
},
AJAX => $Param{AJAX},
);
return $Output;
}
sub GeneralSpecificationsWidget {
my ( $Self, %Param ) = @_;
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# In case of page reload because of errors
my %Errors = %{ $Param{Errors} // {} };
my %GetParam = %{ $Param{GetParam} // {} };
my $Stat;
if ( $Param{StatID} ) {
$Stat = $Kernel::OM->Get('Kernel::System::Stats')->StatsGet(
StatID => $Param{StatID},
UserID => $Param{UserID},
);
}
else {
$Stat->{StatID} = '';
$Stat->{StatNumber} = '';
$Stat->{Valid} = 1;
}
# Check if a time field is selected in the current statistic configuration, because
# only in this case the caching can be activated in the general statistic settings.
my $TimeFieldSelected;
USE:
for my $Use (qw(UseAsXvalue UseAsValueSeries UseAsRestriction)) {
for my $ObjectAttribute ( @{ $Stat->{$Use} } ) {
if ( $ObjectAttribute->{Selected} && $ObjectAttribute->{Block} eq 'Time' ) {
$TimeFieldSelected = 1;
last USE;
}
}
}
my %Frontend;
my %YesNo = (
0 => Translatable('No'),
1 => Translatable('Yes')
);
# Create selectboxes for 'Cache', 'SumRow', 'SumCol', and 'Valid'.
for my $Key (qw(Cache ShowAsDashboardWidget SumRow SumCol)) {
my %SelectionData = %YesNo;
if ( $Key eq 'Cache' && !$TimeFieldSelected ) {
delete $SelectionData{1};
}
$Frontend{ 'Select' . $Key } = $LayoutObject->BuildSelection(
Data => \%SelectionData,
SelectedID => $GetParam{$Key} // $Stat->{$Key} || 0,
Name => $Key,
Class => 'Modernize',
);
}
# New statistics don't get this select.
if ( !$Stat->{ObjectBehaviours}->{ProvidesDashboardWidget} ) {
$Frontend{'SelectShowAsDashboardWidget'} = $LayoutObject->BuildSelection(
Data => {
0 => Translatable('No (not supported)'),
},
SelectedID => 0,
Name => 'ShowAsDashboardWidget',
Class => 'Modernize',
);
}
$Frontend{SelectValid} = $LayoutObject->BuildSelection(
Data => {
0 => Translatable('invalid'),
1 => Translatable('valid'),
},
SelectedID => $GetParam{Valid} // $Stat->{Valid},
Name => 'Valid',
Class => 'Modernize',
);
# get the default selected formats
my $DefaultSelectedFormat = $ConfigObject->Get('Stats::DefaultSelectedFormat') || [];
# Create a new statistic
if ( !$Stat->{StatType} ) {
my $DynamicFiles = $Kernel::OM->Get('Kernel::System::Stats')->GetDynamicFiles();
my %ObjectModules;
DYNAMIC_FILE:
for my $DynamicFile ( sort keys %{ $DynamicFiles // {} } ) {
my $ObjectName = 'Kernel::System::Stats::Dynamic::' . $DynamicFile;
next DYNAMIC_FILE if !$Kernel::OM->Get('Kernel::System::Main')->Require($ObjectName);
my $Object = $ObjectName->new();
next DYNAMIC_FILE if !$Object;
if ( $Object->can('GetStatElement') ) {
$ObjectModules{DynamicMatrix}->{$ObjectName} = $DynamicFiles->{$DynamicFile};
}
else {
$ObjectModules{DynamicList}->{$ObjectName} = $DynamicFiles->{$DynamicFile};
}
}
my $StaticFiles = $Kernel::OM->Get('Kernel::System::Stats')->GetStaticFiles(
OnlyUnusedFiles => 1,
UserID => $Param{UserID},
);
for my $StaticFile ( sort keys %{ $StaticFiles // {} } ) {
$ObjectModules{Static}->{ 'Kernel::System::Stats::Static::' . $StaticFile } = $StaticFiles->{$StaticFile};
}
$Frontend{StatisticPreselection} = $ParamObject->GetParam( Param => 'StatisticPreselection' );
if ( $Frontend{StatisticPreselection} eq 'Static' ) {
$Frontend{StatType} = 'static';
$Frontend{SelectObjectType} = $LayoutObject->BuildSelection(
Data => $ObjectModules{Static},
Name => 'ObjectModule',
Class => 'Modernize Validate_Required' . ( $Errors{ObjectModuleServerError} ? ' ServerError' : '' ),
Translation => 0,
SelectedID => $GetParam{ObjectModule},
);
}
elsif ( $Frontend{StatisticPreselection} eq 'DynamicList' ) {
# remove the default selected graph formats for the dynamic lists
@{$DefaultSelectedFormat} = grep { $_ !~ m{^D3} } @{$DefaultSelectedFormat};
$Frontend{StatType} = 'dynamic';
$Frontend{SelectObjectType} = $LayoutObject->BuildSelection(
Data => $ObjectModules{DynamicList},
Name => 'ObjectModule',
Translation => 1,
Class => 'Modernize ' . ( $Errors{ObjectModuleServerError} ? ' ServerError' : '' ),
SelectedID => $GetParam{ObjectModule} // $ConfigObject->Get('Stats::DefaultSelectedDynamicObject'),
);
}
# DynamicMatrix
else {
$Frontend{StatType} = 'dynamic';
$Frontend{SelectObjectType} = $LayoutObject->BuildSelection(
Data => $ObjectModules{DynamicMatrix},
Name => 'ObjectModule',
Translation => 1,
Class => 'Modernize ' . ( $Errors{ObjectModuleServerError} ? ' ServerError' : '' ),
SelectedID => $GetParam{ObjectModule} // $ConfigObject->Get('Stats::DefaultSelectedDynamicObject'),
);
}
}
# get the avaible formats
my $AvailableFormats = $ConfigObject->Get('Stats::Format');
# create multiselectboxes 'format'
$Stat->{SelectFormat} = $LayoutObject->BuildSelection(
Data => $AvailableFormats,
Name => 'Format',
Class => 'Modernize Validate_Required' . ( $Errors{FormatServerError} ? ' ServerError' : '' ),
Multiple => 1,
Size => 5,
SelectedID => $GetParam{Format} // $Stat->{Format} || $DefaultSelectedFormat,
);
# create multiselectboxes 'permission'
my %Permission = (
Data => { $Kernel::OM->Get('Kernel::System::Group')->GroupList( Valid => 1 ) },
Name => 'Permission',
Class => 'Modernize Validate_Required' . ( $Errors{PermissionServerError} ? ' ServerError' : '' ),
Multiple => 1,
Size => 5,
Translation => 0,
);
if ( $GetParam{Permission} // $Stat->{Permission} ) {
$Permission{SelectedID} = $GetParam{Permission} // $Stat->{Permission};
}
else {
$Permission{SelectedValue} = $ConfigObject->Get('Stats::DefaultSelectedPermissions');
}
$Stat->{SelectPermission} = $LayoutObject->BuildSelection(%Permission);
# provide the timezone field only for dynamic statistics
if (
( $Stat->{StatType} && $Stat->{StatType} eq 'dynamic' )
|| ( $Frontend{StatType} && $Frontend{StatType} eq 'dynamic' )
)
{
my $SelectedTimeZone = $Self->_GetValidTimeZone( TimeZone => $GetParam{TimeZone} ) // $Stat->{TimeZone};
if ( !defined $SelectedTimeZone ) {
my %UserPreferences = $Kernel::OM->Get('Kernel::System::User')->GetPreferences(
UserID => $Param{UserID}
);
$SelectedTimeZone = $Self->_GetValidTimeZone( TimeZone => $UserPreferences{UserTimeZone} )
// Kernel::System::DateTime->OTRSTimeZoneGet();
}
my %TimeZoneBuildSelection = $Self->_TimeZoneBuildSelection();
$Stat->{SelectTimeZone} = $LayoutObject->BuildSelection(
%TimeZoneBuildSelection,
Name => 'TimeZone',
Class => 'Modernize ' . ( $Errors{TimeZoneServerError} ? ' ServerError' : '' ),
SelectedID => $SelectedTimeZone,
);
}
my $Output = $LayoutObject->Output(
TemplateFile => 'Statistics/GeneralSpecificationsWidget',
Data => {
%Frontend,
%{$Stat},
%GetParam,
%Errors,
},
);
return $Output;
}
sub XAxisWidget {
my ( $Self, %Param ) = @_;
my $Stat = $Param{Stat};
# get needed objects
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# if only one value is available select this value
if ( !$Stat->{UseAsXvalue}[0]{Selected} && scalar( @{ $Stat->{UseAsXvalue} } ) == 1 ) {
$Stat->{UseAsXvalue}[0]{Selected} = 1;
$Stat->{UseAsXvalue}[0]{Fixed} = 1;
}
my @XAxisElements;
for my $ObjectAttribute ( @{ $Stat->{UseAsXvalue} } ) {
my %BlockData;
$BlockData{Fixed} = 'checked="checked"';
$BlockData{Checked} = '';
$BlockData{Block} = $ObjectAttribute->{Block};
# things which should be done if this attribute is selected
if ( $ObjectAttribute->{Selected} ) {
$BlockData{Checked} = 'checked="checked"';
if ( !$ObjectAttribute->{Fixed} ) {
$BlockData{Fixed} = '';
}
}
if ( $ObjectAttribute->{Block} eq 'SelectField' || $ObjectAttribute->{Block} eq 'MultiSelectField' ) {
my $DFTreeClass = ( $ObjectAttribute->{ShowAsTree} && $ObjectAttribute->{IsDynamicField} )
? 'DynamicFieldWithTreeView' : '';
$BlockData{SelectField} = $LayoutObject->BuildSelection(
Data => $ObjectAttribute->{Values},
Name => 'XAxis' . $ObjectAttribute->{Element},
Multiple => 1,
Size => 5,
Class => "Modernize $DFTreeClass",
SelectedID => $ObjectAttribute->{SelectedValues},
Translation => $ObjectAttribute->{Translation},
TreeView => $ObjectAttribute->{TreeView} || 0,
Sort => scalar $ObjectAttribute->{Sort},
SortIndividual => scalar $ObjectAttribute->{SortIndividual},
);
if ( $ObjectAttribute->{ShowAsTree} && $ObjectAttribute->{IsDynamicField} ) {
my $TreeSelectionMessage = $LayoutObject->{LanguageObject}->Translate("Show Tree Selection");
$BlockData{SelectField}
.= ' '
. $TreeSelectionMessage . '';
}
}
$BlockData{Name} = $ObjectAttribute->{Name};
$BlockData{Element} = 'XAxis' . $ObjectAttribute->{Element};
# show the attribute block
$LayoutObject->Block(
Name => 'Attribute',
Data => \%BlockData,
);
if ( $ObjectAttribute->{Block} eq 'Time' ) {
my %TimeData = $Self->_TimeOutput(
Output => 'Edit',
Use => 'UseAsXvalue',
%{$ObjectAttribute},
Element => $BlockData{Element},
);
%BlockData = ( %BlockData, %TimeData );
}
my $Block = $ObjectAttribute->{Block};
if ( $Block eq 'SelectField' ) {
$Block = 'MultiSelectField';
}
# store data, which will be sent to JS
push @XAxisElements, $BlockData{Element} if $BlockData{Checked};
# show the input element
$LayoutObject->Block(
Name => $Block,
Data => \%BlockData,
);
}
# send data to JS
$LayoutObject->AddJSData(
Key => 'XAxisElements',
Value => \@XAxisElements,
);
my $Output = $LayoutObject->Output(
TemplateFile => 'Statistics/XAxisWidget',
Data => {
%{$Stat},
},
);
return $Output;
}
sub YAxisWidget {
my ( $Self, %Param ) = @_;
my $Stat = $Param{Stat};
# get needed objects
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my @YAxisElements;
OBJECTATTRIBUTE:
for my $ObjectAttribute ( @{ $Stat->{UseAsValueSeries} } ) {
my %BlockData;
$BlockData{Fixed} = 'checked="checked"';
$BlockData{Checked} = '';
$BlockData{Block} = $ObjectAttribute->{Block};
if ( $ObjectAttribute->{Selected} ) {
$BlockData{Checked} = 'checked="checked"';
if ( !$ObjectAttribute->{Fixed} ) {
$BlockData{Fixed} = '';
}
}
if ( $ObjectAttribute->{Block} eq 'SelectField' || $ObjectAttribute->{Block} eq 'MultiSelectField' ) {
my $DFTreeClass = ( $ObjectAttribute->{ShowAsTree} && $ObjectAttribute->{IsDynamicField} )
? 'DynamicFieldWithTreeView' : '';
$BlockData{SelectField} = $LayoutObject->BuildSelection(
Data => $ObjectAttribute->{Values},
Name => 'YAxis' . $ObjectAttribute->{Element},
Multiple => 1,
Size => 5,
Class => "Modernize $DFTreeClass",
SelectedID => $ObjectAttribute->{SelectedValues},
Translation => $ObjectAttribute->{Translation},
TreeView => $ObjectAttribute->{TreeView} || 0,
Sort => scalar $ObjectAttribute->{Sort},
SortIndividual => scalar $ObjectAttribute->{SortIndividual},
);
if ( $ObjectAttribute->{ShowAsTree} && $ObjectAttribute->{IsDynamicField} ) {
my $TreeSelectionMessage = $LayoutObject->{LanguageObject}->Translate("Show Tree Selection");
$BlockData{SelectField}
.= ' '
. $TreeSelectionMessage . '';
}
}
$BlockData{Name} = $ObjectAttribute->{Name};
$BlockData{Element} = 'YAxis' . $ObjectAttribute->{Element};
# show the attribute block
$LayoutObject->Block(
Name => 'Attribute',
Data => \%BlockData,
);
if ( $ObjectAttribute->{Block} eq 'Time' ) {
# get the selected x axis time scale value
my $SelectedXAxisTimeScaleValue = $Self->_GetSelectedXAxisTimeScaleValue( Stat => $Stat );
my %TimeData = $Self->_TimeOutput(
Output => 'Edit',
Use => 'UseAsValueSeries',
%{$ObjectAttribute},
Element => $BlockData{Element},
SelectedXAxisValue => $SelectedXAxisTimeScaleValue,
);
%BlockData = ( %BlockData, %TimeData );
}
my $Block = $ObjectAttribute->{Block};
if ( $Block eq 'SelectField' ) {
$Block = 'MultiSelectField';
}
# store data, which will be sent to JS
push @YAxisElements, $BlockData{Element} if $BlockData{Checked};
# show the input element
$LayoutObject->Block(
Name => $Block,
Data => \%BlockData,
);
}
# send data to JS
$LayoutObject->AddJSData(
Key => 'YAxisElements',
Value => \@YAxisElements,
);
my $Output = $LayoutObject->Output(
TemplateFile => 'Statistics/YAxisWidget',
Data => {
%{$Stat},
},
);
return $Output;
}
sub RestrictionsWidget {
my ( $Self, %Param ) = @_;
my $Stat = $Param{Stat};
# get needed objects
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my @RestrictionElements;
for my $ObjectAttribute ( @{ $Stat->{UseAsRestriction} } ) {
my %BlockData;
$BlockData{Fixed} = 'checked="checked"';
$BlockData{Checked} = '';
$BlockData{Block} = $ObjectAttribute->{Block};
$BlockData{CSSClass} = $ObjectAttribute->{CSSClass};
$BlockData{HTMLDataAttributes} = $ObjectAttribute->{HTMLDataAttributes};
if ( $ObjectAttribute->{Selected} ) {
$BlockData{Checked} = 'checked="checked"';
if ( !$ObjectAttribute->{Fixed} ) {
$BlockData{Fixed} = "";
}
}
if ( $ObjectAttribute->{SelectedValues} ) {
$BlockData{SelectedValue} = $ObjectAttribute->{SelectedValues}[0];
}
else {
$BlockData{SelectedValue} = '';
$ObjectAttribute->{SelectedValues} = undef;
}
if (
$ObjectAttribute->{Block} eq 'MultiSelectField'
|| $ObjectAttribute->{Block} eq 'SelectField'
)
{
my $DFTreeClass = ( $ObjectAttribute->{ShowAsTree} && $ObjectAttribute->{IsDynamicField} )
? 'DynamicFieldWithTreeView' : '';
$BlockData{SelectField} = $LayoutObject->BuildSelection(
Data => $ObjectAttribute->{Values},
Name => 'Restrictions' . $ObjectAttribute->{Element},
Multiple => 1,
Size => 5,
Class => "Modernize $DFTreeClass",
SelectedID => $ObjectAttribute->{SelectedValues},
Translation => $ObjectAttribute->{Translation},
TreeView => $ObjectAttribute->{TreeView} || 0,
Sort => scalar $ObjectAttribute->{Sort},
SortIndividual => scalar $ObjectAttribute->{SortIndividual},
);
if ( $ObjectAttribute->{ShowAsTree} && $ObjectAttribute->{IsDynamicField} ) {
my $TreeSelectionMessage = $LayoutObject->{LanguageObject}->Translate("Show Tree Selection");
$BlockData{SelectField}
.= ' '
. $TreeSelectionMessage . '';
}
}
$BlockData{Element} = 'Restrictions' . $ObjectAttribute->{Element};
$BlockData{Name} = $ObjectAttribute->{Name};
# show the attribute block
$LayoutObject->Block(
Name => 'Attribute',
Data => \%BlockData,
);
if ( $ObjectAttribute->{Block} eq 'Time' ) {
my %TimeData = $Self->_TimeOutput(
Output => 'Edit',
Use => 'UseAsRestriction',
%{$ObjectAttribute},
Element => $BlockData{Element},
);
%BlockData = ( %BlockData, %TimeData );
}
# store data, which will be sent to JS
push @RestrictionElements, $BlockData{Element} if $BlockData{Checked};
# show the input element
$LayoutObject->Block(
Name => $ObjectAttribute->{Block},
Data => \%BlockData,
);
}
# send data to JS
$LayoutObject->AddJSData(
Key => 'RestrictionElements',
Value => \@RestrictionElements,
);
my $Output = $LayoutObject->Output(
TemplateFile => 'Statistics/RestrictionsWidget',
Data => {
%{$Stat},
},
);
return $Output;
}
sub PreviewWidget {
my ( $Self, %Param ) = @_;
my $Stat = $Param{Stat};
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my %StatsConfigurationErrors;
$Self->StatsConfigurationValidate(
Stat => $Stat,
Errors => \%StatsConfigurationErrors,
);
my %Frontend;
if ( !%StatsConfigurationErrors ) {
$Frontend{PreviewResult} = $Kernel::OM->Get('Kernel::System::Stats')->StatsRun(
StatID => $Stat->{StatID},
GetParam => $Stat,
Preview => 1,
UserID => $Param{UserID},
);
}
# send data to JS
$LayoutObject->AddJSData(
Key => 'PreviewResult',
Value => $Frontend{PreviewResult},
);
my $Output = $LayoutObject->Output(
TemplateFile => 'Statistics/PreviewWidget',
Data => {
%{$Stat},
%Frontend,
StatsConfigurationErrors => \%StatsConfigurationErrors,
},
);
return $Output;
}
sub StatsParamsGet {
my ( $Self, %Param ) = @_;
my $Stat = $Param{Stat};
my $HasUserGetParam = ref $Param{UserGetParam} eq 'HASH';
my %UserGetParam = %{ $Param{UserGetParam} // {} };
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
my $LocalGetParam = sub {
my (%Param) = @_;
my $Param = $Param{Param};
return $HasUserGetParam ? $UserGetParam{$Param} : $ParamObject->GetParam( Param => $Param );
};
my $LocalGetArray = sub {
my (%Param) = @_;
my $Param = $Param{Param};
if ($HasUserGetParam) {
if ( $UserGetParam{$Param} && ref $UserGetParam{$Param} eq 'ARRAY' ) {
return @{ $UserGetParam{$Param} };
}
return;
}
return $ParamObject->GetArray( Param => $Param );
};
my ( %GetParam, @Errors );
# get the time zone param
if ( length $LocalGetParam->( Param => 'TimeZone' ) ) {
$GetParam{TimeZone} = $Self->_GetValidTimeZone( TimeZone => $LocalGetParam->( Param => 'TimeZone' ) )
// $Stat->{TimeZone};
}
# get ExchangeAxis param
if ( length $LocalGetParam->( Param => 'ExchangeAxis' ) ) {
$GetParam{ExchangeAxis} = $LocalGetParam->( Param => 'ExchangeAxis' ) // $Stat->{ExchangeAxis};
}
#
# Static statistics
#
if ( $Stat->{StatType} eq 'static' ) {
my $CurSysDTDetails = $Kernel::OM->Create('Kernel::System::DateTime')->Get();
$GetParam{Year} = $CurSysDTDetails->{Year};
$GetParam{Month} = $CurSysDTDetails->{Month};
$GetParam{Day} = $CurSysDTDetails->{Day};
my $Params = $Kernel::OM->Get('Kernel::System::Stats')->GetParams(
StatID => $Stat->{StatID},
);
PARAMITEM:
for my $ParamItem ( @{$Params} ) {
if ( $ParamItem->{Multiple} ) {
$GetParam{ $ParamItem->{Name} } = [ $LocalGetArray->( Param => $ParamItem->{Name} ) ];
next PARAMITEM;
}
$GetParam{ $ParamItem->{Name} } = $LocalGetParam->( Param => $ParamItem->{Name} );
}
}
#
# Dynamic statistics
#
else {
my $TimePeriod = 0;
my $TimeUpcomingPeriod = 0;
for my $Use (qw(UseAsXvalue UseAsValueSeries UseAsRestriction)) {
$Stat->{$Use} ||= [];
my @Array = @{ $Stat->{$Use} };
my $Counter = 0;
ELEMENT:
for my $Element (@Array) {
next ELEMENT if !$Element->{Selected};
my $ElementName = $Use . $Element->{'Element'};
if ( !$Element->{Fixed} ) {
if ( $LocalGetArray->( Param => $ElementName ) ) {
my @SelectedValues = $LocalGetArray->(
Param => $ElementName
);
$Element->{SelectedValues} = \@SelectedValues;
}
elsif ( $LocalGetParam->( Param => $ElementName ) ) {
my $SelectedValue = $LocalGetParam->(
Param => $ElementName
);
$Element->{SelectedValues} = [$SelectedValue];
}
# set the first value for a single select field, if no selected value is given
if (
$Element->{Block} eq 'SelectField'
&& (
!IsArrayRefWithData( $Element->{SelectedValues} )
|| scalar @{ $Element->{SelectedValues} } > 1
)
)
{
my @Values = sort keys %{ $Element->{Values} };
if (
IsArrayRefWithData( $Element->{SelectedValues} )
&& scalar @{ $Element->{SelectedValues} } > 1
)
{
@Values = @{ $Element->{SelectedValues} };
}
$Element->{SelectedValues} = [ $Values[0] ];
}
if ( $Element->{Block} eq 'InputField' ) {
# Show warning if restrictions contain stop words within ticket search.
my %StopWordFields = $Self->_StopWordFieldsGet();
if ( $StopWordFields{ $Element->{Element} } ) {
my $ErrorMessage = $Self->_StopWordErrorCheck(
$Element->{Element} => $Element->{SelectedValues}[0],
);
if ($ErrorMessage) {
push @Errors, "$Element->{Name}: $ErrorMessage";
}
}
}
if ( $Element->{Block} eq 'Time' ) {
my %Time;
# Check if it is an absolute time period
if ( $Element->{TimeStart} ) {
if ( $LocalGetParam->( Param => $ElementName . 'StartYear' ) ) {
for my $Limit (qw(Start Stop)) {
for my $Unit (qw(Year Month Day Hour Minute Second)) {
if ( defined( $LocalGetParam->( Param => "$ElementName$Limit$Unit" ) ) ) {
$Time{ $Limit . $Unit } = $LocalGetParam->(
Param => $ElementName . "$Limit$Unit",
);
}
}
if ( !defined( $Time{ $Limit . 'Hour' } ) ) {
if ( $Limit eq 'Start' ) {
$Time{StartHour} = 0;
$Time{StartMinute} = 0;
$Time{StartSecond} = 0;
}
elsif ( $Limit eq 'Stop' ) {
$Time{StopHour} = 23;
$Time{StopMinute} = 59;
$Time{StopSecond} = 59;
}
}
elsif ( !defined( $Time{ $Limit . 'Second' } ) ) {
if ( $Limit eq 'Start' ) {
$Time{StartSecond} = 0;
}
elsif ( $Limit eq 'Stop' ) {
$Time{StopSecond} = 59;
}
}
$Time{"Time$Limit"} = sprintf(
"%04d-%02d-%02d %02d:%02d:%02d",
$Time{ $Limit . 'Year' },
$Time{ $Limit . 'Month' },
$Time{ $Limit . 'Day' },
$Time{ $Limit . 'Hour' },
$Time{ $Limit . 'Minute' },
$Time{ $Limit . 'Second' },
);
}
$Element->{TimeStart} = $Time{TimeStart};
$Element->{TimeStop} = $Time{TimeStop};
if ( $Use eq 'UseAsXvalue' ) {
my $TimeStartEpoch = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $Element->{TimeStart},
},
)->ToEpoch();
my $TimeStopEpoch = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $Element->{TimeStop},
},
)->ToEpoch();
$TimePeriod = $TimeStopEpoch - $TimeStartEpoch;
}
}
}
else {
if ( $Use ne 'UseAsValueSeries' ) {
$Time{TimeRelativeUnit}
= $LocalGetParam->( Param => $ElementName . 'TimeRelativeUnit' );
$Time{TimeRelativeCount}
= $LocalGetParam->( Param => $ElementName . 'TimeRelativeCount' );
$Time{TimeRelativeUpcomingCount}
= $LocalGetParam->( Param => $ElementName . 'TimeRelativeUpcomingCount' );
# Use Values of the stat as fallback
$Time{TimeRelativeCount} //= $Element->{TimeRelativeCount};
$Time{TimeRelativeUpcomingCount} //= $Element->{TimeRelativeUpcomingCount};
$Time{TimeRelativeUnit} ||= $Element->{TimeRelativeUnit};
if ( !$Time{TimeRelativeCount} && !$Time{TimeRelativeUpcomingCount} ) {
push @Errors,
Translatable(
'No past complete or the current+upcoming complete relative time value selected.'
);
}
if ( $Use eq 'UseAsXvalue' ) {
$TimePeriod = $Time{TimeRelativeCount} * $Self->_TimeInSeconds(
TimeUnit => $Time{TimeRelativeUnit},
);
$TimeUpcomingPeriod = $Time{TimeRelativeUpcomingCount} * $Self->_TimeInSeconds(
TimeUnit => $Time{TimeRelativeUnit},
);
}
$Element->{TimeRelativeCount} = $Time{TimeRelativeCount};
$Element->{TimeRelativeUpcomingCount} = $Time{TimeRelativeUpcomingCount};
$Element->{TimeRelativeUnit} = $Time{TimeRelativeUnit};
}
}
if ( $Use ne 'UseAsRestriction' ) {
if ( $LocalGetParam->( Param => $ElementName ) ) {
$Element->{SelectedValues} = [ $LocalGetParam->( Param => $ElementName ) ];
}
if ( $LocalGetParam->( Param => $ElementName . 'TimeScaleCount' ) ) {
$Time{TimeScaleCount} = $LocalGetParam->( Param => $ElementName . 'TimeScaleCount' );
# Use Values of the stat as fallback
$Time{TimeScaleCount} ||= $Element->{TimeScaleCount};
$Element->{TimeScaleCount} = $Time{TimeScaleCount};
}
}
}
}
$GetParam{$Use}->[$Counter] = $Element;
$Counter++;
}
if ( ref $GetParam{$Use} ne 'ARRAY' ) {
$GetParam{$Use} = [];
}
}
# check if the timeperiod is too big or the time scale too small
if (
( $GetParam{UseAsXvalue}[0]{Block} && $GetParam{UseAsXvalue}[0]{Block} eq 'Time' )
&& (
!$GetParam{UseAsValueSeries}[0]
|| ( $GetParam{UseAsValueSeries}[0] && $GetParam{UseAsValueSeries}[0]{Block} ne 'Time' )
)
)
{
my $ScalePeriod = $Self->_TimeInSeconds(
TimeUnit => $GetParam{UseAsXvalue}[0]{SelectedValues}[0]
);
# integrate this functionality in the completenesscheck
my $MaxAttr = $ConfigObject->Get('Stats::MaxXaxisAttributes') || 1000;
if (
( $TimePeriod + $TimeUpcomingPeriod ) / ( $ScalePeriod * $GetParam{UseAsXvalue}[0]{TimeScaleCount} )
> $MaxAttr
)
{
push @Errors, Translatable('The selected time period is larger than the allowed time period.');
}
}
if ( $GetParam{UseAsValueSeries}[0]{Block} && $GetParam{UseAsValueSeries}[0]{Block} eq 'Time' ) {
my $TimeScale = $Self->_TimeScale(
SelectedXAxisValue => $GetParam{UseAsXvalue}[0]{SelectedValues}[0],
);
if ( !IsHashRefWithData($TimeScale) ) {
push @Errors,
Translatable(
'No time scale value available for the current selected time scale value on the X axis.'
);
}
}
}
if (@Errors) {
die \@Errors;
}
return %GetParam;
}
sub StatsResultRender {
my ( $Self, %Param ) = @_;
my @StatArray = @{ $Param{StatArray} // [] };
my $Stat = $Param{Stat};
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $TitleArrayRef = shift @StatArray;
my $Title = $TitleArrayRef->[0];
my $HeadArrayRef = shift @StatArray;
# Generate Filename
my $Filename = $Kernel::OM->Get('Kernel::System::Stats')->StringAndTimestamp2Filename(
String => $Stat->{Title} . ' Created',
TimeZone => $Param{TimeZone},
);
# get CSV object
my $CSVObject = $Kernel::OM->Get('Kernel::System::CSV');
# generate D3 output
if ( $Param{Format} =~ m{^D3} ) {
# if array = empty
if ( !@StatArray ) {
push @StatArray, [ ' ', 0 ];
}
my $Output = $LayoutObject->Header(
Value => $Title,
Type => 'Small',
);
# send data to JS
$LayoutObject->AddJSData(
Key => 'D3Data',
Value => {
RawData => [
[$Title],
$HeadArrayRef,
@StatArray,
],
Format => $Param{Format},
}
);
$Output .= $LayoutObject->Output(
Data => {
%{$Stat},
},
TemplateFile => 'Statistics/StatsResultRender/D3',
);
$Output .= $LayoutObject->Footer(
Type => 'Small',
);
return $Output;
}
# generate csv output
if ( $Param{Format} eq 'CSV' ) {
# get Separator from language file
my $UserCSVSeparator = $LayoutObject->{LanguageObject}->{Separator};
if ( $ConfigObject->Get('PreferencesGroups')->{CSVSeparator}->{Active} ) {
my %UserData = $Kernel::OM->Get('Kernel::System::User')->GetUserData(
UserID => $Param{UserID}
);
$UserCSVSeparator = $UserData{UserCSVSeparator} if $UserData{UserCSVSeparator};
}
my $Output = $CSVObject->Array2CSV(
Head => $HeadArrayRef,
Data => \@StatArray,
Separator => $UserCSVSeparator,
);
return $LayoutObject->Attachment(
Filename => $Filename . '.csv',
ContentType => "text/csv",
Content => $Output,
);
}
# generate excel output
elsif ( $Param{Format} eq 'Excel' ) {
my $Output = $CSVObject->Array2CSV(
Head => $HeadArrayRef,
Data => \@StatArray,
Format => 'Excel',
);
return $LayoutObject->Attachment(
Filename => $Filename . '.xlsx',
ContentType => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
Content => $Output,
);
}
# pdf or html output
elsif ( $Param{Format} eq 'Print' ) {
my $PDFString = $Kernel::OM->Get('Kernel::Output::PDF::Statistics')->GeneratePDF(
Stat => $Stat,
Title => $Title,
HeadArrayRef => $HeadArrayRef,
StatArray => \@StatArray,
TimeZone => $Param{TimeZone},
UserID => $Param{UserID},
);
return $LayoutObject->Attachment(
Filename => $Filename . '.pdf',
ContentType => 'application/pdf',
Content => $PDFString,
Type => 'inline',
);
}
}
=head2 StatsConfigurationValidate()
my $StatCorrectlyConfigured = $StatsViewObject->StatsConfigurationValidate(
StatData => \%StatData,
Errors => \%Errors, # Hash to be populated with errors, if any
);
=cut
sub StatsConfigurationValidate {
my ( $Self, %Param ) = @_;
for my $Needed (qw(Stat Errors)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed"
);
return;
}
}
my %GeneralSpecificationFieldErrors;
my ( %XAxisFieldErrors, @XAxisGeneralErrors );
my ( %YAxisFieldErrors, @YAxisGeneralErrors );
my (%RestrictionsFieldErrors);
my %Stat = %{ $Param{Stat} };
# Specification
{
KEY:
for my $Field (qw(Title Description StatType Permission Format ObjectModule)) {
if ( !$Stat{$Field} ) {
$GeneralSpecificationFieldErrors{$Field} = Translatable('This field is required.');
}
}
if ( $Stat{StatType} && $Stat{StatType} eq 'static' && !$Stat{File} ) {
$GeneralSpecificationFieldErrors{File} = Translatable('This field is required.');
}
if ( $Stat{StatType} && $Stat{StatType} eq 'dynamic' && !$Stat{Object} ) {
$GeneralSpecificationFieldErrors{Object} = Translatable('This field is required.');
}
}
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
if ( $Stat{StatType} eq 'dynamic' ) {
# save the selected x axis time scale value for some checks for the y axis
my $SelectedXAxisTimeScaleValue;
# X Axis
{
my $Flag = 0;
XVALUE:
for my $Xvalue ( @{ $Stat{UseAsXvalue} } ) {
next XVALUE if !$Xvalue->{Selected};
if ( $Xvalue->{Block} eq 'Time' ) {
if ( $Xvalue->{TimeStart} && $Xvalue->{TimeStop} ) {
my $TimeStartDTObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $Xvalue->{TimeStart},
},
);
my $TimeStopDTObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $Xvalue->{TimeStop},
},
);
if ( !$TimeStartDTObject || !$TimeStopDTObject ) {
$XAxisFieldErrors{ $Xvalue->{Element} } = Translatable('The selected date is not valid.');
}
elsif ( $TimeStartDTObject > $TimeStopDTObject ) {
$XAxisFieldErrors{ $Xvalue->{Element} }
= Translatable('The selected end time is before the start time.');
}
}
elsif (
!$Xvalue->{TimeRelativeUnit}
|| ( !$Xvalue->{TimeRelativeCount} && !$Xvalue->{TimeRelativeUpcomingCount} )
)
{
$XAxisFieldErrors{ $Xvalue->{Element} }
= Translatable('There is something wrong with your time selection.');
}
if ( !$Xvalue->{SelectedValues}[0] ) {
$XAxisFieldErrors{ $Xvalue->{Element} }
= Translatable('There is something wrong with your time selection.');
}
elsif ( $Xvalue->{Fixed} && $#{ $Xvalue->{SelectedValues} } > 0 ) {
$XAxisFieldErrors{ $Xvalue->{Element} }
= Translatable('There is something wrong with your time selection.');
}
else {
$SelectedXAxisTimeScaleValue = $Xvalue->{SelectedValues}[0];
}
}
elsif ( $Xvalue->{Block} eq 'SelectField' ) {
if ( $Xvalue->{Fixed} && $#{ $Xvalue->{SelectedValues} } > 0 ) {
$XAxisFieldErrors{ $Xvalue->{Element} } = Translatable(
'Please select only one element or allow modification at stat generation time.'
);
}
elsif ( $Xvalue->{Fixed} && !$Xvalue->{SelectedValues}[0] ) {
$XAxisFieldErrors{ $Xvalue->{Element} } = Translatable(
'Please select at least one value of this field or allow modification at stat generation time.'
);
}
}
$Flag = 1;
last XVALUE;
}
if ( !$Flag ) {
push @XAxisGeneralErrors, Translatable('Please select one element for the X-axis.');
}
}
# Y Axis
{
my $Counter = 0;
my $TimeUsed = 0;
VALUESERIES:
for my $ValueSeries ( @{ $Stat{UseAsValueSeries} } ) {
next VALUESERIES if !$ValueSeries->{Selected};
if ( $ValueSeries->{Block} eq 'Time' || $ValueSeries->{Block} eq 'TimeExtended' ) {
if ( $ValueSeries->{Fixed} && $#{ $ValueSeries->{SelectedValues} } > 0 ) {
$YAxisFieldErrors{ $ValueSeries->{Element} }
= Translatable('There is something wrong with your time selection.');
}
elsif ( !$ValueSeries->{SelectedValues}[0] ) {
$YAxisFieldErrors{ $ValueSeries->{Element} }
= Translatable('There is something wrong with your time selection.');
}
my $TimeScale = $Self->_TimeScale(
SelectedXAxisValue => $SelectedXAxisTimeScaleValue,
);
if ( !IsHashRefWithData($TimeScale) ) {
$YAxisFieldErrors{ $ValueSeries->{Element} } = Translatable(
'No time scale value available for the current selected time scale value on the X axis.'
);
}
$TimeUsed++;
}
elsif ( $ValueSeries->{Block} eq 'SelectField' ) {
if ( $ValueSeries->{Fixed} && $#{ $ValueSeries->{SelectedValues} } > 0 ) {
$YAxisFieldErrors{ $ValueSeries->{Element} } = Translatable(
'Please select only one element or allow modification at stat generation time.'
);
}
elsif ( $ValueSeries->{Fixed} && !$ValueSeries->{SelectedValues}[0] ) {
$YAxisFieldErrors{ $ValueSeries->{Element} } = Translatable(
'Please select at least one value of this field or allow modification at stat generation time.'
);
}
}
$Counter++;
}
if ( $Counter > 1 && $TimeUsed ) {
push @YAxisGeneralErrors, Translatable('You can only use one time element for the Y axis.');
}
elsif ( $Counter > 2 ) {
push @YAxisGeneralErrors, Translatable('You can only use one or two elements for the Y axis.');
}
}
# Restrictions
{
RESTRICTION:
for my $Restriction ( @{ $Stat{UseAsRestriction} } ) {
next RESTRICTION if !$Restriction->{Selected};
if ( $Restriction->{Block} eq 'SelectField' ) {
if ( $Restriction->{Fixed} && $#{ $Restriction->{SelectedValues} } > 0 ) {
$RestrictionsFieldErrors{ $Restriction->{Element} } = Translatable(
'Please select only one element or allow modification at stat generation time.'
);
}
elsif ( !$Restriction->{SelectedValues}[0] ) {
$RestrictionsFieldErrors{ $Restriction->{Element} }
= Translatable('Please select at least one value of this field.');
}
}
elsif ( $Restriction->{Block} eq 'InputField' ) {
if ( !$Restriction->{SelectedValues}[0] && $Restriction->{Fixed} ) {
$RestrictionsFieldErrors{ $Restriction->{Element} }
= Translatable('Please provide a value or allow modification at stat generation time.');
last RESTRICTION;
}
# Show warning if restrictions contain stop words within ticket search.
my %StopWordFields = $Self->_StopWordFieldsGet();
if ( $StopWordFields{ $Restriction->{Element} } ) {
my $ErrorMessage = $Self->_StopWordErrorCheck(
$Restriction->{Element} => $Restriction->{SelectedValues}[0],
);
if ($ErrorMessage) {
$RestrictionsFieldErrors{ $Restriction->{Element} } = $ErrorMessage;
}
}
}
elsif ( $Restriction->{Block} eq 'Time' || $Restriction->{Block} eq 'TimeExtended' ) {
if ( $Restriction->{TimeStart} && $Restriction->{TimeStop} ) {
my $TimeStartDTObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $Restriction->{TimeStart},
},
);
my $TimeStopDTObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $Restriction->{TimeStop},
},
);
if ( !$TimeStartDTObject || !$TimeStopDTObject ) {
$RestrictionsFieldErrors{ $Restriction->{Element} }
= Translatable('The selected date is not valid.');
}
elsif ( $TimeStartDTObject > $TimeStopDTObject ) {
$RestrictionsFieldErrors{ $Restriction->{Element} }
= Translatable('The selected end time is before the start time.');
}
}
elsif (
!$Restriction->{TimeRelativeUnit}
|| ( !$Restriction->{TimeRelativeCount} && !$Restriction->{TimeRelativeUpcomingCount} )
)
{
$RestrictionsFieldErrors{ $Restriction->{Element} }
= Translatable('There is something wrong with your time selection.');
}
}
}
}
# Check if the timeperiod is too big or the time scale too small. Also execute this check for
# non-fixed values because it is used in preview and cron stats generation mode.
{
XVALUE:
for my $Xvalue ( @{ $Stat{UseAsXvalue} } ) {
last XVALUE if defined $XAxisFieldErrors{ $Xvalue->{Element} };
next XVALUE if !( $Xvalue->{Selected} && $Xvalue->{Block} eq 'Time' );
my $Flag = 1;
VALUESERIES:
for my $ValueSeries ( @{ $Stat{UseAsValueSeries} } ) {
if ( $ValueSeries->{Selected} && $ValueSeries->{Block} eq 'Time' ) {
$Flag = 0;
last VALUESERIES;
}
}
last XVALUE if !$Flag;
my $ScalePeriod = 0;
my $TimePeriod = 0;
my $TimeUpcomingPeriod = 0;
my $Count = $Xvalue->{TimeScaleCount} ? $Xvalue->{TimeScaleCount} : 1;
$ScalePeriod = $Self->_TimeInSeconds(
TimeUnit => $Xvalue->{SelectedValues}[0],
);
if ( !$ScalePeriod ) {
$XAxisFieldErrors{ $Xvalue->{Element} } = Translatable('Please select a time scale.');
last XVALUE;
}
if ( $Xvalue->{TimeStop} && $Xvalue->{TimeStart} ) {
my $TimeStartEpoch = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $Xvalue->{TimeStart},
},
)->ToEpoch();
my $TimeStopEpoch = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $Xvalue->{TimeStop},
},
)->ToEpoch();
$TimePeriod = $TimeStopEpoch - $TimeStartEpoch;
}
else {
$TimePeriod = $Xvalue->{TimeRelativeCount} * $Self->_TimeInSeconds(
TimeUnit => $Xvalue->{TimeRelativeUnit},
);
$TimeUpcomingPeriod = $Xvalue->{TimeRelativeUpcomingCount} * $Self->_TimeInSeconds(
TimeUnit => $Xvalue->{TimeRelativeUnit},
);
}
my $MaxAttr = $ConfigObject->Get('Stats::MaxXaxisAttributes') || 1000;
if ( ( $TimePeriod + $TimeUpcomingPeriod ) / ( $ScalePeriod * $Count ) > $MaxAttr ) {
$XAxisFieldErrors{ $Xvalue->{Element} }
= Translatable('Your reporting time interval is too small, please use a larger time scale.');
}
last XVALUE;
}
}
}
if (
!%GeneralSpecificationFieldErrors
&& !%XAxisFieldErrors
&& !@XAxisGeneralErrors
&& !%YAxisFieldErrors
&& !@YAxisGeneralErrors
&& !%RestrictionsFieldErrors
)
{
return 1;
}
%{ $Param{Errors} } = (
GeneralSpecificationFieldErrors => \%GeneralSpecificationFieldErrors,
XAxisFieldErrors => \%XAxisFieldErrors,
XAxisGeneralErrors => \@XAxisGeneralErrors,
YAxisFieldErrors => \%YAxisFieldErrors,
YAxisGeneralErrors => \@YAxisGeneralErrors,
RestrictionsFieldErrors => \%RestrictionsFieldErrors,
);
return;
}
sub _TimeOutput {
my ( $Self, %Param ) = @_;
# diffrent output types
my %AllowedOutput = (
Edit => 1,
View => 1,
);
# check if the output type is given and allowed
if ( !$Param{Output} || !$AllowedOutput{ $Param{Output} } ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => "error",
Message => '_TimeOutput: Need allowed output type!',
);
}
# get layout object
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my %TimeOutput;
my %TimeScaleBuildSelection = $Self->_TimeScaleBuildSelection();
my $Element = $Param{Element};
my $ElementID = $Element;
# add the StatID to the ElementID for the view output
if ( $Param{Output} eq 'View' && $Param{StatID} ) {
$ElementID .= '-' . $Param{StatID} . '-' . $Param{OutputCounter};
}
if ( $Param{Use} ne 'UseAsValueSeries' ) {
if ( $Param{Output} eq 'Edit' || ( $Param{TimeStart} && $Param{TimeStop} ) ) {
# check if need params are available
if ( !$Param{TimePeriodFormat} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => "error",
Message => '_TimeOutput: Need TimePeriodFormat!',
);
}
# get time
my $CurSysDTDetails = $Kernel::OM->Create('Kernel::System::DateTime')->Get();
my $Year = $CurSysDTDetails->{Year};
my %TimeConfig;
# default time configuration
$TimeConfig{Format} = $Param{TimePeriodFormat};
$TimeConfig{OverrideTimeZone} = 1;
$TimeConfig{ $Element . 'StartYear' } = $Year - 1;
$TimeConfig{ $Element . 'StartMonth' } = 1;
$TimeConfig{ $Element . 'StartDay' } = 1;
$TimeConfig{ $Element . 'StartHour' } = 0;
$TimeConfig{ $Element . 'StartMinute' } = 0;
$TimeConfig{ $Element . 'StartSecond' } = 1;
$TimeConfig{ $Element . 'StopYear' } = $Year;
$TimeConfig{ $Element . 'StopMonth' } = 12;
$TimeConfig{ $Element . 'StopDay' } = 31;
$TimeConfig{ $Element . 'StopHour' } = 23;
$TimeConfig{ $Element . 'StopMinute' } = 59;
$TimeConfig{ $Element . 'StopSecond' } = 59;
for (qw(Start Stop)) {
$TimeConfig{Prefix} = $Element . $_;
# time setting if available
if (
$Param{ 'Time' . $_ }
&& $Param{ 'Time' . $_ } =~ m{^(\d\d\d\d)-(\d\d)-(\d\d)\s(\d\d):(\d\d):(\d\d)$}xi
)
{
$TimeConfig{ $Element . $_ . 'Year' } = $1;
$TimeConfig{ $Element . $_ . 'Month' } = $2;
$TimeConfig{ $Element . $_ . 'Day' } = $3;
$TimeConfig{ $Element . $_ . 'Hour' } = $4;
$TimeConfig{ $Element . $_ . 'Minute' } = $5;
$TimeConfig{ $Element . $_ . 'Second' } = $6;
}
$TimeOutput{ 'Time' . $_ } = $LayoutObject->BuildDateSelection(%TimeConfig);
}
}
my %TimeCountData;
for my $Counter ( 1 .. 60 ) {
$TimeCountData{$Counter} = $Counter;
}
if ( $Param{Use} eq 'UseAsXvalue' ) {
$TimeOutput{TimeScaleCount} = $LayoutObject->BuildSelection(
Data => \%TimeCountData,
Name => $Element . 'TimeScaleCount',
ID => $ElementID . '-TimeScaleCount',
SelectedID => $Param{TimeScaleCount},
Sort => 'NumericKey',
Class => 'Modernize',
);
}
if ( $Param{Output} eq 'Edit' || $Param{TimeRelativeUnit} ) {
my @TimeCountList = qw(TimeRelativeCount TimeRelativeUpcomingCount);
# add the zero for the time relative count selections
$TimeCountData{0} = '-';
for my $TimeCountName (@TimeCountList) {
$TimeOutput{$TimeCountName} = $LayoutObject->BuildSelection(
Data => \%TimeCountData,
Name => $Element . $TimeCountName,
ID => $ElementID . '-' . $TimeCountName,
SelectedID => $Param{$TimeCountName},
Sort => 'NumericKey',
Class => 'Modernize',
);
}
$TimeOutput{TimeRelativeUnit} = $LayoutObject->BuildSelection(
%TimeScaleBuildSelection,
Name => $Element . 'TimeRelativeUnit',
ID => $ElementID . '-TimeRelativeUnit',
Class => 'TimeRelativeUnit' . $Param{Output},
SelectedID => $Param{TimeRelativeUnitLocalSelectedValue} // $Param{TimeRelativeUnit} // 'Day',
Class => 'Modernize',
);
}
if ( $Param{TimeRelativeUnit} ) {
$TimeOutput{CheckedRelative} = 'checked="checked"';
}
else {
$TimeOutput{CheckedAbsolut} = 'checked="checked"';
}
}
if ( $Param{Use} ne 'UseAsRestriction' ) {
if ( $Param{Output} eq 'View' ) {
$TimeOutput{TimeScaleYAxis} = $Self->_TimeScaleYAxis();
}
%TimeScaleBuildSelection = $Self->_TimeScaleBuildSelection(
SelectedXAxisValue => $Param{SelectedXAxisValue},
SortReverse => 1,
);
$TimeOutput{TimeScaleUnit} = $LayoutObject->BuildSelection(
%TimeScaleBuildSelection,
Name => $Element,
ID => $ElementID,
Class => 'Modernize TimeScale' . $Param{Output},
SelectedID => $Param{TimeScaleUnitLocalSelectedValue} // $Param{SelectedValues}[0] // 'Day',
);
$TimeOutput{TimeScaleElementID} = $ElementID;
}
return %TimeOutput;
}
sub _TimeScaleBuildSelection {
my ( $Self, %Param ) = @_;
my %TimeScaleBuildSelection = (
Data => {
Second => Translatable('second(s)'),
Minute => Translatable('minute(s)'),
Hour => Translatable('hour(s)'),
Day => Translatable('day(s)'),
Week => Translatable('week(s)'),
Month => Translatable('month(s)'),
Quarter => Translatable('quarter(s)'),
HalfYear => Translatable('half-year(s)'),
Year => Translatable('year(s)'),
},
Sort => 'IndividualKey',
SortIndividual => [ 'Second', 'Minute', 'Hour', 'Day', 'Week', 'Month', 'Quarter', 'HalfYear', 'Year' ],
);
# special time scale handling
if ( $Param{SelectedValue} || $Param{SelectedXAxisValue} ) {
my $TimeScale = $Self->_TimeScale(%Param);
# sort the time scale with the defined position
my @TimeScaleSorted = sort { $TimeScale->{$a}->{Position} <=> $TimeScale->{$b}->{Position} } keys %{$TimeScale};
# reverse the sorting
if ( $Param{SortReverse} ) {
@TimeScaleSorted
= sort { $TimeScale->{$b}->{Position} <=> $TimeScale->{$a}->{Position} } keys %{$TimeScale};
}
my %TimeScaleData;
ITEM:
for my $Item (@TimeScaleSorted) {
$TimeScaleData{$Item} = $TimeScale->{$Item}->{Value};
last ITEM if $Param{SelectedValue} && $Param{SelectedValue} eq $Item;
}
$TimeScaleBuildSelection{Data} = \%TimeScaleData;
}
return %TimeScaleBuildSelection;
}
sub _TimeScale {
my ( $Self, %Param ) = @_;
my %TimeScale = (
'Second' => {
Position => 1,
Value => Translatable('second(s)'),
},
'Minute' => {
Position => 2,
Value => Translatable('minute(s)'),
},
'Hour' => {
Position => 3,
Value => Translatable('hour(s)'),
},
'Day' => {
Position => 4,
Value => Translatable('day(s)'),
},
'Week' => {
Position => 5,
Value => Translatable('week(s)'),
},
'Month' => {
Position => 6,
Value => Translatable('month(s)'),
},
'Quarter' => {
Position => 7,
Value => Translatable('quarter(s)'),
},
'HalfYear' => {
Position => 8,
Value => Translatable('half-year(s)'),
},
'Year' => {
Position => 9,
Value => Translatable('year(s)'),
},
);
# allowed y axis time scale values for the selected x axis time value
my $TimeScaleYAxis = $Self->_TimeScaleYAxis();
if ( $Param{SelectedXAxisValue} ) {
if ( IsArrayRefWithData( $TimeScaleYAxis->{ $Param{SelectedXAxisValue} } ) ) {
%TimeScale
= map { $_->{Key} => $TimeScale{ $_->{Key} } } @{ $TimeScaleYAxis->{ $Param{SelectedXAxisValue} } };
}
else {
%TimeScale = ();
}
}
return \%TimeScale;
}
sub _TimeScaleYAxis {
my ( $Self, %Param ) = @_;
# get layout object
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
# allowed y axis time scale values for the selected x axis time value
# x axis value => [ y axis values ],
my %TimeScaleYAxis = (
'Second' => [
{
Key => 'Minute',
Value => $LayoutObject->{LanguageObject}->Translate('minute(s)'),
},
],
'Minute' => [
{
Key => 'Hour',
Value => $LayoutObject->{LanguageObject}->Translate('hour(s)'),
},
],
'Hour' => [
{
Key => 'Day',
Value => $LayoutObject->{LanguageObject}->Translate('day(s)'),
},
],
'Day' => [
{
Key => 'Month',
Value => $LayoutObject->{LanguageObject}->Translate('month(s)'),
},
],
'Week' => [
{
Key => 'Week',
Value => $LayoutObject->{LanguageObject}->Translate('week(s)'),
},
],
'Month' => [
{
Key => 'Year',
Value => $LayoutObject->{LanguageObject}->Translate('year(s)'),
},
],
'Quarter' => [
{
Key => 'Year',
Value => $LayoutObject->{LanguageObject}->Translate('year(s)'),
},
],
'HalfYear' => [
{
Key => 'Year',
Value => $LayoutObject->{LanguageObject}->Translate('year(s)'),
},
],
);
return \%TimeScaleYAxis;
}
sub _GetValidTimeZone {
my ( $Self, %Param ) = @_;
return if !$Param{TimeZone};
# Return passed time zone only if it is valid. It can happen time zone is still an old-style offset.
# Please see bug#13373 for more information.
return $Param{TimeZone} if Kernel::System::DateTime->IsTimeZoneValid( TimeZone => $Param{TimeZone} );
return;
}
sub _TimeZoneBuildSelection {
my ( $Self, %Param ) = @_;
my $TimeZones = Kernel::System::DateTime->TimeZoneList();
my %TimeZoneBuildSelection = (
Data => { map { $_ => $_ } @{$TimeZones} },
);
return %TimeZoneBuildSelection;
}
# ATTENTION: this function delivers only approximations!!!
sub _TimeInSeconds {
my ( $Self, %Param ) = @_;
# check if need params are available
if ( !$Param{TimeUnit} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => "error",
Message => '_TimeInSeconds: Need TimeUnit!',
);
return;
}
my %TimeInSeconds = (
Year => 60 * 60 * 24 * 365,
HalfYear => 60 * 60 * 24 * 182,
Quarter => 60 * 60 * 24 * 91,
Month => 60 * 60 * 24 * 30,
Week => 60 * 60 * 24 * 7,
Day => 60 * 60 * 24,
Hour => 60 * 60,
Minute => 60,
Second => 1,
);
return $TimeInSeconds{ $Param{TimeUnit} };
}
sub _GetSelectedXAxisTimeScaleValue {
my ( $Self, %Param ) = @_;
my $SelectedXAxisTimeScaleValue;
for ( @{ $Param{Stat}->{UseAsXvalue} } ) {
if ( $_->{Selected} && $_->{Block} eq 'Time' ) {
$SelectedXAxisTimeScaleValue = $_->{SelectedValues}[0];
}
}
return $SelectedXAxisTimeScaleValue;
}
sub _StopWordErrorCheck {
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};
}
my $ErrorMessage;
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} };
$ErrorMessage = $LayoutObject->{LanguageObject}->Translate(
'Please remove the following words because they cannot be used for the ticket restrictions: %s.',
join( ',', sort @{ $StopWords->{$Field} } ),
);
}
}
return $ErrorMessage;
}
sub _StopWordFieldsGet {
my ( $Self, %Param ) = @_;
if ( !$Kernel::OM->Get('Kernel::System::Ticket::Article')->SearchStringStopWordsUsageWarningActive() ) {
return ();
}
my %StopWordFields = (
'MIME_From' => 1,
'MIME_To' => 1,
'MIME_Cc' => 1,
'MIME_Subject' => 1,
'MIME_Body' => 1,
);
return %StopWordFields;
}
1;
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L).
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 L.
=cut