import rt 3.8.10
[freeside.git] / rt / lib / RT / Search / Googleish.pm
1
2 # BEGIN BPS TAGGED BLOCK {{{
3 #
4 # COPYRIGHT:
5 #
6 # This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
7 #                                          <sales@bestpractical.com>
8 #
9 # (Except where explicitly superseded by other copyright notices)
10 #
11 #
12 # LICENSE:
13 #
14 # This work is made available to you under the terms of Version 2 of
15 # the GNU General Public License. A copy of that license should have
16 # been provided with this software, but in any event can be snarfed
17 # from www.gnu.org.
18 #
19 # This work is distributed in the hope that it will be useful, but
20 # WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22 # General Public License for more details.
23 #
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software
26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27 # 02110-1301 or visit their web page on the internet at
28 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
29 #
30 #
31 # CONTRIBUTION SUBMISSION POLICY:
32 #
33 # (The following paragraph is not intended to limit the rights granted
34 # to you to modify and distribute this software under the terms of
35 # the GNU General Public License and is only of importance to you if
36 # you choose to contribute your changes and enhancements to the
37 # community by submitting them to Best Practical Solutions, LLC.)
38 #
39 # By intentionally submitting any modifications, corrections or
40 # derivatives to this work, or any other work intended for use with
41 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
42 # you are the copyright holder for those contributions and you grant
43 # Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
44 # royalty-free, perpetual, license to use, copy, create derivative
45 # works based on those contributions, and sublicense and distribute
46 # those contributions and any derivatives thereof.
47 #
48 # END BPS TAGGED BLOCK }}}
49
50 =head1 NAME
51
52   RT::Search::Googlish
53
54 =head1 SYNOPSIS
55
56 =head1 DESCRIPTION
57
58 Use the argument passed in as a "Google-style" set of keywords
59
60 =head1 METHODS
61
62
63
64
65 =cut
66
67 package RT::Search::Googleish;
68
69 use strict;
70 use warnings;
71 use base qw(RT::Search);
72
73 use Regexp::Common qw/delimited/;
74 my $re_delim = qr[$RE{delimited}{-delim=>qq{\'\"}}];
75
76 # sub _Init {{{
77 sub _Init {
78     my $self = shift;
79     my %args = @_;
80
81     $self->{'Queues'} = delete($args{'Queues'}) || [];
82     $self->SUPER::_Init(%args);
83 }
84 # }}}
85
86 # {{{ sub Describe 
87 sub Describe  {
88   my $self = shift;
89   return ($self->loc("No description for [_1]", ref $self));
90 }
91 # }}}
92
93 # {{{ sub QueryToSQL
94 sub QueryToSQL {
95     my $self     = shift;
96     my $query    = shift || $self->Argument;
97
98     my @keywords = grep length, map { s/^\s+//; s/\s+$//; $_ }
99         split /((?:fulltext:)?$re_delim|\s+)/o, $query;
100
101     my (
102         @tql_clauses,  @owner_clauses, @queue_clauses,
103         @user_clauses, @id_clauses,    @status_clauses
104     );
105     my ( $Queue, $User );
106     for my $key (@keywords) {
107
108         # Is this a ticket number? If so, go to it.
109         # But look into subject as well
110         if ( $key =~ m/^\d+$/ ) {
111             push @id_clauses, "id = '$key'", "Subject LIKE '$key'";
112         }
113
114         # if it's quoted string then search it "as is" in subject or fulltext
115         elsif ( $key =~ /^(fulltext:)?($re_delim)$/io ) {
116             if ( $1 ) {
117                 push @tql_clauses, "Content LIKE $2";
118             } else {
119                 push @tql_clauses, "Subject LIKE $2";
120             }
121         }
122
123         elsif ( $key =~ /^fulltext:(.*?)$/i ) {
124             $key = $1;
125             $key =~ s/['\\].*//g;
126             push @tql_clauses, "Content LIKE '$key'";
127
128         }
129
130         elsif ( $key =~ /\w+\@\w+/ ) {
131             push @user_clauses, "Requestor LIKE '$key'";
132         }
133
134         # Is there a status with this name?
135         elsif (
136             $Queue = RT::Queue->new( $self->TicketsObj->CurrentUser )
137             and $Queue->IsValidStatus($key)
138           )
139         {
140             push @status_clauses, "Status = '" . $key . "'";
141         }
142
143         # Is there a queue named $key?
144         elsif ( $Queue = RT::Queue->new( $self->TicketsObj->CurrentUser )
145             and $Queue->Load($key)
146             and $Queue->id
147             and not $Queue->Disabled )
148         {
149             my $quoted_queue = $Queue->Name;
150             $quoted_queue =~ s/'/\\'/g;
151             push @queue_clauses, "Queue = '$quoted_queue'";
152         }
153
154         # Is there a owner named $key?
155         elsif ( $User = RT::User->new( $self->TicketsObj->CurrentUser )
156             and $User->Load($key)
157             and $User->id
158             and $User->Privileged )
159         {
160             push @owner_clauses, "Owner = '" . $User->Name . "'";
161         }
162
163         # Else, subject must contain $key
164         else {
165             $key =~ s/['\\].*//g;
166             push @tql_clauses, "Subject LIKE '$key'";
167         }
168     }
169
170     # restrict to any queues requested by the caller
171     for my $queue (@{ $self->{'Queues'} }) {
172         my $QueueObj = RT::Queue->new($self->TicketsObj->CurrentUser);
173         $QueueObj->Load($queue) or next;
174         my $quoted_queue = $QueueObj->Name;
175         $quoted_queue =~ s/'/\\'/g;
176         push @queue_clauses, "Queue = '$quoted_queue'";
177     }
178
179     push @tql_clauses, join( " OR ", sort @id_clauses );
180     push @tql_clauses, join( " OR ", sort @owner_clauses );
181     if ( ! @status_clauses ) {
182         push @tql_clauses, join( " OR ", map "Status = '$_'", RT::Queue->ActiveStatusArray());
183     } else {
184         push @tql_clauses, join( " OR ", sort @status_clauses );
185     }
186     push @tql_clauses, join( " OR ", sort @user_clauses );
187     push @tql_clauses, join( " OR ", sort @queue_clauses );
188     @tql_clauses = grep { $_ ? $_ = "( $_ )" : undef } @tql_clauses;
189     return join " AND ", sort @tql_clauses;
190 }
191 # }}}
192
193 # {{{ sub Prepare
194 sub Prepare  {
195   my $self = shift;
196   my $tql = $self->QueryToSQL($self->Argument);
197
198   $RT::Logger->debug($tql);
199
200   $self->TicketsObj->FromSQL($tql);
201   return(1);
202 }
203 # }}}
204
205 RT::Base->_ImportOverlays();
206
207 1;