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