From 58d08f216325c2380ff68e11bd22027e1ebc5cb1 Mon Sep 17 00:00:00 2001 From: Shawn M Moore Date: Thu, 13 Apr 2017 16:26:26 +0000 Subject: Factor out a LookupType role from CustomFields This will be added to CustomRoles to support custom roles on assets and other record types. This generalizes and deprecates /Admin/Elements/SelectCustomFieldLookupType in favor of a new /Admin/Elements/SelectLookupType. That way we can use it on the CustomRole Modify page --- lib/RT/CustomField.pm | 157 +------------ lib/RT/Record/Role/LookupType.pm | 250 +++++++++++++++++++++ share/html/Admin/CustomFields/Modify.html | 4 +- .../Admin/Elements/SelectCustomFieldLookupType | 17 +- share/html/Admin/Elements/SelectLookupType | 61 +++++ 5 files changed, 322 insertions(+), 167 deletions(-) create mode 100644 lib/RT/Record/Role/LookupType.pm create mode 100644 share/html/Admin/Elements/SelectLookupType diff --git a/lib/RT/CustomField.pm b/lib/RT/CustomField.pm index c0112ae7da..0db2f68c66 100644 --- a/lib/RT/CustomField.pm +++ b/lib/RT/CustomField.pm @@ -57,7 +57,8 @@ use Scalar::Util 'blessed'; use base 'RT::Record'; use Role::Basic 'with'; -with "RT::Record::Role::Rights"; +with "RT::Record::Role::Rights", + "RT::Record::Role::LookupType"; sub Table {'CustomFields'} @@ -218,7 +219,6 @@ our %FieldTypes = ( my %BUILTIN_GROUPINGS; -my %FRIENDLY_LOOKUP_TYPES = (); __PACKAGE__->RegisterLookupType( 'RT::Queue-RT::Ticket' => "Tickets", ); #loc __PACKAGE__->RegisterLookupType( 'RT::Queue-RT::Ticket-RT::Transaction' => "Ticket Transactions", ); #loc @@ -1414,120 +1414,6 @@ sub SetLookupType { return $self->_Set(Field => 'LookupType', Value =>$lookup); } -=head2 LookupTypes - -Returns an array of LookupTypes available - -=cut - - -sub LookupTypes { - my $self = shift; - return sort keys %FRIENDLY_LOOKUP_TYPES; -} - -=head2 FriendlyLookupType - -Returns a localized description of the type of this custom field - -=cut - -sub FriendlyLookupType { - my $self = shift; - my $lookup = shift || $self->LookupType; - - return ($self->loc( $FRIENDLY_LOOKUP_TYPES{$lookup} )) - if defined $FRIENDLY_LOOKUP_TYPES{$lookup}; - - my @types = map { s/^RT::// ? $self->loc($_) : $_ } - grep { defined and length } - split( /-/, $lookup ) - or return; - - state $LocStrings = [ - "[_1] objects", # loc - "[_1]'s [_2] objects", # loc - "[_1]'s [_2]'s [_3] objects", # loc - ]; - return ( $self->loc( $LocStrings->[$#types], @types ) ); -} - -=head1 RecordClassFromLookupType - -Returns the type of Object referred to by ObjectCustomFields' ObjectId column - -Optionally takes a LookupType to use instead of using the value on the loaded -record. In this case, the method may be called on the class instead of an -object. - -=cut - -sub RecordClassFromLookupType { - my $self = shift; - my $type = shift || $self->LookupType; - my ($class) = ($type =~ /^([^-]+)/); - unless ( $class ) { - if (blessed($self) and $self->LookupType eq $type) { - $RT::Logger->error( - "Custom Field #". $self->id - ." has incorrect LookupType '$type'" - ); - } else { - RT->Logger->error("Invalid LookupType passed as argument: $type"); - } - return undef; - } - return $class; -} - -=head1 ObjectTypeFromLookupType - -Returns the ObjectType used in ObjectCustomFieldValues rows for this CF - -Optionally takes a LookupType to use instead of using the value on the loaded -record. In this case, the method may be called on the class instead of an -object. - -=cut - -sub ObjectTypeFromLookupType { - my $self = shift; - my $type = shift || $self->LookupType; - my ($class) = ($type =~ /([^-]+)$/); - unless ( $class ) { - if (blessed($self) and $self->LookupType eq $type) { - $RT::Logger->error( - "Custom Field #". $self->id - ." has incorrect LookupType '$type'" - ); - } else { - RT->Logger->error("Invalid LookupType passed as argument: $type"); - } - return undef; - } - return $class; -} - -sub CollectionClassFromLookupType { - my $self = shift; - my $record_class = shift || $self->RecordClassFromLookupType; - - return undef unless $record_class; - - my $collection_class; - if ( UNIVERSAL::can($record_class.'Collection', 'new') ) { - $collection_class = $record_class.'Collection'; - } elsif ( UNIVERSAL::can($record_class.'es', 'new') ) { - $collection_class = $record_class.'es'; - } elsif ( UNIVERSAL::can($record_class.'s', 'new') ) { - $collection_class = $record_class.'s'; - } else { - $RT::Logger->error("Can not find a collection class for record class '$record_class'"); - return undef; - } - return $collection_class; -} - =head2 Groupings Object|Class Name, Queue Name|Catalog Name Returns a (sorted and lowercased) list of the groupings in which this custom @@ -1640,20 +1526,6 @@ sub RegisterBuiltInGroupings { $BUILTIN_GROUPINGS{''} = { map { %$_ } values %BUILTIN_GROUPINGS }; } -=head1 IsOnlyGlobal - -Certain custom fields (users, groups) should only be added globally; -codify that set here for reference. - -=cut - -sub IsOnlyGlobal { - my $self = shift; - - return ($self->LookupType =~ /^RT::(?:Group|User)/io); - -} - =head1 AddedTo Returns collection with objects this custom field is added to. @@ -2141,31 +2013,6 @@ sub CurrentUserCanSee { return 0; } -=head2 RegisterLookupType LOOKUPTYPE FRIENDLYNAME - -Tell RT that a certain object accepts custom fields via a lookup type and -provide a friendly name for such CFs. - -Examples: - - 'RT::Queue-RT::Ticket' => "Tickets", # loc - 'RT::Queue-RT::Ticket-RT::Transaction' => "Ticket Transactions", # loc - 'RT::User' => "Users", # loc - 'RT::Group' => "Groups", # loc - 'RT::Queue' => "Queues", # loc - -This is a class method. - -=cut - -sub RegisterLookupType { - my $self = shift; - my $path = shift; - my $friendly_name = shift; - - $FRIENDLY_LOOKUP_TYPES{$path} = $friendly_name; -} - =head2 IncludeContentForValue [VALUE] (and SetIncludeContentForValue) Gets or sets the C for this custom field. RT diff --git a/lib/RT/Record/Role/LookupType.pm b/lib/RT/Record/Role/LookupType.pm new file mode 100644 index 0000000000..3655969aef --- /dev/null +++ b/lib/RT/Record/Role/LookupType.pm @@ -0,0 +1,250 @@ +# BEGIN BPS TAGGED BLOCK {{{ +# +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2018 Best Practical Solutions, LLC +# +# +# (Except where explicitly superseded by other copyright notices) +# +# +# LICENSE: +# +# This work is made available to you under the terms of Version 2 of +# the GNU General Public License. A copy of that license should have +# been provided with this software, but in any event can be snarfed +# from www.gnu.org. +# +# This work is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 or visit their web page on the internet at +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. +# +# +# CONTRIBUTION SUBMISSION POLICY: +# +# (The following paragraph is not intended to limit the rights granted +# to you to modify and distribute this software under the terms of +# the GNU General Public License and is only of importance to you if +# you choose to contribute your changes and enhancements to the +# community by submitting them to Best Practical Solutions, LLC.) +# +# By intentionally submitting any modifications, corrections or +# derivatives to this work, or any other work intended for use with +# Request Tracker, to Best Practical Solutions, LLC, you confirm that +# you are the copyright holder for those contributions and you grant +# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +# royalty-free, perpetual, license to use, copy, create derivative +# works based on those contributions, and sublicense and distribute +# those contributions and any derivatives thereof. +# +# END BPS TAGGED BLOCK }}} + +package RT::Record::Role::LookupType; + +use strict; +use warnings; +use 5.010; + +use Role::Basic; +use Scalar::Util qw(blessed); + +=head1 NAME + +RT::Record::Role::LookupType - Common methods for records which have a LookupType + +=head1 DESCRIPTION + +Certain records, like custom fields, can be applied to different types of +records (tickets, transactions, groups, users, etc). This role implements +such I concerns. + +This role does not manage concerns relating to specifying which records +of a class (as in L). + +=head1 REQUIRES + +=head2 L + +=head2 LookupType + +A C method which returns this record's lookup type is required. +Currently unenforced at compile-time due to poor interactions with +L. You'll hit run-time errors if +this method isn't available in consuming classes, however. + +=cut + +with 'RT::Record::Role'; + +=head1 PROVIDES + +=head2 RegisterLookupType LOOKUPTYPE FRIENDLYNAME + +Tell RT that a certain object accepts records of this role via a lookup +type and provide a friendly name for them. + +Examples: + + 'RT::Queue-RT::Ticket' => "Tickets", # loc + 'RT::Queue-RT::Ticket-RT::Transaction' => "Ticket Transactions", # loc + 'RT::User' => "Users", # loc + 'RT::Group' => "Groups", # loc + 'RT::Queue' => "Queues", # loc + +This is a class method. + +=cut + +my %REGISTRY = (); + +sub RegisterLookupType { + my $class = shift; + my $path = shift; + my $friendly_name = shift; + + die "RegisterLookupType is a class method" if blessed($class); + + $REGISTRY{$class}{$path} = $friendly_name; +} + +=head2 LookupTypes + +Returns an array of LookupTypes available for this record or class + +=cut + +sub LookupTypes { + my $self = shift; + my $class = blessed($self) || $self; + return sort keys %{ $REGISTRY{ $class } }; +} + +=head2 FriendlyLookupType + +Returns a localized description of the LookupType of this record + +=cut + +sub FriendlyLookupType { + my $self = shift; + my $lookup = shift || $self->LookupType; + + my $class = blessed($self) || $self; + + return ($self->loc( $REGISTRY{$class}{$lookup} )) + if defined $REGISTRY{$class}{$lookup}; + + my @types = map { s/^RT::// ? $self->loc($_) : $_ } + grep { defined and length } + split( /-/, $lookup ) + or return; + + state $LocStrings = [ + "[_1] objects", # loc + "[_1]'s [_2] objects", # loc + "[_1]'s [_2]'s [_3] objects", # loc + ]; + return ( $self->loc( $LocStrings->[$#types], @types ) ); +} + +=head1 RecordClassFromLookupType + +Returns the type of Object referred to by ObjectCustomFields' ObjectId column. +(The first part of the LookupType, e.g. the C of +C) + +Optionally takes a LookupType to use instead of using the value on the loaded +record. In this case, the method may be called on the class instead of an +object. + +=cut + +sub RecordClassFromLookupType { + my $self = shift; + my $type = shift || $self->LookupType; + my ($class) = ($type =~ /^([^-]+)/); + unless ( $class ) { + if (blessed($self) and $self->LookupType eq $type) { + $RT::Logger->error( + blessed($self) . " #". $self->id + ." has incorrect LookupType '$type'" + ); + } else { + RT->Logger->error("Invalid LookupType passed as argument: $type"); + } + return undef; + } + return $class; +} + +=head1 ObjectTypeFromLookupType + +Returns the ObjectType for this record. (The last part of the LookupType, +e.g. the C of C) + +Optionally takes a LookupType to use instead of using the value on the loaded +record. In this case, the method may be called on the class instead of an +object. + +=cut + +sub ObjectTypeFromLookupType { + my $self = shift; + my $type = shift || $self->LookupType; + my ($class) = ($type =~ /([^-]+)$/); + unless ( $class ) { + if (blessed($self) and $self->LookupType eq $type) { + $RT::Logger->error( + blessed($self) . " #". $self->id + ." has incorrect LookupType '$type'" + ); + } else { + RT->Logger->error("Invalid LookupType passed as argument: $type"); + } + return undef; + } + return $class; +} + +sub CollectionClassFromLookupType { + my $self = shift; + + my $record_class = shift || $self->RecordClassFromLookupType; + return undef unless $record_class; + + my $collection_class; + if ( UNIVERSAL::can($record_class.'Collection', 'new') ) { + $collection_class = $record_class.'Collection'; + } elsif ( UNIVERSAL::can($record_class.'es', 'new') ) { + $collection_class = $record_class.'es'; + } elsif ( UNIVERSAL::can($record_class.'s', 'new') ) { + $collection_class = $record_class.'s'; + } else { + $RT::Logger->error("Can not find a collection class for record class '$record_class'"); + return undef; + } + return $collection_class; +} + +=head1 IsOnlyGlobal + +Certain record types (users, groups) should only be added globally; +codify that set here for reference. + +=cut + +sub IsOnlyGlobal { + my $self = shift; + + return ($self->LookupType =~ /^RT::(?:Group|User)/io); + +} + +1; diff --git a/share/html/Admin/CustomFields/Modify.html b/share/html/Admin/CustomFields/Modify.html index 2cfc5be13e..daef955a53 100644 --- a/share/html/Admin/CustomFields/Modify.html +++ b/share/html/Admin/CustomFields/Modify.html @@ -96,8 +96,10 @@ % } <&| /Elements/LabeledValue, Label => loc("Applies to") &> - <& /Admin/Elements/SelectCustomFieldLookupType, + <& /Admin/Elements/SelectLookupType, + Class => 'RT::CustomField', Name => "LookupType", + Object => $CustomFieldObj, Default => $CustomFieldObj->LookupType || $LookupType, &> diff --git a/share/html/Admin/Elements/SelectCustomFieldLookupType b/share/html/Admin/Elements/SelectCustomFieldLookupType index f43d543829..95d7970b83 100644 --- a/share/html/Admin/Elements/SelectCustomFieldLookupType +++ b/share/html/Admin/Elements/SelectCustomFieldLookupType @@ -45,16 +45,11 @@ %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} - -<%INIT> -my $cf = RT::CustomField->new($session{'CurrentUser'}); +<& SelectLookupType, %ARGS, Class => 'RT::CustomField' &> +<%INIT> +RT->Deprecated( + Remove => '5.4', + Instead => 'SelectLookupType', +); -<%ARGS> -$Default=> '' -$Name => 'LookupType' - diff --git a/share/html/Admin/Elements/SelectLookupType b/share/html/Admin/Elements/SelectLookupType new file mode 100644 index 0000000000..7b470555cd --- /dev/null +++ b/share/html/Admin/Elements/SelectLookupType @@ -0,0 +1,61 @@ +%# BEGIN BPS TAGGED BLOCK {{{ +%# +%# COPYRIGHT: +%# +%# This software is Copyright (c) 1996-2022 Best Practical Solutions, LLC +%# +%# +%# (Except where explicitly superseded by other copyright notices) +%# +%# +%# LICENSE: +%# +%# This work is made available to you under the terms of Version 2 of +%# the GNU General Public License. A copy of that license should have +%# been provided with this software, but in any event can be snarfed +%# from www.gnu.org. +%# +%# This work is distributed in the hope that it will be useful, but +%# WITHOUT ANY WARRANTY; without even the implied warranty of +%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%# General Public License for more details. +%# +%# You should have received a copy of the GNU General Public License +%# along with this program; if not, write to the Free Software +%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +%# 02110-1301 or visit their web page on the internet at +%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. +%# +%# +%# CONTRIBUTION SUBMISSION POLICY: +%# +%# (The following paragraph is not intended to limit the rights granted +%# to you to modify and distribute this software under the terms of +%# the GNU General Public License and is only of importance to you if +%# you choose to contribute your changes and enhancements to the +%# community by submitting them to Best Practical Solutions, LLC.) +%# +%# By intentionally submitting any modifications, corrections or +%# derivatives to this work, or any other work intended for use with +%# Request Tracker, to Best Practical Solutions, LLC, you confirm that +%# you are the copyright holder for those contributions and you grant +%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, +%# royalty-free, perpetual, license to use, copy, create derivative +%# works based on those contributions, and sublicense and distribute +%# those contributions and any derivatives thereof. +%# +%# END BPS TAGGED BLOCK }}} + +<%INIT> +$Object ||= $Class->new($session{'CurrentUser'}); + +<%ARGS> +$Default => '' +$Name => 'LookupType' +$Object => undef +$Class => '' + -- cgit v1.2.3