rt 4.2.13 ticket#13852
[freeside.git] / rt / share / html / REST / 1.0 / search / dhandler
1 %# BEGIN BPS TAGGED BLOCK {{{
2 %#
3 %# COPYRIGHT:
4 %#
5 %# This software is Copyright (c) 1996-2016 Best Practical Solutions, LLC
6 %#                                          <sales@bestpractical.com>
7 %#
8 %# (Except where explicitly superseded by other copyright notices)
9 %#
10 %#
11 %# LICENSE:
12 %#
13 %# This work is made available to you under the terms of Version 2 of
14 %# the GNU General Public License. A copy of that license should have
15 %# been provided with this software, but in any event can be snarfed
16 %# from www.gnu.org.
17 %#
18 %# This work is distributed in the hope that it will be useful, but
19 %# WITHOUT ANY WARRANTY; without even the implied warranty of
20 %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21 %# General Public License for more details.
22 %#
23 %# You should have received a copy of the GNU General Public License
24 %# along with this program; if not, write to the Free Software
25 %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 %# 02110-1301 or visit their web page on the internet at
27 %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
28 %#
29 %#
30 %# CONTRIBUTION SUBMISSION POLICY:
31 %#
32 %# (The following paragraph is not intended to limit the rights granted
33 %# to you to modify and distribute this software under the terms of
34 %# the GNU General Public License and is only of importance to you if
35 %# you choose to contribute your changes and enhancements to the
36 %# community by submitting them to Best Practical Solutions, LLC.)
37 %#
38 %# By intentionally submitting any modifications, corrections or
39 %# derivatives to this work, or any other work intended for use with
40 %# Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 %# you are the copyright holder for those contributions and you grant
42 %# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
43 %# royalty-free, perpetual, license to use, copy, create derivative
44 %# works based on those contributions, and sublicense and distribute
45 %# those contributions and any derivatives thereof.
46 %#
47 %# END BPS TAGGED BLOCK }}}
48 %# REST/1.0/search/dhandler
49 %#
50 <%ARGS>
51 $query
52 $format => undef
53 $orderby => undef
54 $fields => undef
55 </%ARGS>
56 <%INIT>
57 my $type = $m->dhandler_arg;
58 my ( $status, $output );
59
60 if ( $type =~ /^(ticket|queue|user|group)$/i ) {
61     $status = "200 Ok";
62     $output = '';
63     my $type = lc $1;
64
65     if (
66         $type eq 'user'
67         && !$session{CurrentUser}->HasRight(
68             Object => $RT::System,
69             Right  => 'AdminUsers',
70         )
71       )
72     {
73
74         $status = "403 Forbidden";
75         $output = "Permission denied";
76         goto OUTPUT;
77     }
78
79     my $class = 'RT::' . ucfirst $type . 's';
80     my $objects = $class->new( $session{CurrentUser} );
81
82     # Parse and validate any field specifications.
83     require RT::Interface::REST;
84     my $field = RT::Interface::REST->field_spec;
85     my ( %fields, @fields );
86     if ($fields) {
87         $format ||= "l";
88         unless ( $fields =~ /^(?:$field,)*$field$/ ) {
89             $status = "400 Bad Request";
90             $output = "Invalid field specification: $fields";
91             goto OUTPUT;
92         }
93         @fields = map lc, split /\s*,\s*/, $fields;
94         @fields{@fields} = ();
95         unless ( exists $fields{id} ) {
96             unshift @fields, "id";
97             $fields{id} = ();
98         }
99     }
100
101     $format ||= "s";
102     if ( $format !~ /^[isl]$/ ) {
103         $status = "400 Bad request";
104         $output = "Unknown listing format: $format. (Use i, s, or l.)\n";
105         goto OUTPUT;
106     }
107
108     my ( $n, $s );
109     $n = 0;
110     my @output;
111
112
113     if ( $type eq 'group' ) {
114         $objects->LimitToUserDefinedGroups;
115     }
116
117     if ( defined $query && length $query ) {
118         if ( $type eq 'ticket' ) {
119             my ( $n, $s );
120             eval { ( $n, $s ) = $objects->FromSQL($query); };
121             if ( $@ || $n == 0 ) {
122                 $s ||= $@;
123                 $status = "400 Bad request";
124                 $output = "Invalid query: '$s'.\n";
125                 goto OUTPUT;
126             }
127         }
128         else {
129             require Text::ParseWords;
130             my ( $field, $op, $value ) = Text::ParseWords::shellwords($query);
131             if ( $op !~
132                 /^(?:[!<>]?=|[<>]|(NOT )?LIKE|STARTSWITH|ENDSWITH|MATCHES)$/i )
133             {
134                 $status = "400 Bad Request";
135                 $output = "Invalid operator specification: $op";
136                 goto OUTPUT;
137             }
138
139             if ( ! $search_whitelist{$type}{lc $field} ) {
140                 $status = "400 Bad Request";
141                 $output = "Invalid field specification: $field";
142                 goto OUTPUT;
143             }
144
145
146             if ( $field && $op && defined $value ) {
147                 if ( $field eq 'Disabled' ) {
148                     if ($value) {
149                         if ( $type eq 'queue' ) {
150                             $objects->FindAllRows;
151                             $objects->Limit(
152                                 FIELD    => $field,
153                                 OPERATOR => uc $op,
154                                 VALUE    => $value
155                             );
156                         }
157                         else {
158                             $objects->LimitToDeleted;
159                         }
160                     }
161                     else {
162                         if ( $type eq 'queue' ) {
163                             $objects->UnLimit;
164                         }
165                         else {
166                             $objects->LimitToEnabled;
167                         }
168                     }
169                 }
170                 else {
171                     $objects->Limit(
172                         FIELD    => $field,
173                         OPERATOR => uc $op,
174                         VALUE    => $value,
175                         CASESENSITIVE => 0,
176                     );
177                 }
178             }
179             else {
180                 $output = "Invalid query specification: $query";
181                 goto OUTPUT;
182             }
183         }
184     }
185     else {
186         if ( $type eq 'queue' ) {
187             $objects->UnLimit;
188         }
189         elsif ( $type eq 'user' ) {
190             $objects->LimitToPrivileged;
191         }
192     }
193
194     if ($orderby) {
195         my ( $order, $field ) = $orderby =~ /^([\+\-])?(.+)/;
196         $order = $order && $order eq '-' ? 'DESC' : 'ASC';
197         $objects->OrderBy( FIELD => $field, ORDER => $order );
198     }
199
200     while ( my $object = $objects->Next ) {
201         next if $type eq 'user' && ( $object->id == RT->SystemUser->id || $object->id == RT->Nobody->id );
202         $n++;
203
204         my $id = $object->Id;
205         if ( $format eq "i" ) {
206             $output .= "$type/" . $id . "\n";
207         }
208         elsif ( $format eq "s" ) {
209             if ($fields) {
210                 my $result = $m->comp(
211                     "/REST/1.0/Forms/$type/default",
212                     id     => $id,
213                     format => $format,
214                     fields => \%fields
215                 );
216                 my ( $notes, $order, $key_values, $errors ) = @$result;
217
218                 # If it's the first time through, add our header
219                 if ( $n == 1 ) {
220                     $output .= join( "\t", @$order ) . "\n";
221                 }
222
223                 # Cut off the annoying $type/ before the id;
224                 $key_values->{'id'} = $id;
225                 $output .= join(
226                     "\t",
227                     map {
228                         ref $key_values->{$_} eq 'ARRAY'
229                           ? join( ', ', @{ $key_values->{$_} } )
230                           : $key_values->{$_}
231                       } @$order
232                 ) . "\n";
233             }
234             else {
235                 if ( $type eq 'ticket' ) {
236                     $output .= $object->Id . ": " . $object->Subject . "\n";
237                 }
238                 else {
239                     $output .= $object->Id . ": " . $object->Name . "\n";
240                 }
241             }
242         }
243         else {
244             my $d = $m->comp(
245                 "/REST/1.0/Forms/$type/default",
246                 id     => $id,
247                 format => $format,
248                 fields => \%fields
249             );
250             my ( $c, $o, $k, $e ) = @$d;
251             push @output, [ $c, $o, $k ];
252         }
253     }
254     if ( $n == 0 && $format ne "i" ) {
255         $output = "No matching results.\n";
256     }
257
258     $output = form_compose( \@output ) if @output;
259 }
260 else {
261     $status = "500 Server Error";
262     $output = "Unsupported object type.";
263     goto OUTPUT;
264 }
265
266 OUTPUT:
267 $m->out("RT/". $RT::VERSION . " " . $status ."\n\n");
268 $m->out($output );
269 </%INIT>
270
271 <%ONCE>
272 my %search_whitelist = (
273     queue => {
274         map { lc $_ => 1 }
275           grep { $RT::Record::_TABLE_ATTR->{'RT::Queue'}{$_}{read} }
276           keys %{ $RT::Record::_TABLE_ATTR->{'RT::Queue'} }
277     },
278     user => {
279         disabled => 1,
280         map { lc $_ => 1 }
281           grep { $RT::Record::_TABLE_ATTR->{'RT::User'}{$_}{read} }
282           keys %{ $RT::Record::_TABLE_ATTR->{'RT::User'} }
283     },
284     group => {
285         disabled => 1,
286         map { lc $_ => 1 }
287           grep { $RT::Record::_TABLE_ATTR->{'RT::Group'}{$_}{read} }
288           keys %{ $RT::Record::_TABLE_ATTR->{'RT::Group'} }
289     }
290 );
291
292 </%ONCE>
293