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::Multiselect; 10 11use strict; 12use warnings; 13 14use Kernel::System::VariableCheck qw(:all); 15 16use parent qw(Kernel::System::DynamicField::Driver::BaseSelect); 17 18our @ObjectDependencies = ( 19 'Kernel::Config', 20 'Kernel::System::DynamicFieldValue', 21 'Kernel::System::Log', 22 'Kernel::System::Main', 23); 24 25=head1 NAME 26 27Kernel::System::DynamicField::Driver::Multiselect 28 29=head1 DESCRIPTION 30 31DynamicFields Multiselect Driver delegate 32 33=head1 PUBLIC INTERFACE 34 35This module implements the public interface of L<Kernel::System::DynamicField::Backend>. 36Please look there for a detailed reference of the functions. 37 38=head2 new() 39 40usually, you want to create an instance of this 41by using Kernel::System::DynamicField::Backend->new(); 42 43=cut 44 45sub new { 46 my ( $Type, %Param ) = @_; 47 48 # allocate new hash for object 49 my $Self = {}; 50 bless( $Self, $Type ); 51 52 # set field behaviors 53 $Self->{Behaviors} = { 54 'IsACLReducible' => 1, 55 'IsNotificationEventCondition' => 1, 56 'IsSortable' => 0, 57 'IsFiltrable' => 0, 58 'IsStatsCondition' => 1, 59 'IsCustomerInterfaceCapable' => 1, 60 'IsLikeOperatorCapable' => 1, 61 }; 62 63 # get the Dynamic Field Backend custom extensions 64 my $DynamicFieldDriverExtensions 65 = $Kernel::OM->Get('Kernel::Config')->Get('DynamicFields::Extension::Driver::Multiselect'); 66 67 EXTENSION: 68 for my $ExtensionKey ( sort keys %{$DynamicFieldDriverExtensions} ) { 69 70 # skip invalid extensions 71 next EXTENSION if !IsHashRefWithData( $DynamicFieldDriverExtensions->{$ExtensionKey} ); 72 73 # create a extension config shortcut 74 my $Extension = $DynamicFieldDriverExtensions->{$ExtensionKey}; 75 76 # check if extension has a new module 77 if ( $Extension->{Module} ) { 78 79 # check if module can be loaded 80 if ( 81 !$Kernel::OM->Get('Kernel::System::Main')->RequireBaseClass( $Extension->{Module} ) 82 ) 83 { 84 die "Can't load dynamic fields backend module" 85 . " $Extension->{Module}! $@"; 86 } 87 } 88 89 # check if extension contains more behaviors 90 if ( IsHashRefWithData( $Extension->{Behaviors} ) ) { 91 92 %{ $Self->{Behaviors} } = ( 93 %{ $Self->{Behaviors} }, 94 %{ $Extension->{Behaviors} } 95 ); 96 } 97 } 98 99 return $Self; 100} 101 102sub ValueGet { 103 my ( $Self, %Param ) = @_; 104 105 my $DFValue = $Kernel::OM->Get('Kernel::System::DynamicFieldValue')->ValueGet( 106 FieldID => $Param{DynamicFieldConfig}->{ID}, 107 ObjectID => $Param{ObjectID}, 108 ); 109 110 return if !$DFValue; 111 return if !IsArrayRefWithData($DFValue); 112 return if !IsHashRefWithData( $DFValue->[0] ); 113 114 # extract real values 115 my @ReturnData; 116 for my $Item ( @{$DFValue} ) { 117 push @ReturnData, $Item->{ValueText}; 118 } 119 120 return \@ReturnData; 121} 122 123sub ValueSet { 124 my ( $Self, %Param ) = @_; 125 126 # check for valid possible values list 127 if ( !$Param{DynamicFieldConfig}->{Config}->{PossibleValues} ) { 128 $Kernel::OM->Get('Kernel::System::Log')->Log( 129 Priority => 'error', 130 Message => "Need PossibleValues in DynamicFieldConfig!", 131 ); 132 return; 133 } 134 135 # check value 136 my @Values; 137 if ( ref $Param{Value} eq 'ARRAY' ) { 138 @Values = @{ $Param{Value} }; 139 } 140 else { 141 @Values = ( $Param{Value} ); 142 } 143 144 # get dynamic field value object 145 my $DynamicFieldValueObject = $Kernel::OM->Get('Kernel::System::DynamicFieldValue'); 146 147 my $Success; 148 if ( IsArrayRefWithData( \@Values ) ) { 149 150 # if there is at least one value to set, this means one or more values are selected, 151 # set those values! 152 my @ValueText; 153 for my $Item (@Values) { 154 push @ValueText, { ValueText => $Item }; 155 } 156 157 $Success = $DynamicFieldValueObject->ValueSet( 158 FieldID => $Param{DynamicFieldConfig}->{ID}, 159 ObjectID => $Param{ObjectID}, 160 Value => \@ValueText, 161 UserID => $Param{UserID}, 162 ); 163 } 164 else { 165 166 # otherwise no value was selected, then in fact this means that any value there should be 167 # deleted 168 $Success = $DynamicFieldValueObject->ValueDelete( 169 FieldID => $Param{DynamicFieldConfig}->{ID}, 170 ObjectID => $Param{ObjectID}, 171 UserID => $Param{UserID}, 172 ); 173 } 174 175 return $Success; 176} 177 178sub ValueIsDifferent { 179 my ( $Self, %Param ) = @_; 180 181 # special cases where the values are different but they should be reported as equals 182 if ( 183 !defined $Param{Value1} 184 && ref $Param{Value2} eq 'ARRAY' 185 && !IsArrayRefWithData( $Param{Value2} ) 186 ) 187 { 188 return; 189 } 190 if ( 191 !defined $Param{Value2} 192 && ref $Param{Value1} eq 'ARRAY' 193 && !IsArrayRefWithData( $Param{Value1} ) 194 ) 195 { 196 return; 197 } 198 199 # compare the results 200 return DataIsDifferent( 201 Data1 => \$Param{Value1}, 202 Data2 => \$Param{Value2} 203 ); 204} 205 206sub ValueValidate { 207 my ( $Self, %Param ) = @_; 208 209 # check value 210 my @Values; 211 if ( IsArrayRefWithData( $Param{Value} ) ) { 212 @Values = @{ $Param{Value} }; 213 } 214 else { 215 @Values = ( $Param{Value} ); 216 } 217 218 # get dynamic field value object 219 my $DynamicFieldValueObject = $Kernel::OM->Get('Kernel::System::DynamicFieldValue'); 220 221 my $Success; 222 for my $Item (@Values) { 223 224 $Success = $DynamicFieldValueObject->ValueValidate( 225 Value => { 226 ValueText => $Item, 227 }, 228 UserID => $Param{UserID} 229 ); 230 231 return if !$Success; 232 } 233 234 return $Success; 235} 236 237sub FieldValueValidate { 238 my ( $Self, %Param ) = @_; 239 240 # Check for valid possible values list. 241 if ( !IsHashRefWithData( $Param{DynamicFieldConfig}->{Config}->{PossibleValues} ) ) { 242 $Kernel::OM->Get('Kernel::System::Log')->Log( 243 Priority => 'error', 244 Message => "Need PossibleValues in Multiselect DynamicFieldConfig!", 245 ); 246 return; 247 } 248 249 # Check for defined value. 250 if ( !defined $Param{Value} ) { 251 $Kernel::OM->Get('Kernel::System::Log')->Log( 252 Priority => 'error', 253 Message => "Need Value in Multiselect DynamicField!", 254 ); 255 return; 256 } 257 258 # Check if value parameter exists in possible values config. 259 if ( length $Param{Value} ) { 260 my @Values; 261 if ( ref $Param{Value} eq 'ARRAY' ) { 262 @Values = @{ $Param{Value} }; 263 } 264 else { 265 push @Values, $Param{Value}; 266 } 267 268 for my $Value (@Values) { 269 return if !defined $Param{DynamicFieldConfig}->{Config}->{PossibleValues}->{$Value}; 270 } 271 } 272 273 return 1; 274} 275 276sub EditFieldRender { 277 my ( $Self, %Param ) = @_; 278 279 # take config from field config 280 my $FieldConfig = $Param{DynamicFieldConfig}->{Config}; 281 my $FieldName = 'DynamicField_' . $Param{DynamicFieldConfig}->{Name}; 282 my $FieldLabel = $Param{DynamicFieldConfig}->{Label}; 283 284 my $Value; 285 286 # set the field value or default 287 if ( $Param{UseDefaultValue} ) { 288 $Value = ( defined $FieldConfig->{DefaultValue} ? $FieldConfig->{DefaultValue} : '' ); 289 } 290 $Value = $Param{Value} // $Value; 291 292 # check if a value in a template (GenericAgent etc.) 293 # is configured for this dynamic field 294 if ( 295 IsHashRefWithData( $Param{Template} ) 296 && defined $Param{Template}->{$FieldName} 297 ) 298 { 299 $Value = $Param{Template}->{$FieldName}; 300 } 301 302 # extract the dynamic field value from the web request 303 my $FieldValue = $Self->EditFieldValueGet( 304 %Param, 305 ); 306 307 # set values from ParamObject if present 308 if ( IsArrayRefWithData($FieldValue) ) { 309 $Value = $FieldValue; 310 } 311 312 # check and set class if necessary 313 my $FieldClass = 'DynamicFieldText Modernize'; 314 if ( defined $Param{Class} && $Param{Class} ne '' ) { 315 $FieldClass .= ' ' . $Param{Class}; 316 } 317 318 # set field as mandatory 319 if ( $Param{Mandatory} ) { 320 $FieldClass .= ' Validate_Required'; 321 } 322 323 # set error css class 324 if ( $Param{ServerError} ) { 325 $FieldClass .= ' ServerError'; 326 } 327 328 # set TreeView class 329 if ( $FieldConfig->{TreeView} ) { 330 $FieldClass .= ' DynamicFieldWithTreeView'; 331 } 332 333 # set PossibleValues, use PossibleValuesFilter if defined 334 my $PossibleValues = $Param{PossibleValuesFilter} // $Self->PossibleValuesGet(%Param); 335 336 # check value 337 my $SelectedValuesArrayRef; 338 if ( defined $Value ) { 339 if ( ref $Value eq 'ARRAY' ) { 340 $SelectedValuesArrayRef = $Value; 341 } 342 else { 343 $SelectedValuesArrayRef = [$Value]; 344 } 345 } 346 347 my $DataValues = $Self->BuildSelectionDataGet( 348 DynamicFieldConfig => $Param{DynamicFieldConfig}, 349 PossibleValues => $PossibleValues, 350 Value => $Value, 351 ); 352 353 my $HTMLString = $Param{LayoutObject}->BuildSelection( 354 Data => $DataValues || {}, 355 Name => $FieldName, 356 SelectedID => $SelectedValuesArrayRef, 357 Translation => $FieldConfig->{TranslatableValues} || 0, 358 Class => $FieldClass, 359 HTMLQuote => 1, 360 Multiple => 1, 361 ); 362 363 if ( $FieldConfig->{TreeView} ) { 364 my $TreeSelectionMessage = $Param{LayoutObject}->{LanguageObject}->Translate("Show Tree Selection"); 365 $HTMLString 366 .= ' <a href="#" title="' 367 . $TreeSelectionMessage 368 . '" class="ShowTreeSelection"><span>' 369 . $TreeSelectionMessage . '</span><i class="fa fa-sitemap"></i></a>'; 370 } 371 372 if ( $Param{Mandatory} ) { 373 my $DivID = $FieldName . 'Error'; 374 375 my $FieldRequiredMessage = $Param{LayoutObject}->{LanguageObject}->Translate("This field is required."); 376 377 # for client side validation 378 $HTMLString .= <<"EOF"; 379 380<div id="$DivID" class="TooltipErrorMessage"> 381 <p> 382 $FieldRequiredMessage 383 </p> 384</div> 385EOF 386 } 387 388 if ( $Param{ServerError} ) { 389 390 my $ErrorMessage = $Param{ErrorMessage} || 'This field is required.'; 391 $ErrorMessage = $Param{LayoutObject}->{LanguageObject}->Translate($ErrorMessage); 392 my $DivID = $FieldName . 'ServerError'; 393 394 # for server side validation 395 $HTMLString .= <<"EOF"; 396 397<div id="$DivID" class="TooltipErrorMessage"> 398 <p> 399 $ErrorMessage 400 </p> 401</div> 402EOF 403 } 404 405 if ( $Param{AJAXUpdate} ) { 406 407 my $FieldSelector = '#' . $FieldName; 408 409 my $FieldsToUpdate; 410 if ( IsArrayRefWithData( $Param{UpdatableFields} ) ) { 411 412 # Remove current field from updatable fields list 413 my @FieldsToUpdate = grep { $_ ne $FieldName } @{ $Param{UpdatableFields} }; 414 415 # quote all fields, put commas in between them 416 $FieldsToUpdate = join( ', ', map {"'$_'"} @FieldsToUpdate ); 417 } 418 419 # add js to call FormUpdate() 420 $Param{LayoutObject}->AddJSOnDocumentComplete( Code => <<"EOF"); 421\$('$FieldSelector').bind('change', function (Event) { 422 Core.AJAX.FormUpdate(\$(this).parents('form'), 'AJAXUpdate', '$FieldName', [ $FieldsToUpdate ]); 423}); 424Core.App.Subscribe('Event.AJAX.FormUpdate.Callback', function(Data) { 425 var FieldName = '$FieldName'; 426 if (Data[FieldName] && \$('#' + FieldName).hasClass('DynamicFieldWithTreeView')) { 427 Core.UI.TreeSelection.RestoreDynamicFieldTreeView(\$('#' + FieldName), Data[FieldName], '' , 1); 428 } 429}); 430EOF 431 } 432 433 # call EditLabelRender on the common Driver 434 my $LabelString = $Self->EditLabelRender( 435 %Param, 436 Mandatory => $Param{Mandatory} || '0', 437 FieldName => $FieldName, 438 ); 439 440 my $Data = { 441 Field => $HTMLString, 442 Label => $LabelString, 443 }; 444 445 return $Data; 446} 447 448sub EditFieldValueGet { 449 my ( $Self, %Param ) = @_; 450 451 my $FieldName = 'DynamicField_' . $Param{DynamicFieldConfig}->{Name}; 452 453 my $Value; 454 455 # check if there is a Template and retrieve the dynamic field value from there 456 if ( IsHashRefWithData( $Param{Template} ) && defined $Param{Template}->{$FieldName} ) { 457 $Value = $Param{Template}->{$FieldName}; 458 } 459 460 # otherwise get dynamic field value from the web request 461 elsif ( 462 defined $Param{ParamObject} 463 && ref $Param{ParamObject} eq 'Kernel::System::Web::Request' 464 ) 465 { 466 my @Data = $Param{ParamObject}->GetArray( Param => $FieldName ); 467 468 # delete empty values (can happen if the user has selected the "-" entry) 469 my $Index = 0; 470 ITEM: 471 for my $Item ( sort @Data ) { 472 473 if ( !$Item ) { 474 splice( @Data, $Index, 1 ); 475 next ITEM; 476 } 477 $Index++; 478 } 479 480 $Value = \@Data; 481 } 482 483 if ( defined $Param{ReturnTemplateStructure} && $Param{ReturnTemplateStructure} eq 1 ) { 484 return { 485 $FieldName => $Value, 486 }; 487 } 488 489 # for this field the normal return an the ReturnValueStructure are the same 490 return $Value; 491} 492 493sub EditFieldValueValidate { 494 my ( $Self, %Param ) = @_; 495 496 # get the field value from the http request 497 my $Values = $Self->EditFieldValueGet( 498 DynamicFieldConfig => $Param{DynamicFieldConfig}, 499 ParamObject => $Param{ParamObject}, 500 501 # not necessary for this Driver but place it for consistency reasons 502 ReturnValueStructure => 1, 503 ); 504 505 my $ServerError; 506 my $ErrorMessage; 507 508 # perform necessary validations 509 if ( $Param{Mandatory} && !IsArrayRefWithData($Values) ) { 510 return { 511 ServerError => 1, 512 }; 513 } 514 else { 515 516 # get possible values list 517 my $PossibleValues = $Param{DynamicFieldConfig}->{Config}->{PossibleValues}; 518 519 # overwrite possible values if PossibleValuesFilter 520 if ( defined $Param{PossibleValuesFilter} ) { 521 $PossibleValues = $Param{PossibleValuesFilter}; 522 } 523 524 # validate if value is in possible values list (but let pass empty values) 525 for my $Item ( @{$Values} ) { 526 if ( !$PossibleValues->{$Item} ) { 527 $ServerError = 1; 528 $ErrorMessage = 'The field content is invalid'; 529 } 530 } 531 } 532 533 # create resulting structure 534 my $Result = { 535 ServerError => $ServerError, 536 ErrorMessage => $ErrorMessage, 537 }; 538 539 return $Result; 540} 541 542sub DisplayValueRender { 543 my ( $Self, %Param ) = @_; 544 545 # set HTMLOutput as default if not specified 546 if ( !defined $Param{HTMLOutput} ) { 547 $Param{HTMLOutput} = 1; 548 } 549 550 # set Value and Title variables 551 my $Value = ''; 552 my $Title = ''; 553 my $ValueMaxChars = $Param{ValueMaxChars} || ''; 554 my $TitleMaxChars = $Param{TitleMaxChars} || ''; 555 556 # check value 557 my @Values; 558 if ( ref $Param{Value} eq 'ARRAY' ) { 559 @Values = @{ $Param{Value} }; 560 } 561 else { 562 @Values = ( $Param{Value} ); 563 } 564 565 # get real values 566 my $PossibleValues = $Param{DynamicFieldConfig}->{Config}->{PossibleValues}; 567 my $TranslatableValues = $Param{DynamicFieldConfig}->{Config}->{TranslatableValues}; 568 569 my @ReadableValues; 570 my @ReadableTitles; 571 572 my $ShowValueEllipsis; 573 my $ShowTitleEllipsis; 574 575 VALUEITEM: 576 for my $Item (@Values) { 577 next VALUEITEM if !$Item; 578 579 my $ReadableValue = $Item; 580 581 if ( $PossibleValues->{$Item} ) { 582 $ReadableValue = $PossibleValues->{$Item}; 583 if ($TranslatableValues) { 584 $ReadableValue = $Param{LayoutObject}->{LanguageObject}->Translate($ReadableValue); 585 } 586 } 587 588 my $ReadableLength = length $ReadableValue; 589 590 # set title equal value 591 my $ReadableTitle = $ReadableValue; 592 593 # cut strings if needed 594 if ( $ValueMaxChars ne '' ) { 595 596 if ( length $ReadableValue > $ValueMaxChars ) { 597 $ShowValueEllipsis = 1; 598 } 599 $ReadableValue = substr $ReadableValue, 0, $ValueMaxChars; 600 601 # decrease the max parameter 602 $ValueMaxChars = $ValueMaxChars - $ReadableLength; 603 if ( $ValueMaxChars < 0 ) { 604 $ValueMaxChars = 0; 605 } 606 } 607 608 if ( $TitleMaxChars ne '' ) { 609 610 if ( length $ReadableTitle > $ValueMaxChars ) { 611 $ShowTitleEllipsis = 1; 612 } 613 $ReadableTitle = substr $ReadableTitle, 0, $TitleMaxChars; 614 615 # decrease the max parameter 616 $TitleMaxChars = $TitleMaxChars - $ReadableLength; 617 if ( $TitleMaxChars < 0 ) { 618 $TitleMaxChars = 0; 619 } 620 } 621 622 # HTMLOutput transformations 623 if ( $Param{HTMLOutput} ) { 624 625 $ReadableValue = $Param{LayoutObject}->Ascii2Html( 626 Text => $ReadableValue, 627 ); 628 629 $ReadableTitle = $Param{LayoutObject}->Ascii2Html( 630 Text => $ReadableTitle, 631 ); 632 } 633 634 if ( length $ReadableValue ) { 635 push @ReadableValues, $ReadableValue; 636 } 637 if ( length $ReadableTitle ) { 638 push @ReadableTitles, $ReadableTitle; 639 } 640 } 641 642 # get specific field settings 643 my $FieldConfig = $Kernel::OM->Get('Kernel::Config')->Get('DynamicFields::Driver')->{Multiselect} || {}; 644 645 # set new line separator 646 my $ItemSeparator = $FieldConfig->{ItemSeparator} || ', '; 647 648 $Value = join( $ItemSeparator, @ReadableValues ); 649 $Title = join( $ItemSeparator, @ReadableTitles ); 650 651 if ($ShowValueEllipsis) { 652 $Value .= '...'; 653 } 654 if ($ShowTitleEllipsis) { 655 $Title .= '...'; 656 } 657 658 # this field type does not support the Link Feature 659 my $Link; 660 661 # create return structure 662 my $Data = { 663 Value => $Value, 664 Title => $Title, 665 Link => $Link, 666 }; 667 668 return $Data; 669} 670 671sub SearchFieldParameterBuild { 672 my ( $Self, %Param ) = @_; 673 674 # get field value 675 my $Value = $Self->SearchFieldValueGet(%Param); 676 677 my $DisplayValue; 678 679 if ( defined $Value && !$Value ) { 680 $DisplayValue = ''; 681 } 682 683 if ($Value) { 684 if ( ref $Value eq 'ARRAY' ) { 685 686 my @DisplayItemList; 687 for my $Item ( @{$Value} ) { 688 689 # set the display value 690 my $DisplayItem = $Param{DynamicFieldConfig}->{Config}->{PossibleValues}->{$Item} 691 || $Item; 692 693 # translate the value 694 if ( 695 $Param{DynamicFieldConfig}->{Config}->{TranslatableValues} 696 && defined $Param{LayoutObject} 697 ) 698 { 699 $DisplayItem = $Param{LayoutObject}->{LanguageObject}->Translate($DisplayItem); 700 } 701 702 push @DisplayItemList, $DisplayItem; 703 } 704 705 # combine different values into one string 706 $DisplayValue = join ' + ', @DisplayItemList; 707 } 708 else { 709 710 # set the display value 711 $DisplayValue = $Param{DynamicFieldConfig}->{Config}->{PossibleValues}->{$Value}; 712 713 # translate the value 714 if ( 715 $Param{DynamicFieldConfig}->{Config}->{TranslatableValues} 716 && defined $Param{LayoutObject} 717 ) 718 { 719 $DisplayValue = $Param{LayoutObject}->{LanguageObject}->Translate($DisplayValue); 720 } 721 } 722 } 723 724 # return search parameter structure 725 return { 726 Parameter => { 727 Equals => $Value, 728 }, 729 Display => $DisplayValue, 730 }; 731} 732 733sub StatsFieldParameterBuild { 734 my ( $Self, %Param ) = @_; 735 736 # set PossibleValues 737 my $Values = $Param{DynamicFieldConfig}->{Config}->{PossibleValues}; 738 739 # get historical values from database 740 my $HistoricalValues = $Kernel::OM->Get('Kernel::System::DynamicFieldValue')->HistoricalValueGet( 741 FieldID => $Param{DynamicFieldConfig}->{ID}, 742 ValueType => 'Text,', 743 ); 744 745 # add historic values to current values (if they don't exist anymore) 746 for my $Key ( sort keys %{$HistoricalValues} ) { 747 if ( !$Values->{$Key} ) { 748 $Values->{$Key} = $HistoricalValues->{$Key}; 749 } 750 } 751 752 # use PossibleValuesFilter if defined 753 $Values = $Param{PossibleValuesFilter} // $Values; 754 755 return { 756 Values => $Values, 757 Name => $Param{DynamicFieldConfig}->{Label}, 758 Element => 'DynamicField_' . $Param{DynamicFieldConfig}->{Name}, 759 TranslatableValues => $Param{DynamicFieldConfig}->{Config}->{TranslatableValues}, 760 Block => 'MultiSelectField', 761 }; 762} 763 764sub ReadableValueRender { 765 my ( $Self, %Param ) = @_; 766 767 # set Value and Title variables 768 my $Value = ''; 769 my $Title = ''; 770 771 # check value 772 my @Values; 773 if ( ref $Param{Value} eq 'ARRAY' ) { 774 @Values = @{ $Param{Value} }; 775 } 776 else { 777 @Values = ( $Param{Value} ); 778 } 779 780 my @ReadableValues; 781 782 VALUEITEM: 783 for my $Item (@Values) { 784 next VALUEITEM if !$Item; 785 786 push @ReadableValues, $Item; 787 } 788 789 # set new line separator 790 my $ItemSeparator = ', '; 791 792 # Output transformations 793 $Value = join( $ItemSeparator, @ReadableValues ); 794 $Title = $Value; 795 796 # cut strings if needed 797 if ( $Param{ValueMaxChars} && length($Value) > $Param{ValueMaxChars} ) { 798 $Value = substr( $Value, 0, $Param{ValueMaxChars} ) . '...'; 799 } 800 if ( $Param{TitleMaxChars} && length($Title) > $Param{TitleMaxChars} ) { 801 $Title = substr( $Title, 0, $Param{TitleMaxChars} ) . '...'; 802 } 803 804 # create return structure 805 my $Data = { 806 Value => $Value, 807 Title => $Title, 808 }; 809 810 return $Data; 811} 812 813sub TemplateValueTypeGet { 814 my ( $Self, %Param ) = @_; 815 816 my $FieldName = 'DynamicField_' . $Param{DynamicFieldConfig}->{Name}; 817 818 # set the field types 819 my $EditValueType = 'ARRAY'; 820 my $SearchValueType = 'ARRAY'; 821 822 # return the correct structure 823 if ( $Param{FieldType} eq 'Edit' ) { 824 return { 825 $FieldName => $EditValueType, 826 }; 827 } 828 elsif ( $Param{FieldType} eq 'Search' ) { 829 return { 830 'Search_' . $FieldName => $SearchValueType, 831 }; 832 } 833 else { 834 return { 835 $FieldName => $EditValueType, 836 'Search_' . $FieldName => $SearchValueType, 837 }; 838 } 839} 840 841sub ObjectMatch { 842 my ( $Self, %Param ) = @_; 843 844 my $FieldName = 'DynamicField_' . $Param{DynamicFieldConfig}->{Name}; 845 846 # the attribute must be an array 847 return 0 if !IsArrayRefWithData( $Param{ObjectAttributes}->{$FieldName} ); 848 849 my $Match; 850 851 # search in all values for this attribute 852 VALUE: 853 for my $AttributeValue ( @{ $Param{ObjectAttributes}->{$FieldName} } ) { 854 855 next VALUE if !defined $AttributeValue; 856 857 # only need to match one 858 if ( $Param{Value} eq $AttributeValue ) { 859 $Match = 1; 860 last VALUE; 861 } 862 } 863 864 return $Match; 865} 866 867sub HistoricalValuesGet { 868 my ( $Self, %Param ) = @_; 869 870 # get historical values from database 871 my $HistoricalValues = $Kernel::OM->Get('Kernel::System::DynamicFieldValue')->HistoricalValueGet( 872 FieldID => $Param{DynamicFieldConfig}->{ID}, 873 ValueType => 'Text', 874 ); 875 876 # return the historical values from database 877 return $HistoricalValues; 878} 879 880sub ValueLookup { 881 my ( $Self, %Param ) = @_; 882 883 my @Keys; 884 if ( ref $Param{Key} eq 'ARRAY' ) { 885 @Keys = @{ $Param{Key} }; 886 } 887 else { 888 @Keys = ( $Param{Key} ); 889 } 890 891 # get real values 892 my $PossibleValues = $Param{DynamicFieldConfig}->{Config}->{PossibleValues}; 893 894 # to store final values 895 my @Values; 896 897 KEYITEM: 898 for my $Item (@Keys) { 899 next KEYITEM if !$Item; 900 901 # set the value as the key by default 902 my $Value = $Item; 903 904 # try to convert key to real value 905 if ( $PossibleValues->{$Item} ) { 906 $Value = $PossibleValues->{$Item}; 907 908 # check if translation is possible 909 if ( 910 defined $Param{LanguageObject} 911 && $Param{DynamicFieldConfig}->{Config}->{TranslatableValues} 912 ) 913 { 914 915 # translate value 916 $Value = $Param{LanguageObject}->Translate($Value); 917 } 918 } 919 push @Values, $Value; 920 } 921 922 return \@Values; 923} 924 925sub BuildSelectionDataGet { 926 my ( $Self, %Param ) = @_; 927 928 my $FieldConfig = $Param{DynamicFieldConfig}->{Config}; 929 my $FilteredPossibleValues = $Param{PossibleValues}; 930 931 # get the possible values again as it might or might not contain the possible none and it could 932 # also be overwritten 933 my $ConfigPossibleValues = $Self->PossibleValuesGet(%Param); 934 935 # check if $PossibleValues differs from configured PossibleValues 936 # and show values which are not contained as disabled if TreeView => 1 937 if ( $FieldConfig->{TreeView} ) { 938 939 if ( keys %{$ConfigPossibleValues} != keys %{$FilteredPossibleValues} ) { 940 941 # define variables to use later in the for loop 942 my @Values; 943 my $Parents; 944 my %DisabledElements; 945 my %ProcessedElements; 946 my $PosibleNoneSet; 947 948 my %Values; 949 if ( defined $Param{Value} && IsArrayRefWithData( $Param{Value} ) ) { 950 951 # create a lookup table 952 %Values = map { $_ => 1 } @{ $Param{Value} }; 953 } 954 955 # loop on all filtered possible values 956 for my $Key ( sort keys %{$FilteredPossibleValues} ) { 957 958 # special case for possible none 959 if ( !$Key && !$PosibleNoneSet && $FieldConfig->{PossibleNone} ) { 960 961 my $Selected; 962 if ( 963 !IsHashRefWithData( \%Values ) 964 || ( defined $Values{''} && $Values{''} ) 965 ) 966 { 967 $Selected = 1; 968 } 969 970 # add possible none 971 push @Values, { 972 Key => $Key, 973 Value => $ConfigPossibleValues->{$Key} || '-', 974 Selected => $Selected, 975 }; 976 } 977 978 # try to split its parents GrandParent::Parent::Son 979 my @Elements = split /::/, $Key; 980 981 # reset parents 982 $Parents = ''; 983 984 # get each element in the hierarchy 985 ELEMENT: 986 for my $Element (@Elements) { 987 988 # add its own parents for the complete name 989 my $ElementLongName = $Parents . $Element; 990 991 # set new parent (before skip already processed) 992 $Parents .= $Element . '::'; 993 994 # skip if already processed 995 next ELEMENT if $ProcessedElements{$ElementLongName}; 996 997 my $Disabled; 998 999 # check if element exists in the original data or if it is already marked 1000 if ( 1001 !defined $FilteredPossibleValues->{$ElementLongName} 1002 && !$DisabledElements{$ElementLongName} 1003 ) 1004 { 1005 1006 # mark element as disabled 1007 $DisabledElements{$ElementLongName} = 1; 1008 1009 # also set the disabled flag for current element to add 1010 $Disabled = 1; 1011 } 1012 1013 # set element as already processed 1014 $ProcessedElements{$ElementLongName} = 1; 1015 1016 # check if the current element is the selected one 1017 my $Selected; 1018 if ( IsHashRefWithData( \%Values ) && $Values{$ElementLongName} ) { 1019 $Selected = 1; 1020 } 1021 1022 # add element to the new list of possible values (now including missing parents) 1023 push @Values, { 1024 Key => $ElementLongName, 1025 Value => $ConfigPossibleValues->{$ElementLongName} || $ElementLongName, 1026 Disabled => $Disabled, 1027 Selected => $Selected, 1028 }; 1029 } 1030 } 1031 $FilteredPossibleValues = \@Values; 1032 } 1033 } 1034 1035 return $FilteredPossibleValues; 1036} 1037 10381; 1039 1040=head1 TERMS AND CONDITIONS 1041 1042This software is part of the OTRS project (L<https://otrs.org/>). 1043 1044This software comes with ABSOLUTELY NO WARRANTY. For details, see 1045the enclosed file COPYING for license information (GPL). If you 1046did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>. 1047 1048=cut 1049