Skip to content

Commit

Permalink
Issue #2842: Move existing field handling to their drivers.
Browse files Browse the repository at this point in the history
  • Loading branch information
Sven committed Jan 18, 2024
1 parent e0b6002 commit b6103c5
Show file tree
Hide file tree
Showing 9 changed files with 351 additions and 174 deletions.
45 changes: 45 additions & 0 deletions Kernel/System/DynamicField/Backend.pm
Original file line number Diff line number Diff line change
Expand Up @@ -3095,4 +3095,49 @@ sub ObjectDescriptionGet {
return;
}

=head2 GetFieldState()
Get the new value and possible values for use in FieldRestrictions()
my %Return = $BackendObject->GetFieldState(
%Param,
Visibility => \%Visibility,
CachedVisibility => $CachedVisibility // {},
DynamicFieldConfig => $DynamicFieldConfig,
);
Return
%Return = (
NewValue => $Value,
PossibleValues => {},
);
=cut

sub GetFieldState {
my ( $Self, %Param ) = @_;

# check needed stuff
for my $Needed (qw(DynamicFieldConfig)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!"
);

return;
}
}

# set the dynamic field specific backend
my $DynamicFieldBackend = 'DynamicField' . $Param{DynamicFieldConfig}->{FieldType} . 'Object';

if ( $Self->{$DynamicFieldBackend}->can('GetFieldState') ) {
return $Self->{$DynamicFieldBackend}->GetFieldState(%Param);
}

return;
}

1;
1 change: 1 addition & 0 deletions Kernel/System/DynamicField/Driver/Agent.pm
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ sub new {
'IsHiddenInTicketInformation' => 0,
'IsReferenceField' => 1,
'IsSetCapable' => 1,
'SetsDynamicContent' => 1,
};

$Self->{ReferencedObjectType} = 'Agent';
Expand Down
33 changes: 33 additions & 0 deletions Kernel/System/DynamicField/Driver/BaseReference.pm
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use utf8;
use parent qw(Kernel::System::DynamicField::Driver::BaseEntity);

# core modules
use List::Util qw(any);

# CPAN modules

Expand Down Expand Up @@ -986,4 +987,36 @@ sub PossibleValuesGet {
return \%PossibleValues;
}

sub GetFieldState {
my ( $Self, %Param ) = @_;

my $DynamicFieldConfig = $Param{DynamicFieldConfig};

return () if $DynamicFieldConfig->{Config}{EditFieldMode} eq 'AutoComplete';
return () if !IsArrayRefWithData( $DynamicFieldConfig->{Config}{ReferenceFilterList} );
return () if !any { $Param{ChangedElements}->{ $_->{EqualsObjectAttribute} // '' } } $DynamicFieldConfig->{Config}{ReferenceFilterList}->@*;

# fetch possible values for dynamic field
my $PossibleValues = $Self->PossibleValuesGet(
DynamicFieldConfig => $DynamicFieldConfig,
Object => {
# ticket specific
CustomerUserID => $Param{GetParam}->{CustomerUser},
# general
$Param{GetParam}->%*,
},
);

my %Return = (
PossibleValues => $PossibleValues,
);

my $Value = $Param{GetParam}{DynamicField}{ 'DynamicField_' . $DynamicFieldConfig->{Name} };
if ( $Value && !$PossibleValues->{ $Value } ) {
$Return{NewValue} = undef;
}

return %Return;
}

1;
70 changes: 70 additions & 0 deletions Kernel/System/DynamicField/Driver/BaseScript.pm
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use utf8;
use parent qw(Kernel::System::DynamicField::Driver::Base);

# core modules
use List::Util qw(any);

# CPAN modules

Expand Down Expand Up @@ -71,6 +72,12 @@ sub new {
'IsCustomerInterfaceCapable' => 1,
'IsLikeOperatorCapable' => 1,
'IsScriptField' => 1,
'SetsDynamicContent' => 1,
};

# TODO: probably needs completion for all frontends
$Self->{Uniformity} = {
Dest => 'Queue',
};

return $Self;
Expand Down Expand Up @@ -1019,4 +1026,67 @@ sub Evaluate {
return "No evaluation implemented for this field type.";
}

sub GetFieldState {
my ( $Self, %Param ) = @_;

my %GetParam = $Param{GetParam}->%*;
my $DynamicFieldConfig = $Param{DynamicFieldConfig};

# for ticket dynamic fields we need the queue
$GetParam{Queue} = defined $Param{GetParam}{Queue} ? $Param{GetParam}{Queue}
: $Param{GetParam}{Dest} && $Param{GetParam}{Dest} =~ /\|\|(.+)$/ ? $1
: undef;

# the required args have to be present
for my $Required ( @{ $DynamicFieldConfig->{Config}{RequiredArgs} // [] } ) {
my $Value = $GetParam{DynamicField}{$Required} // $GetParam{$Required};

return () if !$Value || ( ref $Value && !IsArrayRefWithData($Value) );
}

my %ChangedElements = map { $Self->{Uniformity}{$_} // $_ => 1 } keys $Param{ChangedElements}->%*;
delete $ChangedElements{ 'DynamicField_' . $DynamicFieldConfig->{Name} };

# skip if it's only a rerun due to self change
return () if !%ChangedElements && !$Param{InitialRun};

# if specific AJAX triggers are defined only update on changes to them...
if ( IsArrayRefWithData( $DynamicFieldConfig->{Config}{AJAXTriggers} ) ) {
return () if !any { $ChangedElements{$_} } $DynamicFieldConfig->{Config}{AJAXTriggers}->@*;
}

# ...if not, only check in the first run
elsif ( !$Param{InitialRun} ) {
return ();
}

return () if IsArrayRefWithData( $DynamicFieldConfig->{Config}{AJAXTriggers} )
&& !$Param{InitialRun}
&& !any { $ChangedElements{ $Self->{Uniformity}{$_} // $_ } } $DynamicFieldConfig->{Config}{AJAXTriggers}->@*;

my $NewValue = $Self->Evaluate(
DynamicFieldConfig => $DynamicFieldConfig,
Object => {
# ticket specifics
CustomerUserID => $Param{CustomerUser},
TicketID => $Param{TicketID},
# ITSM config item specifics
ConfigItemID => $Param{ConfigItemID},
# general
%GetParam,
},
);

# do nothing if nothing changed
return () if !$Self->ValueIsDifferent(
DynamicFieldConfig => $DynamicFieldConfig,
Value1 => $GetParam{DynamicField}{"DynamicField_$DynamicFieldConfig->{Name}"},
Value2 => $NewValue,
);

return (
NewValue => $NewValue,
);
}

1;
1 change: 1 addition & 0 deletions Kernel/System/DynamicField/Driver/CustomerCompany.pm
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ sub new {
'IsHiddenInTicketInformation' => 0,
'IsReferenceField' => 1,
'IsSetCapable' => 1,
'SetsDynamicContent' => 1,
};

$Self->{ReferencedObjectType} = 'CustomerCompany';
Expand Down
1 change: 1 addition & 0 deletions Kernel/System/DynamicField/Driver/CustomerUser.pm
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ sub new {
'IsHiddenInTicketInformation' => 0,
'IsReferenceField' => 1,
'IsSetCapable' => 1,
'SetsDynamicContent' => 1,
};

$Self->{ReferencedObjectType} = 'CustomerUser';
Expand Down
160 changes: 158 additions & 2 deletions Kernel/System/DynamicField/Driver/Lens.pm
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use parent qw(Kernel::System::DynamicField::Driver::Base);
# CPAN modules

# OTOBO modules
use Kernel::System::VariableCheck qw(IsHashRefWithData IsArrayRefWithData);
use Kernel::System::VariableCheck qw(:all);
use Kernel::Language qw(Translatable);

our @ObjectDependencies = (
Expand Down Expand Up @@ -82,6 +82,7 @@ sub new {
'IsStatsCondition' => 1,
'IsCustomerInterfaceCapable' => 0,
'IsHiddenInTicketInformation' => 0,
'SetsDynamicContent' => 1,
};

return $Self;
Expand Down Expand Up @@ -519,7 +520,7 @@ sub HasBehavior {

# TODO: Think about additional behaviors we can just adopt from the attribute field
# for certain behaviors instead use the attribute field behaviors
if ( grep { $Param{Behavior} } qw/IsACLReducible/ ) {
if ( grep { $Param{Behavior} eq $_ } qw/IsACLReducible/ ) {
my $AttributeDFConfig = $Self->_GetAttributeDFConfig(
LensDynamicFieldConfig => $Param{DynamicFieldConfig},
);
Expand Down Expand Up @@ -560,6 +561,161 @@ sub BuildSelectionDataGet {
);
}

sub GetFieldState {
my ( $Self, %Param ) = @_;

my $DynamicFieldConfig = $Param{DynamicFieldConfig};
my $NeedsReset;

# reset if the referenced object changes
if ( $Param{ChangedElements}{ $DynamicFieldConfig->{Config}{ReferenceDFName} } ) {
$NeedsReset = 1;
}

# or if we have the field reappear
elsif ( $Param{CachedVisibility} && $Param{CachedVisibility}{ 'DynamicField_' . $DynamicFieldConfig->{Name} } == 0 ) {
$NeedsReset = 1;
}

# TODO: also on initial run?

return () if !$NeedsReset;

my $DFParam = $Param{GetParam}{DynamicField};

my $AttributeFieldValue;
my $IsACLReducible = $Self->HasBehavior(
DynamicFieldConfig => $DynamicFieldConfig,
Behavior => 'IsACLReducible',
);

# get the current value of the referenced attribute field if an object is referenced
if ( $DFParam->{ $DynamicFieldConfig->{Config}{ReferenceDFName} } ) {
$AttributeFieldValue = $Self->ValueGet(
DynamicFieldConfig => $DynamicFieldConfig,

# TODO: Instead we could just send $DFParam->{ $DynamicFieldConfig->{Config}{ReferenceDFName} } as ObjectID
# but we would need to interpret it later (from ConfigItemID to LastVersionID, e.g.)
# TODO: Validate the Reference ObjectID here, or earlier, to prevent data leaks!
ObjectID => 1, # will not be used;
UseReferenceEditField => 1,
);
}

my %Return;

# set the new value if it differs
if (
$Self->ValueIsDifferent(
DynamicFieldConfig => $DynamicFieldConfig,
Value1 => $DFParam->{"DynamicField_$DynamicFieldConfig->{Name}"},
Value2 => $AttributeFieldValue,
)
)
{
# if this field is non ACL reducible, set the field values
if ( !$IsACLReducible ) {
return (
NewValue => $AttributeFieldValue,
);
}

$Return{NewValue} = $AttributeFieldValue;

# already write the new value to DFParam, for possible values check further down
$DFParam->{"DynamicField_$DynamicFieldConfig->{Name}"} = $AttributeFieldValue;
}
else {
return () if !$IsACLReducible;
}

# get possible values if ACLReducible
# this is what the FieldRestrictions object would do for other fields
my $PossibleValues = $Self->PossibleValuesGet(
DynamicFieldConfig => $DynamicFieldConfig,
);

# convert possible values key => value to key => key for ACLs using a Hash slice
my %AclData = %{$PossibleValues};
@AclData{ keys %AclData } = keys %AclData;

# set possible values filter from ACLs
if ( $Param{TicketObject} ) {
my $ACL = $Param{TicketObject}->TicketAcl(
%{ $Param{GetParam} },
TicketID => $Param{TicketID},
Action => $Param{Action},
UserID => $Param{UserID},
CustomerUserID => $Param{CustomerUser} || '',
ReturnType => 'Ticket',
ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name},
Data => \%AclData,
);
if ($ACL) {
my %Filter = $Param{TicketObject}->TicketAclData();

# convert Filter key => key back to key => value using map
%{$PossibleValues} = map { $_ => $PossibleValues->{$_} } keys %Filter;
}
}
elsif ( $Param{ConfigItemObject} ) {
my $ACL = $Param{ConfigItemObject}->ConfigItemAcl(
%{ $Param{GetParam} },
ConfigItemID => $Param{ConfigItemID},
Action => $Param{Action},
UserID => $Param{UserID},
ReturnType => 'ConfigItem',
ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name},
Data => \%AclData,
);
if ($ACL) {
my %Filter = $Param{ConfigItemObject}->ConfigItemAclData();

# convert Filter key => key back to key => value using map
%{$PossibleValues} = map { $_ => $PossibleValues->{$_} } keys %Filter;
}
}
else {
$Kernel::OM->Get('Kernel::System::Log')->Log(
'Priority' => 'error',
'Message' => "Need Ticket or CI Object.",
);

return ();
}

# check whether all selected entries are still valid
if ( defined $DFParam->{"DynamicField_$DynamicFieldConfig->{Name}"} &&
( $DFParam->{"DynamicField_$DynamicFieldConfig->{Name}"} || $DFParam->{"DynamicField_$DynamicFieldConfig->{Name}"} eq '0' ) )
{
# multiselect fields
if ( ref( $DFParam->{"DynamicField_$DynamicFieldConfig->{Name}"} ) ) {
SELECTED:
for my $Selected ( @{ $DFParam->{"DynamicField_$DynamicFieldConfig->{Name}"} } ) {

# if a selected value is not possible anymore
if ( !defined $PossibleValues->{$Selected} ) {
$Return{NewValue} = grep { defined $PossibleValues->{$Selected} } @{ $DFParam->{"DynamicField_$DynamicFieldConfig->{Name}"} };

last SELECTED;
}
}
}

# singleselect fields
else {
if ( !defined $PossibleValues->{ $DFParam->{"DynamicField_$DynamicFieldConfig->{Name}"} } ) {
$Return{NewValue} = '';
}
}
}

return (
%Return,
PossibleValues => $PossibleValues,
);
}

=head1 internal methods
Methods that are used only internally.
Expand Down
Loading

0 comments on commit b6103c5

Please sign in to comment.