(CVE-2013-0786) [SECURITY] build_subselect() leaks the existence of products and...
[WebKit-https.git] / Websites / bugs.webkit.org / Bugzilla / Status.pm
1 # -*- Mode: perl; indent-tabs-mode: nil -*-
2 #
3 # The contents of this file are subject to the Mozilla Public
4 # License Version 1.1 (the "License"); you may not use this file
5 # except in compliance with the License. You may obtain a copy of
6 # the License at http://www.mozilla.org/MPL/
7 #
8 # Software distributed under the License is distributed on an "AS
9 # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
10 # implied. See the License for the specific language governing
11 # rights and limitations under the License.
12 #
13 # The Original Code is the Bugzilla Bug Tracking System.
14 #
15 # The Initial Developer of the Original Code is Frédéric Buclin.
16 # Portions created by Frédéric Buclin are Copyright (C) 2007
17 # Frédéric Buclin. All Rights Reserved.
18 #
19 # Contributor(s): Frédéric Buclin <LpSolit@gmail.com>
20
21 use strict;
22
23 package Bugzilla::Status;
24
25 use base qw(Bugzilla::Object Exporter);
26 @Bugzilla::Status::EXPORT = qw(BUG_STATE_OPEN is_open_state closed_bug_statuses);
27
28 ################################
29 #####   Initialization     #####
30 ################################
31
32 use constant DB_TABLE => 'bug_status';
33
34 use constant DB_COLUMNS => qw(
35     id
36     value
37     sortkey
38     isactive
39     is_open
40 );
41
42 use constant NAME_FIELD => 'value';
43 use constant LIST_ORDER => 'sortkey, value';
44
45 ###############################
46 #####     Accessors        ####
47 ###############################
48
49 sub name      { return $_[0]->{'value'};    }
50 sub sortkey   { return $_[0]->{'sortkey'};  }
51 sub is_active { return $_[0]->{'isactive'}; }
52 sub is_open   { return $_[0]->{'is_open'};  }
53
54 ###############################
55 #####       Methods        ####
56 ###############################
57
58 sub BUG_STATE_OPEN {
59     # XXX - We should cache this list.
60     my $dbh = Bugzilla->dbh;
61     return @{$dbh->selectcol_arrayref('SELECT value FROM bug_status WHERE is_open = 1')};
62 }
63
64 # Tells you whether or not the argument is a valid "open" state.
65 sub is_open_state {
66     my ($state) = @_;
67     return (grep($_ eq $state, BUG_STATE_OPEN) ? 1 : 0);
68 }
69
70 sub closed_bug_statuses {
71     my @bug_statuses = Bugzilla::Status->get_all;
72     @bug_statuses = grep { !$_->is_open } @bug_statuses;
73     return @bug_statuses;
74 }
75
76 sub can_change_to {
77     my $self = shift;
78     my $dbh = Bugzilla->dbh;
79
80     if (!ref($self) || !defined $self->{'can_change_to'}) {
81         my ($cond, @args, $self_exists);
82         if (ref($self)) {
83             $cond = '= ?';
84             push(@args, $self->id);
85             $self_exists = 1;
86         }
87         else {
88             $cond = 'IS NULL';
89             # Let's do it so that the code below works in all cases.
90             $self = {};
91         }
92
93         my $new_status_ids = $dbh->selectcol_arrayref("SELECT new_status
94                                                          FROM status_workflow
95                                                    INNER JOIN bug_status
96                                                            ON id = new_status
97                                                         WHERE isactive = 1
98                                                           AND old_status $cond
99                                                      ORDER BY sortkey",
100                                                         undef, @args);
101
102         # Allow the bug status to remain unchanged.
103         push(@$new_status_ids, $self->id) if $self_exists;
104         $self->{'can_change_to'} = Bugzilla::Status->new_from_list($new_status_ids);
105     }
106
107     return $self->{'can_change_to'};
108 }
109
110 sub can_change_from {
111     my $self = shift;
112     my $dbh = Bugzilla->dbh;
113
114     if (!defined $self->{'can_change_from'}) {
115         my $old_status_ids = $dbh->selectcol_arrayref('SELECT old_status
116                                                          FROM status_workflow
117                                                    INNER JOIN bug_status
118                                                            ON id = old_status
119                                                         WHERE isactive = 1
120                                                           AND new_status = ?
121                                                           AND old_status IS NOT NULL',
122                                                         undef, $self->id);
123
124         # Allow the bug status to remain unchanged.
125         push(@$old_status_ids, $self->id);
126         $self->{'can_change_from'} = Bugzilla::Status->new_from_list($old_status_ids);
127     }
128
129     return $self->{'can_change_from'};
130 }
131
132 sub comment_required_on_change_from {
133     my ($self, $old_status) = @_;
134     my ($cond, $values) = $self->_status_condition($old_status);
135     
136     my ($require_comment) = Bugzilla->dbh->selectrow_array(
137         "SELECT require_comment FROM status_workflow
138           WHERE $cond", undef, @$values);
139     return $require_comment;
140 }
141
142 # Used as a helper for various functions that have to deal with old_status
143 # sometimes being NULL and sometimes having a value.
144 sub _status_condition {
145     my ($self, $old_status) = @_;
146     my @values;
147     my $cond = 'old_status IS NULL';
148     # For newly-filed bugs
149     if ($old_status) {
150         $cond = 'old_status = ?';
151         push(@values, $old_status->id);
152     }
153     $cond .= " AND new_status = ?";
154     push(@values, $self->id);
155     return ($cond, \@values);
156 }
157
158 sub add_missing_bug_status_transitions {
159     my $bug_status = shift || Bugzilla->params->{'duplicate_or_move_bug_status'};
160     my $dbh = Bugzilla->dbh;
161     my $new_status = new Bugzilla::Status({name => $bug_status});
162     # Silently discard invalid bug statuses.
163     $new_status || return;
164
165     my $missing_statuses = $dbh->selectcol_arrayref('SELECT id
166                                                        FROM bug_status
167                                                   LEFT JOIN status_workflow
168                                                          ON old_status = id
169                                                         AND new_status = ?
170                                                       WHERE old_status IS NULL',
171                                                       undef, $new_status->id);
172
173     my $sth = $dbh->prepare('INSERT INTO status_workflow
174                              (old_status, new_status) VALUES (?, ?)');
175
176     foreach my $old_status_id (@$missing_statuses) {
177         next if ($old_status_id == $new_status->id);
178         $sth->execute($old_status_id, $new_status->id);
179     }
180 }
181
182 1;
183
184 __END__
185
186 =head1 NAME
187
188 Bugzilla::Status - Bug status class.
189
190 =head1 SYNOPSIS
191
192     use Bugzilla::Status;
193
194     my $bug_status = new Bugzilla::Status({name => 'ASSIGNED'});
195     my $bug_status = new Bugzilla::Status(4);
196
197     my @closed_bug_statuses = closed_bug_statuses();
198
199     Bugzilla::Status::add_missing_bug_status_transitions($bug_status);
200
201 =head1 DESCRIPTION
202
203 Status.pm represents a bug status object. It is an implementation
204 of L<Bugzilla::Object>, and thus provides all methods that
205 L<Bugzilla::Object> provides.
206
207 The methods that are specific to C<Bugzilla::Status> are listed
208 below.
209
210 =head1 METHODS
211
212 =over
213
214 =item C<closed_bug_statuses>
215
216  Description: Returns a list of C<Bugzilla::Status> objects which can have
217               a resolution associated with them ("closed" bug statuses).
218
219  Params:      none.
220
221  Returns:     A list of Bugzilla::Status objects.
222
223 =item C<can_change_to>
224
225  Description: Returns the list of active statuses a bug can be changed to
226               given the current bug status. If this method is called as a
227               class method, then it returns all bug statuses available on
228               bug creation.
229
230  Params:      none.
231
232  Returns:     A list of Bugzilla::Status objects.
233
234 =item C<can_change_from>
235
236  Description: Returns the list of active statuses a bug can be changed from
237               given the new bug status. If the bug status is available on
238               bug creation, this method doesn't return this information.
239               You have to call C<can_change_to> instead.
240
241  Params:      none.
242
243  Returns:     A list of Bugzilla::Status objects.
244
245 =item C<comment_required_on_change_from>
246
247 =over
248
249 =item B<Description>
250
251 Checks if a comment is required to change to this status from another
252 status, according to the current settings in the workflow.
253
254 Note that this doesn't implement the checks enforced by the various
255 C<commenton> parameters--those are checked by internal checks in
256 L<Bugzilla::Bug>.
257
258 =item B<Params>
259
260 C<$old_status> - The status you're changing from.
261
262 =item B<Returns>
263
264 C<1> if a comment is required on this change, C<0> if not.
265
266 =back
267
268 =item C<add_missing_bug_status_transitions>
269
270  Description: Insert all missing transitions to a given bug status.
271
272  Params:      $bug_status - The value (name) of a bug status.
273
274  Returns:     nothing.
275
276 =back
277
278 =cut