summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Lawrence <dlawrence@mozilla.com>2013-07-25 08:41:32 +0200
committerByron Jones <bjones@mozilla.com>2013-07-25 08:41:32 +0200
commitb84db39863cd55166bff584265d255a0f882e64a (patch)
tree3420f010a903c858feb715acea0ab9a7ed23e3b8
parent40fc1852a1d64e2889f0b532efa38573f41fc0e2 (diff)
downloadbugzilla-b84db39863cd55166bff584265d255a0f882e64a.tar.gz
bugzilla-b84db39863cd55166bff584265d255a0f882e64a.tar.xz
Bug 750742: Create new BMO extension called TrackingFlags to move current tracking flags away from custom fields
-rw-r--r--Bugzilla/Bug.pm19
-rw-r--r--Bugzilla/Constants.pm9
-rw-r--r--Bugzilla/Object.pm3
-rw-r--r--Bugzilla/Search/Quicksearch.pm19
-rw-r--r--extensions/BMO/Extension.pm1
-rw-r--r--extensions/BMO/lib/Reports/ReleaseTracking.pm25
-rw-r--r--extensions/BMO/lib/Util.pm17
-rw-r--r--extensions/BMO/template/en/default/bug/create/create-winqual.html.tmpl93
-rw-r--r--extensions/BMO/template/en/default/email/bugmail.txt.tmpl9
-rw-r--r--extensions/BMO/template/en/default/hook/bug/edit-after_custom_fields.html.tmpl128
-rw-r--r--extensions/BMO/web/js/edit_bug.js32
-rw-r--r--extensions/TrackingFlags/Extension.pm324
-rwxr-xr-xpost_bug.cgi4
-rw-r--r--skins/custom/create_bug.css4
-rw-r--r--template/en/default/bug/create/create.html.tmpl67
-rw-r--r--template/en/default/bug/edit.html.tmpl1
-rw-r--r--template/en/default/bug/field.html.tmpl4
-rw-r--r--template/en/default/global/field-descs.none.tmpl1
18 files changed, 508 insertions, 252 deletions
diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm
index 15272e542..d974d8aa0 100644
--- a/Bugzilla/Bug.pm
+++ b/Bugzilla/Bug.pm
@@ -80,7 +80,8 @@ use constant AUDIT_UPDATES => 0;
# This is a sub because it needs to call other subroutines.
sub DB_COLUMNS {
my $dbh = Bugzilla->dbh;
- my @custom = grep {$_->type != FIELD_TYPE_MULTI_SELECT}
+ my @custom = grep {$_->type != FIELD_TYPE_MULTI_SELECT
+ && $_->type != FIELD_TYPE_EXTENSION}
Bugzilla->active_custom_fields;
my @custom_names = map {$_->name} @custom;
@@ -114,9 +115,9 @@ sub DB_COLUMNS {
$dbh->sql_date_format('creation_ts', '%Y.%m.%d %H:%i') . ' AS creation_ts',
$dbh->sql_date_format('deadline', '%Y-%m-%d') . ' AS deadline',
@custom_names);
-
+
Bugzilla::Hook::process("bug_columns", { columns => \@columns });
-
+
return @columns;
}
@@ -214,7 +215,8 @@ sub VALIDATOR_DEPENDENCIES {
};
sub UPDATE_COLUMNS {
- my @custom = grep {$_->type != FIELD_TYPE_MULTI_SELECT}
+ my @custom = grep {$_->type != FIELD_TYPE_MULTI_SELECT
+ && $_->type != FIELD_TYPE_EXTENSION}
Bugzilla->active_custom_fields;
my @custom_names = map {$_->name} @custom;
my @columns = qw(
@@ -2334,7 +2336,8 @@ sub set_all {
$self->_add_remove($params, 'see_also');
# And set custom fields.
- my @custom_fields = Bugzilla->active_custom_fields;
+ my @custom_fields = grep { $_->type != FIELD_TYPE_EXTENSION }
+ Bugzilla->active_custom_fields;
foreach my $field (@custom_fields) {
my $fname = $field->name;
if (exists $params->{$fname}) {
@@ -3830,6 +3833,9 @@ sub editable_bug_fields {
# Ensure field exists before attempting to remove it.
splice(@fields, $location, 1) if ($location > -1);
}
+
+ Bugzilla::Hook::process('bug_editable_bug_fields', { fields => \@fields });
+
# Sorted because the old @::log_columns variable, which this replaces,
# was sorted.
return sort(@fields);
@@ -4344,6 +4350,7 @@ sub _create_cf_accessors {
my $fields = Bugzilla->fields({ custom => 1 });
foreach my $field (@$fields) {
+ next if $field->type == FIELD_TYPE_EXTENSION;
my $accessor = $class->_accessor_for($field);
my $name = "${class}::" . $field->name;
{
@@ -4353,6 +4360,8 @@ sub _create_cf_accessors {
}
}
+ Bugzilla::Hook::process('bug_create_cf_accessors');
+
Bugzilla->request_cache->{"${class}_cf_accessors_created"} = 1;
}
diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm
index b5d3aab61..1f712f25d 100644
--- a/Bugzilla/Constants.pm
+++ b/Bugzilla/Constants.pm
@@ -129,6 +129,8 @@ use Memoize;
FIELD_TYPE_BUG_ID
FIELD_TYPE_BUG_URLS
FIELD_TYPE_KEYWORDS
+ FIELD_TYPE_EXTENSION
+
FIELD_TYPE_HIGHEST_PLUS_ONE
EMPTY_DATETIME_REGEX
@@ -397,7 +399,8 @@ use constant SENDMAIL_PATH => '/usr/lib:/usr/sbin:/usr/ucblib';
# only storage but also logic. For example, we might add a "user" field type
# whose values are stored in an integer column in the database but for which
# we do more than we would do for a standard integer type (f.e. we might
-# display a user picker).
+# display a user picker). Fields of type FIELD_TYPE_EXTENSION should generally
+# be ignored by the core code and is used primary by extensions.
use constant FIELD_TYPE_UNKNOWN => 0;
use constant FIELD_TYPE_FREETEXT => 1;
@@ -409,9 +412,11 @@ use constant FIELD_TYPE_BUG_ID => 6;
use constant FIELD_TYPE_BUG_URLS => 7;
use constant FIELD_TYPE_KEYWORDS => 8;
use constant FIELD_TYPE_DATE => 9;
+use constant FIELD_TYPE_EXTENSION => 99;
+
# Add new field types above this line, and change the below value in the
# obvious fashion
-use constant FIELD_TYPE_HIGHEST_PLUS_ONE => 10;
+use constant FIELD_TYPE_HIGHEST_PLUS_ONE => 100;
use constant EMPTY_DATETIME_REGEX => qr/^[0\-:\sA-Za-z]+$/;
diff --git a/Bugzilla/Object.pm b/Bugzilla/Object.pm
index 96651d191..34134a69f 100644
--- a/Bugzilla/Object.pm
+++ b/Bugzilla/Object.pm
@@ -61,6 +61,9 @@ sub new {
my $class = ref($invocant) || $invocant;
my $object = $class->_init(@_);
bless($object, $class) if $object;
+
+ Bugzilla::Hook::process('object_end_of_new', { object => $object });
+
return $object;
}
diff --git a/Bugzilla/Search/Quicksearch.pm b/Bugzilla/Search/Quicksearch.pm
index 48eaff8d1..c3c11b728 100644
--- a/Bugzilla/Search/Quicksearch.pm
+++ b/Bugzilla/Search/Quicksearch.pm
@@ -384,15 +384,18 @@ sub _handle_field_names {
# Flag and requestee shortcut
if ($or_operand =~ /^(?:flag:)?([^\?]+\?)([^\?]*)$/) {
- my ($flagtype, $requestee) = ($1, $2);
- addChart('flagtypes.name', 'substring', $flagtype, $negate);
- if ($requestee) {
- # AND
- $chart++;
- $and = $or = 0;
- addChart('requestees.login_name', 'substring', $requestee, $negate);
+ # BMO: Do not treat custom fields as flags if value is ?
+ if ($1 !~ /^cf_/) {
+ my ($flagtype, $requestee) = ($1, $2);
+ addChart('flagtypes.name', 'substring', $flagtype, $negate);
+ if ($requestee) {
+ # AND
+ $chart++;
+ $and = $or = 0;
+ addChart('requestees.login_name', 'substring', $requestee, $negate);
+ }
+ return 1;
}
- return 1;
}
# Generic field1,field2,field3:value1,value2 notation.
diff --git a/extensions/BMO/Extension.pm b/extensions/BMO/Extension.pm
index 4f4e4e82d..2129e675d 100644
--- a/extensions/BMO/Extension.pm
+++ b/extensions/BMO/Extension.pm
@@ -889,6 +889,7 @@ sub mailer_before_send {
my @set_values = ();
foreach my $field (@fields) {
+ next if $field->type == FIELD_TYPE_EXTENSION;
my $field_name = $field->name;
next if cf_flag_disabled($field_name, $bug);
next if !$bug->$field_name || $bug->$field_name eq '---';
diff --git a/extensions/BMO/lib/Reports/ReleaseTracking.pm b/extensions/BMO/lib/Reports/ReleaseTracking.pm
index e307da192..f9eabc340 100644
--- a/extensions/BMO/lib/Reports/ReleaseTracking.pm
+++ b/extensions/BMO/lib/Reports/ReleaseTracking.pm
@@ -9,6 +9,7 @@ package Bugzilla::Extension::BMO::Reports::ReleaseTracking;
use strict;
use warnings;
+use Bugzilla::Constants;
use Bugzilla::Error;
use Bugzilla::Extension::BMO::Util;
use Bugzilla::Field;
@@ -121,7 +122,7 @@ sub report {
my @unlink_products;
foreach my $product (@usable_products) {
my @fields =
- grep { is_active_status_field($_->name) }
+ grep { is_active_status_field($_) }
Bugzilla->active_custom_fields({ product => $product });
my @field_ids = map { $_->id } @fields;
if (!scalar @fields) {
@@ -244,6 +245,11 @@ sub report {
$query .= "INNER JOIN bugs_activity a ON a.bug_id = b.bug_id ";
}
+ if (grep($_ == FIELD_TYPE_EXTENSION, map { $_->{type} } @{ $q->{fields} })) {
+ $query .= "LEFT JOIN tracking_flags_bugs AS tfb ON tfb.bug_id = b.bug_id " .
+ "LEFT JOIN tracking_flags AS tf ON tfb.tracking_flag_id = tf.id ";
+ }
+
$query .= "WHERE ";
if ($q->{start_date}) {
@@ -273,11 +279,15 @@ sub report {
if (scalar @{$q->{fields}}) {
my @fields;
foreach my $field (@{$q->{fields}}) {
- push @fields,
- "(" .
- ($field->{value} eq '+' ? '' : '!') .
- "(b.".$field->{name}." IN ('fixed','verified'))" .
- ") ";
+ my $field_sql = "(" . ($field->{value} eq '+' ? '' : '!') . "(";
+ if ($field->{type} == FIELD_TYPE_EXTENSION) {
+ $field_sql .= "tf.name = " . $dbh->quote($field->{name}) . " AND tfb.value";
+ }
+ else {
+ $field_sql .= "b." . $field->{name};
+ }
+ $field_sql .= " IN ('fixed','verified')))";
+ push(@fields, $field_sql);
}
my $join = uc $q->{join};
push @where, '(' . join(" $join ", @fields) . ')';
@@ -383,7 +393,8 @@ sub _parse_query {
my ($id, $value) = ($1, $2);
my $field_obj = Bugzilla::Field->new($id)
or ThrowUserError('report_invalid_parameter', { name => 'field_id' });
- push @fields, { id => $id, value => $value, name => $field_obj->name };
+ push @fields, { id => $id, value => $value,
+ name => $field_obj->name, type => $field_obj->type };
}
$query->{fields} = \@fields;
diff --git a/extensions/BMO/lib/Util.pm b/extensions/BMO/lib/Util.pm
index ccb25758b..b25215f61 100644
--- a/extensions/BMO/lib/Util.pm
+++ b/extensions/BMO/lib/Util.pm
@@ -9,6 +9,7 @@ package Bugzilla::Extension::BMO::Util;
use strict;
use warnings;
+use Bugzilla::Constants;
use Bugzilla::Error;
use Bugzilla::Extension::BMO::Data qw($cf_disabled_flags);
use Date::Parse;
@@ -74,10 +75,20 @@ sub parse_date {
}
sub is_active_status_field {
- my ($field_name) = @_;
- if ($field_name =~ /^cf_status/) {
- return !grep { $field_name eq $_ } @$cf_disabled_flags
+ my ($field) = @_;
+ if ($field->type != FIELD_TYPE_EXTENSION
+ && $field->name =~ /^cf_status/)
+ {
+ return !grep { $field->name eq $_ } @$cf_disabled_flags
}
+
+ if ($field->type == FIELD_TYPE_EXTENSION
+ && $field->can('flag_type')
+ && $field->flag_type eq 'status')
+ {
+ return 1;
+ }
+
return 0;
}
diff --git a/extensions/BMO/template/en/default/bug/create/create-winqual.html.tmpl b/extensions/BMO/template/en/default/bug/create/create-winqual.html.tmpl
index d14cca810..fd21ed4ed 100644
--- a/extensions/BMO/template/en/default/bug/create/create-winqual.html.tmpl
+++ b/extensions/BMO/template/en/default/bug/create/create-winqual.html.tmpl
@@ -611,23 +611,25 @@ TUI_hide_default('expert_fields');
<tbody>
[%# non-tracking flags custom fields %]
-[% FOREACH field = Bugzilla.active_custom_fields %]
+[% FOREACH field = Bugzilla.active_custom_fields(product=>product,type=>1) %]
+ [% NEXT IF field.type == constants.FIELD_TYPE_EXTENSION %]
[% NEXT UNLESS field.enter_bug %]
- [% NEXT IF cf_hidden_in_product(field.name, product.name, component.name, 1) %]
[%# crash-signature gets custom handling %]
- [% NEXT IF field.name == 'cf_crash_signature' %]
-
+ [% IF field.name == 'cf_crash_signature' %]
+ [% show_crash_signature = 1 %]
+ [% NEXT %]
+ [% END %]
[% SET value = ${field.name}.defined ? ${field.name} : "" %]
<tr [% 'class="expert_fields"' IF !field.is_mandatory %]>
- [% INCLUDE bug/field.html.tmpl
- bug = default, field = field, value = value, editable = 1,
+ [% INCLUDE bug/field.html.tmpl
+ bug = default, field = field, value = value, editable = 1,
value_span = 3 %]
</tr>
[% END %]
</tbody>
[%# crash-signature handling %]
-[% UNLESS cf_hidden_in_product('cf_crash_signature', product.name, component.name, 1) %]
+[% IF show_crash_signature %]
<tbody class="expert_fields">
<tr>
<th id="field_label_cf_crash_signature" class="field_label">
@@ -647,23 +649,27 @@ TUI_hide_default('expert_fields');
</tbody>
[% END %]
-[% display_bug_flags = 0 %]
-[% FOREACH field = Bugzilla.active_custom_fields %]
+[% old_tracking_flags = [] %]
+[% old_project_flags = [] %]
+[% FOREACH field = Bugzilla.active_custom_fields(product=>product,type=>2) %]
+ [% NEXT IF field.type == constants.FIELD_TYPE_EXTENSION %]
[% NEXT UNLESS field.enter_bug %]
- [% NEXT IF cf_hidden_in_product(field.name, product.name, component.name, 2) %]
- [% display_bug_flags = 1 %]
- [% LAST %]
+ [% IF cf_is_project_flag(field.name) %]
+ [% old_project_flags.push(field) %]
+ [% ELSE %]
+ [% old_tracking_flags.push(field) %]
+ [% END %]
[% END %]
[% display_flags = 0 %]
[% any_flags_requesteeble = 0 %]
-[% FOREACH flag_type = product.flag_types(is_active=>1).bug %]
+[% FOREACH flag_type = product.flag_types.bug %]
[% display_flags = 1 %]
[% SET any_flags_requesteeble = 1 IF flag_type.is_requestable && flag_type.is_requesteeble %]
[% LAST IF display_flags && any_flags_requesteeable %]
[% END %]
-[% IF display_bug_flags || display_flags %]
+[% IF old_project_flags.size || old_tracking_flags.size || display_flags %]
<tbody class="expert_fields">
<tr>
<th>Flags:</th>
@@ -680,38 +686,63 @@ TUI_hide_default('expert_fields');
<legend>Set [% terms.bug %] flags</legend>
<table cellpadding="0" cellspacing="0">
- <tr>
- [% IF display_bug_flags %]
- <td>
- <table id="bug_tracking_flags">
+ <tr>
+ [% IF old_tracking_flags.size %]
+ <td [% IF project_flags.size %]rowspan="2"[% END %]>
+ <table class="tracking_flags">
<tr>
<th colspan="2" style="text-align:left">Tracking Flags:</th>
</tr>
+ [% FOREACH field = old_tracking_flags %]
+ [% SET value = ${field.name}.defined ? ${field.name} : "" %]
+ <tr>
+ [% INCLUDE bug/field.html.tmpl
+ bug = default
+ field = field
+ value = value
+ editable = 1
+ value_span = 3
+ %]
+ </tr>
+ [% END %]
+ [% Hook.process('tracking_flags_end') %]
+ </table>
+ </td>
+ [% END %]
+ [% IF old_project_flags.size %]
+ <td>
+ <table class="tracking_flags">
<tr>
- [% FOREACH field = Bugzilla.active_custom_fields %]
- [% NEXT UNLESS field.enter_bug %]
- [% NEXT IF cf_hidden_in_product(field.name, product.name, component.name, 2) %]
-
- [% SET value = ${field.name}.defined ? ${field.name} : "" %]
- <tr>
- [% INCLUDE bug/field.html.tmpl
- bug = default, field = field, value = value, editable = 1,
- value_span = 3 %]
- </tr>
- [% END %]
+ <th colspan="2" style="text-align:left">Project Flags:</th>
</tr>
+ [% FOREACH field = old_project_flags %]
+ [% SET value = ${field.name}.defined ? ${field.name} : "" %]
+ <tr>
+ [% INCLUDE bug/field.html.tmpl
+ bug = default
+ field = field
+ value = value
+ editable = 1
+ value_span = 3
+ %]
+ </tr>
+ [% END %]
+ [% Hook.process('project_flags_end') %]
</table>
</td>
+ </tr>
+ <tr>
[% END %]
[% IF display_flags %]
<td>
- [% PROCESS "flag/list.html.tmpl" flag_types = product.flag_types(is_active=>1).bug
+ [% PROCESS "flag/list.html.tmpl" flag_types = product.flag_types.bug
any_flags_requesteeble = any_flags_requesteeble
flag_table_id = "bug_flags"
%]
</td>
[% END %]
- </tr>
+ </tr>
+ [% Hook.process('bug_flags_end') %]
</table>
</fieldset>
</div>
diff --git a/extensions/BMO/template/en/default/email/bugmail.txt.tmpl b/extensions/BMO/template/en/default/email/bugmail.txt.tmpl
index 76fa492ee..771157dd1 100644
--- a/extensions/BMO/template/en/default/email/bugmail.txt.tmpl
+++ b/extensions/BMO/template/en/default/email/bugmail.txt.tmpl
@@ -40,15 +40,16 @@ Configure [% terms.bug %]mail: [% urlbase %]userprefs.cgi?tab=email
Product/Component: [%+ bug.product +%] :: [%+ bug.component %]
[% USE Bugzilla %]
-[% tracking_flags = [] %]
+[% show_tracking_flags = [] %]
[% FOREACH field = Bugzilla.active_custom_fields(product => bug.product_obj, component => bug.component_obj, type => 2) %]
[% NEXT IF cf_flag_disabled(field.name, bug) %]
[% NEXT IF bug.${field.name} == "---" %]
- [% tracking_flags.push(field) %]
+ [% show_tracking_flags.push(field) %]
[% END %]
-[% IF tracking_flags.size %]
+
+[% IF show_tracking_flags.size %]
------- Tracking Flags: -------
-[% FOREACH field = tracking_flags %]
+[% FOREACH field = show_tracking_flags %]
[%+ field.description %]:[% bug.${field.name} %]
[% END %]
[% END %]
diff --git a/extensions/BMO/template/en/default/hook/bug/edit-after_custom_fields.html.tmpl b/extensions/BMO/template/en/default/hook/bug/edit-after_custom_fields.html.tmpl
deleted file mode 100644
index f72267246..000000000
--- a/extensions/BMO/template/en/default/hook/bug/edit-after_custom_fields.html.tmpl
+++ /dev/null
@@ -1,128 +0,0 @@
-[%# ***** BEGIN LICENSE BLOCK *****
- # Version: MPL 1.1
- #
- # The contents of this file are subject to the Mozilla Public License Version
- # 1.1 (the "License"); you may not use this file except in compliance with
- # the License. You may obtain a copy of the License at
- # http://www.mozilla.org/MPL/
- #
- # Software distributed under the License is distributed on an "AS IS" basis,
- # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- # for the specific language governing rights and limitations under the
- # License.
- #
- # The Original Code is the BMO Bugzilla Extension;
- #
- # The Initial Developer of the Original Code is the Mozilla Foundation.
- # Portions created by the Initial Developer are Copyright (C) 2011 the
- # Initial Developer. All Rights Reserved.
- #
- # Contributor(s):
- # Byron Jones <glob@mozilla.com>
- #
- # ***** END LICENSE BLOCK *****
- #%]
-
-[% tracking_flags = [] %]
-[% project_flags = [] %]
-[% FOREACH field = Bugzilla.active_custom_fields(product=>bug.product_obj,component=>bug.component_obj,type=>2) %]
- [% NEXT IF NOT user.id AND bug.${field.name} == "---" %]
- [% NEXT IF cf_flag_disabled(field.name, bug) %]
- [% IF cf_is_project_flag(field.name) %]
- [% project_flags.push(field) %]
- [% ELSE %]
- [% tracking_flags.push(field) %]
- [% END %]
-[% END %]
-
-[% IF project_flags.size %]
- <tr>
- <th class="field_label">
- <label>Project Flags:</label>
- </td>
- <td>
- [% IF bug.check_can_change_field('flagtypes.name', 0, 1) %]
- <table id="project-flags">
- [% FOREACH field = project_flags %]
- [% NEXT IF NOT user.id AND field.value == "---" %]
- <tr id="row_[% field.name FILTER js %]">
- <td>&nbsp;</td>
- <td>
- <label for="[% field.name FILTER html %]">
- [% field_descs.${field.name} FILTER html %]:
- </label>
- </td>
- <td>
- [% PROCESS bug/field.html.tmpl value = bug.${field.name}
- editable = user.id
- no_tds = 1 %]
- [% IF user.id %]
- <span id="ro_[% field.name FILTER html %]" class="bz_hidden">
- [% bug.${field.name} FILTER html %]
- </span>
- [% END %]
- </td>
- </tr>
- [% END %]
- </table>
- [% ELSE %]
- [% FOREACH field = project_flags %]
- [% NEXT IF bug.${field.name} == "---" %]
- [% field_descs.${field.name} FILTER html %]: [% bug.${field.name} FILTER html %]<br>
- [% END %]
- [% END %]
- </td>
- </tr>
-[% END %]
-
-[% IF tracking_flags.size %]
- <tr>
- <th class="field_label">
- <label>Tracking Flags:</label>
- </td>
- <td>
- [% IF bug.check_can_change_field('flagtypes.name', 0, 1) %]
- [% IF user.id %]
- <span id="edit_tracking_fields_action">
- (<a onclick="bmo_show_tracking_flags()" href="javascript:void(0)">edit</a>)
- </span>
- [% END %]
- <table id="custom-flags">
- [% FOREACH field = tracking_flags %]
- [% NEXT IF NOT user.id AND field.value == "---" %]
- <tr id="row_[% field.name FILTER js %]">
- <td>&nbsp;</td>
- <td>
- <label for="[% field.name FILTER html %]">
- [% field_descs.${field.name} FILTER html %]:
- </label>
- </td>
- <td>
- [% PROCESS bug/field.html.tmpl value = bug.${field.name}
- editable = user.id
- no_tds = 1 %]
- [% IF user.id %]
- <span id="ro_[% field.name FILTER html %]" class="bz_hidden">
- [% bug.${field.name} FILTER html %]
- </span>
- [% END %]
- </td>
- </tr>
- [% END %]
- </table>
- [% ELSE %]
- [% FOREACH field = tracking_flags %]
- [% NEXT IF bug.${field.name} == "---" %]
- [% field_descs.${field.name} FILTER html %]: [% bug.${field.name} FILTER html %]<br>
- [% END %]
- [% END %]
- </td>
- </tr>
- <script type="text/javascript">
- var bmo_custom_flags = new Array([% tracking_flags.size FILTER none %]);
- [% FOREACH field = tracking_flags %]
- bmo_custom_flags['[% field.name FILTER js %]'] = '[% bug.${field.name} FILTER js %]';
- [% END %]
- bmo_hide_tracking_flags();
- </script>
-[% END %]
diff --git a/extensions/BMO/web/js/edit_bug.js b/extensions/BMO/web/js/edit_bug.js
index e630eb995..16bef9890 100644
--- a/extensions/BMO/web/js/edit_bug.js
+++ b/extensions/BMO/web/js/edit_bug.js
@@ -23,38 +23,6 @@
* ***** END LICENSE BLOCK *****
*/
-// --- custom flags
-var Dom = YAHOO.util.Dom;
-
-function bmo_hide_tracking_flags() {
- for (var field in bmo_custom_flags) {
- var el = Dom.get(field);
- var value = el ? el.value : bmo_custom_flags[field];
- if (el && (value != bmo_custom_flags[field])) {
- bmo_show_tracking_flags();
- return;
- }
- if (value == '---') {
- Dom.addClass('row_' + field, 'bz_hidden');
- } else {
- Dom.addClass(field, 'bz_hidden');
- Dom.removeClass('ro_' + field, 'bz_hidden');
- }
- }
-}
-
-function bmo_show_tracking_flags() {
- Dom.addClass('edit_tracking_fields_action', 'bz_hidden');
- for (var field in bmo_custom_flags) {
- if (Dom.get(field).value == '---') {
- Dom.removeClass('row_' + field, 'bz_hidden');
- } else {
- Dom.removeClass(field, 'bz_hidden');
- Dom.addClass('ro_' + field, 'bz_hidden');
- }
- }
-}
-
function init_clone_bug_menu(el, bug_id, product, component) {
var diff_url = 'enter_bug.cgi?cloned_bug_id=' + bug_id;
var cur_url = diff_url +
diff --git a/extensions/TrackingFlags/Extension.pm b/extensions/TrackingFlags/Extension.pm
index a7cfe97c9..7aa3f5af3 100644
--- a/extensions/TrackingFlags/Extension.pm
+++ b/extensions/TrackingFlags/Extension.pm
@@ -11,8 +11,80 @@ use strict;
use base qw(Bugzilla::Extension);
+use Bugzilla::Extension::TrackingFlags::Constants;
+use Bugzilla::Extension::TrackingFlags::Flag;
+use Bugzilla::Extension::TrackingFlags::Flag::Bug;
+use Bugzilla::Extension::TrackingFlags::Admin;
+
+use Bugzilla::Bug;
+use Bugzilla::Constants;
+use Bugzilla::Field;
+use Bugzilla::Product;
+use Bugzilla::Component;
+use Bugzilla::Error;
+use Bugzilla::Extension::BMO::Data;
+
our $VERSION = '1';
+sub page_before_template {
+ my ($self, $args) = @_;
+ my $page = $args->{'page_id'};
+ my $vars = $args->{'vars'};
+
+ if ($page eq 'tracking_flags_admin_list.html') {
+ Bugzilla->user->in_group('admin')
+ || ThrowUserError('auth_failure',
+ { group => 'admin',
+ action => 'access',
+ object => 'administrative_pages' });
+ admin_list($vars);
+
+ } elsif ($page eq 'tracking_flags_admin_edit.html') {
+ Bugzilla->user->in_group('admin')
+ || ThrowUserError('auth_failure',
+ { group => 'admin',
+ action => 'access',
+ object => 'administrative_pages' });
+ admin_edit($vars);
+ }
+}
+
+sub template_before_process {
+ my ($self, $args) = @_;
+ my $file = $args->{'file'};
+ my $vars = $args->{'vars'};
+
+ if ($file eq 'bug/create/create.html.tmpl'
+ || $file eq 'bug/create/create-winqual.html.tmpl')
+ {
+ $vars->{'tracking_flags'} = Bugzilla::Extension::TrackingFlags::Flag->match({
+ product => $vars->{'product'}->name,
+ is_active => 1,
+ });
+
+ $vars->{'tracking_flag_types'} = FLAG_TYPES;
+ }
+ elsif ($file eq 'bug/edit.html.tmpl'|| $file eq 'bug/show.xml.tmpl') {
+ # note: bug/edit.html.tmpl doesn't support multiple bugs
+ my $bug = exists $vars->{'bugs'} ? $vars->{'bugs'}[0] : $vars->{'bug'};
+
+ $vars->{'tracking_flags'} = Bugzilla::Extension::TrackingFlags::Flag->match({
+ product => $bug->product,
+ component => $bug->component,
+ bug_id => $bug->id,
+ is_active => 1,
+ });
+
+ $vars->{'tracking_flag_types'} = FLAG_TYPES;
+ }
+ elsif ($file eq 'list/edit-multiple.html.tmpl' && $vars->{'one_product'}) {
+ $vars->{'tracking_flags'} = Bugzilla::Extension::TrackingFlags::Flag->match({
+ product => $vars->{'one_product'}->name,
+ is_active => 1
+ });
+ }
+}
+
sub db_schema_abstract_schema {
my ($self, $args) = @_;
$args->{'schema'}->{'tracking_flags'} = {
@@ -188,4 +260,256 @@ sub db_schema_abstract_schema {
};
}
+sub active_custom_fields {
+ my ($self, $args) = @_;
+ my $fields = $args->{'fields'};
+ my $params = $args->{'params'};
+ my $product = $params->{'product'};
+ my $component = $params->{'component'};
+
+ # Create a hash of current fields based on field names
+ my %field_hash = map { $_->name => $_ } @$$fields;
+
+ my @tracking_flags;
+ if ($product) {
+ my $params = { product_id => $product->id };
+ $params->{'component_id'} = $component->id if $component;
+ @tracking_flags = @{ Bugzilla::Extension::TrackingFlags::Flag->match($params) };
+ }
+ else {
+ @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all;
+ }
+
+ # Add tracking flags to fields hash replacing if already exists for our
+ # flag object instead of the usual Field.pm object
+ foreach my $flag (@tracking_flags) {
+ $field_hash{$flag->name} = $flag;
+ }
+
+ @$$fields = values %field_hash;
+}
+
+sub buglist_columns {
+ my ($self, $args) = @_;
+ my $columns = $args->{columns};
+ my $dbh = Bugzilla->dbh;
+ my @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all;
+ foreach my $flag (@tracking_flags) {
+ $columns->{$flag->name} = {
+ name => "COALESCE(map_" . $flag->name . ".value, '---')",
+ title => $flag->description
+ };
+ }
+}
+
+sub buglist_column_joins {
+ my ($self, $args) = @_;
+ my $column_joins = $args->{'column_joins'};
+ my @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all;
+ foreach my $flag (@tracking_flags) {
+ $column_joins->{$flag->name} = {
+ as => 'map_' . $flag->name,
+ table => 'tracking_flags_bugs',
+ extra => [ 'map_' . $flag->name . '.tracking_flag_id = ' . $flag->flag_id ]
+ };
+ }
+}
+
+sub bug_create_cf_accessors {
+ my ($self, $args) = @_;
+ # Create the custom accessors for the flag values
+ my @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all;
+ foreach my $flag (@tracking_flags) {
+ my $flag_name = $flag->name;
+ my $accessor = sub {
+ my $self = shift;
+ return $self->{$flag_name} if defined $self->{$flag_name};
+ if (!exists $self->{'_tf_bug_values_preloaded'}) {
+ # preload all values currently set for this bug
+ my $bug_values
+ = Bugzilla::Extension::TrackingFlags::Flag::Bug->match({ bug_id => $self->id });
+ foreach my $value (@$bug_values) {
+ $self->{$value->tracking_flag->name} = $value->value;
+ }
+ $self->{'_tf_bug_values_preloaded'} = 1;
+ }
+ return $self->{$flag_name} ||= '---';
+ };
+ my $name = "Bugzilla::Bug::$flag_name";
+ if (!Bugzilla::Bug->can($flag_name)) {
+ no strict 'refs';
+ *{$name} = $accessor;
+ }
+ }
+}
+
+sub bug_editable_bug_fields {
+ my ($self, $args) = @_;
+ my $fields = $args->{'fields'};
+ my @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all;
+ foreach my $flag (@tracking_flags) {
+ push(@$fields, $flag->name);
+ }
+}
+
+sub search_operator_field_override {
+ my ($self, $args) = @_;
+ my $operators = $args->{'operators'};
+ my @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all;
+ foreach my $flag (@tracking_flags) {
+ $operators->{$flag->name} = {
+ _non_changed => sub {
+ _tracking_flags_search_nonchanged($flag->flag_id, @_)
+ }
+ };
+ }
+}
+
+sub _tracking_flags_search_nonchanged {
+ my ($flag_id, $search, $args) = @_;
+ my ($bugs_table, $chart_id, $joins, $value, $operator) =
+ @$args{qw(bugs_table chart_id joins value operator)};
+ my $dbh = Bugzilla->dbh;
+
+ return if ($operator =~ m/^changed/);
+
+ my $bugs_alias = "tracking_flags_bugs_$chart_id";
+ my $flags_alias = "tracking_flags_$chart_id";
+
+ my $bugs_join = {
+ table => 'tracking_flags_bugs',
+ as => $bugs_alias,
+ from => $bugs_table . ".bug_id",
+ to => "bug_id",
+ extra => [$bugs_alias . ".tracking_flag_id = $flag_id"]
+ };
+
+ push(@$joins, $bugs_join);
+
+ $args->{'full_field'} = "$bugs_alias.value";
+}
+
+sub bug_end_of_create {
+ my ($self, $args) = @_;
+ my $bug = $args->{'bug'};
+ my $timestamp = $args->{'timestamp'};
+ my $params = Bugzilla->input_params;
+ my $user = Bugzilla->user;
+
+ my $tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->match({
+ product => $bug->product,
+ component => $bug->component,
+ is_active => 1,
+ });
+
+ foreach my $flag (@$tracking_flags) {
+ next if !$params->{$flag->name};
+ foreach my $value (@{$flag->values}) {
+ next if $value->value ne $params->{$flag->name};
+ next if $value->value eq '---'; # do not insert if value is '---', same as empty
+ if (!$flag->can_set_value($value->value)) {
+ ThrowUserError('tracking_flags_change_denied',
+ { flag => $flag, value => $value });
+ }
+ Bugzilla::Extension::TrackingFlags::Flag::Bug->create({
+ tracking_flag_id => $flag->flag_id,
+ bug_id => $bug->id,
+ value => $value->value,
+ });
+ # Add the name/value pair to the bug object
+ $bug->{$flag->name} = $value->value;
+ }
+ }
+}
+
+sub bug_end_of_update {
+ my ($self, $args) = @_;
+ my $bug = $args->{'bug'};
+ my $timestamp = $args->{'timestamp'};
+ my $changes = $args->{'changes'};
+ my $params = Bugzilla->input_params;
+ my $user = Bugzilla->user;
+
+ # Do not filter by product/component as we may be changing those
+ my $tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->match({
+ bug_id => $bug->id,
+ is_active => 1,
+ });
+
+ my (@flag_changes);
+ foreach my $flag (@$tracking_flags) {
+ my $new_value = $params->{$flag->name} || '---';
+ my $old_value = $flag->bug_flag->value;
+
+ next if $new_value eq $old_value;
+
+ if ($new_value ne $old_value) {
+ # Do not allow if the user cannot set the old value or the new value
+ if (!$flag->can_set_value($new_value)) {
+ ThrowUserError('tracking_flags_change_denied',
+ { flag => $flag, value => $new_value });
+ }
+ push(@flag_changes, { flag => $flag,
+ added => $new_value,
+ removed => $old_value });
+ }
+ }
+
+ foreach my $change (@flag_changes) {
+ my $flag = $change->{'flag'};
+ my $added = $change->{'added'};
+ my $removed = $change->{'removed'};
+
+ if ($added eq '---') {
+ $flag->bug_flag->remove_from_db();
+ }
+ elsif ($removed eq '---') {
+ Bugzilla::Extension::TrackingFlags::Flag::Bug->create({
+ tracking_flag_id => $flag->flag_id,
+ bug_id => $bug->id,
+ value => $added,
+ });
+ }
+ else {
+ $flag->bug_flag->set_value($added);
+ $flag->bug_flag->update($timestamp);
+ }
+
+ $changes->{$flag->name} = [ $removed, $added ];
+ LogActivityEntry($bug->id, $flag->name, $removed, $added, $user->id, $timestamp);
+
+ # Update the name/value pair in the bug object
+ $bug->{$flag->name} = $added;
+ }
+}
+
+sub mailer_before_send {
+ my ($self, $args) = @_;
+ my $email = $args->{email};
+
+ # Add X-Bugzilla-Tracking header or add to it
+ # if already exists
+ if ($email->header('X-Bugzilla-ID')) {
+ my $bug_id = $email->header('X-Bugzilla-ID');
+
+ my $tracking_flags
+ = Bugzilla::Extension::TrackingFlags::Flag->match({ bug_id => $bug_id });
+
+ my @set_values = ();
+ foreach my $flag (@$tracking_flags) {
+ next if $flag->bug_flag->value eq '---';
+ push(@set_values, $flag->description . ":" . $flag->bug_flag->value);
+ }
+
+ if (@set_values) {
+ my $set_values_string = join(' ', @set_values);
+ if ($email->header('X-Bugzilla-Tracking')) {
+ $set_values_string = $email->header('X-Bugzilla-Tracking') .
+ " " . $set_values_string;
+ }
+ $email->header_set('X-Bugzilla-Tracking' => $set_values_string);
+ }
+ }
+}
+
__PACKAGE__->NAME;
diff --git a/post_bug.cgi b/post_bug.cgi
index 839751b58..a201393c1 100755
--- a/post_bug.cgi
+++ b/post_bug.cgi
@@ -98,7 +98,9 @@ $template->process($format->{'template'}, $vars, \$comment)
|| ThrowTemplateError($template->error());
# Include custom fields editable on bug creation.
-my @custom_bug_fields = grep {$_->type != FIELD_TYPE_MULTI_SELECT && $_->enter_bug}
+my @custom_bug_fields = grep {$_->type != FIELD_TYPE_MULTI_SELECT
+ && $_->type != FIELD_TYPE_EXTENSION
+ && $_->enter_bug}
Bugzilla->active_custom_fields;
# Undefined custom fields are ignored to ensure they will get their default
diff --git a/skins/custom/create_bug.css b/skins/custom/create_bug.css
index b1164aa75..b3088048d 100644
--- a/skins/custom/create_bug.css
+++ b/skins/custom/create_bug.css
@@ -1,7 +1,7 @@
-#bug_project_flags .field_label,
-#bug_tracking_flags .field_label {
+.tracking_flags .field_label a {
font-weight: normal !important;
+ color: #000;
}
#guided {
diff --git a/template/en/default/bug/create/create.html.tmpl b/template/en/default/bug/create/create.html.tmpl
index eeb4539d2..3eed01124 100644
--- a/template/en/default/bug/create/create.html.tmpl
+++ b/template/en/default/bug/create/create.html.tmpl
@@ -635,6 +635,7 @@ TUI_hide_default('attachment_text_field');
<tbody>
[%# non-tracking flags custom fields %]
[% FOREACH field = Bugzilla.active_custom_fields(product=>product,type=>1) %]
+ [% NEXT IF field.type == constants.FIELD_TYPE_EXTENSION %]
[% NEXT UNLESS field.enter_bug %]
[%# crash-signature gets custom handling %]
[% IF field.name == 'cf_crash_signature' %]
@@ -671,14 +672,15 @@ TUI_hide_default('attachment_text_field');
</tbody>
[% END %]
-[% tracking_flags = [] %]
-[% project_flags = [] %]
+[% old_tracking_flags = [] %]
+[% old_project_flags = [] %]
[% FOREACH field = Bugzilla.active_custom_fields(product=>product,type=>2) %]
+ [% NEXT IF field.type == constants.FIELD_TYPE_EXTENSION %]
[% NEXT UNLESS field.enter_bug %]
[% IF cf_is_project_flag(field.name) %]
- [% project_flags.push(field) %]
+ [% old_project_flags.push(field) %]
[% ELSE %]
- [% tracking_flags.push(field) %]
+ [% old_tracking_flags.push(field) %]
[% END %]
[% END %]
@@ -690,7 +692,7 @@ TUI_hide_default('attachment_text_field');
[% LAST IF display_flags && any_flags_requesteeable %]
[% END %]
-[% IF project_flags.size || tracking_flags.size || display_flags %]
+[% IF old_project_flags.size || old_tracking_flags.size || display_flags %]
<tbody class="expert_fields">
<tr>
<th>Flags:</th>
@@ -708,41 +710,47 @@ TUI_hide_default('attachment_text_field');
<table cellpadding="0" cellspacing="0">
<tr>
- [% IF tracking_flags.size %]
+ [% IF old_tracking_flags.size %]
<td [% IF project_flags.size %]rowspan="2"[% END %]>
- <table id="bug_tracking_flags">
+ <table class="tracking_flags">
<tr>
<th colspan="2" style="text-align:left">Tracking Flags:</th>
</tr>
- <tr>
- [% FOREACH field = tracking_flags %]
- [% SET value = ${field.name}.defined ? ${field.name} : "" %]
- <tr>
- [% INCLUDE bug/field.html.tmpl
- bug = default, field = field, value = value, editable = 1,
- value_span = 3 %]
- </tr>
- [% END %]
- </tr>
+ [% FOREACH field = old_tracking_flags %]
+ [% SET value = ${field.name}.defined ? ${field.name} : "" %]
+ <tr>
+ [% INCLUDE bug/field.html.tmpl
+ bug = default
+ field = field
+ value = value
+ editable = 1
+ value_span = 3
+ %]
+ </tr>
+ [% END %]
+ [% Hook.process('tracking_flags_end') %]
</table>
</td>
[% END %]
- [% IF project_flags.size %]
+ [% IF old_project_flags.size %]
<td>
- <table id="bug_project_flags">
+ <table class="tracking_flags">
<tr>
<th colspan="2" style="text-align:left">Project Flags:</th>
</tr>
- <tr>
- [% FOREACH field = project_flags %]
- [% SET value = ${field.name}.defined ? ${field.name} : "" %]
- <tr>
- [% INCLUDE bug/field.html.tmpl
- bug = default, field = field, value = value, editable = 1,
- value_span = 3 %]
- </tr>
- [% END %]
- </tr>
+ [% FOREACH field = old_project_flags %]
+ [% SET value = ${field.name}.defined ? ${field.name} : "" %]
+ <tr>
+ [% INCLUDE bug/field.html.tmpl
+ bug = default
+ field = field
+ value = value
+ editable = 1
+ value_span = 3
+ %]
+ </tr>
+ [% END %]
+ [% Hook.process('project_flags_end') %]
</table>
</td>
</tr>
@@ -757,6 +765,7 @@ TUI_hide_default('attachment_text_field');
</td>
[% END %]
</tr>
+ [% Hook.process('bug_flags_end') %]
</table>
</fieldset>
</div>
diff --git a/template/en/default/bug/edit.html.tmpl b/template/en/default/bug/edit.html.tmpl
index e641cee93..8c2d09872 100644
--- a/template/en/default/bug/edit.html.tmpl
+++ b/template/en/default/bug/edit.html.tmpl
@@ -962,6 +962,7 @@
[%# *** Custom Fields *** %]
[% USE Bugzilla %]
[% FOREACH field = Bugzilla.active_custom_fields(product=>bug.product_obj,component=>bug.component_obj,type=>1) %]
+ [% NEXT IF field.type == constants.FIELD_TYPE_EXTENSION %]
[% NEXT IF NOT user.id AND field.value == "---" %]
[% Hook.process('custom_field', 'bug/edit.html.tmpl') %]
[% NEXT IF field.hidden %]
diff --git a/template/en/default/bug/field.html.tmpl b/template/en/default/bug/field.html.tmpl
index 74f632c54..73131225d 100644
--- a/template/en/default/bug/field.html.tmpl
+++ b/template/en/default/bug/field.html.tmpl
@@ -246,6 +246,8 @@
YAHOO.bugzilla.keywordAutocomplete.init('[% field.name FILTER js %]',
'keyword_autocomplete');
</script>
+ [% CASE constants.FIELD_TYPE_EXTENSION %]
+ [% Hook.process('editable') %]
[% END %]
[% ELSE %]
[% SWITCH field.type %]
@@ -269,6 +271,8 @@
</li>
[% END %]
[% '</ul>' IF value.size %]
+ [% CASE constants.FIELD_TYPE_EXTENSION %]
+ [% Hook.process('non_editable') %]
[% CASE %]
[% value.join(', ') FILTER html %]
[% END %]
diff --git a/template/en/default/global/field-descs.none.tmpl b/template/en/default/global/field-descs.none.tmpl
index 1596f3884..731ba37ef 100644
--- a/template/en/default/global/field-descs.none.tmpl
+++ b/template/en/default/global/field-descs.none.tmpl
@@ -62,6 +62,7 @@
${constants.FIELD_TYPE_DATETIME} => "Date/Time",
${constants.FIELD_TYPE_DATE} => "Date",
${constants.FIELD_TYPE_BUG_ID} => "$terms.Bug ID",
+ ${constants.FIELD_TYPE_EXTENSION} => "Extension",
} %]
[% IF in_template_var %]