[Rt-commit] rt branch, 4.4/custom-role-sort-order, created. rt-4.4.4-88-gc04b08ad8

? sunnavy sunnavy at bestpractical.com
Fri Feb 21 15:27:16 EST 2020


The branch, 4.4/custom-role-sort-order has been created
        at  c04b08ad8b585f52c03ea2cb8be338e5241708ee (commit)

- Log -----------------------------------------------------------------
commit 6510315e721299c1cd906f33f2d428452cdde689
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Sat Feb 22 02:30:16 2020 +0800

    Support to set sort order of applied custom roles
    
    This is quite like custom fields but simpler mainly because custom roles
    are only applied at queue level.
    
    The code is originally from custom fields' corresponding pages with the
    following differences:
    
    * No globally applied custom roles
    
    There is no corresponding /Admin/Global/CustomRoles.html and we also
    don't need to consider ObjectCustomRoles with ObjectId=0
    
    * No spearate right like "AssignCustomRoles"
    
    Current AddToObject/RemoveFromObject methods check "AdminCustomRoles"
    right, we stick to that on new page too.
    
    As there is no queue-level "AdminCustomRoles", we don't need to set
    specified queue as context object.
    
    * Params ObjectType/SubType are dropped
    
    * AddToObject/RemoveFromObject are called a bit differently
    
    The API is different from custom field
    
    * A bit code style cleanup

diff --git a/lib/RT/CustomRoles.pm b/lib/RT/CustomRoles.pm
index cca57926c..669d0d88a 100644
--- a/lib/RT/CustomRoles.pm
+++ b/lib/RT/CustomRoles.pm
@@ -172,6 +172,29 @@ sub ApplySortOrder {
     } );
 }
 
+=head2 LimitToNotAdded
+
+Takes either list of object ids or nothing. Limits collection
+to custom roles to listed objects or any corespondingly.
+
+=cut
+
+sub LimitToNotAdded {
+    my $self = shift;
+    return RT::ObjectCustomRoles->new( $self->CurrentUser )->LimitTargetToNotAdded( $self => @_ );
+}
+
+=head2 LimitToAdded
+
+Limits collection to custom roles to listed objects or any corespondingly.
+
+=cut
+
+sub LimitToAdded {
+    my $self = shift;
+    return RT::ObjectCustomRoles->new( $self->CurrentUser )->LimitTargetToAdded( $self => @_ );
+}
+
 RT::Base->_ImportOverlays();
 
 1;
diff --git a/share/html/Admin/Elements/EditCustomRoles b/share/html/Admin/Elements/EditCustomRoles
new file mode 100644
index 000000000..2ab7cc38c
--- /dev/null
+++ b/share/html/Admin/Elements/EditCustomRoles
@@ -0,0 +1,170 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
+%#                                          <sales at bestpractical.com>
+%#
+%# (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 }}}
+<& /Elements/ListActions, actions => \@results &>
+
+<form action="<%RT->Config->Get('WebPath')%><% $m->request_comp->path |n %>" method="post" name="EditCustomRoles">
+<input type="hidden" class="hidden" name="id" value="<% $id %>" />
+
+<h2><&|/l&>Selected Custom Roles</&></h2>
+<& /Elements/CollectionList,
+    %ARGS,
+    Collection    => $added_crs,
+    Rows          => 0,
+    Page          => 1,
+    Format        => $format,
+    DisplayFormat => $display_format,
+    AllowSorting  => 0,
+    ShowEmpty     => 0,
+    PassArguments => [
+        qw(Page Order OrderBy id),
+    ],
+&>
+
+<h2><&|/l&>Unselected Custom Roles</&></h2>
+<& /Elements/CollectionList,
+    OrderBy       => 'Name',
+    Order         => 'ASC',
+    %ARGS,
+    Collection    => $not_added_crs,
+    Rows          => $rows,
+    Format        => $format,
+    DisplayFormat => "'__CheckBox.{AddCustomRole}__',". $format,
+    AllowSorting  => 1,
+    ShowEmpty     => 0,
+    PassArguments => [
+        qw(Page Order OrderBy id),
+    ],
+&>
+
+<& /Elements/Submit, Name => 'UpdateCRs' &>
+</form>
+
+
+<%INIT>
+my $id = $Object->Id or Abort( loc("Invalid object") );
+
+if ( !$Object->CurrentUserHasRight('AdminCustomRoles') ) {
+    $m->out( '<p><i>', loc('(No custom roles)'), '</i></p>' );
+    return;
+}
+
+my @results;
+
+## deal with moving sortorder of custom roles
+if ( $MoveCustomRoleUp ) {
+    my $record = RT::ObjectCustomRole->new( $session{'CurrentUser'} );
+    $record->LoadByCols( ObjectId => $id, CustomRole => $MoveCustomRoleUp );
+    unless ( $record->id ) {
+        push @results, loc("Custom role #[_1] is not applied to this object", $MoveCustomRoleUp);
+        last;
+    }
+
+    my ( $status, $msg ) = $record->MoveUp;
+    push @results, $msg;
+}
+if ( $MoveCustomRoleDown ) {
+    my $record = RT::ObjectCustomRole->new( $session{'CurrentUser'} );
+    $record->LoadByCols( ObjectId => $id, CustomRole => $MoveCustomRoleDown );
+    unless ( $record->id ) {
+        push @results, loc("Custom role #[_1] is not applied to this object", $MoveCustomRoleDown);
+        last;
+    }
+
+    my ( $status, $msg ) = $record->MoveDown;
+    push @results, $msg;
+}
+
+if ( $UpdateCRs ) {
+    for my $cr_id ( @AddCustomRole ) {
+        my $cr = RT::CustomRole->new( $session{'CurrentUser'} );
+        $cr->Load( $cr_id );
+        unless ( $cr->id ) {
+            push @results, loc("Couldn't load CustomRole #[_1]", $cr_id);
+            next;
+        }
+        my ( $status, $msg ) = $cr->AddToObject($id);
+        push @results, $msg;
+    }
+    for my $cr_id ( @RemoveCustomRole ) {
+        my $cr = RT::CustomRole->new( $session{'CurrentUser'} );
+        $cr->Load( $cr_id );
+        unless ( $cr->id ) {
+            push @results, loc("Couldn't load CustomRole #[_1]", $cr_id);
+            next;
+        }
+        my ( $status, $msg ) = $cr->RemoveFromObject($id);
+        push @results, $msg;
+    }
+}
+
+$m->callback( CallbackName => 'UpdateExtraFields', Results => \@results, Object => $Object, %ARGS );
+
+my $added_crs = RT::CustomRoles->new( $session{'CurrentUser'} );
+$added_crs->LimitToObjectId($id);
+$added_crs->ApplySortOrder;
+
+my $not_added_crs = RT::CustomRoles->new( $session{'CurrentUser'} );
+$not_added_crs->LimitToNotAdded( $id );
+
+my $format = RT->Config->Get('AdminSearchResultFormat')->{'CustomRoles'};
+my $rows = RT->Config->Get('AdminSearchResultRows')->{'CustomRoles'} || 50;
+
+my $display_format = $id
+            ? ("'__RemoveCheckBox.{$id}__',". $format .", '__MoveCR.{$id}__'")
+            : ("'__CheckBox.{RemoveCustomRole}__',". $format .", '__MoveCR.{$id}__'");
+$m->callback( CallbackName => 'EditDisplayFormat', DisplayFormat => \$display_format, id => $id );
+
+</%INIT>
+<%ARGS>
+$Object
+$UpdateCRs           => undef
+ at RemoveCustomRole   => ()
+ at AddCustomRole      => ()
+$MoveCustomRoleUp   => undef
+$MoveCustomRoleDown => undef
+</%ARGS>
diff --git a/share/html/Elements/RT__CustomRole/ColumnMap b/share/html/Admin/Queues/CustomRoles.html
similarity index 56%
copy from share/html/Elements/RT__CustomRole/ColumnMap
copy to share/html/Admin/Queues/CustomRoles.html
index 63ef10bf5..9ae98488f 100644
--- a/share/html/Elements/RT__CustomRole/ColumnMap
+++ b/share/html/Admin/Queues/CustomRoles.html
@@ -45,66 +45,16 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-<%ARGS>
-$Name => undef
-$Attr => undef
-$GenericMap => {}
-</%ARGS>
-<%ONCE>
-my $COLUMN_MAP = {
-    Disabled => {
-        title     => 'Status', # loc
-        attribute => 'Disabled',
-        value     => sub { return $_[0]->Disabled ? $_[0]->loc('Disabled') : $_[0]->loc('Enabled') },
-    },
-
-    map(
-        { my $c = $_; $c => {
-            title     => $c, attribute => $c,
-            value     => sub { return $_[0]->$c() },
-        } }
-        qw(Name Description EntryHint)
-    ),
-
-    MaxValues => {
-        title     => 'Number', # loc
-        attribute => 'MaxValues',
-        value     => sub {
-            my $v = $_[0]->MaxValues;
-            return !$v ? $_[0]->loc('Multiple') : $v == 1 ? $_[0]->loc('Single') : $v;
-        },
-    },
-
-    AddedTo => {
-        title     => 'Added', # loc
-        value     => sub {
-            my $collection = $_[0]->AddedTo;
-            return '' unless $collection;
-
-            $collection->RowsPerPage(10);
+<& /Admin/Elements/Header, Title => $title &>
+<& /Elements/Tabs &>
+<& /Admin/Elements/EditCustomRoles, %ARGS, title => $title, Object => $Object &>
 
-            my $found = 0;
-            my $res = '';
-            while ( my $record = $collection->Next ) {
-                $res .= ', ' if $res;
-
-                my $id = '';
-                $id = $record->Name if $record->_Accessible('Name','read');
-                $id ||= "#". $record->id;
-                $res .= $id;
-
-                $found++;
-            }
-            $res .= ', ...' if $found >= 10;
-            return $res;
-        },
-
-    },
-};
-
-</%ONCE>
 <%INIT>
-$m->callback( GenericMap => $GenericMap, COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
-return GetColumnMapEntry( Map => $COLUMN_MAP, Name => $Name, Attribute => $Attr );
+my $Object = RT::Queue->new($session{'CurrentUser'});
+$Object->Load($id) || Abort( loc( "Couldn't load object [_1]", $id ) );
+my $title = loc( 'Custom Roles for queue [_1]', $Object->Name );
 </%INIT>
 
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/share/html/Elements/RT__CustomRole/ColumnMap b/share/html/Elements/RT__CustomRole/ColumnMap
index 63ef10bf5..31f8d25ef 100644
--- a/share/html/Elements/RT__CustomRole/ColumnMap
+++ b/share/html/Elements/RT__CustomRole/ColumnMap
@@ -100,6 +100,58 @@ my $COLUMN_MAP = {
         },
 
     },
+    RemoveCheckBox => {
+        title => sub {
+            my $name = 'RemoveCustomRole';
+            my $checked = $DECODED_ARGS->{ $name .'All' }? 'checked="checked"': '';
+
+            return \qq{<input type="checkbox" name="}, $name, \qq{All" value="1" $checked
+                              onclick="setCheckbox(this, },
+                              $m->interp->apply_escapes($name,'j'),
+                              \qq{)" />};
+        },
+        value => sub {
+            my $id   = $_[0]->id;
+            my $name = 'RemoveCustomRole';
+            my $arg  = $DECODED_ARGS->{$name};
+
+            my $checked = '';
+            if ( $arg && ref $arg ) {
+                $checked = 'checked="checked"' if grep $_ == $id, @$arg;
+            }
+            elsif ( $arg ) {
+                $checked = 'checked="checked"' if $arg == $id;
+            }
+            return \qq{<input type="checkbox" name="}, $name, \qq{" value="$id" $checked />}
+        },
+    },
+    MoveCR => {
+        title => 'Move',
+        value => sub {
+            return '' unless $_[0]->IsAdded( $_[2] );
+
+            my $id      = $_[0]->id;
+            my $name = 'MoveCustomRole';
+            my $args = $m->caller_args( 1 );
+            my @pass = ref $args->{'PassArguments'}
+                ? @{$args->{'PassArguments'}}
+                : ($args->{'PassArguments'});
+            my %pass = map { $_ => $args->{$_} } grep exists $args->{$_}, @pass;
+
+            my $uri = RT->Config->Get('WebPath') . $m->request_path;
+
+            my @res = (
+                \'<a href="',
+                $uri .'?'. $m->comp("/Elements/QueryString", %pass, MoveCustomRoleUp => $id ),
+                \'">', loc('~[Up~]'), \'</a>',
+                \' <a href="',
+                $uri .'?'. $m->comp("/Elements/QueryString", %pass, MoveCustomRoleDown => $id ),
+                \'">', loc('~[Down~]'), \'</a>'
+            );
+
+            return @res;
+        },
+    },
 };
 
 </%ONCE>
diff --git a/share/html/Elements/Tabs b/share/html/Elements/Tabs
index 755c8d219..e70a47c94 100644
--- a/share/html/Elements/Tabs
+++ b/share/html/Elements/Tabs
@@ -354,6 +354,7 @@ my $build_admin_menu = sub {
                 my $txn_cfs = $cfs->child( 'transactions' => title => loc('Transactions'),
                     path => '/Admin/Queues/CustomFields.html?SubType=RT::Ticket-RT::Transaction&id='.$id );
 
+                $queue->child( 'custom-roles' => title => loc('Custom Roles'), path => "/Admin/Queues/CustomRoles.html?id=".$id );
                 $queue->child( 'group-rights' => title => loc('Group Rights'), path => "/Admin/Queues/GroupRights.html?id=".$id );
                 $queue->child( 'user-rights' => title => loc('User Rights'), path => "/Admin/Queues/UserRights.html?id=" . $id );
                 $queue->child( 'history' => title => loc('History'), path => "/Admin/Queues/History.html?id=" . $id );

commit b829029fdd71fb9d35e0dbbbdd95e156cc5f4a9d
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Sat Feb 22 03:05:33 2020 +0800

    Show custom roles in correct order on queue watcher and ticket pages
    
    _ROLES has all the custom roles, but we can't set sort order there as
    they are at the level of class instead of object where sort order really
    applies.
    
    We need to find out the sort order at object level, thus calls like
    "Roles"(it calls Role internally) on an object could return roles in
    correct order.

diff --git a/lib/RT/Record/Role/Roles.pm b/lib/RT/Record/Role/Roles.pm
index b973eb21b..d1f146842 100644
--- a/lib/RT/Record/Role/Roles.pm
+++ b/lib/RT/Record/Role/Roles.pm
@@ -248,7 +248,17 @@ Returns an empty hashref if the role doesn't exist.
 =cut
 
 sub Role {
-    return \%{ $_[0]->_ROLES->{$_[1]} || {} };
+    my $self = shift;
+    my $name = shift;
+    my %item = %{ $self->_ROLES->{$name} || {} };
+
+    if ( ref $self && $self->Id && $name =~ /^RT::CustomRole-(\d+)/ ) {
+        my $id  = $1;
+        my $ocr = RT::ObjectCustomRole->new( $self->CurrentUser );
+        $ocr->LoadByCols( ObjectId => $self->Id, CustomRole => $id );
+        $item{SortOrder} = $ocr->SortOrder if $ocr->Id;
+    }
+    return \%item;
 }
 
 =head2 Roles

commit c04b08ad8b585f52c03ea2cb8be338e5241708ee
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Sat Feb 22 03:53:47 2020 +0800

    Test sort order of custom roles
    
    This is originally from t/customroles/sort_order.t, with a few necessary
    changes to make it work for custom roles and also a tiny bit code tweak.

diff --git a/t/customroles/sort_order.t b/t/customroles/sort_order.t
new file mode 100644
index 000000000..e9ea6c94c
--- /dev/null
+++ b/t/customroles/sort_order.t
@@ -0,0 +1,69 @@
+use strict;
+use warnings;
+
+use RT::Test tests => undef;
+
+my $queue_name = "CRSortQueue-$$";
+my $queue = RT::Test->load_or_create_queue( Name => $queue_name );
+
+diag "create multiple CRs: B, A and C";
+
+for my $name (qw/B A C/) {
+    my $cr = RT::CustomRole->new( RT->SystemUser );
+    my ( $ret, $msg ) = $cr->Create( Name => "CR $name", );
+    ok( $ret, "Custom Role $name created" );
+    ( $ret, $msg ) = $cr->AddToObject( $queue->id );
+    ok( $ret, "Added $name to $queue_name: $msg" );
+}
+
+my ( $baseurl, $m ) = RT::Test->started_ok;
+ok( $m->login(), 'Logged in' );
+
+diag "reorder CRs: C, A and B";
+{
+    $m->get_ok('/Admin/Queues/');
+    $m->follow_link_ok( { text => $queue->id } );
+    $m->follow_link_ok( { id   => 'page-custom-roles' } );
+    my @tmp = ( $m->content =~ /(CR [ABC])/g );
+    is_deeply( \@tmp, [ 'CR B', 'CR A', 'CR C' ], 'Order on admin page' );
+
+    $m->follow_link_ok( { text => '[Up]', n => 3 } );
+    $m->follow_link_ok( { text => '[Up]', n => 2 } );
+    $m->follow_link_ok( { text => '[Up]', n => 3 } );
+
+    @tmp = ( $m->content =~ /(CR [ABC])/g );
+    is_deeply( \@tmp, [ 'CR C', 'CR A', 'CR B' ], 'Order on updated admin page' );
+}
+
+diag "check ticket create, display and edit pages";
+{
+    $m->submit_form_ok(
+        {   form_name => "CreateTicketInQueue",
+            fields    => { Queue => $queue->Name },
+        },
+        'Get ticket create page'
+    );
+
+    my @tmp = ( $m->content =~ /(CR [ABC])/g );
+    is_deeply( \@tmp, [ 'CR C', 'CR A', 'CR B' ], 'Order on ticket create page' );
+
+    $m->submit_form_ok(
+        {   form_name => "TicketCreate",
+            fields    => { Subject => 'test' },
+        },
+        'Submit ticket create form'
+    );
+    my ($tid) = ( $m->content =~ /Ticket (\d+) created/i );
+    ok $tid, "Created a ticket succesfully";
+
+    @tmp = ( $m->content =~ /(CR [ABC])/g );
+    is_deeply( \@tmp, [ 'CR C', 'CR A', 'CR B' ], 'Order on ticket display page' );
+    $m->follow_link_ok( { text => 'People' } );
+
+    @tmp = ( $m->content =~ /(CR [ABC])/g );
+
+    # 3 "WatcherTypeEmail1" select boxes and 1 "Current watchers"
+    is_deeply( \@tmp, [ ( 'CR C', 'CR A', 'CR B' ) x 4 ], 'Order on ticket people page' );
+}
+
+done_testing;

-----------------------------------------------------------------------


More information about the rt-commit mailing list