Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/bestpractical/rt.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsunnavy <sunnavy@bestpractical.com>2022-10-02 14:11:18 +0300
committersunnavy <sunnavy@bestpractical.com>2022-10-19 00:25:59 +0300
commit76f93bf82b0039ae58b352f9becc58c26de4382a (patch)
treed51bcdbf67bb22553d1387c2a2a1f5059cc0cfe8
parent9c386adb1719d811097e5683acac3fcdbf65b671 (diff)
Support custom roles for asset searches
-rw-r--r--lib/RT/Assets.pm57
-rw-r--r--share/html/Asset/Search/Bulk.html35
-rw-r--r--share/html/Search/Elements/BuildFormatString13
-rw-r--r--share/html/Search/Elements/PickBasics2
-rw-r--r--share/html/Search/Elements/PickCustomRoles31
-rw-r--r--share/html/Search/Elements/SelectPersonType28
6 files changed, 135 insertions, 31 deletions
diff --git a/lib/RT/Assets.pm b/lib/RT/Assets.pm
index 046a834e9e..ca7c355be1 100644
--- a/lib/RT/Assets.pm
+++ b/lib/RT/Assets.pm
@@ -89,6 +89,7 @@ our %FIELD_METADATA = (
HeldByGroup => [ 'MEMBERSHIPFIELD' => 'HeldBy', ], #loc_left_pair
Contact => [ 'WATCHERFIELD' => 'Contact', ], #loc_left_pair
ContactGroup => [ 'MEMBERSHIPFIELD' => 'Contact', ], #loc_left_pair
+ CustomRole => [ 'WATCHERFIELD' ], # loc_left_pair
CustomFieldValue => [ 'CUSTOMFIELD' => 'Asset' ], #loc_left_pair
CustomField => [ 'CUSTOMFIELD' => 'Asset' ], #loc_left_pair
@@ -1217,6 +1218,47 @@ sub _StringLimit {
);
}
+=head2 _CustomRoleDecipher
+
+Try and turn a custom role descriptor (e.g. C<CustomRole.{Engineer}>) into
+(role, column, original name).
+
+=cut
+
+sub _CustomRoleDecipher {
+ my ( $self, $string ) = @_;
+
+ # $column could be core fields like "EmailAddress" or CFs like
+ # "CustomField.{Department}", the CF format is used in OrderByCols.
+ my ( $field, $column ) = ( $string =~ /^\{(.+?)\}(?:\.(.+))?$/ );
+
+ my $role;
+
+ if ( $field =~ /\D/ ) {
+ my $roles = RT::CustomRoles->new( $self->CurrentUser );
+ $roles->LimitToLookupType( RT::Asset->CustomFieldLookupType );
+ $roles->Limit( FIELD => 'Name', VALUE => $field, CASESENSITIVE => 0 );
+
+ # in case there are multiple matches, bail out as we
+ # don't know which one to use
+ $role = $roles->First;
+ if ($role) {
+ if ( $roles->Next ) {
+ RT->Logger->error(
+ "Ambiguous custom role named '$field' in AssetSQL; skipping. Perhaps specify __CustomRole.{id}__ instead."
+ );
+ $role = undef;
+ }
+ }
+ }
+ else {
+ $role = RT::CustomRole->new( $self->CurrentUser );
+ $role->Load($field);
+ }
+
+ return ( $role, $column, $field );
+}
+
=head2 _WatcherLimit
Handle watcher limits. (Requestor, CC, etc..)
@@ -1238,18 +1280,25 @@ sub _WatcherLimit {
my $meta = $FIELD_METADATA{ $field };
my $type = $meta->[1] || '';
my $class = $meta->[2] || 'Asset';
+ my $column = $rest{SUBKEY};
+
+ if ($field eq 'CustomRole') {
+ my ($role, $col, $original_name) = $self->_CustomRoleDecipher( $column );
+ $column = $col || 'id';
+ $type = $role ? $role->GroupType : $original_name;
+ }
# Bail if the subfield is not allowed
- if ( $rest{SUBKEY}
- and not grep { $_ eq $rest{SUBKEY} } @{$SEARCHABLE_SUBFIELDS{'User'}})
+ if ( $column
+ and not grep { $_ eq $column } @{$SEARCHABLE_SUBFIELDS{'User'}})
{
- die "Invalid watcher subfield: '$rest{SUBKEY}'";
+ die "Invalid watcher subfield: '$column'";
}
$self->RoleLimit(
TYPE => $type,
CLASS => "RT::$class",
- FIELD => $rest{SUBKEY},
+ FIELD => $column,
OPERATOR => $op,
VALUE => $value,
SUBCLAUSE => "assetsql",
diff --git a/share/html/Asset/Search/Bulk.html b/share/html/Asset/Search/Bulk.html
index f1331b3af2..43b716296d 100644
--- a/share/html/Asset/Search/Bulk.html
+++ b/share/html/Asset/Search/Bulk.html
@@ -124,29 +124,29 @@
</&>
<&| /Widgets/TitleBox, title => loc("People"), class => "asset-people asset-bulk-people", title_class => "inverse" &>
-% for my $rname ( $asset->Roles( ACLOnly => 0 ) ) {
-% my $role = $asset->Role( $rname );
-% if ( $role->{'Single'} ) {
+% for my $rname ( $asset->Roles( ACLOnly => 0, Single => 1 ), map { $_->GroupType } @{ $single_roles->ItemsArrayRef } ) {
% my $input = "SetRoleMember-$rname";
<div class="form-row">
<div class="col-6">
- <&| /Elements/LabeledValue, Label => loc($rname) &>
+ <&| /Elements/LabeledValue, Label => RT::Asset->LabelForRole($rname) &>
<input class="form-control" type="text" value="<% $ARGS{ $input } || '' %>" name="<% $input %>" id="<% $input %>" data-autocomplete="Users" data-autocomplete-return="Name" />
</&>
</div>
</div>
-% } else {
+% }
+
+% for my $rname ( $asset->Roles( ACLOnly => 0, Single => 0 ), map { $_->GroupType } @{ $multi_roles->ItemsArrayRef } ) {
% my $input = "AddRoleMember-$rname";
<div class="form-row">
<div class="col-6">
- <&| /Elements/LabeledValue, Label => loc("Add [_1]", loc($rname)) &>
+ <&| /Elements/LabeledValue, Label => loc("Add [_1]", RT::Asset->LabelForRole($rname)) &>
<input class="form-control" type="text" value="<% $ARGS{ $input } || '' %>" name="<% $input %>" id="<% $input %>" data-autocomplete="Users" data-autocomplete-return="Name" />
</&>
</div>
% $input = "RemoveRoleMember-$rname";
<div class="col-6">
- <&| /Elements/LabeledValue, Label => loc("Remove [_1]", loc($rname)) &>
+ <&| /Elements/LabeledValue, Label => loc("Remove [_1]", RT::Asset->LabelForRole($rname)) &>
<input class="form-control" type="text" value="<% $ARGS{ $input } || '' %>" name="<% $input %>" id="<% $input %>" data-autocomplete="Users" data-autocomplete-return="Name" />
<div class="custom-control custom-checkbox">
@@ -157,7 +157,6 @@
</div>
</div>
% }
-% }
% my $people_cfs = $cfs->Clone;
% $people_cfs->LimitToGrouping( 'RT::Asset' => 'People');
% if ( $people_cfs->Count ) {
@@ -229,6 +228,9 @@ delete $ARGS{$_} foreach grep { $ARGS{$_} =~ /^$/ } keys %ARGS;
$DECODED_ARGS->{'UpdateAssetAll'} = 1 unless @UpdateAsset;
my $cfs;
+my $single_roles = RT::CustomRoles->new( $session{CurrentUser} );
+my $multi_roles = RT::CustomRoles->new( $session{CurrentUser} );
+
if ( $ARGS{Query} ) {
$cfs = RT::CustomFields->new( $session{'CurrentUser'} );
$cfs->LimitToLookupType( RT::Asset->CustomFieldLookupType );
@@ -252,9 +254,26 @@ if ( $ARGS{Query} ) {
}
}
$cfs->LimitToGlobalOrObjectId(@ids);
+
+ if ( @ids ) {
+ $single_roles->LimitToObjectId($_) for @ids;
+ $multi_roles->LimitToObjectId($_) for @ids;
+ }
}
else {
$cfs = $catalog_obj->AssetCustomFields;
+ $single_roles->LimitToObjectId( $catalog_obj->Id );
+ $multi_roles->LimitToObjectId( $catalog_obj->Id );
+}
+
+if ( $single_roles->_isLimited ) {
+ $single_roles->LimitToLookupType( RT::Asset->CustomFieldLookupType );
+ $single_roles->LimitToSingleValue;
+}
+
+if ( $multi_roles->_isLimited ) {
+ $multi_roles->LimitToLookupType( RT::Asset->CustomFieldLookupType );
+ $multi_roles->LimitToMultipleValue;
}
if ( $ARGS{'CreateLinkedTicket'} ){
diff --git a/share/html/Search/Elements/BuildFormatString b/share/html/Search/Elements/BuildFormatString
index 6211f8369c..12e630d857 100644
--- a/share/html/Search/Elements/BuildFormatString
+++ b/share/html/Search/Elements/BuildFormatString
@@ -144,6 +144,19 @@ elsif ( $Class eq 'RT::Assets' ) {
push @fields, "CustomFieldView.{" . $CustomField->Name . "}";
}
+ my $CustomRoles = RT::CustomRoles->new( $session{'CurrentUser'} );
+ foreach my $id ( keys %catalogs ) {
+
+ # Gotta load up the $catalog object, since catalogs get stored by name now.
+ my $catalog = RT::Catalog->new( $session{'CurrentUser'} );
+ $catalog->Load($id);
+ next unless $catalog->Id;
+ $CustomRoles->LimitToObjectId( $catalog->Id );
+ }
+ $CustomRoles->LimitToLookupType( RT::Asset->CustomFieldLookupType ) if $CustomRoles->_isLimited;
+ while ( my $role = $CustomRoles->Next ) {
+ push @fields, 'CustomRole.{' . $role->Name . '}';
+ }
}
else {
$Format ||= RT->Config->Get('DefaultSearchResultFormat');
diff --git a/share/html/Search/Elements/PickBasics b/share/html/Search/Elements/PickBasics
index 4e134b1e81..22ff9d67a0 100644
--- a/share/html/Search/Elements/PickBasics
+++ b/share/html/Search/Elements/PickBasics
@@ -275,7 +275,7 @@ elsif ( $Class eq 'RT::Assets' ) {
Field => {
Type => 'component',
Path => 'SelectPersonType',
- Arguments => { Default => 'Owner', Class => 'RT::Assets' },
+ Arguments => { Default => 'Owner', Class => 'RT::Assets', Catalogs => \%catalogs },
},
Op => {
Type => 'component',
diff --git a/share/html/Search/Elements/PickCustomRoles b/share/html/Search/Elements/PickCustomRoles
index b24956e6fb..ab86e39f04 100644
--- a/share/html/Search/Elements/PickCustomRoles
+++ b/share/html/Search/Elements/PickCustomRoles
@@ -47,20 +47,35 @@
%# END BPS TAGGED BLOCK }}}
<%ARGS>
%queues => ()
+%catalogs => ()
</%ARGS>
<%INIT>
RT->Deprecated( Message => '/Search/Elements/PickCustomRoles is obsolete', Remove => '5.2' );
my $CustomRoles = RT::CustomRoles->new( $session{'CurrentUser'});
-foreach my $id (keys %queues) {
- # Gotta load up the $queue object, since queues get stored by name now.
- my $queue = RT::Queue->new($session{'CurrentUser'});
- $queue->Load($id);
- next unless $queue->Id;
- $CustomRoles->LimitToObjectId($queue->Id);
+if ( %queues ) {
+ foreach my $id (keys %queues) {
+ # Gotta load up the $queue object, since queues get stored by name now.
+ my $queue = RT::Queue->new($session{'CurrentUser'});
+ $queue->Load($id);
+ next unless $queue->Id;
+ $CustomRoles->LimitToObjectId($queue->Id);
+ }
+ # If there are no referenced queues, do not limit LookupType to return 0 custom roles.
+ $CustomRoles->LimitToLookupType( RT::Ticket->CustomFieldLookupType ) if $CustomRoles->_isLimited;
}
-# If there are no referenced queues, do not limit LookupType to return 0 custom roles.
-$CustomRoles->LimitToLookupType( RT::Ticket->CustomFieldLookupType ) if $CustomRoles->_isLimited;
+elsif ( %catalogs ) {
+ foreach my $id (keys %catalogs) {
+ # Gotta load up the $catalog object, since catalogs get stored by name now.
+ my $catalog = RT::Catalog->new($session{'CurrentUser'});
+ $catalog->Load($id);
+ next unless $catalog->Id;
+ $CustomRoles->LimitToObjectId($catalog->Id);
+ }
+ # If there are no referenced catalogs, do not limit LookupType to return 0 custom roles.
+ $CustomRoles->LimitToLookupType( RT::Asset->CustomFieldLookupType ) if $CustomRoles->_isLimited;
+}
+
$m->callback(
CallbackName => 'MassageCustomRoles',
CustomRoles => $CustomRoles,
diff --git a/share/html/Search/Elements/SelectPersonType b/share/html/Search/Elements/SelectPersonType
index 942a3a408a..9e029f78c6 100644
--- a/share/html/Search/Elements/SelectPersonType
+++ b/share/html/Search/Elements/SelectPersonType
@@ -83,10 +83,18 @@
<%INIT>
my ( @types, @subtypes );
+my $CustomRoles = RT::CustomRoles->new( $session{'CurrentUser'});
+
if ( $Class eq 'RT::Assets' ) {
@types = qw(Owner HeldBy Contact);
@subtypes = @{ $RT::Assets::SEARCHABLE_SUBFIELDS{'User'} };
+ foreach my $id (keys %Catalogs) {
+ my $catalog = RT::Catalog->new($session{'CurrentUser'});
+ $catalog->Load($id);
+ next unless $catalog->Id;
+ $CustomRoles->LimitToObjectId($catalog->Id);
+ }
}
else {
if ($Role) {
@@ -106,27 +114,26 @@ else {
else {
@types = qw(Requestor Cc AdminCc Watcher Owner QueueCc QueueAdminCc QueueWatcher);
- my $CustomRoles = RT::CustomRoles->new( $session{'CurrentUser'});
foreach my $id (keys %Queues) {
my $queue = RT::Queue->new($session{'CurrentUser'});
$queue->Load($id);
next unless $queue->Id;
$CustomRoles->LimitToObjectId($queue->Id);
}
-
- # If there are no referenced queues/catalogs, do not limit LookupType to return 0 custom roles.
- $CustomRoles->LimitToLookupType( $Class->RecordClass->CustomFieldLookupType ) if $CustomRoles->_isLimited;
-
- $m->callback(
- CallbackName => 'MassageCustomRoles',
- CustomRoles => $CustomRoles,
- );
- push @types, map { [ "CustomRole.{" . $_->Name . "}", $_->Name ] } @{ $CustomRoles->ItemsArrayRef };
}
@subtypes = @{ $RT::Tickets::SEARCHABLE_SUBFIELDS{'User'} };
}
+# If there are no referenced queues/catalogs, do not limit LookupType to return 0 custom roles.
+$CustomRoles->LimitToLookupType( $Class->RecordClass->CustomFieldLookupType ) if $CustomRoles->_isLimited;
+
+$m->callback(
+ CallbackName => 'MassageCustomRoles',
+ CustomRoles => $CustomRoles,
+);
+push @types, map { [ "CustomRole.{" . $_->Name . "}", $_->Name ] } @{ $CustomRoles->ItemsArrayRef };
+
$m->callback(Types => \@types, Subtypes => \@subtypes);
</%INIT>
@@ -140,4 +147,5 @@ $Name => 'WatcherType'
$Role => undef
@Roles => ()
%Queues => ()
+%Catalogs => ()
</%ARGS>