Also, creating a new product from the web UI asks you to create a component too.
r=glob a=LpSolit
$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;
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();
}
###############################
use Bugzilla::Error;
use Bugzilla::Group;
use Bugzilla::Product;
+use Bugzilla::Component;
use Bugzilla::Classification;
use Bugzilla::Token;
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'),
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);
}
}
+###########################################################################
+# 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
###########################################################################
# 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'
</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"
</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"
</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"
[% 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
<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>
<input type="checkbox" name="createseries" value="1" checked="checked">
</td>
</tr>
+
+ <tr>
+ <td colspan="2"> </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">
[% 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 %]
</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>
</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') %]
[% 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 %],
<[% email FILTER html %]>.
[% 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" %]
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 %]
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'} %]