bug_file_loc => \&_check_bug_file_loc,
bug_severity => \&_check_select_field,
bug_status => \&_check_bug_status,
- bug_type => \&_check_select_field,
+ bug_type => \&_check_bug_type,
cc => \&_check_cc,
comment => \&_check_comment,
component => \&_check_component,
$params->{priority} = Bugzilla->params->{defaultpriority}
unless defined $params->{priority};
- # Bug type can be defined at the component, product or instance level
- unless (defined $params->{bug_type}) {
- my $product
- = (defined $params->{product})
- ? Bugzilla::Product->new({name => $params->{product}, cache => 1})
- : undef;
- my $component
- = ($product && defined $params->{component})
- ? Bugzilla::Component->new({name => $params->{component}, product => $product, cache => 1})
- : undef;
- # The component's default bug type inherits or overrides the default bug
- # type of the product or instance
- $params->{bug_type}
- = ($component)
- ? $component->default_bug_type
- : Bugzilla->params->{default_bug_type};
- }
-
# BMO - per-product hw/os defaults
if (!defined $params->{rep_platform} || !defined $params->{op_sys}) {
if (my $product
return $new_status->name;
}
+sub _check_bug_type {
+ my ($invocant, $type, undef, $params) = @_;
+
+ if (defined $type && trim($type)) {
+ return $invocant->_check_select_field($type, 'bug_type');
+ }
+
+ if (Bugzilla->params->{'require_bug_type'}) {
+ ThrowUserError('bug_type_required');
+ }
+
+ if (blessed $invocant) {
+ return $invocant->component_obj->default_bug_type;
+ }
+
+ my $product
+ = (defined $params->{product})
+ ? Bugzilla::Product->new({name => $params->{product}, cache => 1})
+ : undef;
+ my $component
+ = ($product && defined $params->{component})
+ ? Bugzilla::Component->new({name => $params->{component}, product => $product, cache => 1})
+ : undef;
+
+ return $component
+ ? $component->default_bug_type
+ : Bugzilla->params->{default_bug_type};
+}
+
sub _check_cc {
my ($invocant, $ccs, undef, $params) = @_;
my $component
{name => 'use_see_also', type => 'b', default => 1},
+ {name => 'require_bug_type', type => 'b', default => 1},
+
{
name => 'default_bug_type',
type => 's',
name => 'bug_type',
desc => 'Type',
in_new_bugmail => 1,
- is_mandatory => 1,
type => FIELD_TYPE_SINGLE_SELECT,
buglist => 1
},
=item C<severity> (string) B<Defaulted> - How severe the bug is.
-=item C<type> (string) B<Defaulted> - The basic category of the bug.
+=item C<type> (string) B<Defaulted> - The basic category of the bug. Some
+Bugzilla installations require this to be specified.
=item C<alias> (string) - A brief alias for the bug that can be used
instead of a bug number when accessing this bug. Must be unique in
The flag type is inactive and cannot be used to create new flags.
+=item 135 (Bug Type Required)
+
+You didn't specify a type for the bug.
+
=item 504 (Invalid User)
Either the QA Contact, Assignee, or CC lists have some invalid user
flag_type_not_unique => 133,
flag_type_inactive => 134,
+ # Bug Type errors
+ bug_type_required => 135,
+
# Authentication errors are usually 300-400.
invalid_username_or_password => 300,
account_disabled => 301,
prevents addition of new relationships, but existing ones will continue to
appear.
+require_bug_type
+ If this is on, users are asked to choose a type when they file a new bug.
+
default_bug_type
This is the type that newly entered bugs are set to.
the developer, compared to the developer's other
bugs.
severity string (defaulted) How severe the bug is.
-type string (defaulted) The basic category of the bug.
+type string (defaulted) The basic category of the bug. Some
+ Bugzilla installations require this to be specified.
alias string The alias for the bug that can be used instead of a
bug number when accessing this bug. Must be unique
in all of this Bugzilla.
the type id value to update or add a flag.
* 134 (Inactive Flag Type)
The flag type is inactive and cannot be used to create new flags.
+* 135 (Bug Type Required)
+ You didn't specify a type for the bug.
* 504 (Invalid User)
Either the QA Contact, Assignee, or CC lists have some invalid user
in them. The error message will have more details.
"noresolveonopenblockers" : "0",
"password_complexity" : "no_constraints",
"rememberlogin" : "on",
+ "require_bug_type" : "1",
"requirelogin" : "0",
"urlbase" : "http://bugzilla.example.com/",
"use_regression_fields" : "1",
* noresolveonopenblockers
* password_complexity
* rememberlogin
+* require_bug_type
* requirelogin
* search_allow_no_criteria
* urlbase
field = bug_fields.bug_type
field_type = constants.FIELD_TYPE_SINGLE_SELECT
use_buttons = 1
+ required = Param('require_bug_type')
help = "https://wiki.mozilla.org/BMO/UserGuide/BugFields#bug_type"
%]
<span class="bug-type-label iconic-text" data-type="[% bug.bug_type FILTER html %]">
# edit HTML instead of replacing it. forces edit_only (default: false);
# default: (string) default value (e.g. used as a placeholder in user fields)
# help: (string) optional URL that describes the field (requires a label to be defined and visible)
- # required: (boolean) if the field is required. (At present, only implemented for FIELD_TYPE_FREETEXT)
+ # required: (boolean) if the field is required. (At present, only implemented for FIELD_TYPE_FREETEXT
+ # and FIELD_TYPE_SINGLE_SELECT)
# action: (hash) show a button to the right of the edit field (user fields only currently). keys:
# id: (string) optional button id
# class: (string) optional button class
<input type="hidden" id="[% name FILTER html %]-dirty">
[% IF use_buttons %]
<div role="radiogroup" class="buttons toggle" id="[% name FILTER html %]"
- [%= 'aria-required="true"' IF field.is_mandatory %] [%= aria_labelledby_html FILTER none %]>
+ [%= 'aria-required="true"' IF (field.is_mandatory || required) %] [%= aria_labelledby_html FILTER none %]>
[% IF values.defined %]
[% FOREACH v IN values %]
[% NEXT IF NOT v.is_active AND NOT value.contains(v.name).size %]
[% NEXT IF NOT bug.check_can_change_field(name, bug.${field_name}, v.name) %]
- [% NEXT IF field.is_mandatory && v.name == '--' %]
+ [% NEXT IF (field.is_mandatory || required) && v.name == '--' %]
<div class="item">
<input id="v[% v.id FILTER html %]_[% name FILTER html %]" type="radio" name="[% name FILTER html %]"
value="[% v.name FILTER html %]" [% " checked" IF value.contains(v.name).size %]>
var attach_data = theform.data;
var attach_desc = theform.description;
+ const $bug_type_group = document.querySelector('#bug_type');
+
var current_errors = YAHOO.util.Dom.getElementsByClassName(
'validation_error_text', null, theform);
for (var i = 0; i < current_errors.length; i++) {
_errorFor(component);
focus_me = component;
}
- if (!bug_type.value) {
- _errorFor(document.querySelector('#bug_type'));
+ if ($bug_type_group.matches('[aria-required="true"]') && !bug_type.value) {
+ _errorFor($bug_type_group);
focus_me = bug_type[0];
}
use constant PRIVATE_BUG_USER => 'QA_Selenium_TEST';
use constant CREATE_BUG => {
+ 'type' => 'defect',
'priority' => 'Highest',
'status' => 'CONFIRMED',
'version' => 'unspecified',
use warnings;
use lib qw(lib ../../lib ../../local/lib/perl5);
use Storable qw(dclone);
-use Test::More tests => 293;
+use Test::More tests => 311;
use QA::Util;
use QA::Tests qw(create_bug_fields PRIVATE_BUG_USER);
},
},
+ type => {
+ undefined => {faultstring => 'you must first choose a type', value => undef},
+ invalid => {
+ faultstring => "There is no Type named 'does-not-exist'.",
+ value => 'does-not-exist'
+ },
+ },
+
severity => {
undefined => {faultstring => 'You must select/enter a Severity.', value => ''},
invalid => {
user => PRIVATE_BUG_USER,
args => {
%$bug_fields,
+ type => 'defect',
product => 'QA-Selenium-TEST',
component => 'QA-Selenium-TEST',
target_milestone => 'QAMilestone',
use constant PRODUCT_FIELDS => qw(version target_milestone component);
use constant ALL_FIELDS =>
(GLOBAL_GENERAL_FIELDS, ALL_SELECT_FIELDS, PRODUCT_FIELDS);
-use constant MANDATORY_FIELDS => qw(short_desc product version component bug_type);
+use constant MANDATORY_FIELDS => qw(short_desc product version component);
use constant PUBLIC_PRODUCT => 'Another Product';
use constant PRIVATE_PRODUCT => 'QA-Selenium-TEST';
use DateTime;
use QA::Util;
use QA::Tests qw(bug_tests PRIVATE_BUG_USER);
-use Test::More tests => 1012;
+use Test::More tests => 1036;
my ($config, @clients) = get_rpc_clients();
my $xmlrpc = $clients[0];
use DateTime;
use QA::Util;
use QA::Tests qw(bug_tests PRIVATE_BUG_USER);
-use Test::More tests => 1012;
+use Test::More tests => 1036;
my ($config, @clients) = get_rpc_clients();
my $xmlrpc = $clients[0];
use Test::More;
my ($config, @clients) = get_rpc_clients();
-plan tests => $config->{test_extensions} ? 515 : 506;
+plan tests => $config->{test_extensions} ? 533 : 524;
my ($public_bug, $private_bug) = $clients[0]->bz_create_test_bugs('private');
default_bug_type => "This is the type that newly entered $terms.bugs are set to.",
+ require_bug_type =>
+ "If this is on, users are asked to choose a type when they file a new ${terms.bug}.",
+
collapsed_comment_tags => "A comma separated list of tags which, when applied " _
"to comments, will cause them to be collapsed by default",
}
[% INCLUDE bug/field.html.tmpl
bug = default, field = bug_fields.bug_type, editable = 1, use_buttons = 1,
- value = default.bug_type %]
+ value = default.bug_type, required = Param('require_bug_type') %]
</tr>
[% needs_extra_tr = 1 %]
# rowspan: a "rowspan" value for the label's <th>.
# tag_name: the tag to use to surround the label
# accesskey: access key for the label
+ # required: Whether the field should be entered or selected. This can override
+ # the field definition's `is_mandatory` property.
+ # use_buttons: Whether the field should be displayed as rich radio buttons.
+ # Use this for a single-select field with a few options.
#%]
[% DEFAULT tag_name = "th" %]
<[% tag_name FILTER html %] class="field_label [% ' bz_hidden_field' IF hidden %]
- [%- ' required' IF field.is_mandatory && NOT bug.id; ' for-buttons' IF use_buttons %]"
+ [%- ' required' IF (field.is_mandatory || required) && NOT bug.id; ' for-buttons' IF use_buttons %]"
id="field_label_[% field.name FILTER html %]"
[% IF rowspan %] rowspan="[% rowspan FILTER html %]"[% END %]>
# override_legal_values (optional): The list of legal values, for select fields.
# editable: Whether the field should be displayed as an editable
# <input> or as just the plain text of its value.
+ # required: Whether the field should be entered or selected. This can override
+ # the field definition's `is_mandatory` property.
+ # use_buttons: Whether the field should be displayed as rich radio buttons.
+ # Use this for a single-select field with a few options.
# allow_dont_change: display the --do_not_change-- option for select fields.
# value_span: A colspan for the table cell containing
# the field value.
value="[% value FILTER html %]" size="40"
maxlength="[% constants.MAX_FREETEXT_LENGTH FILTER none %]"
[% IF field.type == constants.FIELD_TYPE_INTEGER %] pattern="\d+[% IF dontchange %]|[% dontchange FILTER html %][% END %]" [% END %]
- [% ' aria-required="true"' IF field.is_mandatory %]>
+ [% ' aria-required="true"' IF (field.is_mandatory || required) %]>
[% CASE [constants.FIELD_TYPE_DATETIME, constants.FIELD_TYPE_DATE] %]
[% size = (field.type == constants.FIELD_TYPE_DATE) ? 10 : 20 %]
<input name="[% field.name FILTER html %]" size="[% size FILTER none %]"
id="[% field.name FILTER html %]"
value="[% value FILTER html %]"
- [% ' aria-required="true"' IF field.is_mandatory %]
+ [% ' aria-required="true"' IF (field.is_mandatory || required) %]
onchange="updateCalendarFromField(this)">
<button type="button" class="calendar_button"
id="button_calendar_[% field.name FILTER html %]"
<span id="[% field.name FILTER html %]_input_area">
<input name="[% field.name FILTER html %]" id="[% field.name FILTER html %]"
value="[% value FILTER html %]" size="7"
- [% ' aria-required="true"' IF field.is_mandatory %]>
+ [% ' aria-required="true"' IF (field.is_mandatory || required) %]>
</span>
<input type="hidden" id="[% field.name FILTER html %]_dirty">
[% IF use_buttons %]
<div role="radiogroup" class="buttons toggle" id="[% field.name FILTER html %]"
- [% ' aria-required="true"' IF field.is_mandatory %]>
+ [% ' aria-required="true"' IF (field.is_mandatory || required) %]>
[% ELSE %]
<select id="[% field.name FILTER html %]"
name="[% field.name FILTER html %]"
[% SET field_size = field.legal_values.size %]
[% END %]
size="[% field_size FILTER html %]" multiple="multiple"
- [% ' aria-required="true"' IF field.is_mandatory %]
+ [% ' aria-required="true"' IF (field.is_mandatory || required) %]
[% END %]
>
[% IF allow_dont_change %]
[% END %]
[% IF use_buttons %]
- [% NEXT IF field.is_mandatory && legal_value.name == '--' %]
+ [% NEXT IF (field.is_mandatory || required) && legal_value.name == '--' %]
<div class="item">
<input id="[% option_id FILTER html %]" type="radio"
name="[% field.name FILTER html %]" value="[% legal_value.name FILTER html %]"
<div id="[% field.name FILTER html %]_input">
[% INCLUDE global/textarea.html.tmpl
id = field.name name = field.name minrows = 4 maxrows = 8
- cols = 60 defaultcontent = value mandatory = field.is_mandatory %]
+ cols = 60 defaultcontent = value mandatory = (field.is_mandatory || required) %]
</div>
<script [% script_nonce FILTER none %]>
hideEditableField('[% field.name FILTER js %]_edit_container',
[% bug_id FILTER uri %]&GoAheadAndLogIn=1">log
in to an account</a> with the appropriate permissions.
+ [% ELSIF error == "bug_type_required" %]
+ [% title = "Bug Type Required" %]
+ To file a new [% terms.bug %], you must first choose a type:
+ [% FOREACH type = bug_fields.bug_type.legal_values.pluck('name') %]
+ [% IF loop.last %] or [% ELSIF !loop.first %], [% END %]
+ <strong>[% type FILTER html %]</strong>
+ [% END %].
+
[% ELSIF error == "bug_url_invalid" %]
[% title = "Invalid $terms.Bug URL" %]
<code>[% url FILTER html %]</code> is not a valid URL to [% terms.abug %].