diff --git a/CHANGES.md b/CHANGES.md index ce7baac754..cc6d6670e3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,5 @@ # 7.1.1 2024-??-?? + - 2024-07-04 Improved rendering of dynamic fields on AdminGenericAgent, AdminNotificationEvent, AdminACL and AdminDynamicFieldScreenConfiguration. - 2024-07-02 Updated required minimum database versions. Refactored database version checks and added them to the installer. - 2024-06-21 Added functionality to use user fullname for mention label. - 2024-06-20 Fixed output of last mention date in dashboard widget "Last mentions". diff --git a/Kernel/Modules/AdminACL.pm b/Kernel/Modules/AdminACL.pm index d076190980..ef27f98ca7 100644 --- a/Kernel/Modules/AdminACL.pm +++ b/Kernel/Modules/AdminACL.pm @@ -757,16 +757,28 @@ sub _ShowEdit { AutoComplete => 'off', ); - # get list of all possible dynamic fields - my $DynamicFieldList = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldList( - ObjectType => 'Ticket', - ResultType => 'HASH', - ); - my %DynamicFieldNames = reverse %{$DynamicFieldList}; my %DynamicFields; - for my $DynamicFieldName ( sort keys %DynamicFieldNames ) { - $DynamicFields{ 'DynamicField_' . $DynamicFieldName } = $DynamicFieldName; + my $DynamicFieldList = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet( + Valid => 1, + ObjectType => ['Ticket'], + ); + + DYNAMICFIELDCONFIG: + for my $DynamicFieldConfig ( @{$DynamicFieldList} ) { + next DYNAMICFIELDCONFIG if !IsHashRefWithData($DynamicFieldConfig); + + my $TranslatedLabel = $LayoutObject->{LanguageObject}->Translate( $DynamicFieldConfig->{Label} ); + my $CombinedLabel = ( + $TranslatedLabel eq $DynamicFieldConfig->{Name} + ? $TranslatedLabel + : $TranslatedLabel . ' (' . $DynamicFieldConfig->{Name} . ')' + ); + + $DynamicFields{ 'DynamicField_' . $DynamicFieldConfig->{Name} } = $CombinedLabel; } + + %DynamicFields = map { $_ => $DynamicFields{$_} } sort keys %DynamicFields; + $Param{ACLKeysLevel3DynamicFields} = $LayoutObject->BuildSelection( Data => \%DynamicFields, Name => 'NewDataKeyDropdown', diff --git a/Kernel/Modules/AdminDynamicFieldScreenConfiguration.pm b/Kernel/Modules/AdminDynamicFieldScreenConfiguration.pm index ff83026c87..eb3e8ee7dd 100644 --- a/Kernel/Modules/AdminDynamicFieldScreenConfiguration.pm +++ b/Kernel/Modules/AdminDynamicFieldScreenConfiguration.pm @@ -313,12 +313,27 @@ sub _ShowOverview { ); } else { + + # Get all dynamic fields and translate the name. + my @DynamicFields; for my $DynamicField ( sort keys %DynamicFields ) { + my $Name = $LayoutObject->{LanguageObject}->Translate( $DynamicFields{$DynamicField} ); + push @DynamicFields, { + Name => $Name, + DynamicField => $DynamicField, + }; + } + + # Sort by translated name. + my @SortedDynamicFields = sort { $a->{Name} cmp $b->{Name} } @DynamicFields; + + # Show dynamic fields orderd by translated name on overview. + for my $DynamicField (@SortedDynamicFields) { $LayoutObject->Block( Name => 'DynamicFieldOverviewRow', Data => { - DynamicField => $DynamicField, - Name => $DynamicFields{$DynamicField}, + DynamicField => $DynamicField->{DynamicField}, + Name => $DynamicField->{Name}, }, ); } diff --git a/Kernel/Modules/AdminGenericAgent.pm b/Kernel/Modules/AdminGenericAgent.pm index c8329499ba..781df41e9d 100644 --- a/Kernel/Modules/AdminGenericAgent.pm +++ b/Kernel/Modules/AdminGenericAgent.pm @@ -11,6 +11,7 @@ package Kernel::Modules::AdminGenericAgent; use strict; use warnings; +use utf8; use Kernel::System::VariableCheck qw(:all); use Kernel::Language qw(Translatable); @@ -391,8 +392,9 @@ sub Run { if ( $Widget eq 'Select' ) { $DynamicFieldHTML = $DynamicFieldBackendObject->SearchFieldRender( DynamicFieldConfig => $DynamicFieldConfig, - Profile => \%JobData, LayoutObject => $LayoutObject, + DefaultValue => $DynamicFieldConfig->{Config}->{DefaultValue}, + Profile => \%JobData, Type => $Type, ); } @@ -438,6 +440,7 @@ sub Run { } } } + $DynamicFieldHTML = $DynamicFieldBackendObject->EditFieldRender( DynamicFieldConfig => $DynamicFieldConfig, PossibleValuesFilter => $PossibleValuesFilter, @@ -452,7 +455,26 @@ sub Run { ); } - $DynamicFieldHTML->{ID} = $SelectedValue; + my $HTMLLabel = $DynamicFieldHTML->{Label}; + my $TranslatedLabel = $LayoutObject->{LanguageObject}->Translate( $DynamicFieldConfig->{Label} ); + my $CombinedLabel = ( + $TranslatedLabel eq $DynamicFieldConfig->{Name} + ? $TranslatedLabel + : $TranslatedLabel . ' (' . $DynamicFieldConfig->{Name} . ')' + ); + + $HTMLLabel =~ s{(.+)\Q$TranslatedLabel\E(.+)}{$1 $CombinedLabel $2}smx; + + $DynamicFieldHTML->{Label} = $HTMLLabel; + $DynamicFieldHTML->{ID} = $SelectedValue; + $DynamicFieldHTML->{Type} = $DynamicFieldConfig->{FieldType}; + + if ( $DynamicFieldConfig->{Config}->{DateRestriction} ) { + $DynamicFieldHTML->{ValidateDateInFuture} + = ( $DynamicFieldConfig->{Config}->{DateRestriction} eq 'DisablePastDates' ? 'true' : 'false' ); + $DynamicFieldHTML->{ValidateDateNotInFuture} + = ( $DynamicFieldConfig->{Config}->{DateRestriction} eq 'DisableFutureDates' ? 'true' : 'false' ); + } my $Output = $LayoutObject->JSONEncode( Data => $DynamicFieldHTML, @@ -1134,9 +1156,7 @@ sub _MaskUpdate { next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences); # Translate dynamic field label. - my $TranslatedDynamicFieldLabel = $LayoutObject->{LanguageObject}->Translate( - $DynamicFieldConfig->{Label}, - ); + my $TranslatedLabel = $LayoutObject->{LanguageObject}->Translate( $DynamicFieldConfig->{Label} ); PREFERENCE: for my $Preference ( @{$SearchFieldPreferences} ) { @@ -1150,14 +1170,18 @@ sub _MaskUpdate { $TranslatedSuffix = ' (' . $TranslatedSuffix . ')'; } - my $Key = 'Search_DynamicField_' . $DynamicFieldConfig->{Name} . $Preference->{Type}; - my $Text = $TranslatedDynamicFieldLabel . $TranslatedSuffix; + my $Key = 'Search_DynamicField_' . $DynamicFieldConfig->{Name} . $Preference->{Type}; + my $CombinedLabel = ( + $TranslatedLabel eq $DynamicFieldConfig->{Name} + ? $TranslatedLabel . $TranslatedSuffix + : $TranslatedLabel . $TranslatedSuffix . ' (' . $DynamicFieldConfig->{Name} . ')' + ); # Save all dynamic fields for JS. $DynamicFieldsJS{$Key} = { ID => $DynamicFieldConfig->{ID}, Type => $Preference->{Type}, - Text => $Text, + Text => $CombinedLabel, }; # Decide if dynamic field go to add fields dropdown or selected fields area. @@ -1166,19 +1190,21 @@ sub _MaskUpdate { # Get field HTML. my $DynamicFieldHTML = $DynamicFieldBackendObject->SearchFieldRender( DynamicFieldConfig => $DynamicFieldConfig, + LayoutObject => $LayoutObject, Profile => \%JobData, - DefaultValue => - $Self->{Config}->{Defaults}->{DynamicField}->{ $DynamicFieldConfig->{Name} }, - LayoutObject => $LayoutObject, - Type => $Preference->{Type}, + Type => $Preference->{Type}, ); next PREFERENCE if !IsHashRefWithData($DynamicFieldHTML); + my $HTMLLabel = $DynamicFieldHTML->{Label}; + my $Search = $TranslatedLabel . $TranslatedSuffix; + $HTMLLabel =~ s{(.+)\Q$Search\E(.+)}{$1 $CombinedLabel $2}smx; + $LayoutObject->Block( Name => 'SelectedDynamicFields', Data => { - Label => $DynamicFieldHTML->{Label}, + Label => $HTMLLabel, Field => $DynamicFieldHTML->{Field}, ID => $Key, }, @@ -1187,12 +1213,14 @@ sub _MaskUpdate { else { push @AddDynamicFields, { Key => $Key, - Value => $Text, + Value => $CombinedLabel, }; } } } + @AddDynamicFields = sort { $a->{Value} cmp $b->{Value} } @AddDynamicFields; + my $DynamicFieldsStrg = $LayoutObject->BuildSelection( PossibleNone => 1, Data => \@AddDynamicFields, @@ -1278,10 +1306,17 @@ sub _MaskUpdate { $Used = 1; } + my $TranslatedLabel = $LayoutObject->{LanguageObject}->Translate( $DynamicFieldConfig->{Label} ); + my $CombinedLabel = ( + $TranslatedLabel eq $DynamicFieldConfig->{Name} + ? $TranslatedLabel + : $TranslatedLabel . ' (' . $DynamicFieldConfig->{Name} . ')' + ); + # Save all new dynamic fields for JS. $DynamicFieldsJS{$Key} = { ID => $DynamicFieldConfig->{ID}, - Text => $DynamicFieldConfig->{Name}, + Text => $CombinedLabel, }; # Decide if dynamic field go to add fields dropdown or selected fields area. @@ -1321,10 +1356,13 @@ sub _MaskUpdate { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldHTML); + my $HTMLLabel = $DynamicFieldHTML->{Label}; + $HTMLLabel =~ s{(.+)\Q$TranslatedLabel\E(.+)}{$1 $CombinedLabel $2}smx; + $LayoutObject->Block( Name => 'SelectedNewDynamicFields', Data => { - Label => $DynamicFieldHTML->{Label}, + Label => $HTMLLabel, Field => $DynamicFieldHTML->{Field}, ID => $Key, }, @@ -1333,11 +1371,13 @@ sub _MaskUpdate { else { push @AddNewDynamicFields, { Key => $Key, - Value => $DynamicFieldConfig->{Name}, + Value => $CombinedLabel, }; } } + @AddNewDynamicFields = sort { $a->{Value} cmp $b->{Value} } @AddNewDynamicFields; + my $NewDynamicFieldsStrg = $LayoutObject->BuildSelection( PossibleNone => 1, Data => \@AddNewDynamicFields, diff --git a/Kernel/Modules/AdminNotificationEvent.pm b/Kernel/Modules/AdminNotificationEvent.pm index 368b5043cf..0271c935b8 100644 --- a/Kernel/Modules/AdminNotificationEvent.pm +++ b/Kernel/Modules/AdminNotificationEvent.pm @@ -11,6 +11,7 @@ package Kernel::Modules::AdminNotificationEvent; use strict; use warnings; +use utf8; our $ObjectManagerDisabled = 1; @@ -567,7 +568,7 @@ sub Run { # ------------------------------------------------------------ # # delete # ------------------------------------------------------------ # - if ( $Self->{Subaction} eq 'Delete' ) { + elsif ( $Self->{Subaction} eq 'Delete' ) { # challenge token check for write action $LayoutObject->ChallengeTokenCheck(); @@ -690,7 +691,7 @@ sub Run { # ------------------------------------------------------------ # # NotificationImport # ------------------------------------------------------------ # - if ( $Self->{Subaction} eq 'NotificationImport' ) { + elsif ( $Self->{Subaction} eq 'NotificationImport' ) { # challenge token check for write action $LayoutObject->ChallengeTokenCheck(); @@ -767,9 +768,67 @@ sub Run { return $Output; } - # ------------------------------------------------------------ + # ------------------------------------------------------------ # + # Add dynamic fields to ticket filter on notifications by AJAX + # ------------------------------------------------------------ # + elsif ( $Self->{Subaction} eq 'AddDynamicField' ) { + my $DynamicFieldID = $ParamObject->GetParam( Param => 'DynamicFieldID' ); + my $SelectedValue = $ParamObject->GetParam( Param => 'SelectedValue' ); + + my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); + my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField'); + + my $DynamicFieldConfig = $DynamicFieldObject->DynamicFieldGet( + ID => $DynamicFieldID, + ); + + # get notification data + my %NotificationData; + my $NotificationID = $ParamObject->GetParam( Param => 'ID' ) || ''; + if ($NotificationID) { + %NotificationData = $NotificationEventObject->NotificationGet( + ID => $NotificationID, + ); + } + $NotificationData{Profile} = $NotificationID; + $NotificationData{Subaction} = $Self->{Subaction}; + + my $DynamicFieldHTML = $DynamicFieldBackendObject->SearchFieldRender( + DynamicFieldConfig => $DynamicFieldConfig, + LayoutObject => $LayoutObject, + DefaultValue => $DynamicFieldConfig->{Config}->{DefaultValue}, + Profile => \%NotificationData || {}, + ); + + my $HTMLLabel = $DynamicFieldHTML->{Label}; + my $TranslatedLabel = $LayoutObject->{LanguageObject}->Translate( $DynamicFieldConfig->{Label} ); + my $CombinedLabel = ( + $TranslatedLabel eq $DynamicFieldConfig->{Name} + ? $TranslatedLabel + : $TranslatedLabel . ' (' . $DynamicFieldConfig->{Name} . ')' + ); + + $HTMLLabel =~ s{(.+)\Q$TranslatedLabel\E(.+)}{$1 $CombinedLabel $2}smx; + + $DynamicFieldHTML->{Label} = $HTMLLabel; + $DynamicFieldHTML->{ID} = $SelectedValue; + + my $Output = $LayoutObject->JSONEncode( + Data => $DynamicFieldHTML, + ); + + # Send JSON response. + return $LayoutObject->Attachment( + ContentType => 'application/json; charset=' . $LayoutObject->{Charset}, + Content => $Output, + Type => 'inline', + NoCache => 1, + ); + } + + # ------------------------------------------------------------ # # overview - # ------------------------------------------------------------ + # ------------------------------------------------------------ # else { $Self->_Overview(); my $Output = $LayoutObject->Header(); @@ -1031,54 +1090,106 @@ sub _Edit { ); } - # create dynamic field HTML for set with historical data options - my $PrintDynamicFieldsSearchHeader = 1; + my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField'); + my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); - # cycle trough the activated Dynamic Fields for this screen - my $DynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet( + my @AddDynamicFields; + my %DynamicFieldsJS; + + my $DynamicField = $DynamicFieldObject->DynamicFieldListGet( Valid => 1, ObjectType => ['Ticket'], ); - my $BackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); - DYNAMICFIELD: for my $DynamicFieldConfig ( @{$DynamicField} ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); # skip all dynamic fields that are not designed to be notification triggers - my $IsNotificationEventCondition = $BackendObject->HasBehavior( + my $IsNotificationEventCondition = $DynamicFieldBackendObject->HasBehavior( DynamicFieldConfig => $DynamicFieldConfig, Behavior => 'IsNotificationEventCondition', ); next DYNAMICFIELD if !$IsNotificationEventCondition; - # get field HTML - my $DynamicFieldHTML = $BackendObject->SearchFieldRender( + # get search field preferences + my $SearchFieldPreferences = $DynamicFieldBackendObject->SearchFieldPreferences( DynamicFieldConfig => $DynamicFieldConfig, - Profile => $Param{DynamicFieldValues} || {}, - LayoutObject => $LayoutObject, - UseLabelHints => 0, ); - next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldHTML); + next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences); - if ($PrintDynamicFieldsSearchHeader) { - $LayoutObject->Block( Name => 'DynamicField' ); - $PrintDynamicFieldsSearchHeader = 0; - } + my $Key = 'Search_DynamicField_' . $DynamicFieldConfig->{Name}; - # output dynamic field - $LayoutObject->Block( - Name => 'DynamicFieldElement', - Data => { - Label => $DynamicFieldHTML->{Label}, - Field => $DynamicFieldHTML->{Field}, - }, + # Translate dynamic field label. + my $TranslatedLabel = $LayoutObject->{LanguageObject}->Translate( $DynamicFieldConfig->{Label} ); + my $CombinedLabel = ( + $TranslatedLabel eq $DynamicFieldConfig->{Name} + ? $TranslatedLabel + : $TranslatedLabel . ' (' . $DynamicFieldConfig->{Name} . ')' ); + + # Save all dynamic fields for JS. + $DynamicFieldsJS{$Key} = { + ID => $DynamicFieldConfig->{ID}, + Text => $CombinedLabel, + }; + + # Decide if dynamic field go to add fields dropdown or selected fields area. + if ( defined $Param{Data}{$Key} ) { + + # Get field HTML. + my $DynamicFieldHTML = $DynamicFieldBackendObject->SearchFieldRender( + DynamicFieldConfig => $DynamicFieldConfig, + LayoutObject => $LayoutObject, + Profile => $Param{Data}, + ); + + next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldHTML); + + my $HTMLLabel = $DynamicFieldHTML->{Label}; + $HTMLLabel =~ s{(.+)\Q$TranslatedLabel\E(.+)}{$1 $CombinedLabel $2}smx; + + $LayoutObject->Block( + Name => 'SelectedDynamicFields', + Data => { + Label => $HTMLLabel, + Field => $DynamicFieldHTML->{Field}, + ID => $Key, + }, + ); + } + else { + push @AddDynamicFields, { + Key => $Key, + Value => $CombinedLabel, + }; + } } + @AddDynamicFields = sort { $a->{Value} cmp $b->{Value} } @AddDynamicFields; + + my $DynamicFieldsStrg = $LayoutObject->BuildSelection( + PossibleNone => 1, + Data => \@AddDynamicFields, + Name => 'AddDynamicFields', + Multiple => 0, + Class => 'Modernize', + ); + + $LayoutObject->Block( + Name => 'AddDynamicFields', + Data => { + DynamicFieldsStrg => $DynamicFieldsStrg, + }, + ); + + $LayoutObject->AddJSData( + Key => 'DynamicFieldsJS', + Value => \%DynamicFieldsJS, + ); + # add rich text editor if ( $Param{RichText} ) { diff --git a/Kernel/Output/HTML/Templates/Standard/AdminDynamicFieldScreenConfiguration.tt b/Kernel/Output/HTML/Templates/Standard/AdminDynamicFieldScreenConfiguration.tt index a544b08e4b..6c0f955c0d 100644 --- a/Kernel/Output/HTML/Templates/Standard/AdminDynamicFieldScreenConfiguration.tt +++ b/Kernel/Output/HTML/Templates/Standard/AdminDynamicFieldScreenConfiguration.tt @@ -71,7 +71,7 @@ [% RenderBlockStart("DynamicFieldOverviewRow") %]