Putting 4.2.0 on top of 4.0.17
[usit-rt.git] / share / html / Articles / Article / Search.html
CommitLineData
84fb5b46
MKG
1%# BEGIN BPS TAGGED BLOCK {{{
2%#
3%# COPYRIGHT:
4%#
403d7b0b 5%# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
84fb5b46
MKG
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<& /Elements/Header, Title => loc("Search for articles") &>
49<& /Elements/Tabs &>
50
51% unless ( keys %ARGS ) {
52% my $Classes = RT::Classes->new($session{'CurrentUser'});
53% $Classes->LimitToEnabled();
54<table width="100%" border="0">
55<tr>
56<td valign="top" width="50%">
af59614d
MKG
57<%perl>
58if (not $Classes->First) {
59 $m->comp("/Articles/Elements/NeedsSetup");
60} else {
61 $Classes->GotoFirstItem; # Reset for loop below
62}
63</%perl>
84fb5b46
MKG
64<ul>
65% while (my $class = $Classes->Next) {
66<li><a href="<%RT->Config->Get('WebPath')%>/Articles/Article/Search.html?<% $m->comp('/Elements/QueryString', %filtered, Class => $class->id) %>"><&|/l, $class->Name&>in class [_1]</&></a></li>
67% }
68</ul>
69</td>
70<td valign="top" width="50%">
71<form action="Search.html" method="get">
af59614d 72<& /Widgets/TitleBoxStart, title => loc('Saved searches') &>
84fb5b46
MKG
73<&|/l&>Load saved search:</&><br />
74<& Elements/SelectSavedSearches, Name => 'LoadSavedSearch', Default => $CurrentSearch &>
75<input value="<%loc('Load')%>" name="Load" type="submit" />
af59614d 76<& /Widgets/TitleBoxEnd &>
84fb5b46
MKG
77</form>
78</td>
79</tr>
80</table>
81% return;
82% }
83
84<& /Elements/ListActions, actions => \@results &>
403d7b0b 85<div style="float:right;margin-top:15px"><a href="#criteria"><&|/l&>Modify this search...</&></a></div>
84fb5b46
MKG
86
87% if ($articles->BuildSelectCountQuery =~ /WHERE/i) {
88<h2><&|/l&>Search results</&></h2>
89<& /Elements/CollectionList,
90 Collection => $articles,
91 AllowSorting => 1,
92 OrderBy => \@OrderBy,
93 Order => \@Order,
94 Format => $format,
95 GenericQueryArgs => { %filtered, Format => $format, },
96 &>
97% }
98
99<br />
100<br />
101<br />
102<a name="criteria"></a>
103<form action="Search.html" method="get">
104<& Elements/ShowSearchCriteria, dates => \%dates, RefersTo => $RefersTo, customfields => $customfields, ReferredToBy => $ReferredToBy, %ARGS &>
105<br />
106<br />
107<& Elements/ShowSavedSearches, CurrentSearch => $CurrentSearch,
108 Name => ($search ? $search->Name : undef),
109 Privacy => ($search ? $search->Privacy : undef) &>
110
111</form>
112
113<div align=right>
114<a href="<%RT->Config->Get('WebPath')%>/Articles/Article/Search.html<%$QueryString%>"><&|/l&>Bookmarkable link for this search</&></a><br />
115</div>
116<%init>
117use RT::SavedSearch;
118my @results;
119my $articles = RT::Articles->new( $session{'CurrentUser'} );
120my $format = q{
121 '<a href="__WebPath__/Articles/Article/Display.html?id=__id__">__id__</a>/TITLE:#',
122 '<a href="__WebPath__/Articles/Article/Display.html?id=__id__">__Name__</a>/TITLE:Name',
123 '__ClassName__',
124 '__CreatedRelative__',
125 '__LastUpdatedRelative__',
126 '__Summary__',
127 '__Topics__', };
128
129# {{{ Quicksearch logic
130
131# If it is a number, load the article with that ID. Otherwise, search
132# on name and summary.
133if ($ARGS{'q'} && $ARGS{'q'} =~ /^(\d+)$/) {
134 return $m->comp("/Articles/Article/Display.html", id => $1);
135}
136# }}}
137
138# {{{ Saved search logic
139
140my $search;
141
142# The keys in %ARGS that are not saved and loaded with named searches.
143# These need to be treated specially.
144my @metakeys = qw/NewSearchName CurrentSearch SearchPrivacy Save Load
145 Update Delete/;
146
147if ($CurrentSearch =~ /^(.*-\d+)-SavedSearch-(\d+)$/) {
148 $search = RT::SavedSearch->new($session{'CurrentUser'});
149 $search->Load($1, $2);
150}
151
152# Have we been asked to load a search?
153
154if ($ARGS{'Load'}) {
155 if ($ARGS{'LoadSavedSearch'} =~ /^(.*-\d+)-SavedSearch-(\d+)$/ ) {
af59614d
MKG
156 my $privacy = $1;
157 my $search_id = $2;
158
159 $search = RT::SavedSearch->new($session{'CurrentUser'});
160 my ($ret, $msg) = $search->Load($privacy, $search_id);
161 if ($ret) {
162 my $searchargs = $search->GetParameter('args');
163 # Clean out ARGS and fill it in with the saved args from the
164 # loaded search.
165 foreach my $key (@metakeys) {
166 $searchargs->{$key} = $ARGS{$key};
167 }
168 %ARGS = %{$searchargs};
169 $CurrentSearch = "$privacy-SavedSearch-$search_id";
170 } else {
171 push(@results, loc("Error: could not load saved search [_1]: [_2]",
172 $ARGS{'LoadSavedSearch'}, $msg));
173 }
84fb5b46 174 } else {
af59614d 175 push(@results, loc("Invalid [_1] argument", 'LoadSavedSearch'));
84fb5b46
MKG
176 }
177}
178
179# ...or have we been asked to save, update, or delete a search?
180
181if ($ARGS{'Save'}) {
182 my %searchargs = %ARGS;
183 foreach my $key (@metakeys) {
af59614d 184 delete $searchargs{$key};
84fb5b46
MKG
185 }
186
187 $search = RT::SavedSearch->new($session{'CurrentUser'});
188 unless ($ARGS{'SearchPrivacy'} =~ /^(.*)-(\d+)$/) {
af59614d
MKG
189 # This shouldn't really happen, but hey.
190 push(@results, loc("WARNING: Saving search to user-level privacy"));
191 $ARGS{'SearchPrivacy'} = 'RT::User-'.$session{'CurrentUser'}->Id;
84fb5b46 192 }
af59614d
MKG
193 my ($ret, $msg) = $search->Save(Privacy => $ARGS{'SearchPrivacy'},
194 Type => 'Article',
195 Name => $ARGS{'NewSearchName'},
196 SearchParams => {'args' => \%searchargs});
84fb5b46 197 if ($ret) {
af59614d
MKG
198 $CurrentSearch = $ARGS{'SearchPrivacy'} . "-SavedSearch-" .
199 $search->Id;
200 push(@results, loc("Created search [_1]", $search->Name));
84fb5b46
MKG
201 } else {
202 undef $search; # if we bomb out creating a search
203 # we don't want to have the empty object hang around
af59614d 204 push(@results, loc("Could not create search: [_1]", $msg));
84fb5b46
MKG
205 }
206} elsif ($ARGS{'Update'}) {
207 if ($ARGS{'SearchPrivacy'} != $search->Privacy) {
af59614d
MKG
208 push(@results,
209 loc("Error: cannot change privacy value of existing search"));
84fb5b46 210 } else {
af59614d
MKG
211 my %searchargs = %ARGS;
212 foreach my $key (@metakeys) {
213 delete $searchargs{$key};
214 }
215
216 # We already have a search loaded, because CurrentSearch is set,
217 # or else we would not have gotten here.
218 my ($ret, $msg) = $search->Update(Name => $ARGS{'NewSearchName'},
219 SearchParams => \%searchargs);
220 if ($ret) {
221 push(@results, loc("Search [_1] updated", $search->Name));
222 } else {
223 push(@results, loc("Error: search [_1] not updated: [_2]",
224 $search->Name, $msg));
225 }
84fb5b46
MKG
226 }
227} elsif ($ARGS{'Delete'}) {
228 # Keep track of this, as we are about to delete the search.
229 my $searchname = $search->Name;
230 my ($ret, $msg) = $search->Delete;
231 if ($ret) {
af59614d
MKG
232 $ARGS{'CurrentSearch'} = undef;
233 push(@results, loc("Deleted search [_1]", $searchname));
234 # Get rid of all the state.
235 foreach my $key (keys %ARGS) {
236 delete $ARGS{$key};
237 }
238 $CurrentSearch = 'new';
239 $search = undef;
240 $RefersTo = undef;
241 $ReferredToBy = undef;
84fb5b46 242 } else {
af59614d
MKG
243 push(@results, loc("Could not delete search [_1]: [_2]",
244 $searchname, $msg));
84fb5b46
MKG
245 }
246}
247
248# }}}
249
250 # Don't want to search for a null class when there is no class specced
251my $customfields = RT::CustomFields->new( $session{'CurrentUser'} );
252my %dates;
253
254$articles->Search( %ARGS, CustomFields => $customfields, Dates => \%dates,
255 OrderBy => \@OrderBy, Order => \@Order, );
256
257$m->callback( %ARGS, _Search => $articles );
258
259my %filtered = %ARGS;
260delete $filtered{$_} for (@metakeys, "EditTopics", "ExpandTopics");
261delete $filtered{$_} for grep {$filtered{$_} !~ /\S/} keys %filtered;
262@filtered{qw(OrderBy Order)} = (\@OrderBy, \@Order);
263my $QueryString = "?".$m->comp('/Elements/QueryString', %filtered);
264</%init>
265
266<%ARGS>
267$CreatedBefore => ''
268$CreatedAfter => ''
269$LastUpdatedBefore => ''
270$LastUpdatedAfter => ''
271$RefersTo => undef
272$ReferredToBy => undef
273$CurrentSearch => 'new'
274@OrderBy => ()
275@Order => ()
276</%ARGS>