1# --
2# Copyright (C) 2001-2020 OTRS AG, https://otrs.com/
3# --
4# This software comes with ABSOLUTELY NO WARRANTY. For details, see
5# the enclosed file COPYING for license information (GPL). If you
6# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
7# --
8
9package Kernel::Modules::AgentStatistics;
10
11use strict;
12use warnings;
13
14use List::Util qw( first );
15
16use Kernel::System::VariableCheck qw(:all);
17use Kernel::Language qw(Translatable);
18
19our $ObjectManagerDisabled = 1;
20
21sub new {
22    my ( $Type, %Param ) = @_;
23
24    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
25
26    # allocate new hash for object
27    my $Self = {};
28    bless( $Self, $Type );
29
30    for my $NeededData (qw( UserID Subaction AccessRo SessionID ))
31    {
32        if ( !$Param{$NeededData} ) {
33            $LayoutObject->FatalError(
34                Message => $LayoutObject->{LanguageObject}->Translate( 'Parameter %s is missing.', $NeededData ),
35            );
36        }
37        $Self->{$NeededData} = $Param{$NeededData};
38    }
39
40    # AccessRw controls the adding/editing of statistics.
41    for my $Param (qw( AccessRw RequestedURL )) {
42        if ( $Param{$Param} ) {
43            $Self->{$Param} = $Param{$Param};
44        }
45    }
46
47    return $Self;
48}
49
50sub Run {
51    my ( $Self, %Param ) = @_;
52
53    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
54
55    # set breadcrumbpath for overview screen and it will be used as base for other screens
56    @{ $Self->{BreadcrumbPath} } = (
57        {
58            Name =>
59                $LayoutObject->{LanguageObject}->Translate('Statistics Overview'),
60            Link => 'AgentStatistics;Subaction=Overview',
61        }
62    );
63
64    my $Subaction = $Self->{Subaction};
65
66    my %RoSubactions = (
67        Overview => 'OverviewScreen',
68        View     => 'ViewScreen',
69        Run      => 'RunAction',
70    );
71
72    if ( $RoSubactions{$Subaction} ) {
73        if ( !$Self->{AccessRo} ) {
74            return $LayoutObject->NoPermission( WithHeader => 'yes' );
75        }
76        my $Method = $RoSubactions{$Subaction};
77        return $Self->$Method();
78    }
79
80    my %RwSubactions = (
81        Add                             => 'AddScreen',
82        AddAction                       => 'AddAction',
83        Edit                            => 'EditScreen',
84        EditAction                      => 'EditAction',
85        Import                          => 'ImportScreen',
86        ImportAction                    => 'ImportAction',
87        ExportAction                    => 'ExportAction',
88        DeleteAction                    => 'DeleteAction',
89        ExportAction                    => 'ExportAction',
90        GeneralSpecificationsWidgetAJAX => 'GeneralSpecificationsWidgetAJAX',
91    );
92
93    if ( $RwSubactions{$Subaction} ) {
94        if ( !$Self->{AccessRw} ) {
95            return $LayoutObject->NoPermission( WithHeader => 'yes' );
96        }
97        my $Method = $RwSubactions{$Subaction};
98        return $Self->$Method();
99    }
100
101    # No (known) subaction?
102    return $LayoutObject->ErrorScreen(
103        Message => Translatable('Invalid Subaction.'),
104    );
105}
106
107sub OverviewScreen {
108    my ( $Self, %Param ) = @_;
109
110    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
111    my $ParamObject  = $Kernel::OM->Get('Kernel::System::Web::Request');
112    my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
113
114    # Get Params
115    $Param{SearchPageShown} = $ConfigObject->Get('Stats::SearchPageShown') || 50;
116    $Param{SearchLimit}     = $ConfigObject->Get('Stats::SearchLimit')     || 1000;
117    $Param{OrderBy}   = $ParamObject->GetParam( Param => 'OrderBy' )   || 'ID';
118    $Param{Direction} = $ParamObject->GetParam( Param => 'Direction' ) || 'ASC';
119    $Param{StartHit} = int( $ParamObject->GetParam( Param => 'StartHit' ) || 1 );
120
121    # get all Stats from the db
122    my $Result = $Kernel::OM->Get('Kernel::System::Stats')->GetStatsList(
123        AccessRw  => $Self->{AccessRw},
124        OrderBy   => $Param{OrderBy},
125        Direction => $Param{Direction},
126        UserID    => $Self->{UserID},
127    );
128
129    if ( $Param{StartHit} > 1 && $Param{StartHit} > scalar @{ $Result || [] } ) {
130        return $LayoutObject->Redirect( OP => "Action=AgentStatistics;Subaction=Overview" );
131    }
132
133    my %Order2CSSSort = (
134        ASC  => 'SortAscending',
135        DESC => 'SortDescending',
136    );
137
138    my %InverseSorting = (
139        ASC  => 'DESC',
140        DESC => 'ASC',
141    );
142
143    $Param{ 'CSSSort' . $Param{OrderBy} } = $Order2CSSSort{ $Param{Direction} };
144    for my $Type (qw(ID Title Object)) {
145        $Param{"LinkSort$Type"} = ( $Param{OrderBy} eq $Type ) ? $InverseSorting{ $Param{Direction} } : 'ASC';
146    }
147
148    # build the info
149    my %Pagination = $LayoutObject->PageNavBar(
150        Limit     => $Param{SearchLimit},
151        StartHit  => $Param{StartHit},
152        PageShown => $Param{SearchPageShown},
153        AllHits   => $#{$Result} + 1,
154        Action    => 'Action=AgentStatistics;Subaction=Overview',
155        Link      => ";Direction=$Param{Direction};OrderBy=$Param{OrderBy};",
156        IDPrefix  => 'AgentStatisticsOverview'
157    );
158
159    # list result
160    my $Index = -1;
161    for ( my $Z = 0; ( $Z < $Param{SearchPageShown} && $Index < $#{$Result} ); $Z++ ) {
162        $Index = $Param{StartHit} + $Z - 1;
163        my $StatID = $Result->[$Index];
164        my $Stat   = $Kernel::OM->Get('Kernel::System::Stats')->StatsGet(
165            StatID             => $StatID,
166            NoObjectAttributes => 1,
167            UserID             => $Self->{UserID},
168        );
169
170        # get the object name
171        if ( $Stat->{StatType} eq 'static' ) {
172            $Stat->{ObjectName} = $Stat->{File};
173        }
174
175        # if no object name is defined use an empty string
176        $Stat->{ObjectName} ||= '';
177
178        $LayoutObject->Block(
179            Name => 'Result',
180            Data => {
181                %$Stat,
182                AccessRw => $Self->{AccessRw},
183            },
184        );
185    }
186
187    # build output
188    my $Output = $LayoutObject->Header(
189        Title => Translatable('Overview'),
190        Area  => 'Statistics',
191    );
192    $Output .= $LayoutObject->NavigationBar();
193
194    $Output .= $LayoutObject->Output(
195        Data => {
196            %Pagination,
197            %Param,
198            AccessRw       => $Self->{AccessRw},
199            BreadcrumbPath => $Self->{BreadcrumbPath},
200        },
201        TemplateFile => 'AgentStatisticsOverview',
202    );
203    $Output .= $LayoutObject->Footer();
204    return $Output;
205}
206
207sub ImportScreen {
208    my ( $Self, %Param ) = @_;
209
210    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
211
212    my %Errors = %{ $Param{Errors} // {} };
213
214    my $Output = $LayoutObject->Header(
215        Title => Translatable('Import'),
216        Area  => 'Statistics',
217    );
218    $Output .= $LayoutObject->NavigationBar();
219    $Output .= $LayoutObject->Output(
220        TemplateFile => 'AgentStatisticsImport',
221        Data         => {
222            %Errors,
223            BreadcrumbPath => $Self->{BreadcrumbPath},
224        },
225    );
226    $Output .= $LayoutObject->Footer();
227    return $Output;
228}
229
230sub ImportAction {
231    my ( $Self, %Param ) = @_;
232
233    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
234    my $ParamObject  = $Kernel::OM->Get('Kernel::System::Web::Request');
235
236    my %Errors;
237
238    # challenge token check for write action
239    $LayoutObject->ChallengeTokenCheck();
240
241    my $UploadFile = $ParamObject->GetParam( Param => 'File' );
242    if ($UploadFile) {
243        my %UploadStuff = $ParamObject->GetUploadAll(
244            Param    => 'File',
245            Encoding => 'Raw'
246        );
247        if ( $UploadStuff{Content} =~ m{<otrs_stats>}x ) {
248            my $StatID = $Kernel::OM->Get('Kernel::System::Stats')->Import(
249                Content => $UploadStuff{Content},
250                UserID  => $Self->{UserID},
251            );
252
253            if ( !$StatID ) {
254                $Errors{FileServerError}        = 'ServerError';
255                $Errors{FileServerErrorMessage} = Translatable("Statistic could not be imported.");
256            }
257            else {
258
259                # Redirect to statistic edit page.
260                return $LayoutObject->Redirect(
261                    OP => "Action=AgentStatistics;Subaction=Edit;StatID=$StatID"
262                );
263            }
264        }
265        else {
266            $Errors{FileServerError}        = 'ServerError';
267            $Errors{FileServerErrorMessage} = Translatable("Please upload a valid statistic file.");
268        }
269    }
270    else {
271        $Errors{FileServerError}        = 'ServerError';
272        $Errors{FileServerErrorMessage} = Translatable("This field is required.");
273    }
274
275    return $Self->ImportScreen( Errors => \%Errors );
276}
277
278sub ExportAction {
279    my ( $Self, %Param ) = @_;
280
281    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
282
283    $LayoutObject->ChallengeTokenCheck();
284
285    my $StatID = $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam( Param => 'StatID' );
286    if ( !$StatID ) {
287        return $LayoutObject->ErrorScreen(
288            Message => Translatable('Export: Need StatID!'),
289        );
290    }
291
292    my $ExportFile = $Kernel::OM->Get('Kernel::System::Stats')->Export(
293        StatID => $StatID,
294        UserID => $Self->{UserID},
295    );
296
297    return $LayoutObject->Attachment(
298        Filename    => $ExportFile->{Filename},
299        Content     => $ExportFile->{Content},
300        ContentType => 'text/xml',
301    );
302}
303
304sub DeleteAction {
305    my ( $Self, %Param ) = @_;
306
307    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
308    my $ParamObject  = $Kernel::OM->Get('Kernel::System::Web::Request');
309
310    my $StatID = $ParamObject->GetParam( Param => 'StatID' );
311    if ( !$StatID ) {
312        return $LayoutObject->ErrorScreen(
313            Message => Translatable('Delete: Get no StatID!'),
314        );
315    }
316
317    # challenge token check for write action
318    $LayoutObject->ChallengeTokenCheck();
319    $Kernel::OM->Get('Kernel::System::Stats')->StatsDelete(
320        StatID => $StatID,
321        UserID => $Self->{UserID},
322    );
323    return $LayoutObject->Redirect( OP => "Action=AgentStatistics;Subaction=Overview" );
324}
325
326sub EditScreen {
327    my ( $Self, %Param ) = @_;
328
329    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
330    my $ParamObject  = $Kernel::OM->Get('Kernel::System::Web::Request');
331    my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
332
333    # get param
334    if ( !( $Param{StatID} = $ParamObject->GetParam( Param => 'StatID' ) ) ) {
335        return $LayoutObject->ErrorScreen(
336            Message => Translatable('Need StatID!'),
337        );
338    }
339
340    my $Stat = $Kernel::OM->Get('Kernel::System::Stats')->StatsGet(
341        StatID => $Param{StatID},
342        UserID => $Self->{UserID},
343    );
344
345    my %Frontend;
346    $Frontend{GeneralSpecificationsWidget}
347        = $Kernel::OM->Get('Kernel::Output::HTML::Statistics::View')->GeneralSpecificationsWidget(
348        StatID => $Stat->{StatID},
349        UserID => $Self->{UserID},
350        );
351
352    if ( $Stat->{StatType} eq 'dynamic' ) {
353        $Frontend{XAxisWidget} = $Kernel::OM->Get('Kernel::Output::HTML::Statistics::View')->XAxisWidget(
354            Stat   => $Stat,
355            UserID => $Self->{UserID},
356        );
357        $Frontend{YAxisWidget} = $Kernel::OM->Get('Kernel::Output::HTML::Statistics::View')->YAxisWidget(
358            Stat   => $Stat,
359            UserID => $Self->{UserID},
360        );
361        $Frontend{RestrictionsWidget} = $Kernel::OM->Get('Kernel::Output::HTML::Statistics::View')->RestrictionsWidget(
362            Stat   => $Stat,
363            UserID => $Self->{UserID},
364        );
365        $Frontend{PreviewWidget} = $Kernel::OM->Get('Kernel::Output::HTML::Statistics::View')->PreviewWidget(
366            Stat   => $Stat,
367            UserID => $Self->{UserID},
368        );
369    }
370
371    my $Output = $LayoutObject->Header(
372        Title => Translatable('Edit'),
373        Area  => 'Statistics',
374    );
375    $Output .= $LayoutObject->NavigationBar();
376
377    $Output .= $LayoutObject->Output(
378        TemplateFile => 'AgentStatisticsEdit',
379        Data         => {
380            %Frontend,
381            %{$Stat},
382            BreadcrumbPath => $Self->{BreadcrumbPath},
383        },
384    );
385    $Output .= $LayoutObject->Footer();
386    return $Output;
387}
388
389sub EditAction {
390    my ( $Self, %Param ) = @_;
391
392    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
393    my $ParamObject  = $Kernel::OM->Get('Kernel::System::Web::Request');
394    my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
395
396    my %Errors;
397
398    my $Stat = $Kernel::OM->Get('Kernel::System::Stats')->StatsGet(
399        StatID => $ParamObject->GetParam( Param => 'StatID' ),
400        UserID => $Self->{UserID},
401    );
402
403    if ( !$Stat ) {
404        return $LayoutObject->ErrorScreen(
405            Message => Translatable('Need StatID!'),
406        );
407    }
408
409    #
410    # General Specification
411    #
412    my %Data;
413    for my $Key (qw(Title Description Valid)) {
414        $Data{$Key} = $ParamObject->GetParam( Param => $Key ) // '';
415        if ( !length $Data{$Key} ) {    # Valid can be 0
416            $Errors{ $Key . 'ServerError' } = 'ServerError';
417        }
418    }
419
420    if ( length $ParamObject->GetParam( Param => 'TimeZone' ) ) {
421        $Data{TimeZone} = $ParamObject->GetParam( Param => 'TimeZone' );
422    }
423
424    for my $Key (qw(SumRow SumCol Cache ShowAsDashboardWidget)) {
425        $Data{$Key} = $ParamObject->GetParam( Param => $Key ) // '';
426    }
427
428    for my $Key (qw(Permission Format)) {
429        $Data{$Key} = [ $ParamObject->GetArray( Param => $Key ) ];
430        if ( !@{ $Data{$Key} } ) {
431            $Errors{ $Key . 'ServerError' } = 'ServerError';
432
433            #$Data{$Key} = '';
434        }
435    }
436
437    #
438    # X Axis
439    #
440    if ( $Stat->{StatType} eq 'dynamic' ) {
441        my $SelectedElement = $ParamObject->GetParam( Param => 'XAxisSelectedElement' );
442        $Data{StatType} = $Stat->{StatType};
443
444        OBJECTATTRIBUTE:
445        for my $ObjectAttribute ( @{ $Stat->{UseAsXvalue} } ) {
446            next OBJECTATTRIBUTE if !defined $SelectedElement;
447            next OBJECTATTRIBUTE if $SelectedElement ne 'XAxis' . $ObjectAttribute->{Element};
448
449            my @Array = $ParamObject->GetArray( Param => $SelectedElement );
450            $Data{UseAsXvalue}[0]{SelectedValues} = \@Array;
451            $Data{UseAsXvalue}[0]{Element}        = $ObjectAttribute->{Element};
452            $Data{UseAsXvalue}[0]{Block}          = $ObjectAttribute->{Block};
453            $Data{UseAsXvalue}[0]{Selected}       = 1;
454
455            my $Fixed = $ParamObject->GetParam( Param => 'Fixed' . $SelectedElement );
456            $Data{UseAsXvalue}[0]{Fixed} = $Fixed ? 1 : 0;
457
458            # Check if Time was selected
459            next OBJECTATTRIBUTE if $ObjectAttribute->{Block} ne 'Time';
460
461            # This part is only needed if the block time is selected
462            # perhaps a separate function is better
463            my %Time;
464            $Data{UseAsXvalue}[0]{TimeScaleCount}
465                = $ParamObject->GetParam( Param => $SelectedElement . 'TimeScaleCount' ) || 1;
466            my $TimeSelect = $ParamObject->GetParam( Param => $SelectedElement . 'TimeSelect' ) || 'Absolut';
467
468            if ( $TimeSelect eq 'Absolut' ) {
469                for my $Limit (qw(Start Stop)) {
470                    for my $Unit (qw(Year Month Day Hour Minute Second)) {
471                        if ( defined( $ParamObject->GetParam( Param => "$SelectedElement$Limit$Unit" ) ) ) {
472                            $Time{ $Limit . $Unit } = $ParamObject->GetParam(
473                                Param => "$SelectedElement$Limit$Unit",
474                            );
475                        }
476                    }
477                    if ( !defined( $Time{ $Limit . 'Hour' } ) ) {
478                        if ( $Limit eq 'Start' ) {
479                            $Time{StartHour}   = 0;
480                            $Time{StartMinute} = 0;
481                            $Time{StartSecond} = 0;
482                        }
483                        elsif ( $Limit eq 'Stop' ) {
484                            $Time{StopHour}   = 23;
485                            $Time{StopMinute} = 59;
486                            $Time{StopSecond} = 59;
487                        }
488                    }
489                    elsif ( !defined( $Time{ $Limit . 'Second' } ) ) {
490                        if ( $Limit eq 'Start' ) {
491                            $Time{StartSecond} = 0;
492                        }
493                        elsif ( $Limit eq 'Stop' ) {
494                            $Time{StopSecond} = 59;
495                        }
496                    }
497
498                    $Data{UseAsXvalue}[0]{"Time$Limit"} = sprintf(
499                        "%04d-%02d-%02d %02d:%02d:%02d",
500                        $Time{ $Limit . 'Year' },
501                        $Time{ $Limit . 'Month' },
502                        $Time{ $Limit . 'Day' },
503                        $Time{ $Limit . 'Hour' },
504                        $Time{ $Limit . 'Minute' },
505                        $Time{ $Limit . 'Second' },
506                    );
507                }
508            }
509            else {
510                $Data{UseAsXvalue}[0]{TimeRelativeUnit} = $ParamObject->GetParam(
511                    Param => $SelectedElement . 'TimeRelativeUnit'
512                );
513                $Data{UseAsXvalue}[0]{TimeRelativeCount} = $ParamObject->GetParam(
514                    Param => $SelectedElement . 'TimeRelativeCount'
515                );
516                $Data{UseAsXvalue}[0]{TimeRelativeUpcomingCount} = $ParamObject->GetParam(
517                    Param => $SelectedElement . 'TimeRelativeUpcomingCount'
518                );
519            }
520        }
521    }
522
523    #
524    # Y Axis
525    #
526    if ( $Stat->{StatType} eq 'dynamic' ) {
527
528        my $Index = 0;
529        $Data{StatType} = $Stat->{StatType};
530
531        OBJECTATTRIBUTE:
532        for my $ObjectAttribute ( @{ $Stat->{UseAsValueSeries} } ) {
533            my $Element = 'YAxis' . $ObjectAttribute->{Element};
534            if ( !$ParamObject->GetParam( Param => "Select$Element" ) ) {
535                next OBJECTATTRIBUTE;
536            }
537
538            my @Array = $ParamObject->GetArray( Param => $Element );
539            $Data{UseAsValueSeries}[$Index]{SelectedValues} = \@Array;
540            $Data{UseAsValueSeries}[$Index]{Element}        = $ObjectAttribute->{Element};
541            $Data{UseAsValueSeries}[$Index]{Block}          = $ObjectAttribute->{Block};
542            $Data{UseAsValueSeries}[$Index]{Selected}       = 1;
543
544            my $FixedElement = 'Fixed' . $Element;
545            my $Fixed        = $ParamObject->GetParam( Param => $FixedElement );
546            $Data{UseAsValueSeries}[$Index]{Fixed} = $Fixed ? 1 : 0;
547
548            # Check if Time was selected
549            if ( $ObjectAttribute->{Block} eq 'Time' ) {
550
551                # for working with extended time
552                $Data{UseAsValueSeries}[$Index]{TimeScaleCount} = $ParamObject->GetParam(
553                    Param => $Element . 'TimeScaleCount'
554                ) || 1;
555
556                # check if the current selected value is allowed for the x axis selected time scale
557                my $SelectedXAxisTimeScaleValue = $Data{UseAsXvalue}[0]{SelectedValues}[0];
558
559                my $TimeScale = $Kernel::OM->Get('Kernel::Output::HTML::Statistics::View')->_TimeScale(
560                    SelectedXAxisValue => $SelectedXAxisTimeScaleValue,
561                );
562
563                my %TimeScaleLookup = map { $_ => 1 } sort keys %{$TimeScale};
564
565                # set the first allowed time scale value as default
566                if (
567                    !$Data{UseAsValueSeries}[$Index]{SelectedValues}[0]
568                    || !exists $TimeScaleLookup{ $Data{UseAsValueSeries}[$Index]{SelectedValues}[0] }
569                    )
570                {
571
572                    my @TimeScaleSorted
573                        = sort { $TimeScale->{$a}->{Position} <=> $TimeScale->{$b}->{Position} } keys %{$TimeScale};
574
575                    $Data{UseAsValueSeries}[$Index]{SelectedValues}[0] = $TimeScaleSorted[0];
576                }
577            }
578            $Index++;
579        }
580
581        $Data{UseAsValueSeries} ||= [];
582    }
583
584    #
585    # Restrictions
586    #
587    if ( $Stat->{StatType} eq 'dynamic' ) {
588        my $Index = 0;
589        $Data{StatType} = $Stat->{StatType};
590
591        OBJECTATTRIBUTE:
592        for my $ObjectAttribute ( @{ $Stat->{UseAsRestriction} } ) {
593
594            my $Element = 'Restrictions' . $ObjectAttribute->{Element};
595            if ( !$ParamObject->GetParam( Param => "Select$Element" ) ) {
596                next OBJECTATTRIBUTE;
597            }
598
599            my @Array = $ParamObject->GetArray( Param => $Element );
600            $Data{UseAsRestriction}[$Index]{SelectedValues} = \@Array;
601            $Data{UseAsRestriction}[$Index]{Element}        = $ObjectAttribute->{Element};
602            $Data{UseAsRestriction}[$Index]{Block}          = $ObjectAttribute->{Block};
603            $Data{UseAsRestriction}[$Index]{Selected}       = 1;
604
605            my $Fixed = $ParamObject->GetParam( Param => 'Fixed' . $Element );
606            $Data{UseAsRestriction}[$Index]{Fixed} = $Fixed ? 1 : 0;
607
608            if ( $ObjectAttribute->{Block} eq 'Time' ) {
609                my %Time;
610                my $TimeSelect = $ParamObject->GetParam( Param => $Element . 'TimeSelect' )
611                    || 'Absolut';
612                if ( $TimeSelect eq 'Absolut' ) {
613                    for my $Limit (qw(Start Stop)) {
614                        for my $Unit (qw(Year Month Day Hour Minute Second)) {
615                            if ( defined( $ParamObject->GetParam( Param => "$Element$Limit$Unit" ) ) )
616                            {
617                                $Time{ $Limit . $Unit } = $ParamObject->GetParam(
618                                    Param => "$Element$Limit$Unit",
619                                );
620                            }
621                        }
622                        if ( !defined( $Time{ $Limit . 'Hour' } ) ) {
623                            if ( $Limit eq 'Start' ) {
624                                $Time{StartHour}   = 0;
625                                $Time{StartMinute} = 0;
626                                $Time{StartSecond} = 0;
627                            }
628                            elsif ( $Limit eq 'Stop' ) {
629                                $Time{StopHour}   = 23;
630                                $Time{StopMinute} = 59;
631                                $Time{StopSecond} = 59;
632                            }
633                        }
634                        elsif ( !defined( $Time{ $Limit . 'Second' } ) ) {
635                            if ( $Limit eq 'Start' ) {
636                                $Time{StartSecond} = 0;
637                            }
638                            elsif ( $Limit eq 'Stop' ) {
639                                $Time{StopSecond} = 59;
640                            }
641                        }
642
643                        $Data{UseAsRestriction}[$Index]{"Time$Limit"} = sprintf(
644                            "%04d-%02d-%02d %02d:%02d:%02d",
645                            $Time{ $Limit . 'Year' },
646                            $Time{ $Limit . 'Month' },
647                            $Time{ $Limit . 'Day' },
648                            $Time{ $Limit . 'Hour' },
649                            $Time{ $Limit . 'Minute' },
650                            $Time{ $Limit . 'Second' },
651                        );
652                    }
653                }
654                else {
655                    $Data{UseAsRestriction}[$Index]{TimeRelativeUnit} = $ParamObject->GetParam(
656                        Param => $Element . 'TimeRelativeUnit'
657                    );
658                    $Data{UseAsRestriction}[$Index]{TimeRelativeCount} = $ParamObject->GetParam(
659                        Param => $Element . 'TimeRelativeCount'
660                    );
661                    $Data{UseAsRestriction}[$Index]{TimeRelativeUpcomingCount} = $ParamObject->GetParam(
662                        Param => $Element . 'TimeRelativeUpcomingCount'
663                    );
664                }
665            }
666
667            $Index++;
668        }
669
670        $Data{UseAsRestriction} ||= [];
671    }
672
673    # my @Notify = $Kernel::OM->Get('Kernel::System::Stats')->CompletenessCheck(
674    #     StatData => {
675    #         %{$Stat},
676    #         %Data,
677    #     },
678    #     Section => 'Specification'
679    # );
680
681    if (%Errors) {
682        return $Self->EditScreen(
683            Errors   => \%Errors,
684            GetParam => \%Data,
685        );
686    }
687
688    $Kernel::OM->Get('Kernel::System::Stats')->StatsUpdate(
689        StatID => $Stat->{StatID},
690        Hash   => \%Data,
691        UserID => $Self->{UserID},
692    );
693
694    if ( $ParamObject->GetParam( Param => 'ReturnToStatisticOverview' ) ) {
695        return $LayoutObject->Redirect( OP => "Action=AgentStatistics;Subaction=Overview" );
696    }
697
698    return $LayoutObject->Redirect(
699        OP => "Action=AgentStatistics;Subaction=Edit;StatID=$Stat->{StatID}"
700    );
701}
702
703sub ViewScreen {
704    my ( $Self, %Param ) = @_;
705
706    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
707    my $ParamObject  = $Kernel::OM->Get('Kernel::System::Web::Request');
708    my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
709
710    my @Errors;
711    if ( ref $Param{Errors} eq 'ARRAY' ) {
712        @Errors = @{ $Param{Errors} || [] };
713    }
714
715    # get StatID
716    my $StatID = $ParamObject->GetParam( Param => 'StatID' );
717    if ( !$StatID ) {
718        return $LayoutObject->ErrorScreen(
719            Message => Translatable('Need StatID!'),
720        );
721    }
722
723    # get message if one available
724    #my $Message = $ParamObject->GetParam( Param => 'Message' );
725
726    # Get all statistics that the current user may see (does permission check).
727    my $StatsList = $Kernel::OM->Get('Kernel::System::Stats')->StatsListGet(
728        UserID => $Self->{UserID},
729    );
730    if ( !IsHashRefWithData( $StatsList->{$StatID} ) ) {
731        return $LayoutObject->ErrorScreen(
732            Message => Translatable('Could not load stat.'),
733        );
734    }
735
736    my $Stat = $Kernel::OM->Get('Kernel::System::Stats')->StatsGet(
737        StatID => $StatID,
738        UserID => $Self->{UserID},
739    );
740
741    # get param
742    if ( !IsHashRefWithData($Stat) ) {
743        return $LayoutObject->ErrorScreen(
744            Message => Translatable('Could not load stat.'),
745        );
746    }
747
748    my %Frontend;
749
750    $Frontend{StatsParamsWidget} = $Kernel::OM->Get('Kernel::Output::HTML::Statistics::View')->StatsParamsWidget(
751        Stat   => $Stat,
752        UserID => $Self->{UserID},
753    );
754
755    my $Output = $LayoutObject->Header(
756        Title => Translatable('View'),
757        Area  => 'Statistics',
758    );
759    $Output .= $LayoutObject->NavigationBar();
760
761    $Output .= $LayoutObject->Output(
762        TemplateFile => 'AgentStatisticsView',
763        Data         => {
764            AccessRw => $Self->{AccessRw},
765            Errors   => \@Errors,
766            %Frontend,
767            %{$Stat},
768            BreadcrumbPath => $Self->{BreadcrumbPath},
769        },
770    );
771    $Output .= $LayoutObject->Footer();
772    return $Output;
773}
774
775sub AddScreen {
776    my ( $Self, %Param ) = @_;
777
778    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
779    my $ParamObject  = $Kernel::OM->Get('Kernel::System::Web::Request');
780
781    # In case of page reload because of errors
782    my %Errors   = %{ $Param{Errors}   // {} };
783    my %GetParam = %{ $Param{GetParam} // {} };
784
785    my %Frontend;
786
787    my $DynamicFiles = $Kernel::OM->Get('Kernel::System::Stats')->GetDynamicFiles();
788    DYNAMIC_FILE:
789    for my $DynamicFile ( sort keys %{ $DynamicFiles // {} } ) {
790        my $ObjectName = 'Kernel::System::Stats::Dynamic::' . $DynamicFile;
791
792        next DYNAMIC_FILE if !$Kernel::OM->Get('Kernel::System::Main')->Require($ObjectName);
793        my $Object = $ObjectName->new();
794        next DYNAMIC_FILE if !$Object;
795        if ( $Object->can('GetStatElement') ) {
796            $Frontend{ShowAddDynamicMatrixButton}++;
797        }
798        else {
799            $Frontend{ShowAddDynamicListButton}++;
800        }
801    }
802
803    my $StaticFiles = $Kernel::OM->Get('Kernel::System::Stats')->GetStaticFiles(
804        OnlyUnusedFiles => 1,
805        UserID          => $Self->{UserID},
806    );
807    if ( scalar keys %{$StaticFiles} ) {
808        $Frontend{ShowAddStaticButton}++;
809    }
810
811    # This is a page reload because of validation errors
812    if (%Errors) {
813        $Frontend{StatisticPreselection} = $ParamObject->GetParam( Param => 'StatisticPreselection' );
814        $Frontend{GeneralSpecificationsWidget}
815            = $Kernel::OM->Get('Kernel::Output::HTML::Statistics::View')->GeneralSpecificationsWidget(
816            Errors   => \%Errors,
817            GetParam => \%GetParam,
818            UserID   => $Self->{UserID},
819            );
820        $Frontend{ShowFormInitially} = 1;
821    }
822
823    # generate manual link
824    my $ManualVersion = $Kernel::OM->Get('Kernel::Config')->Get('Version');
825    $ManualVersion =~ m{^(\d{1,2}).+};
826    $ManualVersion = $1;
827
828    # build output
829    my $Output = $LayoutObject->Header(
830        Title => Translatable('Add New Statistic'),
831        Area  => 'Statistics',
832    );
833    $Output .= $LayoutObject->NavigationBar();
834    $Output .= $LayoutObject->Output(
835        TemplateFile => 'AgentStatisticsAdd',
836        Data         => {
837            %Frontend,
838            %Errors,
839            ManualVersion  => $ManualVersion,
840            BreadcrumbPath => $Self->{BreadcrumbPath},
841        },
842    );
843    $Output .= $LayoutObject->Footer();
844    return $Output;
845}
846
847sub AddAction {
848    my ( $Self, %Param ) = @_;
849
850    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
851    my $ParamObject  = $Kernel::OM->Get('Kernel::System::Web::Request');
852    my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
853
854    my %Errors;
855
856    my %Data;
857    for my $Key (qw(Title Description ObjectModule StatType Valid)) {
858        $Data{$Key} = $ParamObject->GetParam( Param => $Key ) // '';
859        if ( !length $Data{$Key} ) {    # Valid can be 0
860            $Errors{ $Key . 'ServerError' } = 'ServerError';
861        }
862    }
863
864    if ( length $ParamObject->GetParam( Param => 'TimeZone' ) ) {
865        $Data{TimeZone} = $ParamObject->GetParam( Param => 'TimeZone' );
866    }
867
868    # This seems to be historical metadata that is needed for display.
869    my $Object = $Data{ObjectModule};
870    $Object = [ split( m{::}, $Object ) ]->[-1];
871    if ( $Data{StatType} eq 'static' ) {
872        $Data{File} = $Object;
873    }
874    else {
875        $Data{Object} = $Object;
876    }
877
878    my $StatsObject = $Kernel::OM->Get('Kernel::System::Stats');
879
880    my $ObjectModuleCheck = $StatsObject->ObjectModuleCheck(
881        StatType                     => $Data{StatType},
882        ObjectModule                 => $Data{ObjectModule},
883        CheckAlreadyUsedStaticObject => 1,
884    );
885
886    if ( !$ObjectModuleCheck ) {
887        $Errors{ObjectModuleServerError} = 'ServerError';
888    }
889
890    for my $Key (qw(SumRow SumCol Cache ShowAsDashboardWidget)) {
891        $Data{$Key} = $ParamObject->GetParam( Param => $Key ) // '';
892    }
893
894    for my $Key (qw(Permission Format)) {
895        $Data{$Key} = [ $ParamObject->GetArray( Param => $Key ) ];
896        if ( !@{ $Data{$Key} } ) {
897            $Errors{ $Key . 'ServerError' } = 'ServerError';
898
899            #$Data{$Key} = '';
900        }
901    }
902
903    # my @Notify = $Kernel::OM->Get('Kernel::System::Stats')->CompletenessCheck(
904    #     StatData => \%Data,
905    #     Section  => 'Specification'
906    # );
907
908    if (%Errors) {
909        return $Self->AddScreen(
910            Errors   => \%Errors,
911            GetParam => \%Data,
912        );
913    }
914
915    $Param{StatID} = $StatsObject->StatsAdd(
916        UserID => $Self->{UserID},
917    );
918    if ( !$Param{StatID} ) {
919        return $LayoutObject->ErrorScreen(
920            Message => Translatable('Could not create statistic.'),
921        );
922    }
923    $StatsObject->StatsUpdate(
924        StatID => $Param{StatID},
925        Hash   => \%Data,
926        UserID => $Self->{UserID},
927    );
928
929    # For static stats, the configuration is finished
930    if ( $Data{StatType} eq 'static' ) {
931        return $LayoutObject->Redirect(
932            OP => "Action=AgentStatistics;Subaction=View;StatID=$Param{StatID}",
933        );
934    }
935
936    # Continue configuration for dynamic stats
937    return $LayoutObject->Redirect(
938        OP => "Action=AgentStatistics;Subaction=Edit;StatID=$Param{StatID}",
939    );
940}
941
942sub RunAction {
943    my ( $Self, %Param ) = @_;
944
945    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
946    my $ParamObject  = $Kernel::OM->Get('Kernel::System::Web::Request');
947    my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
948
949    for my $Name (qw(Format StatID Name Cached)) {
950        $Param{$Name} = $ParamObject->GetParam( Param => $Name );
951    }
952    my @RequiredParams = (qw(Format StatID));
953    if ( $Param{Cached} ) {
954        push @RequiredParams, 'Name';
955    }
956    for my $Required (@RequiredParams) {
957        if ( !$Param{$Required} ) {
958            return $LayoutObject->ErrorScreen(
959                Message => $LayoutObject->{LanguageObject}->Translate( 'Run: Get no %s!', $Required ),
960            );
961        }
962    }
963
964    my $Stat = $Kernel::OM->Get('Kernel::System::Stats')->StatsGet(
965        StatID => $Param{StatID},
966        UserID => $Self->{UserID},
967    );
968
969    # permission check
970    if ( !$Self->{AccessRw} ) {
971        my $UserPermission = 0;
972
973        return $LayoutObject->NoPermission( WithHeader => 'yes' ) if !$Stat->{Valid};
974
975        # get user groups
976        my %GroupList = $Kernel::OM->Get('Kernel::System::Group')->PermissionUserGet(
977            UserID => $Self->{UserID},
978            Type   => 'ro',
979        );
980
981        GROUPID:
982        for my $GroupID ( @{ $Stat->{Permission} } ) {
983
984            next GROUPID if !$GroupID;
985            next GROUPID if !$GroupList{$GroupID};
986
987            $UserPermission = 1;
988
989            last GROUPID;
990        }
991
992        return $LayoutObject->NoPermission( WithHeader => 'yes' ) if !$UserPermission;
993    }
994
995    # get params
996    my %GetParam = eval {
997        $Kernel::OM->Get('Kernel::Output::HTML::Statistics::View')->StatsParamsGet(
998            Stat   => $Stat,
999            UserID => $Self->{UserID},
1000        );
1001    };
1002    if ($@) {
1003        return $Self->ViewScreen( Errors => $@ );
1004    }
1005
1006    # run stat...
1007    my @StatArray;
1008
1009    # called from within the dashboard. will use the same mechanism and configuration like in
1010    # the dashboard stats - the (cached) result will be the same as seen in the dashboard
1011    if ( $Param{Cached} ) {
1012
1013        # get settings for specified stats by using the dashboard configuration for the agent
1014        my %Preferences = $Kernel::OM->Get('Kernel::System::User')->GetPreferences(
1015            UserID => $Self->{UserID},
1016        );
1017        my $PrefKeyStatsConfiguration = 'UserDashboardStatsStatsConfiguration' . $Param{Name};
1018        my $StatsSettings             = {};
1019        if ( $Preferences{$PrefKeyStatsConfiguration} ) {
1020            $StatsSettings = $Kernel::OM->Get('Kernel::System::JSON')->Decode(
1021                Data => $Preferences{$PrefKeyStatsConfiguration},
1022            );
1023        }
1024        @StatArray = @{
1025            $Kernel::OM->Get('Kernel::System::Stats')->StatsResultCacheGet(
1026                StatID       => $Param{StatID},
1027                UserGetParam => $StatsSettings,
1028                UserID       => $Self->{UserID},
1029            );
1030        };
1031    }
1032
1033    # called normally within the stats area - generate stats now and use provided configuraton
1034    else {
1035        @StatArray = @{
1036            $Kernel::OM->Get('Kernel::System::Stats')->StatsRun(
1037                StatID   => $Param{StatID},
1038                GetParam => \%GetParam,
1039                UserID   => $Self->{UserID},
1040            );
1041        };
1042    }
1043
1044    return $Kernel::OM->Get('Kernel::Output::HTML::Statistics::View')->StatsResultRender(
1045        StatArray => \@StatArray,
1046        Stat      => $Stat,
1047        TimeZone  => $GetParam{TimeZone},
1048        UserID    => $Self->{UserID},
1049        %Param
1050    );
1051}
1052
1053sub GeneralSpecificationsWidgetAJAX {
1054
1055    my ( $Self, %Param ) = @_;
1056
1057    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
1058    return $LayoutObject->Attachment(
1059        ContentType => 'text/html',
1060        Content     => $Kernel::OM->Get('Kernel::Output::HTML::Statistics::View')
1061            ->GeneralSpecificationsWidget( UserID => $Self->{UserID} ),
1062        Type    => 'inline',
1063        NoCache => 1,
1064    );
1065}
1066
1067# ATTENTION: this function delivers only approximations!!!
1068sub _TimeInSeconds {
1069    my ( $Self, %Param ) = @_;
1070
1071    # check if need params are available
1072    if ( !$Param{TimeUnit} ) {
1073        return $Kernel::OM->Get('Kernel::Output::HTML::Layout')
1074            ->ErrorScreen( Message => '_TimeInSeconds: Need TimeUnit!' );
1075    }
1076
1077    my %TimeInSeconds = (
1078        Year     => 31536000,    # 60 * 60 * 24 * 365
1079        HalfYear => 15724800,    # 60 * 60 * 24 * 182
1080        Quarter  => 7862400,     # 60 * 60 * 24 * 91
1081        Month    => 2592000,     # 60 * 60 * 24 * 30
1082        Week     => 604800,      # 60 * 60 * 24 * 7
1083        Day      => 86400,       # 60 * 60 * 24
1084        Hour     => 3600,        # 60 * 60
1085        Minute   => 60,
1086        Second   => 1,
1087    );
1088
1089    return $TimeInSeconds{ $Param{TimeUnit} };
1090}
1091
10921;
1093