limit ticket creation queue dropdowns based on ACL, RT#7778
authormark <mark>
Sat, 1 Jan 2011 00:47:01 +0000 (00:47 +0000)
committermark <mark>
Sat, 1 Jan 2011 00:47:01 +0000 (00:47 +0000)
13 files changed:
FS/FS/TicketSystem/RT_Internal.pm
httemplate/view/cust_main/tickets.html
rt/FREESIDE_MODIFIED
rt/Makefile.in
rt/configure.ac
rt/lib/RT/Interface/Web.pm
rt/lib/RT/Principal_Overlay.pm
rt/lib/RT/Queue_Overlay.pm
rt/lib/RT/System.pm
rt/lib/RT/Test.pm
rt/sbin/rt-session-viewer.in [new file with mode: 0644]
rt/share/html/Elements/SelectQueue
rt/share/html/Ticket/Create.html

index befafb8..6ae8881 100644 (file)
@@ -301,6 +301,35 @@ sub correspond_ticket {
   $Ticket->Correspond( Content => $param{'content'} );
 }
 
+=item queues SESSION_HASHREF [, ACL ]
+
+Retrieve a list of queues.  Pass the name of an RT access control right, 
+such as 'CreateTicket', to return only queues on which the current user 
+has that right.  Otherwise this will return all queues with the 'SeeQueue' 
+right.
+
+=cut
+
+sub queues {
+  my( $self, $session, $acl ) = @_;
+  $session = $self->session($session);
+
+  my $showall = $acl ? 0 : 1;
+  my @result = ();
+  my $q = new RT::Queues($session->{'CurrentUser'});
+  $q->UnLimit;
+  while (my $queue = $q->Next) {
+    if ($showall || $queue->CurrentUserHasRight($acl)) {
+      push @result, {
+        Id          => $queue->Id,
+        Name        => $queue->Name,
+        Description => $queue->Description,
+      };
+    }
+  }
+  return map { $_->{Id} => $_->{Name} } @result;
+}
+
 #shameless false laziness w/RT::Interface::Web::AttemptExternalAuth
 # to get logged into RT from afar
 sub _web_external_auth {
index e90ae52..eea5a7f 100644 (file)
@@ -11,7 +11,9 @@ function updateTicketLink() {
 </SCRIPT>
 <A id="CreateTicketLink" HREF="<% $new_link %>">Create new ticket</A>
  in queue
-% my %queues = FS::TicketSystem->queues();
+%# fetch list of queues in which the user can create tickets
+% my $session = FS::TicketSystem->session();
+% my %queues = FS::TicketSystem->queues($session, 'CreateTicket');
 % if( $conf->exists('ticket_system-force_default_queueid') ) {
 <B><% $queues{$new_param{'Queue'}} %></B>
 <INPUT TYPE="hidden" NAME="Queue" VALUE="<% $new_param{'Queue'} %>">
@@ -19,7 +21,6 @@ function updateTicketLink() {
 % else {
 <SELECT NAME="Queue" id="Queue" onchange="updateTicketLink()">
 % foreach my $queueid ( sort { $queues{$a} cmp $queues{$b} } keys %queues ) {
-%   #should consider whether the user has ACL to create ticket in each queue
     <OPTION VALUE="<% $queueid %>"
             <% $queueid == $new_param{'Queue'} ? 'SELECTED' : '' %>
     ><% $queues{$queueid} |h %>
index aea074a..7347124 100644 (file)
@@ -29,6 +29,20 @@ lib/RT/URI/freeside.pm
 lib/RT/URI/freeside/Internal.pm
 lib/RT/URI/freeside/XMLRPC.pm
 
+# 3.9-fix-queue-caching bugfix branch
+# github.com/bestpractical/rt/commit/7e211c1199836d49f007d7f677105e5c73cc0348
+Makefile.in
+configure.ac
+lib/RT/Principal_Overlay.pm
+lib/RT/Queue_Overlay.pm
+lib/RT/System.pm
+lib/RT/Test.pm
+lib/RT/Interface/Web.pm
+sbin/rt-session-viewer.in
+share/html/Elements/SelectQueue
+
+share/html/Ticket/Create.html # queue select dropdown on Ticket/Create
+
  share/html/autohandler #Footer getting appended where unwelcome
  share/html/index.html #option to redirect to ticket display on quick create
  share/html/Admin/CustomFields/Modify.html #CheckMandatoryFields
@@ -51,7 +65,6 @@ share/html/Elements/ShowLink_Checklist
  share/html/Elements/SelectCustomerAgent #SearchCustomerFields
  share/html/Elements/SelectCustomerClass #SearchCustomerFields
  share/html/Elements/SelectCustomerTag #SearchCustomerFields
- html/Ticket/Create.html #XXX TODO
  share/html/Search/Build.html
  share/html/Search/Elements/BuildFormatString
  share/html/Search/Elements/PickCFs #customfield date patch
index bef1025..1f42102 100644 (file)
@@ -169,6 +169,7 @@ SYSTEM_BINARIES             =       rt-dump-database \
                                rt-email-dashboards \
                                rt-email-group-admin \
                                rt-server \
+                               rt-session-viewer \
                                rt-test-dependencies \
                                rt-clean-sessions \
                                rt-shredder \
index 6bac3b1..ce9138d 100644 (file)
@@ -3,7 +3,7 @@ dnl
 dnl Process this file with autoconf to produce a configure script
 dnl
 dnl Embed in generated ./configure script the following CVS info:
-AC_REVISION($Revision: 1.1.1.12 $)dnl
+AC_REVISION($Revision: 1.2 $)dnl
 
 dnl Setup autoconf
 AC_PREREQ([2.53])
@@ -398,6 +398,7 @@ AC_CONFIG_FILES([
                  etc/upgrade/3.8-ical-extension
                  etc/upgrade/split-out-cf-categories
                  sbin/rt-attributes-viewer
+                 sbin/rt-session-viewer
                  sbin/rt-dump-database
                  sbin/rt-setup-database
                  sbin/rt-test-dependencies
index edb719d..106209d 100644 (file)
@@ -1345,8 +1345,6 @@ sub ParseDateToISO {
 sub ProcessACLChanges {
     my $ARGSref = shift;
 
-    #XXX: why don't we get ARGSref like in other Process* subs?
-
     my @results;
 
     foreach my $arg ( keys %$ARGSref ) {
index 75f89ca..42474f8 100644 (file)
@@ -163,6 +163,8 @@ sub GrantRight {
 
     my $type = $self->_GetPrincipalTypeForACL();
 
+    RT->System->QueueCacheNeedsUpdate(1) if $args{'Right'} eq 'SeeQueue';
+
     # If it's a user, we really want to grant the right to their 
     # user equivalence group
     return $ace->Create(
@@ -210,6 +212,8 @@ sub RevokeRight {
         PrincipalType => $type,
         PrincipalId   => $self->Id
     );
+
+    RT->System->QueueCacheNeedsUpdate(1) if $args{'Right'} eq 'SeeQueue';
     return ($status, $msg) unless $status;
     return $ace->Delete;
 }
index 98bdec5..dcca84e 100644 (file)
@@ -381,6 +381,8 @@ sub Create {
             unless $status;
     }
 
+    RT->System->QueueCacheNeedsUpdate(1);
+
     return ( $id, $self->loc("Queue created") );
 }
 
@@ -421,6 +423,8 @@ sub SetDisabled {
 
     $RT::Handle->Commit();
 
+    RT->System->QueueCacheNeedsUpdate(1);
+
     if ( $val == 1 ) {
         return (1, $self->loc("Queue disabled"));
     } else {
index 2a23e32..e61e35f 100644 (file)
@@ -189,6 +189,28 @@ sub SubjectTag {
     return grep !$seen{lc $_}++, values %$map;
 }
 
+=head2 QueueCacheNeedsUpdate ( 1 )
+
+Attribute to decide when SelectQueue needs to flush the list of queues
+  and retrieve new ones.  Set when queues are created, enabled/disabled
+  and on certain acl changes.  Should also better understand group management.
+
+If passed a true value, will update the attribute to be the current time.
+
+=cut
+
+sub QueueCacheNeedsUpdate {
+    my $self = shift;
+    my $update = shift;
+
+    if ($update) {
+        return $self->SetAttribute(Name => 'QueueCacheNeedsUpdate', Content => time);
+    } else {
+        my $cache = $self->FirstAttribute('QueueCacheNeedsUpdate');
+        return (defined $cache ? $cache->Content : 0 );
+    }
+}
+
 eval "require RT::System_Vendor";
 die $@ if ($@ && $@ !~ qr{^Can't locate RT/System_Vendor.pm});
 eval "require RT::System_Local";
index b8d1683..64b736f 100644 (file)
@@ -1027,6 +1027,9 @@ sub start_standalone_server {
     $RT::Handle->dbh( undef );
     RT->ConnectToDatabase;
 
+    # the attribute cache holds on to a stale dbh
+    delete $RT::System->{attributes};
+
     return ($ret, RT::Test::Web->new);
 }
 
diff --git a/rt/sbin/rt-session-viewer.in b/rt/sbin/rt-session-viewer.in
new file mode 100644 (file)
index 0000000..37e050a
--- /dev/null
@@ -0,0 +1,121 @@
+#!@PERL@
+# BEGIN BPS TAGGED BLOCK {{{
+# 
+# COPYRIGHT:
+# 
+# This software is Copyright (c) 1996-2010 Best Practical Solutions, LLC
+#                                          <jesse@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 }}}
+use strict;
+use warnings;
+
+# fix lib paths, some may be relative
+BEGIN {
+    require File::Spec;
+    my @libs = ("@RT_LIB_PATH@", "@LOCAL_LIB_PATH@");
+    my $bin_path;
+
+    for my $lib (@libs) {
+        unless ( File::Spec->file_name_is_absolute($lib) ) {
+            unless ($bin_path) {
+                if ( File::Spec->file_name_is_absolute(__FILE__) ) {
+                    $bin_path = ( File::Spec->splitpath(__FILE__) )[1];
+                }
+                else {
+                    require FindBin;
+                    no warnings "once";
+                    $bin_path = $FindBin::Bin;
+                }
+            }
+            $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
+        }
+        unshift @INC, $lib;
+    }
+}
+
+use Getopt::Long;
+my %opt;
+GetOptions( \%opt, 'help|h', );
+
+my $session_id = shift;
+
+if ( $opt{help} || !$session_id ) {
+    require Pod::Usage;
+    Pod::Usage::pod2usage({ verbose => 2 });
+    exit;
+}
+
+require RT;
+RT::LoadConfig();
+RT::Init();
+
+require RT::Interface::Web::Session;
+my %session;
+
+tie %session, 'RT::Interface::Web::Session', $session_id;
+unless ( $session{'_session_id'} eq $session_id ) {
+    print STDERR "Couldn't load session $session_id\n";
+    exit 1;
+}
+
+use Data::Dumper;
+print "Content of session $session_id: ". Dumper( \%session);
+
+__END__
+
+=head1 NAME
+
+rt-session-viewer - show the content of a user's session
+
+=head1 SYNOPSIS
+
+    # show the content of a session
+    rt-session-viewer 2c21c8a2909c14eff12975dd2cc7b9a3
+
+=head1 DESCRIPTION
+
+This script deserializes and print content of a session identified
+by <session id>. May be useful for developers and for troubleshooting
+problems.
+
+=cut
index c78afe9..5b146a7 100755 (executable)
@@ -55,7 +55,7 @@
 %     if ($ShowNullOption) {
   <option value="">-</option>
 %     }
-%     for my $queue (@{$session{$cache_key}}) {
+%     for my $queue (@{$session{$cache_key}{queues}}) {
   <option value="<% ($NamedValues ? $queue->{Name} : $queue->{Id}) %>" 
 
 % if ($queue->{Id} eq ($Default||'') || $queue->{Name} eq ($Default||'')) {
@@ -90,18 +90,27 @@ my $cache_key = "SelectQueue---"
                 . $session{'CurrentUser'}->Id
                 . "---$CheckQueueRight---$ShowAllQueues";
 
-if (not defined $session{$cache_key} and not $Lite) {
+if ( defined $session{$cache_key} && ref $session{$cache_key} eq 'ARRAY') {
+    delete $session{$cache_key};
+}
+if ( defined $session{$cache_key} &&
+     $session{$cache_key}{lastupdated} <= RT->System->QueueCacheNeedsUpdate ) {
+    delete $session{$cache_key};
+}
+
+if ( not defined $session{$cache_key} and not $Lite ) {
     my $q = new RT::Queues($session{'CurrentUser'});
     $q->UnLimit;
-    
+
     while (my $queue = $q->Next) {
         if ($ShowAllQueues || $queue->CurrentUserHasRight($CheckQueueRight)) {
-            push @{$session{$cache_key}}, {
+            push @{$session{$cache_key}{queues}}, {
                 Id          => $queue->Id,
                 Name        => $queue->Name,
                 Description => $queue->Description,
             };
         }
     }
+    $session{$cache_key}{lastupdated} = time();
 }
 </%init>
index 094b5a0..33e933d 100755 (executable)
@@ -70,6 +70,7 @@
   Name => 'Queue', 
   Default => $QueueObj->Name,
   ShowNullOption => 0,
+  ShowAllQueues => 0,
   OnChange => "document.getElementsByName('id')[0].value = ''; form.submit()" &>
 </td>
 <td class="label"><&|/l&>Status</&>: