aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Bugzilla/Bug.pm80
-rw-r--r--Bugzilla/Status.pm33
-rw-r--r--template/en/default/bug/knob.html.tmpl123
3 files changed, 91 insertions, 145 deletions
diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm
index 83e95aecd..2c3a11e8c 100644
--- a/Bugzilla/Bug.pm
+++ b/Bugzilla/Bug.pm
@@ -1110,7 +1110,7 @@ sub _check_bug_status {
my $old_status; # Note that this is undef for new bugs.
if (ref $invocant) {
- @valid_statuses = @{$invocant->status->can_change_to};
+ @valid_statuses = @{$invocant->statuses_available};
$product = $invocant->product_obj;
$old_status = $invocant->status;
my $comments = $invocant->{added_comments} || [];
@@ -1118,12 +1118,11 @@ sub _check_bug_status {
}
else {
@valid_statuses = @{Bugzilla::Status->can_change_to()};
- }
-
- if (!$product->votes_to_confirm) {
- # UNCONFIRMED becomes an invalid status if votes_to_confirm is 0,
- # even if you are in editbugs.
- @valid_statuses = grep {$_->name ne 'UNCONFIRMED'} @valid_statuses;
+ if (!$product->votes_to_confirm) {
+ # UNCONFIRMED becomes an invalid status if votes_to_confirm is 0,
+ # even if you are in editbugs.
+ @valid_statuses = grep {$_->name ne 'UNCONFIRMED'} @valid_statuses;
+ }
}
# Check permissions for users filing new bugs.
@@ -2167,6 +2166,8 @@ sub set_status {
my $old_status = $self->status;
$self->set('bug_status', $status);
delete $self->{'status'};
+ delete $self->{'statuses_available'};
+ delete $self->{'choices'};
my $new_status = $self->status;
if ($new_status->is_open) {
@@ -2680,11 +2681,6 @@ sub isopened {
return is_open_state($self->{bug_status}) ? 1 : 0;
}
-sub isunconfirmed {
- my $self = shift;
- return ($self->bug_status eq 'UNCONFIRMED') ? 1 : 0;
-}
-
sub keywords {
my ($self) = @_;
return join(', ', (map { $_->name } @{$self->keyword_objects}));
@@ -2805,6 +2801,31 @@ sub status {
return $self->{'status'};
}
+sub statuses_available {
+ my $self = shift;
+ return [] if $self->{'error'};
+ return $self->{'statuses_available'}
+ if defined $self->{'statuses_available'};
+
+ my @statuses = @{ $self->status->can_change_to };
+
+ # UNCONFIRMED is only a valid status if it is enabled in this product.
+ if (!$self->product_obj->votes_to_confirm) {
+ @statuses = grep { $_->name ne 'UNCONFIRMED' } @statuses;
+ }
+
+ my @available;
+ foreach my $status (@statuses) {
+ # Make sure this is a legal status transition
+ next if !$self->check_can_change_field(
+ 'bug_status', $self->status->name, $status->name);
+ push(@available, $status);
+ }
+
+ $self->{'statuses_available'} = \@available;
+ return $self->{'statuses_available'};
+}
+
sub show_attachment_flags {
my ($self) = @_;
return $self->{'show_attachment_flags'}
@@ -2958,9 +2979,10 @@ sub choices {
}
my %choices = (
- product => \@products,
- component => $self->product_obj->components,
- version => $self->product_obj->versions,
+ bug_status => $self->statuses_available,
+ product => \@products,
+ component => $self->product_obj->components,
+ version => $self->product_obj->versions,
target_milestone => $self->product_obj->milestones,
);
@@ -3451,12 +3473,7 @@ sub check_can_change_field {
}
# *Only* users with (product-specific) "canconfirm" privs can confirm bugs.
- if ($field eq 'canconfirm'
- || ($field eq 'everconfirmed' && $newvalue)
- || ($field eq 'bug_status'
- && $oldvalue eq 'UNCONFIRMED'
- && is_open_state($newvalue)))
- {
+ if ($self->_changes_everconfirmed($field, $oldvalue, $newvalue)) {
$$PrivilegesRequired = 3;
return $user->in_group('canconfirm', $self->{'product_id'});
}
@@ -3532,6 +3549,24 @@ sub check_can_change_field {
return 0;
}
+# A helper for check_can_change_field
+sub _changes_everconfirmed {
+ my ($self, $field, $old, $new) = @_;
+ return 1 if $field eq 'everconfirmed';
+ if ($field eq 'bug_status') {
+ if ($self->everconfirmed) {
+ # Moving a confirmed bug to UNCONFIRMED will change everconfirmed.
+ return 1 if $new eq 'UNCONFIRMED';
+ }
+ else {
+ # Moving an unconfirmed bug to an open state that isn't
+ # UNCONFIRMED will confirm the bug.
+ return 1 if (is_open_state($new) and $new ne 'UNCONFIRMED');
+ }
+ }
+ return 0;
+}
+
#
# Field Validation
#
@@ -3626,8 +3661,7 @@ sub _validate_attribute {
my @valid_attributes = (
# Miscellaneous properties and methods.
qw(error groups product_id component_id
- comments milestoneurl attachments
- isopened isunconfirmed
+ comments milestoneurl attachments isopened
flag_types num_attachment_flag_types
show_attachment_flags any_flags_requesteeble),
diff --git a/Bugzilla/Status.pm b/Bugzilla/Status.pm
index 4720dc129..4d1281e7e 100644
--- a/Bugzilla/Status.pm
+++ b/Bugzilla/Status.pm
@@ -176,28 +176,6 @@ sub can_change_to {
return $self->{'can_change_to'};
}
-sub can_change_from {
- my $self = shift;
- my $dbh = Bugzilla->dbh;
-
- if (!defined $self->{'can_change_from'}) {
- my $old_status_ids = $dbh->selectcol_arrayref('SELECT old_status
- FROM status_workflow
- INNER JOIN bug_status
- ON id = old_status
- WHERE isactive = 1
- AND new_status = ?
- AND old_status IS NOT NULL',
- undef, $self->id);
-
- # Allow the bug status to remain unchanged.
- push(@$old_status_ids, $self->id);
- $self->{'can_change_from'} = Bugzilla::Status->new_from_list($old_status_ids);
- }
-
- return $self->{'can_change_from'};
-}
-
sub comment_required_on_change_from {
my ($self, $old_status) = @_;
my ($cond, $values) = $self->_status_condition($old_status);
@@ -300,17 +278,6 @@ below.
Returns: A list of Bugzilla::Status objects.
-=item C<can_change_from>
-
- Description: Returns the list of active statuses a bug can be changed from
- given the new bug status. If the bug status is available on
- bug creation, this method doesn't return this information.
- You have to call C<can_change_to> instead.
-
- Params: none.
-
- Returns: A list of Bugzilla::Status objects.
-
=item C<comment_required_on_change_from>
=over
diff --git a/template/en/default/bug/knob.html.tmpl b/template/en/default/bug/knob.html.tmpl
index 10d58e51b..ac14e6dc0 100644
--- a/template/en/default/bug/knob.html.tmpl
+++ b/template/en/default/bug/knob.html.tmpl
@@ -23,71 +23,33 @@
[% PROCESS global/variables.none.tmpl %]
<div id="status">
- [% initial_action_shown = 0 %]
- [% show_resolution = 0 %]
- [% bug_status_select_displayed = 0 %]
+ [% PROCESS bug/field.html.tmpl
+ no_tds = 1
+ field = bug_fields.bug_status
+ value = bug.bug_status
+ override_legal_values = bug.choices.bug_status
+ editable = bug.choices.bug_status.size > 1
+ %]
- [% closed_status_array = [] %]
- [%# These actions are based on the current custom workflow. %]
- [% FOREACH bug_status = bug.status.can_change_to %]
- [% NEXT IF bug.isunconfirmed && bug_status.is_open && !bug.user.canconfirm %]
- [% NEXT IF bug.isopened && !bug.isunconfirmed && bug_status.is_open && !bug.user.canedit %]
- [% NEXT IF (!bug_status.is_open || !bug.isopened) && !bug.user.canedit && !bug.user.isreporter %]
- [%# Special hack to only display UNCO or REOP when reopening, but not both;
- # for compatibility with older versions. %]
- [% NEXT IF !bug.isopened && (bug.everconfirmed && bug_status.name == "UNCONFIRMED"
- || !bug.everconfirmed && bug_status.name == "REOPENED") %]
- [% IF NOT bug_status_select_displayed %]
- <select name="bug_status" id="bug_status">
- [% bug_status_select_displayed = 1 %]
- [% END %]
- [% PROCESS initial_action %]
- [% NEXT IF bug_status.name == bug.bug_status %]
- <option value="[% bug_status.name FILTER html %]">
- [% display_value("bug_status", bug_status.name) FILTER html %]
- </option>
- [% IF !bug_status.is_open %]
- [% show_resolution = 1 %]
- [% filtered_status = bug_status.name FILTER js %]
- [% closed_status_array.push( filtered_status ) %]
- [% END %]
+ [% IF bug.resolution
+ OR bug.check_can_change_field('resolution', bug.resolution, 1)
+ %]
+ <noscript><br>resolved&nbsp;as&nbsp;</noscript>
[% END %]
- [%# These actions are special and are independent of the workflow. %]
- [% IF bug.user.canedit || bug.user.isreporter %]
- [% IF NOT bug_status_select_displayed %]
- <select name="bug_status" id="bug_status">
- [% bug_status_select_displayed = 1 %]
- [% END %]
- [% IF bug.isopened %]
- [% IF bug.resolution %]
- [% PROCESS initial_action %]
- [% END %]
- [% ELSIF bug.resolution != "MOVED" || bug.user.canmove %]
- [% PROCESS initial_action %]
- [% show_resolution = 1 %]
- [% END %]
- [% END %]
- [% IF bug_status_select_displayed %]
- </select>
- [% ELSE %]
- [% display_value("bug_status", bug.bug_status) FILTER html %]
- [% IF bug.resolution %]
- [%+ display_value("resolution", bug.resolution) FILTER html %]
- [% IF bug.dup_id %]
- <span id="duplicate_display">of
- [% "${terms.bug} ${bug.dup_id}" FILTER bug_link(bug.dup_id) FILTER none %]</span>
- [% END %]
- [% END %]
- [% END %]
- [% IF bug.user.canedit || bug.user.isreporter %]
- [% IF show_resolution %]
- <noscript><br>resolved&nbsp;as&nbsp;</noscript>
- <span id="resolution_settings">[% PROCESS select_resolution %]</span>
- [% END %]
+ <span id="resolution_settings">
+ [% PROCESS bug/field.html.tmpl
+ no_tds = 1
+ field = bug_fields.resolution
+ value = bug.resolution
+ override_legal_values = bug.choices.resolution
+ editable = bug.check_can_change_field('resolution', bug.resolution, 1)
+ %]
+ </span>
+
+ [% IF bug.check_can_change_field('dup_id', 0, 1) %]
<noscript><br> duplicate</noscript>
-
- <span id="duplicate_settings">of
+ <span id="duplicate_settings">of
<span id="dup_id_container" class="bz_default_hidden">
[% "${terms.bug} ${bug.dup_id}" FILTER bug_link(bug.dup_id) FILTER none %]
(<a href="#" id="dup_id_edit_action">edit</a>)
@@ -101,11 +63,20 @@
<div id="dup_id_discoverable" class="bz_default_hidden">
<a href="#" id="dup_id_discoverable_action">Mark as Duplicate</a>
</div>
+ [% ELSIF bug.dup_id %]
+ <noscript><br> duplicate</noscript>
+ <span id="duplicate_display">of
+ [% "${terms.bug} ${bug.dup_id}" FILTER bug_link(bug.dup_id) FILTER none %]</span>
[% END %]
</div>
+
<script type="text/javascript">
- var close_status_array = new Array("[% closed_status_array.join('", "') FILTER replace(',$', '')
- FILTER none %]");
+ var close_status_array = [
+ [% FOREACH status = bug.choices.bug_status %]
+ [% NEXT IF status.is_open %]
+ '[% status.name FILTER js %]'[% ',' UNLESS loop.last %]
+ [% END %]
+ ];
YAHOO.util.Dom.removeClass('dup_id_discoverable', 'bz_default_hidden');
hideEditableField( "dup_id_container", "dup_id", 'dup_id_edit_action',
'dup_id', '[% bug.dup_id FILTER js %]' )
@@ -127,29 +98,3 @@
[% INCLUDE "bug/field-events.js.tmpl" field = select_fields.bug_status %]
[% INCLUDE "bug/field-events.js.tmpl" field = select_fields.resolution %]
</script>
-
-[%# Common actions %]
-
-[% BLOCK initial_action %]
- [% IF !initial_action_shown %]
- <option selected value="[% bug.bug_status FILTER html %]">
- [% display_value("bug_status", bug.bug_status) FILTER html %]
- </option>
- [% IF !bug.isopened %]
- [% show_resolution = 1 %]
- [% filtered_status = bug.bug_status FILTER js %]
- [% closed_status_array.push(filtered_status) %]
- [% END %]
- [% initial_action_shown = 1 %]
- [% END %]
-[% END %]
-
-[% BLOCK select_resolution %]
- <select name="resolution" id="resolution">
- [% FOREACH r = bug.choices.resolution %]
- <option value="[% r.name FILTER html %]"
- [% ' selected="selected"' IF r.name == bug.resolution %]>
- [% display_value("resolution", r.name) FILTER html %]</option>
- [% END %]
- </select>
-[% END %]