X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=rt%2Flib%2FRT%2FSearch%2FGoogleish.pm;fp=rt%2Flib%2FRT%2FSearch%2FGoogleish.pm;h=0000000000000000000000000000000000000000;hb=1c538bfabc2cd31f27067505f0c3d1a46cba6ef0;hp=a688f582a2f94664c80d00370e8071db8d819233;hpb=4f5619288413a185e9933088d9dd8c5afbc55dfa;p=freeside.git diff --git a/rt/lib/RT/Search/Googleish.pm b/rt/lib/RT/Search/Googleish.pm deleted file mode 100644 index a688f582a..000000000 --- a/rt/lib/RT/Search/Googleish.pm +++ /dev/null @@ -1,271 +0,0 @@ -# BEGIN BPS TAGGED BLOCK {{{ -# -# COPYRIGHT: -# -# This software is Copyright (c) 1996-2015 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 }}} - -=head1 NAME - - RT::Search::Googleish - -=head1 SYNOPSIS - -=head1 DESCRIPTION - -Use the argument passed in as a "Google-style" set of keywords - -=head1 METHODS - -=cut - -package RT::Search::Googleish; - -use strict; -use warnings; -use base qw(RT::Search); - -use Regexp::Common qw/delimited/; - -# Only a subset of limit types AND themselves together. "queue:foo -# queue:bar" is an OR, but "subject:foo subject:bar" is an AND -our %AND = ( - content => 1, - subject => 1, -); - -sub _Init { - my $self = shift; - my %args = @_; - - $self->{'Queues'} = delete( $args{'Queues'} ) || []; - $self->SUPER::_Init(%args); -} - -sub Describe { - my $self = shift; - return ( $self->loc( "Keyword and intuition-based searching", ref $self ) ); -} - -sub Prepare { - my $self = shift; - my $tql = $self->QueryToSQL( $self->Argument ); - - $RT::Logger->debug($tql); - - $self->TicketsObj->FromSQL($tql); - return (1); -} - -sub QueryToSQL { - my $self = shift; - my $query = shift || $self->Argument; - - my %limits; - $query =~ s/^\s*//; - while ($query =~ /^\S/) { - if ($query =~ s/^ - (?: - (\w+) # A straight word - (?:\. # With an optional .foo - ($RE{delimited}{-delim=>q['"]} - |[\w-]+ # Allow \w + dashes - ) # Which could be ."foo bar", too - )? - ) - : # Followed by a colon - ($RE{delimited}{-delim=>q['"]} - |\S+ - ) # And a possibly-quoted foo:"bar baz" - \s*//ix) { - my ($type, $extra, $value) = ($1, $2, $3); - ($value, my ($quoted)) = $self->Unquote($value); - $extra = $self->Unquote($extra) if defined $extra; - $self->Dispatch(\%limits, $type, $value, $quoted, $extra); - } elsif ($query =~ s/^($RE{delimited}{-delim=>q['"]}|\S+)\s*//) { - # If there's no colon, it's just a word or quoted string - my($val, $quoted) = $self->Unquote($1); - $self->Dispatch(\%limits, $self->GuessType($val, $quoted), $val, $quoted); - } - } - $self->Finalize(\%limits); - - my @clauses; - for my $subclause (sort keys %limits) { - next unless @{$limits{$subclause}}; - - my $op = $AND{lc $subclause} ? "AND" : "OR"; - push @clauses, "( ".join(" $op ", @{$limits{$subclause}})." )"; - } - - return join " AND ", @clauses; -} - -sub Dispatch { - my $self = shift; - my ($limits, $type, $contents, $quoted, $extra) = @_; - $contents =~ s/(['\\])/\\$1/g; - $extra =~ s/(['\\])/\\$1/g if defined $extra; - - my $method = "Handle" . ucfirst(lc($type)); - $method = "HandleDefault" unless $self->can($method); - my ($key, @tsql) = $self->$method($contents, $quoted, $extra); - push @{$limits->{$key}}, @tsql; -} - -sub Unquote { - # Given a word or quoted string, unquote it if it is quoted, - # removing escaped quotes. - my $self = shift; - my ($token) = @_; - if ($token =~ /^$RE{delimited}{-delim=>q['"]}{-keep}$/) { - my $quote = $2 || $5; - my $value = $3 || $6; - $value =~ s/\\(\\|$quote)/$1/g; - return wantarray ? ($value, 1) : $value; - } else { - return wantarray ? ($token, 0) : $token; - } -} - -sub Finalize { - my $self = shift; - my ($limits) = @_; - - # Apply default "active status" limit if we don't have any status - # limits ourselves, and we're not limited by id - if (not $limits->{status} and not $limits->{id} - and RT::Config->Get('OnlySearchActiveTicketsInSimpleSearch', $self->TicketsObj->CurrentUser)) { - $limits->{status} = [map {s/(['\\])/\\$1/g; "Status = '$_'"} RT::Queue->ActiveStatusArray()]; - } - - # Respect the "only search these queues" limit if we didn't - # specify any queues ourselves - if (not $limits->{queue} and not $limits->{id}) { - for my $queue ( @{ $self->{'Queues'} } ) { - my $QueueObj = RT::Queue->new( $self->TicketsObj->CurrentUser ); - next unless $QueueObj->Load($queue); - my $name = $QueueObj->Name; - $name =~ s/(['\\])/\\$1/g; - push @{$limits->{queue}}, "Queue = '$name'"; - } - } -} - -our @GUESS = ( - [ 10 => sub { return "subject" if $_[1] } ], - [ 20 => sub { return "id" if /^#?\d+$/ } ], - [ 30 => sub { return "requestor" if /\w+@\w+/} ], - [ 35 => sub { return "domain" if /^@\w+/} ], - [ 40 => sub { - return "status" if RT::Queue->new( $_[2] )->IsValidStatus( $_ ) - }], - [ 40 => sub { return "status" if /^((in)?active|any)$/i } ], - [ 50 => sub { - my $q = RT::Queue->new( $_[2] ); - return "queue" if $q->Load($_) and $q->Id and not $q->Disabled - }], - [ 60 => sub { - my $u = RT::User->new( $_[2] ); - return "owner" if $u->Load($_) and $u->Id and $u->Privileged - }], - [ 70 => sub { return "owner" if $_ eq "me" } ], -); - -sub GuessType { - my $self = shift; - my ($val, $quoted) = @_; - - my $cu = $self->TicketsObj->CurrentUser; - for my $sub (map $_->[1], sort {$a->[0] <=> $b->[0]} @GUESS) { - local $_ = $val; - my $ret = $sub->($val, $quoted, $cu); - return $ret if $ret; - } - return "default"; -} - -# $_[0] is $self -# $_[1] is escaped value without surrounding single quotes -# $_[2] is a boolean of "was quoted by the user?" -# ensure this is false before you do smart matching like $_[1] eq "me" -# $_[3] is escaped subkey, if any (see HandleCf) -sub HandleDefault { return subject => "Subject LIKE '$_[1]'"; } -sub HandleSubject { return subject => "Subject LIKE '$_[1]'"; } -sub HandleFulltext { return content => "Content LIKE '$_[1]'"; } -sub HandleContent { return content => "Content LIKE '$_[1]'"; } -sub HandleId { $_[1] =~ s/^#//; return id => "Id = $_[1]"; } -sub HandleStatus { - if ($_[1] =~ /^active$/i and !$_[2]) { - return status => map {s/(['\\])/\\$1/g; "Status = '$_'"} RT::Queue->ActiveStatusArray(); - } elsif ($_[1] =~ /^inactive$/i and !$_[2]) { - return status => map {s/(['\\])/\\$1/g; "Status = '$_'"} RT::Queue->InactiveStatusArray(); - } elsif ($_[1] =~ /^any$/i and !$_[2]) { - return 'status'; - } else { - return status => "Status = '$_[1]'"; - } -} -sub HandleOwner { - if (!$_[2] and $_[1] eq "me") { - return owner => "Owner.id = '__CurrentUser__'"; - } - elsif (!$_[2] and $_[1] =~ /\w+@\w+/) { - return owner => "Owner.EmailAddress = '$_[1]'"; - } else { - return owner => "Owner = '$_[1]'"; - } -} -sub HandleWatcher { - return watcher => (!$_[2] and $_[1] eq "me") ? "Watcher.id = '__CurrentUser__'" : "Watcher = '$_[1]'"; -} -sub HandleRequestor { return requestor => "Requestor STARTSWITH '$_[1]'"; } -sub HandleDomain { $_[1] =~ s/^@?/@/; return requestor => "Requestor ENDSWITH '$_[1]'"; } -sub HandleQueue { return queue => "Queue = '$_[1]'"; } -sub HandleQ { return queue => "Queue = '$_[1]'"; } -sub HandleCf { return "cf.$_[3]" => "'CF.{$_[3]}' LIKE '$_[1]'"; } - -RT::Base->_ImportOverlays(); - -1;