2477 lines
91 KiB
Perl
2477 lines
91 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::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}
|
|
.= ' <a href="#" title="'
|
|
. $TreeSelectionMessage
|
|
. '" class="ShowTreeSelection"><span>'
|
|
. $TreeSelectionMessage . '</span><i class="fa fa-sitemap"></i></a>';
|
|
}
|
|
}
|
|
|
|
$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}
|
|
.= ' <a href="#" title="'
|
|
. $TreeSelectionMessage
|
|
. '" class="ShowTreeSelection"><span>'
|
|
. $TreeSelectionMessage . '</span><i class="fa fa-sitemap"></i></a>';
|
|
}
|
|
}
|
|
|
|
$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}
|
|
.= ' <a href="#" title="'
|
|
. $TreeSelectionMessage
|
|
. '" class="ShowTreeSelection"><span>'
|
|
. $TreeSelectionMessage . '</span><i class="fa fa-sitemap"></i></a>';
|
|
}
|
|
}
|
|
|
|
$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<https://otrs.org/>).
|
|
|
|
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<https://www.gnu.org/licenses/gpl-3.0.txt>.
|
|
|
|
=cut
|