summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Bugzilla/Component.pm5
-rw-r--r--Bugzilla/Version.pm9
-rwxr-xr-xeditproducts.cgi27
-rwxr-xr-xsanitycheck.cgi20
-rw-r--r--template/en/default/admin/components/edit-common.html.tmpl18
-rw-r--r--template/en/default/admin/products/create.html.tmpl17
-rw-r--r--template/en/default/admin/products/edit-common.html.tmpl36
-rw-r--r--template/en/default/admin/sanitycheck/messages.html.tmpl7
-rw-r--r--template/en/default/global/messages.html.tmpl4
-rw-r--r--template/en/default/global/user-error.html.tmpl10
10 files changed, 120 insertions, 33 deletions
diff --git a/Bugzilla/Component.pm b/Bugzilla/Component.pm
index 215119715..ceca7e794 100644
--- a/Bugzilla/Component.pm
+++ b/Bugzilla/Component.pm
@@ -155,6 +155,11 @@ sub remove_from_db {
$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 (Bugzilla->params->{'allowbugdeletion'}) {
require Bugzilla::Bug;
diff --git a/Bugzilla/Version.pm b/Bugzilla/Version.pm
index 7f53add13..250c474ca 100644
--- a/Bugzilla/Version.pm
+++ b/Bugzilla/Version.pm
@@ -143,12 +143,21 @@ sub remove_from_db {
my $self = shift;
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
# associated with it.
if ($self->bug_count) {
ThrowUserError("version_has_bugs", { nb => $self->bug_count });
}
$self->SUPER::remove_from_db();
+
+ $dbh->bz_commit_transaction();
}
###############################
diff --git a/editproducts.cgi b/editproducts.cgi
index 6d5c5e593..ed6596130 100755
--- a/editproducts.cgi
+++ b/editproducts.cgi
@@ -37,6 +37,7 @@ use Bugzilla::Util;
use Bugzilla::Error;
use Bugzilla::Group;
use Bugzilla::Product;
+use Bugzilla::Component;
use Bugzilla::Classification;
use Bugzilla::Token;
@@ -176,7 +177,13 @@ if ($action eq 'new') {
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,
name => $product_name,
description => scalar $cgi->param('description'),
@@ -186,7 +193,23 @@ if ($action eq 'new') {
create_series => scalar $cgi->param('createseries'),
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);
diff --git a/sanitycheck.cgi b/sanitycheck.cgi
index da308aaeb..7b8177d83 100755
--- a/sanitycheck.cgi
+++ b/sanitycheck.cgi
@@ -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
###########################################################################
diff --git a/template/en/default/admin/components/edit-common.html.tmpl b/template/en/default/admin/components/edit-common.html.tmpl
index 069b56cfd..6f65095af 100644
--- a/template/en/default/admin/components/edit-common.html.tmpl
+++ b/template/en/default/admin/components/edit-common.html.tmpl
@@ -22,16 +22,20 @@
# 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>
- <td valign="top">Component:</td>
+ <th align="right">Component:</th>
<td><input size="64" maxlength="64" name="component"
value="[%- comp.name FILTER html %]"></td>
</tr>
<tr>
- <td valign="top">Component Description:</td>
+ <th align="right">Component Description:</th>
<td>
[% INCLUDE global/textarea.html.tmpl
- name = 'description'
+ name = desc_name
minrows = 4
cols = 64
wrap = 'virtual'
@@ -40,7 +44,7 @@
</td>
</tr>
<tr>
- <td valign="top"><label for="initialowner">Default Assignee:</label></td>
+ <th align="right"><label for="initialowner">Default Assignee:</label></th>
<td>
[% INCLUDE global/userselect.html.tmpl
name => "initialowner"
@@ -52,7 +56,7 @@
</tr>
[% IF Param('useqacontact') %]
<tr>
- <td valign="top"><label for="initialqacontact">Default QA contact:</label></td>
+ <th align="right"><label for="initialqacontact">Default QA contact:</label></th>
<td>
[% INCLUDE global/userselect.html.tmpl
name => "initialqacontact"
@@ -65,9 +69,7 @@
</tr>
[% END %]
<tr>
- <td valign="top">
- <label for="initialcc">Default CC List:</label>
- </td>
+ <th align="right"><label for="initialcc">Default CC List:</label></th>
<td>
[% INCLUDE global/userselect.html.tmpl
name => "initialcc"
diff --git a/template/en/default/admin/products/create.html.tmpl b/template/en/default/admin/products/create.html.tmpl
index 3af81fb23..2b60645ab 100644
--- a/template/en/default/admin/products/create.html.tmpl
+++ b/template/en/default/admin/products/create.html.tmpl
@@ -25,7 +25,8 @@
[% PROCESS global/header.html.tmpl
title = title
style_urls = ['skins/standard/admin.css']
- javascript_urls = ['js/util.js']
+ javascript_urls = ['js/util.js', 'js/field.js']
+ yui = [ 'autocomplete' ]
%]
[% DEFAULT
@@ -42,7 +43,7 @@
<tr>
<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 %]">
</td>
</tr>
@@ -52,6 +53,18 @@
<input type="checkbox" name="createseries" value="1" checked="checked">
</td>
</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>
<input type="submit" id="add-product" value="Add">
diff --git a/template/en/default/admin/products/edit-common.html.tmpl b/template/en/default/admin/products/edit-common.html.tmpl
index 4812707cd..eac33ea9a 100644
--- a/template/en/default/admin/products/edit-common.html.tmpl
+++ b/template/en/default/admin/products/edit-common.html.tmpl
@@ -25,7 +25,7 @@
[% IF Param('useclassification') %]
<tr>
- <th align="right"><b>Classification:</b></th>
+ <th align="right">Classification:</th>
<td><b>[% classification.name FILTER html %]</b></td>
</tr>
[% END %]
@@ -43,6 +43,23 @@
</td>
</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') -%]
<tr>
<th align="right">Default milestone:</th>
@@ -63,21 +80,4 @@
</tr>
[% 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') %]
diff --git a/template/en/default/admin/sanitycheck/messages.html.tmpl b/template/en/default/admin/sanitycheck/messages.html.tmpl
index 88264d820..494a8cdf0 100644
--- a/template/en/default/admin/sanitycheck/messages.html.tmpl
+++ b/template/en/default/admin/sanitycheck/messages.html.tmpl
@@ -229,6 +229,13 @@
[% ELSIF san_tag == "profile_login_start" %]
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" %]
Bad profile email address, id=[% id FILTER html %],
&lt;[% email FILTER html %]&gt;.
diff --git a/template/en/default/global/messages.html.tmpl b/template/en/default/global/messages.html.tmpl
index 01eb32651..a57449d7c 100644
--- a/template/en/default/global/messages.html.tmpl
+++ b/template/en/default/global/messages.html.tmpl
@@ -796,9 +796,7 @@
[% ELSIF message_tag == "product_created" %]
[% title = "Product Created" %]
- The product <em>[% product.name FILTER html %]</em> has been created. You will need to
- <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.
+ The product <em>[% product.name FILTER html %]</em> has been created.
[% ELSIF message_tag == "product_deleted" %]
[% title = "Product Deleted" %]
diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl
index 52c6156b6..7dac2ee55 100644
--- a/template/en/default/global/user-error.html.tmpl
+++ b/template/en/default/global/user-error.html.tmpl
@@ -373,6 +373,11 @@
You must reassign those [% terms.bugs %] to another component before you
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" %]
[% title = "Component Name Is Too Long" %]
The name of a component is limited to [% constants.MAX_COMPONENT_SIZE FILTER html %]
@@ -1630,6 +1635,11 @@
version! You must reassign those [% terms.bugs %] to another version
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" %]
[% title = "Deletion not activated" %]
[% admindocslinks = {'useradmin.html' => 'User administration'} %]