Commit 589632e9 authored by Frédéric Buclin's avatar Frédéric Buclin

Bug 661476: sanitycheck.pl should check if all products have components defined.

Also, creating a new product from the web UI asks you to create a component too. r=glob a=LpSolit
parent 664920c7
...@@ -155,6 +155,11 @@ sub remove_from_db { ...@@ -155,6 +155,11 @@ sub remove_from_db {
$dbh->bz_start_transaction(); $dbh->bz_start_transaction();
# Products must have at least one component.
if (scalar(@{$self->product->components}) == 1) {
ThrowUserError('component_is_last', { comp => $self });
}
if ($self->bug_count) { if ($self->bug_count) {
if (Bugzilla->params->{'allowbugdeletion'}) { if (Bugzilla->params->{'allowbugdeletion'}) {
require Bugzilla::Bug; require Bugzilla::Bug;
......
...@@ -143,12 +143,21 @@ sub remove_from_db { ...@@ -143,12 +143,21 @@ sub remove_from_db {
my $self = shift; my $self = shift;
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
$dbh->bz_start_transaction();
# Products must have at least one version.
if (scalar(@{$self->product->versions}) == 1) {
ThrowUserError('version_is_last', { version => $self });
}
# The version cannot be removed if there are bugs # The version cannot be removed if there are bugs
# associated with it. # associated with it.
if ($self->bug_count) { if ($self->bug_count) {
ThrowUserError("version_has_bugs", { nb => $self->bug_count }); ThrowUserError("version_has_bugs", { nb => $self->bug_count });
} }
$self->SUPER::remove_from_db(); $self->SUPER::remove_from_db();
$dbh->bz_commit_transaction();
} }
############################### ###############################
......
...@@ -37,6 +37,7 @@ use Bugzilla::Util; ...@@ -37,6 +37,7 @@ use Bugzilla::Util;
use Bugzilla::Error; use Bugzilla::Error;
use Bugzilla::Group; use Bugzilla::Group;
use Bugzilla::Product; use Bugzilla::Product;
use Bugzilla::Component;
use Bugzilla::Classification; use Bugzilla::Classification;
use Bugzilla::Token; use Bugzilla::Token;
...@@ -176,7 +177,13 @@ if ($action eq 'new') { ...@@ -176,7 +177,13 @@ if ($action eq 'new') {
check_token_data($token, 'add_product'); check_token_data($token, 'add_product');
my %create_params = ( Bugzilla::User::match_field ({
'initialowner' => { 'type' => 'single' },
'initialqacontact' => { 'type' => 'single' },
'initialcc' => { 'type' => 'multi' },
});
my %product_create_params = (
classification => $classification_name, classification => $classification_name,
name => $product_name, name => $product_name,
description => scalar $cgi->param('description'), description => scalar $cgi->param('description'),
...@@ -186,7 +193,23 @@ if ($action eq 'new') { ...@@ -186,7 +193,23 @@ if ($action eq 'new') {
create_series => scalar $cgi->param('createseries'), create_series => scalar $cgi->param('createseries'),
allows_unconfirmed => scalar $cgi->param('allows_unconfirmed'), allows_unconfirmed => scalar $cgi->param('allows_unconfirmed'),
); );
my $product = Bugzilla::Product->create(\%create_params);
$dbh->bz_start_transaction();
my $product = Bugzilla::Product->create(\%product_create_params);
my @initial_cc = $cgi->param('initialcc');
my %component_create_params = (
product => $product,
name => trim($cgi->param('component') || ''),
description => scalar $cgi->param('comp_desc'),
initialowner => scalar $cgi->param('initialowner'),
initialqacontact => scalar $cgi->param('initialqacontact'),
initial_cc => \@initial_cc,
create_series => scalar $cgi->param('createseries'),
);
Bugzilla::Component->create(\%component_create_params);
$dbh->bz_commit_transaction();
delete_token($token); delete_token($token);
......
...@@ -748,6 +748,26 @@ if (scalar(@invalid_flags)) { ...@@ -748,6 +748,26 @@ if (scalar(@invalid_flags)) {
} }
########################################################################### ###########################################################################
# Check for products with no component
###########################################################################
Status('product_check_start');
my $products_missing_data = $dbh->selectcol_arrayref(
'SELECT DISTINCT products.name
FROM products
LEFT JOIN components
ON components.product_id = products.id
LEFT JOIN versions
ON versions.product_id = products.id
WHERE components.id IS NULL
OR versions.id IS NULL');
if (scalar(@$products_missing_data)) {
Status('product_alert', { name => $_ }, 'alert') foreach @$products_missing_data;
}
###########################################################################
# General bug checks # General bug checks
########################################################################### ###########################################################################
......
...@@ -22,16 +22,20 @@ ...@@ -22,16 +22,20 @@
# comp: object; Bugzilla::Component object. # comp: object; Bugzilla::Component object.
#%] #%]
[%# When called from the "New Product" page, the component description field
# must have a name different from the product description field. %]
[% DEFAULT desc_name = "description" %]
<tr> <tr>
<td valign="top">Component:</td> <th align="right">Component:</th>
<td><input size="64" maxlength="64" name="component" <td><input size="64" maxlength="64" name="component"
value="[%- comp.name FILTER html %]"></td> value="[%- comp.name FILTER html %]"></td>
</tr> </tr>
<tr> <tr>
<td valign="top">Component Description:</td> <th align="right">Component Description:</th>
<td> <td>
[% INCLUDE global/textarea.html.tmpl [% INCLUDE global/textarea.html.tmpl
name = 'description' name = desc_name
minrows = 4 minrows = 4
cols = 64 cols = 64
wrap = 'virtual' wrap = 'virtual'
...@@ -40,7 +44,7 @@ ...@@ -40,7 +44,7 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<td valign="top"><label for="initialowner">Default Assignee:</label></td> <th align="right"><label for="initialowner">Default Assignee:</label></th>
<td> <td>
[% INCLUDE global/userselect.html.tmpl [% INCLUDE global/userselect.html.tmpl
name => "initialowner" name => "initialowner"
...@@ -52,7 +56,7 @@ ...@@ -52,7 +56,7 @@
</tr> </tr>
[% IF Param('useqacontact') %] [% IF Param('useqacontact') %]
<tr> <tr>
<td valign="top"><label for="initialqacontact">Default QA contact:</label></td> <th align="right"><label for="initialqacontact">Default QA contact:</label></th>
<td> <td>
[% INCLUDE global/userselect.html.tmpl [% INCLUDE global/userselect.html.tmpl
name => "initialqacontact" name => "initialqacontact"
...@@ -65,9 +69,7 @@ ...@@ -65,9 +69,7 @@
</tr> </tr>
[% END %] [% END %]
<tr> <tr>
<td valign="top"> <th align="right"><label for="initialcc">Default CC List:</label></th>
<label for="initialcc">Default CC List:</label>
</td>
<td> <td>
[% INCLUDE global/userselect.html.tmpl [% INCLUDE global/userselect.html.tmpl
name => "initialcc" name => "initialcc"
......
...@@ -25,7 +25,8 @@ ...@@ -25,7 +25,8 @@
[% PROCESS global/header.html.tmpl [% PROCESS global/header.html.tmpl
title = title title = title
style_urls = ['skins/standard/admin.css'] style_urls = ['skins/standard/admin.css']
javascript_urls = ['js/util.js'] javascript_urls = ['js/util.js', 'js/field.js']
yui = [ 'autocomplete' ]
%] %]
[% DEFAULT [% DEFAULT
...@@ -42,7 +43,7 @@ ...@@ -42,7 +43,7 @@
<tr> <tr>
<th align="right">Version:</th> <th align="right">Version:</th>
<td><input size="64" maxlength="255" name="version" <td><input size="20" maxlength="64" name="version"
value="[% version FILTER html %]"> value="[% version FILTER html %]">
</td> </td>
</tr> </tr>
...@@ -52,6 +53,18 @@ ...@@ -52,6 +53,18 @@
<input type="checkbox" name="createseries" value="1" checked="checked"> <input type="checkbox" name="createseries" value="1" checked="checked">
</td> </td>
</tr> </tr>
<tr>
<td colspan="2">&nbsp;</td>
</tr>
<tr>
<td colspan="2">
This product must have at least one component.
You will be able to create additional components later:
</td>
</tr>
[% PROCESS "admin/components/edit-common.html.tmpl" desc_name = "comp_desc" %]
</table> </table>
<input type="submit" id="add-product" value="Add"> <input type="submit" id="add-product" value="Add">
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
[% IF Param('useclassification') %] [% IF Param('useclassification') %]
<tr> <tr>
<th align="right"><b>Classification:</b></th> <th align="right">Classification:</th>
<td><b>[% classification.name FILTER html %]</b></td> <td><b>[% classification.name FILTER html %]</b></td>
</tr> </tr>
[% END %] [% END %]
...@@ -43,6 +43,23 @@ ...@@ -43,6 +43,23 @@
</td> </td>
</tr> </tr>
<tr>
<th align="right">Open for [% terms.bug %] entry:</th>
<td><input type="checkbox" name="is_active" value="1"
[% ' checked="checked"' IF product.is_active %]>
</td>
</tr>
<tr>
<th align="right">
<label for="allows_unconfirmed">Enable the
[%+ display_value('bug_status', 'UNCONFIRMED') FILTER html %] status
in this product:</label>
</th>
<td><input type="checkbox" id="allows_unconfirmed" name="allows_unconfirmed"
[% ' checked="checked"' IF product.allows_unconfirmed %]>
</td>
</tr>
[% IF Param('usetargetmilestone') -%] [% IF Param('usetargetmilestone') -%]
<tr> <tr>
<th align="right">Default milestone:</th> <th align="right">Default milestone:</th>
...@@ -63,21 +80,4 @@ ...@@ -63,21 +80,4 @@
</tr> </tr>
[% END %] [% END %]
<tr>
<th align="right">Open for [% terms.bug %] entry:</th>
<td><input type="checkbox" name="is_active" value="1"
[% ' checked="checked"' IF product.is_active %]>
</td>
</tr>
<tr>
<th align="right">
<label for="allows_unconfirmed">Enable the
[%+ display_value('bug_status', 'UNCONFIRMED') FILTER html %] status
in this product:</label>
</th>
<td><input type="checkbox" id="allows_unconfirmed" name="allows_unconfirmed"
[% ' checked="checked"' IF product.allows_unconfirmed %]>
</td>
</tr>
[% Hook.process('rows') %] [% Hook.process('rows') %]
...@@ -229,6 +229,13 @@ ...@@ -229,6 +229,13 @@
[% ELSIF san_tag == "profile_login_start" %] [% ELSIF san_tag == "profile_login_start" %]
Checking profile logins. Checking profile logins.
[% ELSIF san_tag == "product_alert" %]
Product <a href="editproducts.cgi?product=[% name FILTER html%]">
[%- name FILTER html %]</a> has no components or no versions.
[% ELSIF san_tag == "product_check_start" %]
Checking products with no components or versions.
[% ELSIF san_tag == "profile_login_alert" %] [% ELSIF san_tag == "profile_login_alert" %]
Bad profile email address, id=[% id FILTER html %], Bad profile email address, id=[% id FILTER html %],
&lt;[% email FILTER html %]&gt;. &lt;[% email FILTER html %]&gt;.
......
...@@ -796,9 +796,7 @@ ...@@ -796,9 +796,7 @@
[% ELSIF message_tag == "product_created" %] [% ELSIF message_tag == "product_created" %]
[% title = "Product Created" %] [% title = "Product Created" %]
The product <em>[% product.name FILTER html %]</em> has been created. You will need to The product <em>[% product.name FILTER html %]</em> has been created.
<a href="editcomponents.cgi?action=add&product=[% product.name FILTER uri %]">
add at least one component</a> before anyone can enter [% terms.bugs %] against this product.
[% ELSIF message_tag == "product_deleted" %] [% ELSIF message_tag == "product_deleted" %]
[% title = "Product Deleted" %] [% title = "Product Deleted" %]
......
...@@ -373,6 +373,11 @@ ...@@ -373,6 +373,11 @@
You must reassign those [% terms.bugs %] to another component before you You must reassign those [% terms.bugs %] to another component before you
can delete this one. can delete this one.
[% ELSIF error == "component_is_last" %]
[% title = BLOCK %]Last Component in this Product[% END %]
'[% comp.name FILTER html %]' is the last component of the
'[% comp.product.name FILTER html %]' product. You cannot delete it.
[% ELSIF error == "component_name_too_long" %] [% ELSIF error == "component_name_too_long" %]
[% title = "Component Name Is Too Long" %] [% title = "Component Name Is Too Long" %]
The name of a component is limited to [% constants.MAX_COMPONENT_SIZE FILTER html %] The name of a component is limited to [% constants.MAX_COMPONENT_SIZE FILTER html %]
...@@ -1630,6 +1635,11 @@ ...@@ -1630,6 +1635,11 @@
version! You must reassign those [% terms.bugs %] to another version version! You must reassign those [% terms.bugs %] to another version
before you can delete this one. before you can delete this one.
[% ELSIF error == "version_is_last" %]
[% title = BLOCK %]Last Version in this Product[% END %]
'[% version.name FILTER html %]' is the last version of the
'[% version.product.name FILTER html %]' product. You cannot delete it.
[% ELSIF error == "users_deletion_disabled" %] [% ELSIF error == "users_deletion_disabled" %]
[% title = "Deletion not activated" %] [% title = "Deletion not activated" %]
[% admindocslinks = {'useradmin.html' => 'User administration'} %] [% admindocslinks = {'useradmin.html' => 'User administration'} %]
......
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