Commit 09becc83 authored by lpsolit%gmail.com's avatar lpsolit%gmail.com

Bug 375191: Remove ChangeStatus() and ChangeResolution() from process_bug.cgi -…

Bug 375191: Remove ChangeStatus() and ChangeResolution() from process_bug.cgi - Patch by Fré©ric Buclin <LpSolit@gmail.com> r=gerv r=mkanat r=bkor a=LpSolit
parent edcc538e
...@@ -1495,6 +1495,90 @@ sub bug_alias_to_id { ...@@ -1495,6 +1495,90 @@ sub bug_alias_to_id {
} }
##################################################################### #####################################################################
# Workflow Control routines
#####################################################################
sub get_new_status_and_resolution {
my ($self, $action, $resolution) = @_;
my $dbh = Bugzilla->dbh;
my $status;
if ($action eq 'none') {
# Leaving the status unchanged doesn't need more investigation.
return ($self->bug_status, $self->resolution);
}
elsif ($action eq 'reopen') {
$status = $self->everconfirmed ? 'REOPENED' : 'UNCONFIRMED';
$resolution = '';
}
elsif ($action =~ /^reassign(?:bycomponent)?$/) {
if (!is_open_state($self->bug_status) || $self->bug_status eq 'UNCONFIRMED') {
$status = $self->bug_status;
}
else {
$status = 'NEW';
}
$resolution = $self->resolution;
}
elsif ($action eq 'duplicate') {
# Only alter the bug status if the bug is currently open.
$status = is_open_state($self->bug_status) ? 'RESOLVED' : $self->bug_status;
$resolution = 'DUPLICATE';
}
elsif ($action eq 'change_resolution') {
$status = $self->bug_status;
# You cannot change the resolution of an open bug.
ThrowUserError('resolution_not_allowed') if is_open_state($status);
$resolution || ThrowUserError('missing_resolution', {status => $status});
}
elsif ($action eq 'clearresolution') {
$status = $self->bug_status;
is_open_state($status) || ThrowUserError('missing_resolution', {status => $status});
$resolution = '';
}
else {
# That's where actions not requiring any specific trigger (such as future
# custom statuses) come.
# XXX - This is hardcoded here for now, but will disappear soon when
# this routine will look at the DB directly to get the workflow.
if ($action eq 'confirm') {
$status = 'NEW';
}
elsif ($action eq 'accept') {
$status = 'ASSIGNED';
}
elsif ($action eq 'resolve') {
$status = 'RESOLVED';
}
elsif ($action eq 'verify') {
$status = 'VERIFIED';
}
elsif ($action eq 'close') {
$status = 'CLOSED';
}
else {
ThrowCodeError('unknown_action', { action => $action });
}
if (is_open_state($status)) {
# Open bugs have no resolution.
$resolution = '';
}
else {
# All non-open statuses must have a resolution.
$resolution || ThrowUserError('missing_resolution', {status => $status});
}
}
# Now it's time to validate the bug resolution.
# Bug resolutions have no workflow specific rules, so any valid
# resolution will do it.
check_field('resolution', $resolution) if ($resolution ne '');
trick_taint($resolution);
return ($status, $resolution);
}
#####################################################################
# Subroutines # Subroutines
##################################################################### #####################################################################
......
...@@ -643,9 +643,6 @@ sub DoComma { ...@@ -643,9 +643,6 @@ sub DoComma {
$::comma = ","; $::comma = ",";
} }
# $everconfirmed is used by ChangeStatus() to determine whether we are
# confirming the bug or not.
local our $everconfirmed;
sub DoConfirm { sub DoConfirm {
my $bug = shift; my $bug = shift;
if ($bug->check_can_change_field("canconfirm", 0, 1, if ($bug->check_can_change_field("canconfirm", 0, 1,
...@@ -653,111 +650,6 @@ sub DoConfirm { ...@@ -653,111 +650,6 @@ sub DoConfirm {
{ {
DoComma(); DoComma();
$::query .= "everconfirmed = 1"; $::query .= "everconfirmed = 1";
$everconfirmed = 1;
}
}
sub ChangeStatus {
my ($str) = (@_);
my $cgi = Bugzilla->cgi;
my $dbh = Bugzilla->dbh;
if (!$cgi->param('dontchange')
|| $str ne $cgi->param('dontchange')) {
DoComma();
if ($cgi->param('knob') eq 'reopen') {
# When reopening, we need to check whether the bug was ever
# confirmed or not
$::query .= "bug_status = CASE WHEN everconfirmed = 1 THEN " .
$dbh->quote($str) . " ELSE 'UNCONFIRMED' END";
} elsif (is_open_state($str)) {
# Note that we cannot combine this with the above branch - here we
# need to check if bugs.bug_status is open, (since we don't want to
# reopen closed bugs when reassigning), while above the whole point
# is to reopen a closed bug.
# Currently, the UI doesn't permit a user to reassign a closed bug
# from the single bug page (only during a mass change), but they
# could still hack the submit, so don't restrict this extended
# check to the mass change page for safety/sanity/consistency
# purposes.
# The logic for this block is:
# If the new state is open:
# If the old state was open
# If the bug was confirmed
# - move it to the new state
# Else
# - Set the state to unconfirmed
# Else
# - leave it as it was
# This is valid only because 'reopen' is the only thing which moves
# from closed to open, and it's handled above
# This also relies on the fact that confirming and accepting have
# already called DoConfirm before this is called
my @open_state = map($dbh->quote($_), BUG_STATE_OPEN);
my $open_state = join(", ", @open_state);
# If we are changing everconfirmed to 1, we have to take this change
# into account and the new bug status is given by $str.
my $cond = $dbh->quote($str);
# If we are not setting everconfirmed, the new bug status depends on
# the actual value of everconfirmed, which is bug-specific.
unless ($everconfirmed) {
$cond = "(CASE WHEN everconfirmed = 1 THEN " . $cond .
" ELSE 'UNCONFIRMED' END)";
}
$::query .= "bug_status = CASE WHEN bug_status IN($open_state) THEN " .
$cond . " ELSE bug_status END";
} else {
$::query .= "bug_status = ?";
push(@values, $str);
}
# If bugs are reassigned and their status is "UNCONFIRMED", they
# should keep this status instead of "NEW" as suggested here.
# This point is checked for each bug later in the code.
$cgi->param('bug_status', $str);
}
}
sub ChangeResolution {
my ($bug, $str) = (@_);
my $dbh = Bugzilla->dbh;
my $cgi = Bugzilla->cgi;
if (!$cgi->param('dontchange')
|| $str ne $cgi->param('dontchange'))
{
# Make sure the user is allowed to change the resolution.
# If the user is changing several bugs at once using the UI,
# then he has enough privs to do so. In the case he is hacking
# the URL, we don't care if he reads --UNKNOWN-- as a resolution
# in the error message.
my $old_resolution = '-- UNKNOWN --';
my $bug_id = $cgi->param('id');
if ($bug_id) {
$old_resolution =
$dbh->selectrow_array('SELECT resolution FROM bugs WHERE bug_id = ?',
undef, $bug_id);
}
unless ($bug->check_can_change_field('resolution', $old_resolution, $str,
\$PrivilegesRequired))
{
$vars->{'oldvalue'} = $old_resolution;
$vars->{'newvalue'} = $str;
$vars->{'field'} = 'resolution';
$vars->{'privs'} = $PrivilegesRequired;
ThrowUserError("illegal_change", $vars);
}
DoComma();
$::query .= "resolution = ?";
trick_taint($str);
push(@values, $str);
# We define this variable here so that customized installations
# may set rules based on the resolution in Bug::check_can_change_field().
$cgi->param('resolution', $str);
} }
} }
...@@ -1042,13 +934,11 @@ SWITCH: for ($cgi->param('knob')) { ...@@ -1042,13 +934,11 @@ SWITCH: for ($cgi->param('knob')) {
}; };
/^confirm$/ && CheckonComment( "confirm" ) && do { /^confirm$/ && CheckonComment( "confirm" ) && do {
DoConfirm($bug); DoConfirm($bug);
ChangeStatus('NEW');
last SWITCH; last SWITCH;
}; };
/^accept$/ && CheckonComment( "accept" ) && do { /^accept$/ && CheckonComment( "accept" ) && do {
DoConfirm($bug); DoConfirm($bug);
ChangeStatus('ASSIGNED'); if (Bugzilla->params->{"usetargetmilestone"}
if (Bugzilla->params->{"usetargetmilestone"}
&& Bugzilla->params->{"musthavemilestoneonaccept"}) && Bugzilla->params->{"musthavemilestoneonaccept"})
{ {
$requiremilestone = 1; $requiremilestone = 1;
...@@ -1056,7 +946,6 @@ SWITCH: for ($cgi->param('knob')) { ...@@ -1056,7 +946,6 @@ SWITCH: for ($cgi->param('knob')) {
last SWITCH; last SWITCH;
}; };
/^clearresolution$/ && CheckonComment( "clearresolution" ) && do { /^clearresolution$/ && CheckonComment( "clearresolution" ) && do {
ChangeResolution($bug, '');
last SWITCH; last SWITCH;
}; };
/^(resolve|change_resolution)$/ && CheckonComment( "resolve" ) && do { /^(resolve|change_resolution)$/ && CheckonComment( "resolve" ) && do {
...@@ -1080,8 +969,6 @@ SWITCH: for ($cgi->param('knob')) { ...@@ -1080,8 +969,6 @@ SWITCH: for ($cgi->param('knob')) {
# RESOLVED bugs should have no time remaining; # RESOLVED bugs should have no time remaining;
# more time can be added for the VERIFY step, if needed. # more time can be added for the VERIFY step, if needed.
_remove_remaining_time(); _remove_remaining_time();
ChangeStatus('RESOLVED');
} }
else { else {
# You cannot use change_resolution if there is at least # You cannot use change_resolution if there is at least
...@@ -1094,16 +981,12 @@ SWITCH: for ($cgi->param('knob')) { ...@@ -1094,16 +981,12 @@ SWITCH: for ($cgi->param('knob')) {
ThrowUserError('resolution_not_allowed') if $is_open; ThrowUserError('resolution_not_allowed') if $is_open;
} }
ChangeResolution($bug, $cgi->param('resolution'));
last SWITCH; last SWITCH;
}; };
/^reassign$/ && CheckonComment( "reassign" ) && do { /^reassign$/ && CheckonComment( "reassign" ) && do {
if ($cgi->param('andconfirm')) { if ($cgi->param('andconfirm')) {
DoConfirm($bug); DoConfirm($bug);
} }
ChangeStatus('NEW');
DoComma();
if (defined $cgi->param('assigned_to') if (defined $cgi->param('assigned_to')
&& trim($cgi->param('assigned_to')) ne "") { && trim($cgi->param('assigned_to')) ne "") {
$assignee = login_to_id(trim($cgi->param('assigned_to')), THROW_ERROR); $assignee = login_to_id(trim($cgi->param('assigned_to')), THROW_ERROR);
...@@ -1125,6 +1008,7 @@ SWITCH: for ($cgi->param('knob')) { ...@@ -1125,6 +1008,7 @@ SWITCH: for ($cgi->param('knob')) {
} else { } else {
ThrowUserError("reassign_to_empty"); ThrowUserError("reassign_to_empty");
} }
DoComma();
$::query .= "assigned_to = ?"; $::query .= "assigned_to = ?";
push(@values, $assignee); push(@values, $assignee);
$assignee_checked = 1; $assignee_checked = 1;
...@@ -1134,23 +1018,17 @@ SWITCH: for ($cgi->param('knob')) { ...@@ -1134,23 +1018,17 @@ SWITCH: for ($cgi->param('knob')) {
if ($cgi->param('compconfirm')) { if ($cgi->param('compconfirm')) {
DoConfirm($bug); DoConfirm($bug);
} }
ChangeStatus('NEW');
last SWITCH; last SWITCH;
}; };
/^reopen$/ && CheckonComment( "reopen" ) && do { /^reopen$/ && CheckonComment( "reopen" ) && do {
ChangeStatus('REOPENED');
ChangeResolution($bug, '');
last SWITCH; last SWITCH;
}; };
/^verify$/ && CheckonComment( "verify" ) && do { /^verify$/ && CheckonComment( "verify" ) && do {
ChangeStatus('VERIFIED');
last SWITCH; last SWITCH;
}; };
/^close$/ && CheckonComment( "close" ) && do { /^close$/ && CheckonComment( "close" ) && do {
# CLOSED bugs should have no time remaining. # CLOSED bugs should have no time remaining.
_remove_remaining_time(); _remove_remaining_time();
ChangeStatus('CLOSED');
last SWITCH; last SWITCH;
}; };
/^duplicate$/ && CheckonComment( "duplicate" ) && do { /^duplicate$/ && CheckonComment( "duplicate" ) && do {
...@@ -1196,9 +1074,6 @@ SWITCH: for ($cgi->param('knob')) { ...@@ -1196,9 +1074,6 @@ SWITCH: for ($cgi->param('knob')) {
# DUPLICATE bugs should have no time remaining. # DUPLICATE bugs should have no time remaining.
_remove_remaining_time(); _remove_remaining_time();
ChangeStatus('RESOLVED');
ChangeResolution($bug, 'DUPLICATE');
last SWITCH; last SWITCH;
}; };
...@@ -1391,8 +1266,30 @@ if ($prod_changed && Bugzilla->params->{"strict_isolation"}) { ...@@ -1391,8 +1266,30 @@ if ($prod_changed && Bugzilla->params->{"strict_isolation"}) {
foreach my $id (@idlist) { foreach my $id (@idlist) {
my $query = $basequery; my $query = $basequery;
my @bug_values = @values; my @bug_values = @values;
# XXX We really have to get rid of $::comma.
my $comma = $::comma;
my $old_bug_obj = new Bugzilla::Bug($id); my $old_bug_obj = new Bugzilla::Bug($id);
my $status;
my $resolution = $old_bug_obj->resolution;
# These are the only actions where we care about the resolution field.
if ($cgi->param('knob') =~ /^(?:resolve|change_resolution)$/) {
$resolution = $cgi->param('resolution');
}
($status, $resolution) =
$old_bug_obj->get_new_status_and_resolution(scalar $cgi->param('knob'), $resolution);
if ($status ne $old_bug_obj->bug_status) {
$query .= "$comma bug_status = ?";
push(@bug_values, $status);
$comma = ',';
}
if ($resolution ne $old_bug_obj->resolution) {
$query .= "$comma resolution = ?";
push(@bug_values, $resolution);
$comma = ',';
}
if ($cgi->param('knob') eq 'reassignbycomponent') { if ($cgi->param('knob') eq 'reassignbycomponent') {
# We have to check whether the bug is moved to another product # We have to check whether the bug is moved to another product
# and/or component before reassigning. If $component is defined, # and/or component before reassigning. If $component is defined,
...@@ -1402,24 +1299,23 @@ foreach my $id (@idlist) { ...@@ -1402,24 +1299,23 @@ foreach my $id (@idlist) {
FROM components FROM components
WHERE components.id = ?', WHERE components.id = ?',
undef, $new_comp_id); undef, $new_comp_id);
$query .= ", assigned_to = ?"; $query .= "$comma assigned_to = ?";
push(@bug_values, $assignee); push(@bug_values, $assignee);
$comma = ',';
if (Bugzilla->params->{"useqacontact"}) { if (Bugzilla->params->{"useqacontact"}) {
$qacontact = $dbh->selectrow_array('SELECT initialqacontact $qacontact = $dbh->selectrow_array('SELECT initialqacontact
FROM components FROM components
WHERE components.id = ?', WHERE components.id = ?',
undef, $new_comp_id); undef, $new_comp_id);
if ($qacontact) { if ($qacontact) {
$query .= ", qa_contact = ?"; $query .= "$comma qa_contact = ?";
push(@bug_values, $qacontact); push(@bug_values, $qacontact);
} }
else { else {
$query .= ", qa_contact = NULL"; $query .= "$comma qa_contact = NULL";
} }
} }
# And add in the Default CC for the Component. # And add in the Default CC for the Component.
my $comp_obj = $component || new Bugzilla::Component($new_comp_id); my $comp_obj = $component || new Bugzilla::Component($new_comp_id);
my @new_init_cc = @{$comp_obj->initial_cc}; my @new_init_cc = @{$comp_obj->initial_cc};
...@@ -1466,20 +1362,18 @@ foreach my $id (@idlist) { ...@@ -1466,20 +1362,18 @@ foreach my $id (@idlist) {
} }
$oldhash{$col} = $oldvalues[$i]; $oldhash{$col} = $oldvalues[$i];
$formhash{$col} = $cgi->param($col) if defined $cgi->param($col); $formhash{$col} = $cgi->param($col) if defined $cgi->param($col);
# The status and resolution are defined by the workflow.
$formhash{$col} = $status if $col eq 'bug_status';
$formhash{$col} = $resolution if $col eq 'resolution';
$i++; $i++;
} }
# If the user is reassigning bugs, we need to: # If the user is reassigning bugs, we need to:
# - convert $newhash{'assigned_to'} and $newhash{'qa_contact'} # - convert $newhash{'assigned_to'} and $newhash{'qa_contact'}
# email addresses into their corresponding IDs; # email addresses into their corresponding IDs;
# - update $newhash{'bug_status'} to its real state if the bug
# is in the unconfirmed state.
$formhash{'qa_contact'} = $qacontact if Bugzilla->params->{'useqacontact'}; $formhash{'qa_contact'} = $qacontact if Bugzilla->params->{'useqacontact'};
if ($cgi->param('knob') eq 'reassignbycomponent' if ($cgi->param('knob') eq 'reassignbycomponent'
|| $cgi->param('knob') eq 'reassign') { || $cgi->param('knob') eq 'reassign') {
$formhash{'assigned_to'} = $assignee; $formhash{'assigned_to'} = $assignee;
if ($oldhash{'bug_status'} eq 'UNCONFIRMED') {
$formhash{'bug_status'} = $oldhash{'bug_status'};
}
} }
# This hash is required by Bug::check_can_change_field(). # This hash is required by Bug::check_can_change_field().
my $cgi_hash = { my $cgi_hash = {
...@@ -1487,9 +1381,6 @@ foreach my $id (@idlist) { ...@@ -1487,9 +1381,6 @@ foreach my $id (@idlist) {
'knob' => scalar $cgi->param('knob') 'knob' => scalar $cgi->param('knob')
}; };
foreach my $col (@editable_bug_fields) { foreach my $col (@editable_bug_fields) {
# The 'resolution' field is checked by ChangeResolution(),
# i.e. only if we effectively use it.
next if ($col eq 'resolution');
if (exists $formhash{$col} if (exists $formhash{$col}
&& !$old_bug_obj->check_can_change_field($col, $oldhash{$col}, $formhash{$col}, && !$old_bug_obj->check_can_change_field($col, $oldhash{$col}, $formhash{$col},
\$PrivilegesRequired, $cgi_hash)) \$PrivilegesRequired, $cgi_hash))
...@@ -1665,14 +1556,12 @@ foreach my $id (@idlist) { ...@@ -1665,14 +1556,12 @@ foreach my $id (@idlist) {
} }
$query .= " WHERE bug_id = ?"; $query .= " WHERE bug_id = ?";
push(@bug_values, $id); push(@bug_values, $id);
if ($::comma ne "") { if ($comma ne '') {
$dbh->do($query, undef, @bug_values); $dbh->do($query, undef, @bug_values);
} }
# Check for duplicates if the bug is [re]open or its resolution is changed. # Check for duplicates if the bug is [re]open or its resolution is changed.
my $resolution = $dbh->selectrow_array(
q{SELECT resolution FROM bugs WHERE bug_id = ?}, undef, $id);
if ($resolution ne 'DUPLICATE') { if ($resolution ne 'DUPLICATE') {
$dbh->do(q{DELETE FROM duplicates WHERE dupe = ?}, undef, $id); $dbh->do(q{DELETE FROM duplicates WHERE dupe = ?}, undef, $id);
} }
......
...@@ -977,6 +977,11 @@ ...@@ -977,6 +977,11 @@
does not exist. does not exist.
[% END %] [% END %]
[% ELSIF error == "missing_resolution" %]
[% title = "Resolution Required" %]
A valid resolution is required to mark [% terms.bugs %] as
[%+ status FILTER upper FILTER html %].
[% ELSIF error == "move_bugs_disabled" %] [% ELSIF error == "move_bugs_disabled" %]
[% title = BLOCK %][% terms.Bug %] Moving Disabled[% END %] [% title = BLOCK %][% terms.Bug %] Moving Disabled[% END %]
Sorry, [% terms.bug %] moving has been disabled. If you need Sorry, [% terms.bug %] moving has been disabled. If you need
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment