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::System::DynamicField::Driver::BaseDateTime; 10 11use strict; 12use warnings; 13 14use Kernel::System::VariableCheck qw(:all); 15use Kernel::Language qw(Translatable); 16 17use parent qw(Kernel::System::DynamicField::Driver::Base); 18 19our @ObjectDependencies = ( 20 'Kernel::System::DateTime', 21 'Kernel::System::DB', 22 'Kernel::System::DynamicFieldValue', 23 'Kernel::System::Log', 24); 25 26=head1 NAME 27 28Kernel::System::DynamicField::Driver::BaseDateTime - sub module of 29Kernel::System::DynamicField::Driver::Date and 30Kernel::System::DynamicField::Driver::DateTime 31 32=head1 DESCRIPTION 33 34Date common functions. 35 36=head1 PUBLIC INTERFACE 37 38=cut 39 40sub ValueGet { 41 my ( $Self, %Param ) = @_; 42 43 my $DFValue = $Kernel::OM->Get('Kernel::System::DynamicFieldValue')->ValueGet( 44 FieldID => $Param{DynamicFieldConfig}->{ID}, 45 ObjectID => $Param{ObjectID}, 46 ); 47 48 return if !$DFValue; 49 return if !IsArrayRefWithData($DFValue); 50 return if !IsHashRefWithData( $DFValue->[0] ); 51 52 return $DFValue->[0]->{ValueDateTime}; 53} 54 55sub ValueSet { 56 my ( $Self, %Param ) = @_; 57 58 my $Success = $Kernel::OM->Get('Kernel::System::DynamicFieldValue')->ValueSet( 59 FieldID => $Param{DynamicFieldConfig}->{ID}, 60 ObjectID => $Param{ObjectID}, 61 Value => [ 62 { 63 ValueDateTime => $Param{Value}, 64 }, 65 ], 66 UserID => $Param{UserID}, 67 ); 68 69 return $Success; 70} 71 72sub ValueValidate { 73 my ( $Self, %Param ) = @_; 74 75 my $Prefix = 'DynamicField_' . $Param{DynamicFieldConfig}->{Name}; 76 my $DateRestriction = $Param{DynamicFieldConfig}->{Config}->{DateRestriction}; 77 78 my $Success = $Kernel::OM->Get('Kernel::System::DynamicFieldValue')->ValueValidate( 79 Value => { 80 ValueDateTime => $Param{Value}, 81 }, 82 UserID => $Param{UserID} 83 ); 84 85 if ($DateRestriction) { 86 87 # get time object 88 my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); 89 90 my $SystemTime = $DateTimeObject->ToEpoch(); 91 92 my $ValueSystemTime = $DateTimeObject->Set( 93 String => $Param{Value}, 94 ); 95 $ValueSystemTime = $ValueSystemTime ? $DateTimeObject->ToEpoch() : undef; 96 97 if ( $DateRestriction eq 'DisableFutureDates' && $ValueSystemTime > $SystemTime ) { 98 $Kernel::OM->Get('Kernel::System::Log')->Log( 99 Priority => 'error', 100 Message => 101 "The value for the Data field ($Param{DynamicFieldConfig}->{Name}) is in the future! The date needs to be in the past!", 102 ); 103 return; 104 } 105 elsif ( $DateRestriction eq 'DisablePastDates' && $ValueSystemTime < $SystemTime ) { 106 $Kernel::OM->Get('Kernel::System::Log')->Log( 107 Priority => 'error', 108 Message => 109 "The value for the Date field ($Param{DynamicFieldConfig}->{Name}) is in the past! The date needs to be in the future!", 110 ); 111 return; 112 } 113 } 114 115 return $Success; 116} 117 118sub SearchSQLGet { 119 my ( $Self, %Param ) = @_; 120 121 my %Operators = ( 122 Equals => '=', 123 GreaterThan => '>', 124 GreaterThanEquals => '>=', 125 SmallerThan => '<', 126 SmallerThanEquals => '<=', 127 ); 128 129 if ( $Param{Operator} eq 'Empty' ) { 130 if ( $Param{SearchTerm} ) { 131 return " $Param{TableAlias}.value_date IS NULL "; 132 } 133 else { 134 return " $Param{TableAlias}.value_date IS NOT NULL "; 135 } 136 } 137 elsif ( !$Operators{ $Param{Operator} } ) { 138 $Kernel::OM->Get('Kernel::System::Log')->Log( 139 'Priority' => 'error', 140 'Message' => "Unsupported Operator $Param{Operator}", 141 ); 142 return; 143 } 144 145 my $SQL = " $Param{TableAlias}.value_date $Operators{ $Param{Operator} } '"; 146 $SQL .= $Kernel::OM->Get('Kernel::System::DB')->Quote( $Param{SearchTerm} ) . "' "; 147 return $SQL; 148} 149 150sub SearchSQLOrderFieldGet { 151 my ( $Self, %Param ) = @_; 152 153 return "$Param{TableAlias}.value_date"; 154} 155 156sub EditFieldRender { 157 my ( $Self, %Param ) = @_; 158 159 # take config from field config 160 my $FieldConfig = $Param{DynamicFieldConfig}->{Config}; 161 my $FieldName = 'DynamicField_' . $Param{DynamicFieldConfig}->{Name}; 162 my $FieldLabel = $Param{DynamicFieldConfig}->{Label}; 163 164 my $Value; 165 166 # set the field value or default 167 if ( $Param{UseDefaultValue} ) { 168 $Value = $FieldConfig->{DefaultValue} || ''; 169 } 170 171 if ( defined $Param{Value} ) { 172 $Value = $Param{Value}; 173 } 174 175 if ($Value) { 176 my ( $Year, $Month, $Day, $Hour, $Minute, $Second ) = $Value =~ 177 m{ \A ( \d{4} ) - ( \d{2} ) - ( \d{2} ) \s ( \d{2} ) : ( \d{2} ) : ( \d{2} ) \z }xms; 178 179 # If a value is sent this value must be active, then the Used part needs to be set to 1 180 # otherwise user can easily forget to mark the checkbox and this could lead into data 181 # lost (Bug#8258). 182 $FieldConfig->{ $FieldName . 'Used' } = 1; 183 $FieldConfig->{ $FieldName . 'Year' } = $Year; 184 $FieldConfig->{ $FieldName . 'Month' } = $Month; 185 $FieldConfig->{ $FieldName . 'Day' } = $Day; 186 $FieldConfig->{ $FieldName . 'Hour' } = $Hour; 187 $FieldConfig->{ $FieldName . 'Minute' } = $Minute; 188 } 189 190 # extract the dynamic field value from the web request 191 # TransformDates is always needed from EditFieldRender Bug#8452 192 my $FieldValues = $Self->EditFieldValueGet( 193 TransformDates => 1, 194 ReturnValueStructure => 1, 195 %Param, 196 ); 197 198 # set values from ParamObject if present 199 if ( defined $FieldValues && IsHashRefWithData($FieldValues) ) { 200 for my $Type (qw(Used Year Month Day Hour Minute)) { 201 $FieldConfig->{ $FieldName . $Type } = $FieldValues->{ $FieldName . $Type }; 202 } 203 } 204 205 # check and set class if necessary 206 # Bug#9358: Class 'DateSelection' is needed for CustomerInterface 207 my $FieldClass = 'DynamicFieldText DateSelection'; 208 if ( defined $Param{Class} && $Param{Class} ne '' ) { 209 $FieldClass .= ' ' . $Param{Class}; 210 } 211 212 # set field as mandatory 213 if ( $Param{Mandatory} ) { 214 $FieldClass .= ' Validate_Required'; 215 } 216 217 # set error css class 218 if ( $Param{ServerError} ) { 219 $FieldClass .= ' ServerError'; 220 } 221 222 # to set the predefined based on a time difference 223 my $DiffTime = $FieldConfig->{DefaultValue}; 224 if ( !defined $DiffTime || $DiffTime !~ m/^ \s* -? \d+ \s* $/smx ) { 225 $DiffTime = 0; 226 } 227 228 # to set the years range 229 my %YearsPeriodRange; 230 if ( defined $FieldConfig->{YearsPeriod} && $FieldConfig->{YearsPeriod} eq '1' ) { 231 %YearsPeriodRange = ( 232 YearPeriodPast => $FieldConfig->{YearsInPast} || 0, 233 YearPeriodFuture => $FieldConfig->{YearsInFuture} || 0, 234 ); 235 } 236 237 # date restrictions 238 if ( $FieldConfig->{DateRestriction} ) { 239 if ( $FieldConfig->{DateRestriction} eq 'DisablePastDates' ) { 240 $FieldConfig->{ValidateDateInFuture} = 1; 241 } 242 elsif ( $FieldConfig->{DateRestriction} eq 'DisableFutureDates' ) { 243 $FieldConfig->{ValidateDateNotInFuture} = 1; 244 } 245 } 246 247 my $HTMLString = $Param{LayoutObject}->BuildDateSelection( 248 %Param, 249 Prefix => $FieldName, 250 Format => 'DateInputFormatLong', 251 $FieldName . 'Class' => $FieldClass, 252 DiffTime => $DiffTime, 253 $FieldName . Required => $Param{Mandatory} || 0, 254 $FieldName . Optional => 1, 255 Validate => 1, 256 %{$FieldConfig}, 257 %YearsPeriodRange, 258 ); 259 260 if ( $Param{Mandatory} ) { 261 my $DivID = $FieldName . 'UsedError'; 262 263 my $FieldRequiredMessage = $Param{LayoutObject}->{LanguageObject}->Translate("This field is required."); 264 265 # for client side validation 266 $HTMLString .= <<"EOF"; 267 268<div id="$DivID" class="TooltipErrorMessage"> 269 <p> 270 $FieldRequiredMessage 271 </p> 272</div> 273EOF 274 } 275 276 if ( $Param{ServerError} ) { 277 278 my $ErrorMessage = $Param{ErrorMessage} || 'This field is required.'; 279 $ErrorMessage = $Param{LayoutObject}->{LanguageObject}->Translate($ErrorMessage); 280 my $DivID = $FieldName . 'UsedServerError'; 281 282 # for server side validation 283 $HTMLString .= <<"EOF"; 284 285<div id="$DivID" class="TooltipErrorMessage"> 286 <p> 287 $ErrorMessage 288 </p> 289</div> 290EOF 291 } 292 293 # call EditLabelRender on the common Driver 294 my $LabelString = $Self->EditLabelRender( 295 %Param, 296 Mandatory => $Param{Mandatory} || '0', 297 FieldName => $FieldName . 'Used', 298 ); 299 300 my $Data = { 301 Field => $HTMLString, 302 Label => $LabelString, 303 }; 304 305 return $Data; 306} 307 308sub EditFieldValueGet { 309 my ( $Self, %Param ) = @_; 310 311 # set the Prefix as the dynamic field name 312 my $Prefix = 'DynamicField_' . $Param{DynamicFieldConfig}->{Name}; 313 314 my %DynamicFieldValues; 315 316 # check if there is a Template and retrieve the dynamic field value from there 317 if ( IsHashRefWithData( $Param{Template} ) && defined $Param{Template}->{ $Prefix . 'Used' } ) { 318 for my $Type (qw(Used Year Month Day Hour Minute)) { 319 $DynamicFieldValues{ $Prefix . $Type } = $Param{Template}->{ $Prefix . $Type } || 0; 320 } 321 } 322 323 # otherwise get dynamic field value from the web request 324 elsif ( 325 defined $Param{ParamObject} 326 && ref $Param{ParamObject} eq 'Kernel::System::Web::Request' 327 ) 328 { 329 for my $Type (qw(Used Year Month Day Hour Minute)) { 330 $DynamicFieldValues{ $Prefix . $Type } = $Param{ParamObject}->GetParam( 331 Param => $Prefix . $Type, 332 ) || 0; 333 } 334 } 335 336 # return if the field is empty (e.g. initial screen) 337 return if !$DynamicFieldValues{ $Prefix . 'Used' } 338 && !$DynamicFieldValues{ $Prefix . 'Year' } 339 && !$DynamicFieldValues{ $Prefix . 'Month' } 340 && !$DynamicFieldValues{ $Prefix . 'Day' } 341 && !$DynamicFieldValues{ $Prefix . 'Hour' } 342 && !$DynamicFieldValues{ $Prefix . 'Minute' }; 343 344 # check if need and can transform dates 345 # transform the dates early for ReturnValueStructure or ManualTimeStamp Bug#8452 346 if ( $Param{TransformDates} && $Param{LayoutObject} ) { 347 348 # transform time stamp based on user time zone 349 %DynamicFieldValues = $Param{LayoutObject}->TransformDateSelection( 350 %DynamicFieldValues, 351 Prefix => $Prefix, 352 ); 353 } 354 355 # check if return value structure is needed 356 if ( defined $Param{ReturnValueStructure} && $Param{ReturnValueStructure} eq '1' ) { 357 return \%DynamicFieldValues; 358 } 359 360 # check if return template structure is needed 361 if ( defined $Param{ReturnTemplateStructure} && $Param{ReturnTemplateStructure} eq '1' ) { 362 return \%DynamicFieldValues; 363 } 364 365 # add seconds as 0 to the DynamicFieldValues hash 366 $DynamicFieldValues{ 'DynamicField_' . $Param{DynamicFieldConfig}->{Name} . 'Second' } = 0; 367 368 my $ManualTimeStamp = ''; 369 370 if ( $DynamicFieldValues{ $Prefix . 'Used' } ) { 371 372 # add a leading zero for date parts that could be less than ten to generate a correct 373 # time stamp 374 for my $Type (qw(Month Day Hour Minute Second)) { 375 $DynamicFieldValues{ $Prefix . $Type } = sprintf "%02d", 376 $DynamicFieldValues{ $Prefix . $Type }; 377 } 378 379 my $Year = $DynamicFieldValues{ $Prefix . 'Year' } || '0000'; 380 my $Month = $DynamicFieldValues{ $Prefix . 'Month' } || '00'; 381 my $Day = $DynamicFieldValues{ $Prefix . 'Day' } || '00'; 382 my $Hour = $DynamicFieldValues{ $Prefix . 'Hour' } || '00'; 383 my $Minute = $DynamicFieldValues{ $Prefix . 'Minute' } || '00'; 384 my $Second = $DynamicFieldValues{ $Prefix . 'Second' } || '00'; 385 386 $ManualTimeStamp = 387 $Year . '-' . $Month . '-' . $Day . ' ' 388 . $Hour . ':' . $Minute . ':' . $Second; 389 } 390 391 return $ManualTimeStamp; 392} 393 394sub EditFieldValueValidate { 395 my ( $Self, %Param ) = @_; 396 397 # get the field value from the http request 398 my $Value = $Self->EditFieldValueGet( 399 DynamicFieldConfig => $Param{DynamicFieldConfig}, 400 ParamObject => $Param{ParamObject}, 401 ReturnValueStructure => 1, 402 ); 403 404 # on normal basis Used field could be empty but if there was no value from EditFieldValueGet() 405 # it must be an error 406 if ( !defined $Value ) { 407 return { 408 ServerError => 1, 409 ErrorMessage => 'Invalid Date!' 410 }; 411 } 412 413 my $ServerError; 414 my $ErrorMessage; 415 416 # set the date time prefix as field name 417 my $Prefix = 'DynamicField_' . $Param{DynamicFieldConfig}->{Name}; 418 419 # date restriction 420 my $DateRestriction = $Param{DynamicFieldConfig}->{Config}->{DateRestriction}; 421 422 # perform necessary validations 423 if ( $Param{Mandatory} && !$Value->{ $Prefix . 'Used' } ) { 424 $ServerError = 1; 425 } 426 427 if ( $Value->{ $Prefix . 'Used' } && $DateRestriction ) { 428 429 my $Year = $Value->{ $Prefix . 'Year' } || '0000'; 430 my $Month = $Value->{ $Prefix . 'Month' } || '00'; 431 my $Day = $Value->{ $Prefix . 'Day' } || '00'; 432 my $Hour = $Value->{ $Prefix . 'Hour' } || '00'; 433 my $Minute = $Value->{ $Prefix . 'Minute' } || '00'; 434 my $Second = $Value->{ $Prefix . 'Second' } || '00'; 435 436 my $ManualTimeStamp = 437 $Year . '-' . $Month . '-' . $Day . ' ' 438 . $Hour . ':' . $Minute . ':' . $Second; 439 440 # get time object 441 my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); 442 443 my $SystemTime = $DateTimeObject->ToEpoch(); 444 445 my $ValueSystemTime = $DateTimeObject->Set( 446 String => $ManualTimeStamp, 447 ); 448 $ValueSystemTime = $ValueSystemTime ? $DateTimeObject->ToEpoch() : undef; 449 450 if ( $DateRestriction eq 'DisableFutureDates' && $ValueSystemTime > $SystemTime ) { 451 $ServerError = 1; 452 $ErrorMessage = "Invalid date (need a past date)!"; 453 } 454 elsif ( $DateRestriction eq 'DisablePastDates' && $ValueSystemTime < $SystemTime ) { 455 $ServerError = 1; 456 $ErrorMessage = "Invalid date (need a future date)!"; 457 } 458 } 459 460 # create resulting structure 461 my $Result = { 462 ServerError => $ServerError, 463 ErrorMessage => $ErrorMessage, 464 }; 465 466 return $Result; 467} 468 469sub DisplayValueRender { 470 my ( $Self, %Param ) = @_; 471 472 my $Value = ''; 473 474 # convert date to localized string 475 if ( defined $Param{Value} ) { 476 $Value = $Param{LayoutObject}->{LanguageObject}->FormatTimeString( 477 $Param{Value}, 478 'DateFormat', 479 'NoSeconds', 480 ); 481 } 482 483 # in this Driver there is no need for HTMLOutput 484 # Title is always equal to Value 485 my $Title = $Value; 486 487 # set field link form config 488 my $Link = $Param{DynamicFieldConfig}->{Config}->{Link} || ''; 489 my $LinkPreview = $Param{DynamicFieldConfig}->{Config}->{LinkPreview} || ''; 490 491 my $Data = { 492 Value => $Value, 493 Title => $Title, 494 Link => $Link, 495 LinkPreview => $LinkPreview, 496 }; 497 498 return $Data; 499} 500 501sub SearchFieldRender { 502 my ( $Self, %Param ) = @_; 503 504 # take config from field config 505 my $FieldConfig = $Param{DynamicFieldConfig}->{Config}; 506 my $FieldName = 'Search_DynamicField_' . $Param{DynamicFieldConfig}->{Name}; 507 508 # set the default type 509 $Param{Type} ||= 'TimeSlot'; 510 511 # add type to FieldName 512 $FieldName .= $Param{Type}; 513 514 my $FieldLabel = $Param{DynamicFieldConfig}->{Label}; 515 516 my $Value; 517 518 my %DefaultValue; 519 520 if ( defined $Param{DefaultValue} ) { 521 my @Items = split /;/, $Param{DefaultValue}; 522 523# format example of the key name for TimePoint: 524# 525# Search_DynamicField_DateTest1TimePointFormat=week;Search_DynamicField_DateTest1TimePointStart=Before;Search_DynamicField_DateTest1TimePointValue=7; 526 527# format example of the key name for TimeSlot: 528# 529# Search_DynamicField_DateTest1TimeSlotStartYear=1974;Search_DynamicField_DateTest1TimeSlotStartMonth=01;Search_DynamicField_DateTest1TimeSlotStartDay=26; 530# Search_DynamicField_DateTest1TimeSlotStartHour=00;Search_DynamicField_DateTest1TimeSlotStartMinute=00;Search_DynamicField_DateTest1TimeSlotStartSecond=00; 531# Search_DynamicField_DateTest1TimeSlotStopYear=2013;Search_DynamicField_DateTest1TimeSlotStopMonth=01;Search_DynamicField_DateTest1TimeSlotStopDay=26; 532# Search_DynamicField_DateTest1TimeSlotStopHour=23;Search_DynamicField_DateTest1TimeSlotStopMinute=59;Search_DynamicField_DateTest1TimeSlotStopSecond=59; 533 534 my $KeyName = 'Search_DynamicField_' . $Param{DynamicFieldConfig}->{Name} . $Param{Type}; 535 536 ITEM: 537 for my $Item (@Items) { 538 my ( $Key, $Value ) = split /=/, $Item; 539 540 # only handle keys that match the current type 541 next ITEM if $Key !~ m{ $Param{Type} }xms; 542 543 if ( $Param{Type} eq 'TimePoint' ) { 544 545 if ( $Key eq $KeyName . 'Format' ) { 546 $DefaultValue{Format}->{$Key} = $Value; 547 } 548 elsif ( $Key eq $KeyName . 'Start' ) { 549 $DefaultValue{Start}->{$Key} = $Value; 550 } 551 elsif ( $Key eq $KeyName . 'Value' ) { 552 $DefaultValue{Value}->{$Key} = $Value; 553 } 554 555 next ITEM; 556 } 557 558 if ( $Key =~ m{Start} ) { 559 $DefaultValue{ValueStart}->{$Key} = $Value; 560 } 561 elsif ( $Key =~ m{Stop} ) { 562 $DefaultValue{ValueStop}->{$Key} = $Value; 563 } 564 } 565 } 566 567 # set the field value 568 if (%DefaultValue) { 569 $Value = \%DefaultValue; 570 } 571 572 # get the field value, this function is always called after the profile is loaded 573 my $FieldValues = $Self->SearchFieldValueGet( 574 %Param, 575 ); 576 577 if ( 578 defined $FieldValues 579 && $Param{Type} eq 'TimeSlot' 580 && defined $FieldValues->{ValueStart} 581 && defined $FieldValues->{ValueStop} 582 ) 583 { 584 $Value = $FieldValues; 585 } 586 587 elsif ( 588 defined $FieldValues 589 && $Param{Type} eq 'TimePoint' 590 && defined $FieldValues->{Format} 591 && defined $FieldValues->{Start} 592 && defined $FieldValues->{Value} 593 ) 594 { 595 $Value = $FieldValues; 596 } 597 598 # check and set class if necessary 599 my $FieldClass = 'DynamicFieldDateTime'; 600 601 # set as checked if necessary 602 my $FieldChecked = ( defined $Value->{$FieldName} && $Value->{$FieldName} == 1 ? 'checked="checked"' : '' ); 603 604 my $HTMLString = <<"EOF"; 605 <input type="hidden" id="$FieldName" name="$FieldName" value="1"/> 606EOF 607 608 if ( $Param{ConfirmationCheckboxes} ) { 609 $HTMLString = <<"EOF"; 610 <input type="checkbox" id="$FieldName" name="$FieldName" value="1" $FieldChecked/> 611EOF 612 } 613 614 # build HTML for TimePoint 615 if ( $Param{Type} eq 'TimePoint' ) { 616 617 $HTMLString .= $Param{LayoutObject}->BuildSelection( 618 Data => { 619 'Before' => Translatable('more than ... ago'), 620 'Last' => Translatable('within the last ...'), 621 'Next' => Translatable('within the next ...'), 622 'After' => Translatable('in more than ...'), 623 }, 624 Sort => 'IndividualKey', 625 SortIndividual => [ 'Before', 'Last', 'Next', 'After' ], 626 Name => $FieldName . 'Start', 627 SelectedID => $Value->{Start}->{ $FieldName . 'Start' } || 'Last', 628 ); 629 $HTMLString .= ' ' . $Param{LayoutObject}->BuildSelection( 630 Data => [ 1 .. 59 ], 631 Name => $FieldName . 'Value', 632 SelectedID => $Value->{Value}->{ $FieldName . 'Value' } || 1, 633 ); 634 $HTMLString .= ' ' . $Param{LayoutObject}->BuildSelection( 635 Data => { 636 minute => Translatable('minute(s)'), 637 hour => Translatable('hour(s)'), 638 day => Translatable('day(s)'), 639 week => Translatable('week(s)'), 640 month => Translatable('month(s)'), 641 year => Translatable('year(s)'), 642 }, 643 Name => $FieldName . 'Format', 644 SelectedID => $Value->{Format}->{ $FieldName . 'Format' } || 'day', 645 ); 646 647 my $AdditionalText; 648 if ( $Param{UseLabelHints} ) { 649 $AdditionalText = Translatable('before/after'); 650 } 651 652 # call EditLabelRender on the common backend 653 my $LabelString = $Self->EditLabelRender( 654 %Param, 655 FieldName => $FieldName, 656 AdditionalText => $AdditionalText, 657 ); 658 659 my $Data = { 660 Field => $HTMLString, 661 Label => $LabelString, 662 }; 663 664 return $Data; 665 } 666 667 # to set the years range 668 my %YearsPeriodRange; 669 if ( defined $FieldConfig->{YearsPeriod} && $FieldConfig->{YearsPeriod} eq '1' ) { 670 %YearsPeriodRange = ( 671 YearPeriodPast => $FieldConfig->{YearsInPast} || 0, 672 YearPeriodFuture => $FieldConfig->{YearsInFuture} || 0, 673 ); 674 } 675 676 # build HTML for start value set 677 $HTMLString .= $Param{LayoutObject}->BuildDateSelection( 678 %Param, 679 Prefix => $FieldName . 'Start', 680 Format => 'DateInputFormatLong', 681 $FieldName . 'Class' => $FieldClass, 682 DiffTime => -( ( 60 * 60 * 24 ) * 30 ), 683 Validate => 1, 684 %{ $Value->{ValueStart} }, 685 %YearsPeriodRange, 686 OverrideTimeZone => 1, 687 ); 688 689 # to put a line break between the two search dates 690 my $LineBreak = ' <br/>'; 691 692 # in screens where the confirmation checkboxes is set, there is no need to render the filed in 693 # two lines (e.g. AdminGenericAgentn CustomerTicketSearch) 694 if ( $Param{ConfirmationCheckboxes} ) { 695 $LineBreak = ''; 696 } 697 698 $HTMLString .= ' ' . $Param{LayoutObject}->{LanguageObject}->Translate("and") . "$LineBreak\n"; 699 700 # build HTML for stop value set 701 $HTMLString .= $Param{LayoutObject}->BuildDateSelection( 702 %Param, 703 Prefix => $FieldName . 'Stop', 704 Format => 'DateInputFormatLong', 705 $FieldName . 'Class' => $FieldClass, 706 DiffTime => +( ( 60 * 60 * 24 ) * 30 ), 707 Validate => 1, 708 %{ $Value->{ValueStop} }, 709 %YearsPeriodRange, 710 OverrideTimeZone => 1, 711 ); 712 713 my $AdditionalText; 714 if ( $Param{UseLabelHints} ) { 715 $AdditionalText = Translatable('between'); 716 } 717 718 # call EditLabelRender on the common Driver 719 my $LabelString = $Self->EditLabelRender( 720 %Param, 721 FieldName => $FieldName, 722 AdditionalText => $AdditionalText, 723 ); 724 725 my $Data = { 726 Field => $HTMLString, 727 Label => $LabelString, 728 }; 729 730 return $Data; 731} 732 733sub SearchFieldValueGet { 734 my ( $Self, %Param ) = @_; 735 736 # set the Prefix as the dynamic field name 737 my $Prefix = 'Search_DynamicField_' . $Param{DynamicFieldConfig}->{Name}; 738 739 # set the default type 740 $Param{Type} ||= 'TimeSlot'; 741 742 # add type to prefix 743 $Prefix .= $Param{Type}; 744 745 if ( $Param{Type} eq 'TimePoint' ) { 746 747 # get dynamic field value 748 my %DynamicFieldValues; 749 for my $Type (qw(Start Value Format)) { 750 751 # get dynamic field value form param object 752 if ( defined $Param{ParamObject} ) { 753 754 # return if value was not checked (useful in customer interface) 755 return if !$Param{ParamObject}->GetParam( Param => $Prefix ); 756 757 $DynamicFieldValues{ $Prefix . $Type } = $Param{ParamObject}->GetParam( 758 Param => $Prefix . $Type, 759 ); 760 } 761 762 # otherwise get the value from the profile 763 elsif ( defined $Param{Profile} ) { 764 765 # return if value was not checked (useful in customer interface) 766 return if !$Param{Profile}->{$Prefix}; 767 768 $DynamicFieldValues{ $Prefix . $Type } = $Param{Profile}->{ $Prefix . $Type }; 769 } 770 else { 771 return; 772 } 773 } 774 775 # return if the field is empty (e.g. initial screen) 776 return if !$DynamicFieldValues{ $Prefix . 'Start' } 777 && !$DynamicFieldValues{ $Prefix . 'Value' } 778 && !$DynamicFieldValues{ $Prefix . 'Format' }; 779 780 $DynamicFieldValues{$Prefix} = 1; 781 782 # check if return value structure is needed 783 if ( defined $Param{ReturnProfileStructure} && $Param{ReturnProfileStructure} eq '1' ) { 784 return \%DynamicFieldValues; 785 } 786 787 return { 788 Format => { 789 $Prefix . 'Format' => $DynamicFieldValues{ $Prefix . 'Format' } || 'Last', 790 }, 791 Start => { 792 $Prefix . 'Start' => $DynamicFieldValues{ $Prefix . 'Start' } || 'day', 793 }, 794 Value => { 795 $Prefix . 'Value' => $DynamicFieldValues{ $Prefix . 'Value' } || 1, 796 }, 797 $Prefix => 1, 798 }; 799 } 800 801 # get dynamic field value 802 my %DynamicFieldValues; 803 for my $Type (qw(Start Stop)) { 804 for my $Part (qw(Year Month Day Hour Minute)) { 805 806 # get dynamic field value from param object 807 if ( defined $Param{ParamObject} ) { 808 809 # return if value was not checked (useful in customer interface) 810 return if !$Param{ParamObject}->GetParam( Param => $Prefix ); 811 812 $DynamicFieldValues{ $Prefix . $Type . $Part } = $Param{ParamObject}->GetParam( 813 Param => $Prefix . $Type . $Part, 814 ); 815 } 816 817 # otherwise get the value from the profile 818 elsif ( defined $Param{Profile} ) { 819 820 # return if value was not checked (useful in customer interface) 821 return if !$Param{Profile}->{$Prefix}; 822 823 $DynamicFieldValues{ $Prefix . $Type . $Part } = $Param{Profile}->{ $Prefix . $Type . $Part }; 824 } 825 else { 826 return; 827 } 828 } 829 } 830 831 # return if the field is empty (e.g. initial screen) 832 return if !$DynamicFieldValues{ $Prefix . 'StartYear' } 833 && !$DynamicFieldValues{ $Prefix . 'StartMonth' } 834 && !$DynamicFieldValues{ $Prefix . 'StartDay' } 835 && !$DynamicFieldValues{ $Prefix . 'StopYear' } 836 && !$DynamicFieldValues{ $Prefix . 'StopMonth' } 837 && !$DynamicFieldValues{ $Prefix . 'StopDay' }; 838 839 $DynamicFieldValues{ $Prefix . 'StartSecond' } = '00'; 840 $DynamicFieldValues{ $Prefix . 'StopSecond' } = '59'; 841 842 $DynamicFieldValues{$Prefix} = 1; 843 844 # check if return value structure is needed 845 if ( defined $Param{ReturnProfileStructure} && $Param{ReturnProfileStructure} eq '1' ) { 846 return \%DynamicFieldValues; 847 } 848 849 # add a leading zero for date parts that could be less than ten to generate a correct 850 # time stamp 851 for my $Type (qw(Start Stop)) { 852 for my $Part (qw(Month Day Hour Minute Second)) { 853 $DynamicFieldValues{ $Prefix . $Type . $Part } = sprintf "%02d", 854 $DynamicFieldValues{ $Prefix . $Type . $Part }; 855 } 856 } 857 858 my $ValueStart = { 859 $Prefix . 'StartYear' => $DynamicFieldValues{ $Prefix . 'StartYear' } || '0000', 860 $Prefix . 'StartMonth' => $DynamicFieldValues{ $Prefix . 'StartMonth' } || '00', 861 $Prefix . 'StartDay' => $DynamicFieldValues{ $Prefix . 'StartDay' } || '00', 862 $Prefix . 'StartHour' => $DynamicFieldValues{ $Prefix . 'StartHour' } || '00', 863 $Prefix . 'StartMinute' => $DynamicFieldValues{ $Prefix . 'StartMinute' } || '00', 864 $Prefix . 'StartSecond' => $DynamicFieldValues{ $Prefix . 'StartSecond' } || '00', 865 }; 866 867 my $ValueStop = { 868 $Prefix . 'StopYear' => $DynamicFieldValues{ $Prefix . 'StopYear' } || '0000', 869 $Prefix . 'StopMonth' => $DynamicFieldValues{ $Prefix . 'StopMonth' } || '00', 870 $Prefix . 'StopDay' => $DynamicFieldValues{ $Prefix . 'StopDay' } || '00', 871 $Prefix . 'StopHour' => $DynamicFieldValues{ $Prefix . 'StopHour' } || '00', 872 $Prefix . 'StopMinute' => $DynamicFieldValues{ $Prefix . 'StopMinute' } || '00', 873 $Prefix . 'StopSecond' => $DynamicFieldValues{ $Prefix . 'StopSecond' } || '00', 874 }; 875 876 return { 877 $Prefix => 1, 878 ValueStart => $ValueStart, 879 ValueStop => $ValueStop, 880 }; 881} 882 883sub SearchFieldPreferences { 884 my ( $Self, %Param ) = @_; 885 886 my @Preferences = ( 887 { 888 Type => 'TimePoint', 889 LabelSuffix => 'before/after', 890 }, 891 { 892 Type => 'TimeSlot', 893 LabelSuffix => 'between', 894 }, 895 ); 896 897 return \@Preferences; 898} 899 900sub SearchFieldParameterBuild { 901 my ( $Self, %Param ) = @_; 902 903 # set the default type 904 $Param{Type} ||= 'TimeSlot'; 905 906 # get field value 907 my $Value = $Self->SearchFieldValueGet(%Param); 908 909 my $DisplayValue; 910 911 if ( defined $Value && !$Value ) { 912 $DisplayValue = ''; 913 } 914 915 # do not search if value was not checked (useful for customer interface) 916 if ( !$Value ) { 917 return { 918 Parameter => { 919 Equals => $Value, 920 }, 921 Display => $DisplayValue, 922 }; 923 } 924 925 # search for a wild card in the value 926 if ( $Value && IsHashRefWithData($Value) ) { 927 928 my $Prefix = 'Search_DynamicField_' . $Param{DynamicFieldConfig}->{Name}; 929 930 $Prefix .= $Param{Type}; 931 932 if ( 933 $Param{Type} eq 'TimePoint' 934 && $Value->{Start}->{ $Prefix . 'Start' } 935 && $Value->{Format}->{ $Prefix . 'Format' } 936 && $Value->{Value}->{ $Prefix . 'Value' } 937 && $Value->{$Prefix} 938 ) 939 { 940 941 # to store the search parameters 942 my %Parameter; 943 944 # store in local variables for easier handling 945 my $Format = $Value->{Format}->{ $Prefix . 'Format' }; 946 my $Start = $Value->{Start}->{ $Prefix . 'Start' }; 947 my $Value = $Value->{Value}->{ $Prefix . 'Value' }; 948 949 my $DiffTimeMinutes = 0; 950 if ( $Format eq 'minute' ) { 951 $DiffTimeMinutes = $Value; 952 } 953 elsif ( $Format eq 'hour' ) { 954 $DiffTimeMinutes = $Value * 60; 955 } 956 elsif ( $Format eq 'day' ) { 957 $DiffTimeMinutes = $Value * 60 * 24; 958 } 959 elsif ( $Format eq 'week' ) { 960 $DiffTimeMinutes = $Value * 60 * 24 * 7; 961 } 962 elsif ( $Format eq 'month' ) { 963 $DiffTimeMinutes = $Value * 60 * 24 * 30; 964 } 965 elsif ( $Format eq 'year' ) { 966 $DiffTimeMinutes = $Value * 60 * 24 * 365; 967 } 968 969 # get time object 970 my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); 971 972 # get the current time in epoch seconds and as time-stamp 973 my $Now = $DateTimeObject->ToEpoch(); 974 my $NowTimeStamp = $DateTimeObject->ToString(); 975 976 # calculate difference time seconds 977 my $DiffTimeSeconds = $DiffTimeMinutes * 60; 978 979 my $DisplayValue = ''; 980 981 # define to search before or after that time stamp 982 if ( $Start eq 'Before' ) { 983 984 # we must subtract the difference because it is in the past 985 my $DateTimeObjectBefore = $Kernel::OM->Create( 986 'Kernel::System::DateTime', 987 ObjectParams => { 988 Epoch => $Now - $DiffTimeSeconds, 989 } 990 ); 991 992 # only search dates in the past (before the time stamp) 993 $Parameter{SmallerThanEquals} = $DateTimeObjectBefore->ToString(); 994 995 # set the display value 996 $DisplayValue = '<= ' . $DateTimeObjectBefore->ToString(); 997 } 998 elsif ( $Start eq 'Last' ) { 999 1000 # we must subtract the differences because it is in the past 1001 my $DateTimeObjectLast = $Kernel::OM->Create( 1002 'Kernel::System::DateTime', 1003 ObjectParams => { 1004 Epoch => $Now - $DiffTimeSeconds, 1005 } 1006 ); 1007 1008 # search dates in the past (after the time stamp and up to now) 1009 $Parameter{GreaterThanEquals} = $DateTimeObjectLast->ToString(); 1010 $Parameter{SmallerThanEquals} = $NowTimeStamp; 1011 1012 # set the display value 1013 $DisplayValue = $DateTimeObjectLast->ToString() . ' - ' . $NowTimeStamp; 1014 } 1015 elsif ( $Start eq 'Next' ) { 1016 1017 # we must add the difference because it is in the future 1018 my $DateTimeObjectNext = $Kernel::OM->Create( 1019 'Kernel::System::DateTime', 1020 ObjectParams => { 1021 Epoch => $Now + $DiffTimeSeconds, 1022 } 1023 ); 1024 1025 # search dates in the future (after now and up to the time stamp) 1026 $Parameter{GreaterThanEquals} = $NowTimeStamp; 1027 $Parameter{SmallerThanEquals} = $DateTimeObjectNext->ToString(); 1028 1029 # set the display value 1030 $DisplayValue = $NowTimeStamp . ' - ' . $DateTimeObjectNext->ToString(); 1031 } 1032 elsif ( $Start eq 'After' ) { 1033 1034 # we must add the difference because it is in the future 1035 my $DateTimeObjectAfter = $Kernel::OM->Create( 1036 'Kernel::System::DateTime', 1037 ObjectParams => { 1038 Epoch => $Now + $DiffTimeSeconds, 1039 } 1040 ); 1041 1042 # only search dates in the future (after the time stamp) 1043 $Parameter{GreaterThanEquals} = $DateTimeObjectAfter->ToString(); 1044 1045 # set the display value 1046 $DisplayValue = '>= ' . $DateTimeObjectAfter->ToString(); 1047 } 1048 1049 # return search parameter structure 1050 return { 1051 Parameter => \%Parameter, 1052 Display => $DisplayValue, 1053 }; 1054 } 1055 1056 my $ValueStart = $Value->{ValueStart}->{ $Prefix . 'StartYear' } . '-' 1057 . $Value->{ValueStart}->{ $Prefix . 'StartMonth' } . '-' 1058 . $Value->{ValueStart}->{ $Prefix . 'StartDay' } . ' ' 1059 . $Value->{ValueStart}->{ $Prefix . 'StartHour' } . ':' 1060 . $Value->{ValueStart}->{ $Prefix . 'StartMinute' } . ':' 1061 . $Value->{ValueStart}->{ $Prefix . 'StartSecond' }; 1062 1063 my $ValueStop = $Value->{ValueStop}->{ $Prefix . 'StopYear' } . '-' 1064 . $Value->{ValueStop}->{ $Prefix . 'StopMonth' } . '-' 1065 . $Value->{ValueStop}->{ $Prefix . 'StopDay' } . ' ' 1066 . $Value->{ValueStop}->{ $Prefix . 'StopHour' } . ':' 1067 . $Value->{ValueStop}->{ $Prefix . 'StopMinute' } . ':' 1068 . $Value->{ValueStop}->{ $Prefix . 'StopSecond' }; 1069 1070 # return search parameter structure 1071 return { 1072 Parameter => { 1073 GreaterThanEquals => $ValueStart, 1074 SmallerThanEquals => $ValueStop, 1075 }, 1076 Display => $ValueStart . ' - ' . $ValueStop, 1077 }; 1078 } 1079 1080 return; 1081} 1082 1083sub StatsFieldParameterBuild { 1084 my ( $Self, %Param ) = @_; 1085 1086 return { 1087 Name => $Param{DynamicFieldConfig}->{Label}, 1088 Element => 'DynamicField_' . $Param{DynamicFieldConfig}->{Name}, 1089 TimePeriodFormat => 'DateInputFormatLong', 1090 Block => 'Time', 1091 }; 1092} 1093 1094sub StatsSearchFieldParameterBuild { 1095 my ( $Self, %Param ) = @_; 1096 1097 my $Value = $Param{Value}; 1098 1099 # set operator 1100 my $Operator = $Param{Operator}; 1101 return {} if !$Operator; 1102 1103 return { 1104 $Operator => $Value, 1105 }; 1106} 1107 1108sub ReadableValueRender { 1109 my ( $Self, %Param ) = @_; 1110 1111 my $Value = defined $Param{Value} ? $Param{Value} : ''; 1112 1113 # only keep date and time without seconds or milliseconds 1114 $Value =~ s{\A (\d{4} - \d{2} - \d{2} [ ] \d{2} : \d{2} ) }{$1}xms; 1115 1116 # Title is always equal to Value 1117 my $Title = $Value; 1118 1119 my $Data = { 1120 Value => $Value, 1121 Title => $Title, 1122 }; 1123 1124 return $Data; 1125} 1126 1127sub TemplateValueTypeGet { 1128 my ( $Self, %Param ) = @_; 1129 1130 my $FieldName = 'DynamicField_' . $Param{DynamicFieldConfig}->{Name}; 1131 1132 # set the field types 1133 my $EditValueType = 'SCALAR'; 1134 my $SearchValueType = 'SCALAR'; 1135 1136 # return the correct structure 1137 if ( $Param{FieldType} eq 'Edit' ) { 1138 return { 1139 $FieldName => $EditValueType, 1140 }; 1141 } 1142 elsif ( $Param{FieldType} eq 'Search' ) { 1143 return { 1144 'Search_' . $FieldName => $SearchValueType, 1145 }; 1146 } 1147 else { 1148 return { 1149 $FieldName => $EditValueType, 1150 'Search_' . $FieldName => $SearchValueType, 1151 }; 1152 } 1153} 1154 1155sub RandomValueSet { 1156 my ( $Self, %Param ) = @_; 1157 1158 my $YearValue = int( rand(40) ) + 1_990; 1159 my $MonthValue = int( rand(9) ) + 1; 1160 my $DayValue = int( rand(10) ) + 10; 1161 my $HourValue = int( rand(12) ) + 10; 1162 my $MinuteValue = int( rand(30) ) + 10; 1163 my $SecondValue = int( rand(30) ) + 10; 1164 1165 my $Value = $YearValue . '-0' . $MonthValue . '-' . $DayValue . ' ' 1166 . $HourValue . ':' . $MinuteValue . ':' . $SecondValue; 1167 1168 my $Success = $Self->ValueSet( 1169 %Param, 1170 Value => $Value, 1171 ); 1172 1173 if ( !$Success ) { 1174 return { 1175 Success => 0, 1176 }; 1177 } 1178 return { 1179 Success => 1, 1180 Value => $Value, 1181 }; 1182} 1183 1184sub ObjectMatch { 1185 my ( $Self, %Param ) = @_; 1186 1187 my $FieldName = 'DynamicField_' . $Param{DynamicFieldConfig}->{Name}; 1188 1189 # not supported 1190 return 0; 1191} 1192 1193sub HistoricalValuesGet { 1194 my ( $Self, %Param ) = @_; 1195 1196 # get historical values from database 1197 my $HistoricalValues = $Kernel::OM->Get('Kernel::System::DynamicFieldValue')->HistoricalValueGet( 1198 FieldID => $Param{DynamicFieldConfig}->{ID}, 1199 ValueType => 'DateTime', 1200 ); 1201 1202 # return the historical values from database 1203 return $HistoricalValues; 1204} 1205 1206sub ValueLookup { 1207 my ( $Self, %Param ) = @_; 1208 1209 my $Value = defined $Param{Key} ? $Param{Key} : ''; 1210 1211 # check if a translation is possible 1212 if ( defined $Param{LanguageObject} ) { 1213 1214 # translate value 1215 $Value = $Param{LanguageObject}->FormatTimeString( 1216 $Value, 1217 'DateFormat', 1218 'NoSeconds', 1219 ); 1220 } 1221 1222 return $Value; 1223} 1224 12251; 1226 1227=head1 TERMS AND CONDITIONS 1228 1229This software is part of the OTRS project (L<https://otrs.org/>). 1230 1231This software comes with ABSOLUTELY NO WARRANTY. For details, see 1232the enclosed file COPYING for license information (GPL). If you 1233did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>. 1234 1235=cut 1236