[Rt-commit] rt branch, 4.0/selfservice-autocomplete, created. rt-4.0.5-135-g9f3c35e

Jim Brandt jbrandt at bestpractical.com
Fri May 11 16:54:18 EDT 2012


The branch, 4.0/selfservice-autocomplete has been created
        at  9f3c35ed8ff158b8d280e2da4d9a5c6bf427c85d (commit)

- Log -----------------------------------------------------------------
commit 9f3c35ed8ff158b8d280e2da4d9a5c6bf427c85d
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Fri May 11 16:49:17 2012 -0400

    Add user autocomplete for SelfService pages.
    
    This commit creates RT libraries for Autocomplete functionality
    and creates a new Autocomplete Mason template for User autocomplete.
    
    I haven't touched the existing Autocomplete in Helpers, but those
    could be converted once this commit is reviewed and tested.
    
    One remaining question is whether there should be any additional
    restructions on SelfService autocomplete.

diff --git a/lib/RT/Autocomplete.pm b/lib/RT/Autocomplete.pm
new file mode 100644
index 0000000..65d42c4
--- /dev/null
+++ b/lib/RT/Autocomplete.pm
@@ -0,0 +1,153 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2012 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 }}}
+
+=head1 NAME
+
+  RT::Autocomplete - generic baseclass for autocomplete classes
+
+=head1 SYNOPSIS
+
+    use RT::Autocomplete;
+    my $auto = RT::Autocomplete->new(\%args);
+    my $result_obj = $auto->FetchSuggestions(%args);
+
+=head1 DESCRIPTION
+
+Create the list of suggested values for an autocomplete field.
+
+=head1 METHODS
+
+
+=cut
+
+package RT::Autocomplete;
+use base RT::Base;
+use base Class::Accessor::Fast;
+
+use strict;
+use warnings;
+
+sub new {
+    my $proto = shift;
+    my $class = ref($proto) || $proto;
+    my $self  = {};
+    bless( $self, $class );
+    my ( $status, $msg ) = $self->_Init(@_);
+    RT::Logger->warn($msg) unless $status;
+    return $status ? $self : $status;
+}
+
+sub _Init {
+    my $self        = shift;
+    my %args = (
+        return     => '',
+        term       => undef,
+        delim      => undef,
+        max        => 10,
+        privileged => undef,
+        exclude    => '',
+        op         => undef,
+        @_
+    );
+
+    return ( 0, 'CurrentUser required.' )
+      unless exists $args{CurrentUser}
+	and defined $args{CurrentUser};
+
+    # Require privileged users or overriding config
+    return ( 0, 'Permission Denied' )
+      unless $args{CurrentUser}->Privileged
+	or RT->Config->Get('AllowUserAutocompleteForUnprivileged');
+
+    $self->CurrentUser( $args{CurrentUser} );
+
+    return ( 0, "No term provided." )
+        unless defined $args{return}
+            and defined $args{term}
+            and length $args{term};
+
+    # Only allow certain return fields
+    $args{return} = 'EmailAddress'
+        unless $args{return} =~ /^(?:EmailAddress|Name|RealName)$/;
+
+    $self->{'Return'} = $args{return};
+
+   # Use our delimeter if we have one
+    if ( defined $args{delim} and length $args{delim} ) {
+        if ( $args{delim} eq ',' ) {
+            $args{delim} = qr/,\s*/;
+        } else {
+            $args{delim} = qr/\Q$args{delim}\E/;
+        }
+
+        # If the field handles multiple values, pop the last one off
+        $args{term} = ( split $args{delim}, $args{term} )[-1]
+            if $args{term} =~ $args{delim};
+    }
+
+    $self->{'Term'} = $args{term};
+    $self->{'Max'} = $args{max};
+    $self->{'Privileged'} = $args{privileged};
+    $self->{'Exclude'} = $args{exclude};
+    $self->{'Op'} = $args{op};
+
+    $self->mk_accessors( qw(Return Term Max Privileged Exclude Op) );
+
+    return ( 1, 'Object created.' );
+}
+
+sub FetchSuggestions {
+    my $self = shift;
+
+    # Nothing to see here. Look in a child class.
+    return;
+}
+
+RT::Base->_ImportOverlays();
+
+1;
diff --git a/lib/RT/Autocomplete/Users.pm b/lib/RT/Autocomplete/Users.pm
new file mode 100644
index 0000000..e220e16
--- /dev/null
+++ b/lib/RT/Autocomplete/Users.pm
@@ -0,0 +1,103 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2012 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 }}}
+
+package RT::Autocomplete::Users;
+
+use strict;
+use warnings;
+use base qw(RT::Autocomplete);
+
+=head1 NAME
+
+RT::Autocomplete:Users - Autocomplete for users
+
+=head1 DESCRIPTION
+
+Perform searches on user fields like EmailAddress and Name to find users
+to suggest in user entry fields in forms.
+
+=cut
+
+sub FetchSuggestions {
+    my $self = shift;
+
+    my %fields = %{ RT->Config->Get('UserAutocompleteFields')
+		      || { EmailAddress => 1, Name => 1, RealName => 'LIKE' } };
+
+
+    # If an operator is provided, check against only the returned field
+    # using that operator
+    %fields = ( $self->Return => $self->Op ) if $self->Op;
+
+    my $users = RT::Users->new($self->CurrentUser);
+    $users->RowsPerPage($self->Max);
+
+    $users->LimitToPrivileged() if $self->Privileged;
+
+    while ( my ( $name, $op ) = each %fields ) {
+        $op = 'STARTSWITH'
+            unless $op =~ /^(?:LIKE|(?:START|END)SWITH|=|!=)$/i;
+
+        $users->Limit(
+            FIELD           => $name,
+            OPERATOR        => $op,
+            VALUE           => $self->Term,
+            ENTRYAGGREGATOR => 'OR',
+            SUBCLAUSE       => 'autocomplete',
+        );
+    }
+
+    # Exclude users we don't want
+    foreach ( split /\s*,\s*/, $self->Exclude ) {
+        $users->Limit( FIELD => 'id', VALUE => $_, OPERATOR => '!=' );
+    }
+
+    return $users;
+}
+
+1;
diff --git a/share/html/NoAuth/js/userautocomplete.js b/share/html/NoAuth/js/userautocomplete.js
index db244d1..694810c 100644
--- a/share/html/NoAuth/js/userautocomplete.js
+++ b/share/html/NoAuth/js/userautocomplete.js
@@ -69,8 +69,17 @@ jQuery(function() {
         if (!inputName || !inputName.match(applyto))
             continue;
 
+	var path_re = /SelfService/;
+	var autocomplete_path;
+	if (path_re.test(window.location.pathname)){
+	    autocomplete_path = "<% RT->Config->Get('WebPath')%>/SelfService/Autocomplete/Users";
+	}
+	else{
+	    autocomplete_path = "<% RT->Config->Get('WebPath')%>/Helpers/Autocomplete/Users";
+	}
+
         var options = {
-            source: "<% RT->Config->Get('WebPath')%>/Helpers/Autocomplete/Users"
+            source: autocomplete_path
         };
 
         var queryargs = [];
diff --git a/share/html/SelfService/Autocomplete/Users b/share/html/SelfService/Autocomplete/Users
new file mode 100644
index 0000000..34b54d2
--- /dev/null
+++ b/share/html/SelfService/Autocomplete/Users
@@ -0,0 +1,76 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 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 }}}
+% $r->content_type('application/json');
+<% JSON( \@suggestions ) |n %>
+% $m->abort;
+<%INIT>
+
+use RT::Autocomplete::Users;
+my $auto = RT::Autocomplete::Users->new(
+	   CurrentUser => $session{'CurrentUser'}, %ARGS);
+
+# Do nothing if it failed. Not a fatal error,
+# but autocomplete won't work.
+$m->abort unless $auto;
+
+my $users = $auto->FetchSuggestions;
+
+my @suggestions;
+
+while ( my $user = $users->Next ) {
+    next if $user->id == RT->SystemUser->id
+         or $user->id == RT->Nobody->id;
+
+    my $formatted = $m->scomp('/Elements/ShowUser', User => $user, NoEscape => 1);
+    $formatted =~ s/\n//g;
+    my $return = $auto->Return;
+    my $suggestion = { label => $formatted, value => $user->$return };
+    $m->callback( CallbackName => "ModifySuggestion", suggestion => $suggestion, user => $user );
+    push @suggestions, $suggestion;
+}
+</%INIT>
diff --git a/t/api/autocomplete.t b/t/api/autocomplete.t
new file mode 100644
index 0000000..fd0cb0b
--- /dev/null
+++ b/t/api/autocomplete.t
@@ -0,0 +1,35 @@
+
+use strict;
+use warnings;
+use RT;
+use RT::Test tests => 7;
+
+use_ok('RT::Autocomplete');
+
+# my $user = RT::Test->load_or_create_user(
+#     Name => 'user_a', Password => 'password',
+# );
+
+my $user = RT::User->new(RT->SystemUser);
+$user->Load("root");
+ok ($user->Id, "Found the root user");
+
+my $auto = RT::Autocomplete->new(
+				CurrentUser => $user,
+				term => 'ro',);
+
+isa_ok($auto, 'RT::Autocomplete');
+
+use_ok('RT::Autocomplete::Users');
+
+my $auto_user = RT::Autocomplete::Users->new(
+				CurrentUser => $user,
+				term => 'ro',);
+
+isa_ok($auto_user, 'RT::Autocomplete::Users');
+
+my $users_obj = $auto_user->FetchSuggestions;
+isa_ok($users_obj, 'RT::Users');
+
+my $u = $users_obj->Next;
+is( $u->Name, 'root', 'Found root user.');
diff --git a/t/web/self_service_autocomplete.t b/t/web/self_service_autocomplete.t
new file mode 100644
index 0000000..56072af
--- /dev/null
+++ b/t/web/self_service_autocomplete.t
@@ -0,0 +1,75 @@
+use strict;
+use warnings;
+use RT::Test tests => 16;
+use JSON qw(from_json);
+
+RT->Config->Set('AllowUserAutocompleteForUnprivileged', 1);
+my ($url, $m) = RT::Test->started_ok;
+
+my ($ticket) =
+  RT::Test->create_ticket( Queue => 'General', Subject => 'test subject' );
+
+my $user_a = RT::Test->load_or_create_user(
+    Name => 'user_a', Password => 'password',
+);
+ok $user_a && $user_a->id, 'loaded or created user';
+
+my $user_b = RT::Test->load_or_create_user(
+    Name => 'user_b', Password => 'password',
+);
+ok $user_b && $user_b->id, 'loaded or created user';
+
+$m->login();
+
+$m->get_ok( '/SelfService/Autocomplete/Users',
+	    'request with no params' );
+
+# Output has an extra encoded newline. Not sure where it's
+# coming from.
+$m->content_contains('', 'empty with no params');
+
+$m->get_ok( '/SelfService/Autocomplete/Users?return=Name',
+	    'request with no params' );
+
+$m->content_contains('', 'empty with just return param');
+
+autocomplete_contains('us', 'user_a', $m);
+
+# Shouldn't get root with a term of us.
+autocomplete_lacks('us', 'root', $m);
+
+# Lifted from ticket_owner_autocomplete.t and modified
+# Should probably be put somewhere shared.
+
+sub autocomplete {
+    my $term = shift;
+    my $agent = shift;
+    $agent->get_ok("/SelfService/Autocomplete/Users?delim=,&term=$term&return=Name", "fetched autocomplete values");
+    return from_json($agent->content);
+}
+
+sub autocomplete_contains {
+    my $term = shift;
+    my $expected = shift;
+    my $agent = shift;
+
+    my $results = autocomplete( $term, $agent );
+
+    my %seen;
+    $seen{$_->{value}}++ for @$results;
+    $expected = [$expected] unless ref $expected eq 'ARRAY';
+    is((scalar grep { not $seen{$_} } @$expected), 0, "got all expected values");
+}
+
+sub autocomplete_lacks {
+    my $term = shift;
+    my $lacks = shift;
+    my $agent = shift;
+
+    my $results = autocomplete( $term, $agent );
+
+    my %seen;
+    $seen{$_->{value}}++ for @$results;
+    $lacks = [$lacks] unless ref $lacks eq 'ARRAY';
+    is((scalar grep { $seen{$_} } @$lacks), 0, "didn't get any unexpected values");
+}

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


More information about the Rt-commit mailing list