1 #!/usr/bin/env perl -wT
2 # -*- Mode: perl; indent-tabs-mode: nil -*-
4 # The contents of this file are subject to the Mozilla Public
5 # License Version 1.1 (the "License"); you may not use this file
6 # except in compliance with the License. You may obtain a copy of
7 # the License at http://www.mozilla.org/MPL/
9 # Software distributed under the License is distributed on an "AS
10 # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11 # implied. See the License for the specific language governing
12 # rights and limitations under the License.
14 # The Original Code is the Bugzilla Bug Tracking System.
16 # The Initial Developer of the Original Code is Netscape Communications
17 # Corporation. Portions created by Netscape are
18 # Copyright (C) 1998 Netscape Communications Corporation. All
21 # Contributor(s): Terry Weissman <terry@mozilla.org>
22 # David Gardiner <david.gardiner@unisa.edu.au>
23 # Matthias Radestock <matthias@sorted.org>
24 # Gervase Markham <gerv@gerv.net>
25 # Byron Jones <bugzilla@glob.com.au>
26 # Max Kanat-Alexander <mkanat@bugzilla.org>
33 use Bugzilla
::Constants
;
38 use Bugzilla
::Product
;
39 use Bugzilla
::Keyword
;
41 use Bugzilla
::Install
::Util
qw(vers_cmp);
43 my $cgi = Bugzilla
->cgi;
44 my $dbh = Bugzilla
->dbh;
45 my $template = Bugzilla
->template;
47 my $buffer = $cgi->query_string();
49 my $user = Bugzilla
->login();
50 my $userid = $user->id;
52 # Backwards compatibility hack -- if there are any of the old QUERY_*
53 # cookies around, and we are logged in, then move them into the database
54 # and nuke the cookie. This is required for Bugzilla 2.8 and earlier.
57 foreach my $i ($cgi->cookie()) {
58 if ($i =~ /^QUERY_(.*)$/) {
59 push(@oldquerycookies, [$1, $i, $cgi->cookie($i)]);
62 if (defined $cgi->cookie('DEFAULTQUERY')) {
63 push(@oldquerycookies, [DEFAULT_QUERY_NAME
, 'DEFAULTQUERY',
64 $cgi->cookie('DEFAULTQUERY')]);
66 if (@oldquerycookies) {
67 foreach my $ref (@oldquerycookies) {
68 my ($name, $cookiename, $value) = (@
$ref);
70 # If the query name contains invalid characters, don't import.
71 $name =~ /[<>&]/ && next;
73 $dbh->bz_start_transaction();
74 my $query = $dbh->selectrow_array(
75 "SELECT query FROM namedqueries " .
76 "WHERE userid = ? AND name = ?",
77 undef, ($userid, $name));
79 $dbh->do("INSERT INTO namedqueries " .
80 "(userid, name, query) VALUES " .
81 "(?, ?, ?)", undef, ($userid, $name, $value));
83 $dbh->bz_commit_transaction();
85 $cgi->remove_cookie($cookiename);
90 if ($cgi->param('nukedefaultquery')) {
92 $dbh->do("DELETE FROM namedqueries" .
93 " WHERE userid = ? AND name = ?",
94 undef, ($userid, DEFAULT_QUERY_NAME
));
101 $userdefaultquery = $dbh->selectrow_array(
102 "SELECT query FROM namedqueries " .
103 "WHERE userid = ? AND name = ?",
104 undef, ($userid, DEFAULT_QUERY_NAME
));
109 # We pass the defaults as a hash of references to arrays. For those
110 # Items which are single-valued, the template should only reference [0]
111 # and ignore any multiple values.
114 my $cgi = Bugzilla
->cgi;
115 $buf = new Bugzilla
::CGI
($buf);
118 # Nothing must be undef, otherwise the template complains.
119 foreach my $name ("bug_status", "resolution", "assigned_to",
120 "rep_platform", "priority", "bug_severity",
121 "classification", "product", "reporter", "op_sys",
122 "component", "version", "chfield", "chfieldfrom",
123 "chfieldto", "chfieldvalue", "target_milestone",
124 "email", "emailtype", "emailreporter",
125 "emailassigned_to", "emailcc", "emailqa_contact",
126 "emaillongdesc", "content",
127 "changedin", "votes", "short_desc", "short_desc_type",
128 "long_desc", "long_desc_type", "bug_file_loc",
129 "bug_file_loc_type", "status_whiteboard",
130 "status_whiteboard_type", "bug_id",
131 "bugidtype", "keywords", "keywords_type",
132 "deadlinefrom", "deadlineto",
133 "x_axis_field", "y_axis_field", "z_axis_field",
134 "chart_format", "cumulate", "x_labels_vertical",
135 "category", "subcategory", "name", "newcategory",
136 "newsubcategory", "public", "frequency")
138 $default{$name} = [];
141 # we won't prefill the boolean chart data from this query if
142 # there are any being submitted via params
143 my $prefillcharts = (grep(/^field-/, $cgi->param)) ?
0 : 1;
145 # Iterate over the URL parameters
146 foreach my $name ($buf->param()) {
147 my @values = $buf->param($name);
149 # If the name begins with the string 'field', 'type', 'value', or
150 # 'negate', then it is part of the boolean charts. Because
151 # these are built different than the rest of the form, we need
152 # to store these as parameters. We also need to indicate that
153 # we found something so the default query isn't added in if
154 # all we have are boolean chart items.
155 if ($name =~ m/^(?:field|type|value|negate)/) {
156 $cgi->param(-name
=> $name, -value
=> $values[0]) if ($prefillcharts);
159 # If the name ends in a number (which it does for the fields which
160 # are part of the email searching), we use the array
161 # positions to show the defaults for that number field.
162 elsif ($name =~ m/^(.+)(\d)$/ && defined($default{$1})) {
164 $default{$1}->[$2] = $values[0];
166 elsif (exists $default{$name}) {
168 push (@
{$default{$name}}, @values);
174 if (!PrefillForm
($buffer)) {
175 # Ah-hah, there was no form stuff specified. Do it again with the
177 if ($userdefaultquery) {
178 PrefillForm
($userdefaultquery);
180 PrefillForm
(Bugzilla
->params->{"defaultquery"});
184 if (!scalar(@
{$default{'chfieldto'}}) || $default{'chfieldto'}->[0] eq "") {
185 $default{'chfieldto'} = ["Now"];
188 # if using groups for entry, then we don't want people to see products they
189 # don't have access to. Remove them from the list.
190 my @selectable_products = sort {lc($a->name) cmp lc($b->name)}
191 @
{$user->get_selectable_products};
192 Bugzilla
::Product
::preload
(\
@selectable_products);
194 # Create the component, version and milestone lists.
199 foreach my $product (@selectable_products) {
200 $components{$_->name} = 1 foreach (@
{$product->components});
201 $versions{$_->name} = 1 foreach (@
{$product->versions});
202 $milestones{$_->name} = 1 foreach (@
{$product->milestones});
205 my @components = sort(keys %components);
206 my @versions = sort { vers_cmp
(lc($a), lc($b)) } keys %versions;
207 my @milestones = sort(keys %milestones);
209 $vars->{'product'} = \
@selectable_products;
211 # Create data structures representing each classification
212 if (Bugzilla
->params->{'useclassification'}) {
213 $vars->{'classification'} = $user->get_selectable_classifications;
216 # We use 'component_' because 'component' is a Template Toolkit reserved word.
217 $vars->{'component_'} = \
@components;
219 $vars->{'version'} = \
@versions;
221 if (Bugzilla
->params->{'usetargetmilestone'}) {
222 $vars->{'target_milestone'} = \
@milestones;
225 $vars->{'have_keywords'} = Bugzilla
::Keyword
::keyword_count
();
227 my $legal_resolutions = get_legal_field_values
('resolution');
228 push(@
$legal_resolutions, "---"); # Oy, what a hack.
229 # Another hack - this array contains "" for some reason. See bug 106589.
230 $vars->{'resolution'} = [grep ($_, @
$legal_resolutions)];
234 push @chfields, "[Bug creation]";
236 # This is what happens when you have variables whose definition depends
237 # on the DB schema, and then the underlying schema changes...
238 foreach my $val (editable_bug_fields
()) {
239 if ($val eq 'classification_id') {
240 $val = 'classification';
241 } elsif ($val eq 'product_id') {
243 } elsif ($val eq 'component_id') {
246 push @chfields, $val;
249 if (Bugzilla
->user->in_group(Bugzilla
->params->{'timetrackinggroup'})) {
250 push @chfields, "work_time";
252 @chfields = grep($_ ne "estimated_time", @chfields);
253 @chfields = grep($_ ne "remaining_time", @chfields);
255 @chfields = (sort(@chfields));
256 $vars->{'chfield'} = \
@chfields;
257 $vars->{'bug_status'} = get_legal_field_values
('bug_status');
258 $vars->{'rep_platform'} = get_legal_field_values
('rep_platform');
259 $vars->{'op_sys'} = get_legal_field_values
('op_sys');
260 $vars->{'priority'} = get_legal_field_values
('priority');
261 $vars->{'bug_severity'} = get_legal_field_values
('bug_severity');
264 my @fields = Bugzilla
->get_fields({ obsolete
=> 0 });
266 # If we're not in the time-tracking group, exclude time-tracking fields.
267 if (!Bugzilla
->user->in_group(Bugzilla
->params->{'timetrackinggroup'})) {
268 foreach my $tt_field (qw(estimated_time remaining_time work_time
269 percentage_complete deadline))
271 @fields = grep($_->name ne $tt_field, @fields);
275 @fields = sort {lc($a->description) cmp lc($b->description)} @fields;
276 unshift(@fields, { name
=> "noop", description
=> "---" });
277 $vars->{'fields'} = \
@fields;
279 # Creating new charts - if the cmd-add value is there, we define the field
280 # value so the code sees it and creates the chart. It will attempt to select
281 # "xyzzy" as the default, and fail. This is the correct behaviour.
282 foreach my $cmd (grep(/^cmd-/, $cgi->param)) {
283 if ($cmd =~ /^cmd-add(\d+)-(\d+)-(\d+)$/) {
284 $cgi->param(-name
=> "field$1-$2-$3", -value
=> "xyzzy");
288 if (!$cgi->param('field0-0-0')) {
289 $cgi->param(-name
=> 'field0-0-0', -value
=> "xyzzy");
292 # Create data structure of boolean chart info. It's an array of arrays of
293 # arrays - with the inner arrays having three members - field, type and
296 for (my $chart = 0; $cgi->param("field$chart-0-0"); $chart++) {
298 for (my $row = 0; $cgi->param("field$chart-$row-0"); $row++) {
300 for (my $col = 0; $cgi->param("field$chart-$row-$col"); $col++) {
301 my $value = $cgi->param("value$chart-$row-$col");
302 if (!defined($value)) {
305 push(@cols, { field
=> $cgi->param("field$chart-$row-$col"),
306 type
=> $cgi->param("type$chart-$row-$col") || 'noop',
311 push(@charts, {'rows' => \
@rows, 'negate' => scalar($cgi->param("negate$chart")) });
314 $default{'charts'} = \
@charts;
318 $vars->{'namedqueries'} = $dbh->selectcol_arrayref(
319 "SELECT name FROM namedqueries " .
320 "WHERE userid = ? AND name != ? " .
322 undef, ($userid, DEFAULT_QUERY_NAME
));
327 my @orders = ('Bug Number', 'Importance', 'Assignee', 'Last Changed');
329 if ($cgi->cookie('LASTORDER')) {
330 $deforder = "Reuse same sort as last time";
331 unshift(@orders, $deforder);
334 if ($cgi->param('order')) { $deforder = $cgi->param('order') }
336 $vars->{'userdefaultquery'} = $userdefaultquery;
337 $vars->{'orders'} = \
@orders;
338 $default{'order'} = [$deforder || 'Importance'];
340 if (($cgi->param('query_format') || $cgi->param('format') || "")
341 eq "create-series") {
342 require Bugzilla
::Chart
;
343 $vars->{'category'} = Bugzilla
::Chart
::getVisibleSeries
();
346 $vars->{'known_name'} = $cgi->param('known_name');
349 # Add in the defaults.
350 $vars->{'default'} = \
%default;
352 $vars->{'format'} = $cgi->param('format');
353 $vars->{'query_format'} = $cgi->param('query_format');
355 # Set default page to "specific" if none provided
356 if (!($cgi->param('query_format') || $cgi->param('format'))) {
357 if (defined $cgi->cookie('DEFAULTFORMAT')) {
358 $vars->{'format'} = $cgi->cookie('DEFAULTFORMAT');
360 $vars->{'format'} = 'specific';
364 # Set cookie to current format as default, but only if the format
365 # one that we should remember.
366 if (defined($vars->{'format'}) && IsValidQueryType
($vars->{'format'})) {
367 $cgi->send_cookie(-name
=> 'DEFAULTFORMAT',
368 -value
=> $vars->{'format'},
369 -expires
=> "Fri, 01-Jan-2038 00:00:00 GMT");
372 # Generate and return the UI (HTML page) from the appropriate template.
373 # If we submit back to ourselves (for e.g. boolean charts), we need to
374 # preserve format information; hence query_format taking priority over
376 my $format = $template->get_format("search/search",
377 $vars->{'query_format'} || $vars->{'format'},
378 scalar $cgi->param('ctype'));
380 print $cgi->header($format->{'ctype'});
382 $template->process($format->{'template'}, $vars)
383 || ThrowTemplateError
($template->error());