summaryrefslogtreecommitdiffstats
path: root/extensions/BugmailFilter
diff options
context:
space:
mode:
authorByron Jones <glob@mozilla.com>2014-09-03 08:06:29 +0200
committerByron Jones <glob@mozilla.com>2014-09-03 08:06:29 +0200
commite1e2ca422db2ad5c4a19c0901b6402d3a7f0b175 (patch)
tree1712ff552852b3c8c55e1d30d0374b2136afb148 /extensions/BugmailFilter
parentbd23fbfeccd49a406f6a8d99a21371ad7ade07ba (diff)
downloadbugzilla-e1e2ca422db2ad5c4a19c0901b6402d3a7f0b175.tar.gz
bugzilla-e1e2ca422db2ad5c4a19c0901b6402d3a7f0b175.tar.xz
Bug 1054138: add the ability to filter on "fields containing the string"
Diffstat (limited to 'extensions/BugmailFilter')
-rw-r--r--extensions/BugmailFilter/Extension.pm75
-rw-r--r--extensions/BugmailFilter/lib/Filter.pm31
-rw-r--r--extensions/BugmailFilter/template/en/default/account/prefs/bugmail_filter.html.tmpl14
-rw-r--r--extensions/BugmailFilter/template/en/default/hook/global/user-error-errors.html.tmpl9
-rw-r--r--extensions/BugmailFilter/web/js/bugmail-filter.js36
5 files changed, 119 insertions, 46 deletions
diff --git a/extensions/BugmailFilter/Extension.pm b/extensions/BugmailFilter/Extension.pm
index 053b07575..035de241c 100644
--- a/extensions/BugmailFilter/Extension.pm
+++ b/extensions/BugmailFilter/Extension.pm
@@ -45,6 +45,9 @@ sub user_preferences {
user_id => Bugzilla->user->id,
};
$params->{field_name} = $input->{field} || IS_NULL;
+ if ($params->{field_name} eq '~') {
+ $params->{field_name} = '~' . $input->{field_contains};
+ }
$params->{relationship} = $input->{relationship} || IS_NULL;
if (my $product_name = $input->{product}) {
my $product = Bugzilla::Product->check({
@@ -160,6 +163,20 @@ sub user_preferences {
}) }
];
+ # set field_description
+ foreach my $filter (@{ $vars->{filters} }) {
+ my $field_name = $filter->field_name;
+ if (!$field_name) {
+ $filter->field_description('Any');
+ }
+ elsif (substr($field_name, 0, 1) eq '~') {
+ $filter->field_description('~ ' . substr($field_name, 1));
+ }
+ else {
+ $filter->field_description($fields{$field_name} || $filter->field->description);
+ }
+ }
+
# build a list of tracking-flags, grouped by type
require Bugzilla::Extension::TrackingFlags::Constants;
require Bugzilla::Extension::TrackingFlags::Flag;
@@ -203,39 +220,34 @@ sub user_wants_mail {
});
return unless @$filters;
- my $fields = [ map { $_->{field_name} } @$diffs ];
+ my $fields = [
+ map { {
+ filter_field => $_->{field_name}, # filter's field_name
+ field_name => $_->{field_name}, # raw bugzilla field_name
+ } }
+ @$diffs
+ ];
# insert fake fields for new attachments and comments
if (@$comments) {
if (grep { $_->type == CMT_ATTACHMENT_CREATED } @$comments) {
- push @$fields, 'attachment.created';
+ push @$fields, { filter_field => 'attachment.created' };
}
if (grep { $_->type != CMT_ATTACHMENT_CREATED } @$comments) {
- push @$fields, 'comment.created';
+ push @$fields, { filter_field => 'comment.created' };
}
}
- # replace tracking flag fields with fake tracking flag types
+ # set filter_field on tracking flags to tracking.$type
require Bugzilla::Extension::TrackingFlags::Flag;
- my %count;
- my @tracking_flags;
- foreach my $field (@$fields, Bugzilla->tracking_flag_names) {
- $count{$field}++;
- }
- foreach my $field (keys %count) {
- push @tracking_flags, $field
- if $count{$field} > 1;
- }
- my %tracking_types =
- map { $_->flag_type => 1 }
- @{ Bugzilla::Extension::TrackingFlags::Flag->match({
- name => \@tracking_flags
- })};
- foreach my $type (keys %tracking_types) {
- push @$fields, 'tracking.' . $type;
- }
- foreach my $field (Bugzilla->tracking_flag_names) {
- $fields = [ grep { $_ ne $field } @$fields ];
+ my @tracking_flags = Bugzilla->tracking_flags;
+ foreach my $field (@$fields) {
+ next unless my $field_name = $field->{field_name};
+ foreach my $tracking_flag (@tracking_flags) {
+ if ($field_name eq $tracking_flag->name) {
+ $field->{filter_field} = 'tracking.'. $tracking_flag->flag_type;
+ }
+ }
}
if (_should_drop($fields, $filters, $args)) {
@@ -290,43 +302,42 @@ sub _should_drop {
# exclusions
# drop email where we are excluding all changed fields
- my %exclude = map { $_ => 0 } @$fields;
my $params = {
product_id => $bug->product_id,
component_id => $bug->component_id,
rel_map => \@rel_map,
};
- foreach my $field_name (@$fields) {
- $params->{field_name} = $field_name;
+ foreach my $field (@$fields) {
+ $params->{field} = $field;
foreach my $filter (grep { $_->is_exclude } @$filters) {
if ($filter->matches($params)) {
- $exclude{$field_name} = 1;
+ $field->{exclude} = 1;
last;
}
}
}
# no need to process includes if nothing was excluded
- if (!grep { $exclude{$_} } @$fields) {
+ if (!grep { $_->{exclude} } @$fields) {
return 0;
}
# inclusions
# flip the bit for fields that should be included
- foreach my $field_name (@$fields) {
- $params->{field_name} = $field_name;
+ foreach my $field (@$fields) {
+ $params->{field} = $field;
foreach my $filter (grep { $_->is_include } @$filters) {
if ($filter->matches($params)) {
- $exclude{$field_name} = 0;
+ $field->{exclude} = 0;
last;
}
}
}
# drop if all fields are still excluded
- return !(grep { !$exclude{$_} } keys %exclude);
+ return !(grep { !$_->{exclude} } @$fields);
}
# catch when fields are renamed, and update the field_name entires
diff --git a/extensions/BugmailFilter/lib/Filter.pm b/extensions/BugmailFilter/lib/Filter.pm
index 9a0d0f89a..5e83bd52a 100644
--- a/extensions/BugmailFilter/lib/Filter.pm
+++ b/extensions/BugmailFilter/lib/Filter.pm
@@ -19,6 +19,7 @@ use Bugzilla::Extension::BugmailFilter::FakeField;
use Bugzilla::Field;
use Bugzilla::Product;
use Bugzilla::User;
+use Bugzilla::Util qw(trim);
use constant DB_TABLE => 'bugmail_filters';
@@ -85,10 +86,20 @@ sub field_name {
return $_[0]->{field_name} //= '';
}
+sub field_description {
+ my ($self, $value) = @_;
+ $self->{field_description} = $value if defined($value);
+ return $self->{field_description};
+}
+
sub field {
my ($self) = @_;
return unless $self->{field_name};
if (!$self->{field}) {
+ if (substr($self->{field_name}, 0, 1) eq '~') {
+ # this should never happen
+ die "not implemented";
+ }
foreach my $field (
@{ Bugzilla::Extension::BugmailFilter::FakeField->fake_fields() },
@{ Bugzilla::Extension::BugmailFilter::FakeField->tracking_flag_fields() },
@@ -133,6 +144,14 @@ sub _check_user {
sub _check_field_name {
my ($class, $field_name) = @_;
return undef unless $field_name;
+ if (substr($field_name, 0, 1) eq '~') {
+ $field_name = lc(trim($field_name));
+ $field_name =~ /^~[a-z0-9_\.]+$/
+ || ThrowUserError('bugmail_filter_invalid');
+ length($field_name) <= 64
+ || ThrowUserError('bugmail_filter_too_long');
+ return $field_name;
+ }
foreach my $rh (@{ FAKE_FIELD_NAMES() }) {
return $field_name if $rh->{name} eq $field_name;
}
@@ -147,8 +166,16 @@ sub _check_field_name {
sub matches {
my ($self, $args) = @_;
- if ($self->{field_name} && $self->{field_name} ne $args->{field_name}) {
- return 0;
+ if (my $field_name = $self->{field_name}) {
+ if (substr($field_name, 0, 1) eq '~') {
+ my $substring = quotemeta(substr($field_name, 1));
+ if ($args->{field}->{field_name} !~ /$substring/i) {
+ return 0;
+ }
+ }
+ elsif ($field_name ne $args->{field}->{filter_field}) {
+ return 0;
+ }
}
if ($self->{product_id} && $self->{product_id} != $args->{product_id}) {
diff --git a/extensions/BugmailFilter/template/en/default/account/prefs/bugmail_filter.html.tmpl b/extensions/BugmailFilter/template/en/default/account/prefs/bugmail_filter.html.tmpl
index 12c745adb..43d18f516 100644
--- a/extensions/BugmailFilter/template/en/default/account/prefs/bugmail_filter.html.tmpl
+++ b/extensions/BugmailFilter/template/en/default/account/prefs/bugmail_filter.html.tmpl
@@ -48,16 +48,24 @@ var cpts = new Array();
[% field.description FILTER html %]
</option>
[% END %]
+ <option value="~">Contains:</option>
</select>
</td>
<td class="blurb">
the field that was changed
</td>
</tr>
+<tr id="field_contains_row" class="bz_default_hidden">
+ <td>&nbsp;</td>
+ <td>
+ <input name="field_contains" id="field_contains"
+ placeholder="field name" maxlength="63">
+ </td>
+</tr>
<tr>
<th>Product:</th>
<td>
- <select name="product" id="product" onChange="onFilterProductChange()">
+ <select name="product" id="product">
<option value="">__Any__</option>
[% FOREACH product IN selectable_products %]
<option>[% product.name FILTER html %]</option>
@@ -139,11 +147,11 @@ var cpts = new Array();
<tr class="[% "row_odd" UNLESS loop.count % 2 %]">
<td>
<input type="checkbox" name="remove" value="[% filter.id FILTER none %]"
- onChange="onRemoveChange()">
+ onChange="onFilterRemoveChange()">
</td>
<td>[% filter.product ? filter.product.name : 'Any' FILTER html %]</td>
<td>[% filter.component ? filter.component.name : 'Any' FILTER html %]</td>
- <td>[% filter.field ? fields.${filter.field.name} || filter.field.description : 'Any' FILTER html %]</td>
+ <td>[% filter.field_description FILTER html %]</td>
<td>[% filter.relationship ? filter.relationship_name : 'Any' FILTER html %]</td>
<td>[% filter.action ? 'Exclude' : 'Include' %]</td>
</tr>
diff --git a/extensions/BugmailFilter/template/en/default/hook/global/user-error-errors.html.tmpl b/extensions/BugmailFilter/template/en/default/hook/global/user-error-errors.html.tmpl
index 380c07ee5..4be894c6f 100644
--- a/extensions/BugmailFilter/template/en/default/hook/global/user-error-errors.html.tmpl
+++ b/extensions/BugmailFilter/template/en/default/hook/global/user-error-errors.html.tmpl
@@ -10,4 +10,13 @@
[% title = "Filter Already Exists" %]
A filter already exists with the selected criteria.
+[% ELSIF error == "bugmail_filter_too_long" %]
+ [% title = "Invalid Field Name" %]
+ The field name to filter on is too long (must be 63 character or fewer).
+
+[% ELSIF error == "bugmail_filter_invalid" %]
+ [% title = "Invalid Field Name" %]
+ The field name contains invalid characters (alpha-numeric, underscore and
+ period only).
+
[% END %]
diff --git a/extensions/BugmailFilter/web/js/bugmail-filter.js b/extensions/BugmailFilter/web/js/bugmail-filter.js
index 2b320bbbb..c24528861 100644
--- a/extensions/BugmailFilter/web/js/bugmail-filter.js
+++ b/extensions/BugmailFilter/web/js/bugmail-filter.js
@@ -6,19 +6,33 @@
* defined by the Mozilla Public License, v. 2.0. */
var Dom = YAHOO.util.Dom;
-var Event = YAHOO.util.Event;
+
+function onFilterFieldChange() {
+ if (Dom.get('field').value == '~') {
+ Dom.removeClass('field_contains_row', 'bz_default_hidden');
+ Dom.get('field_contains').focus();
+ Dom.get('field_contains').select();
+ }
+ else {
+ Dom.addClass('field_contains_row', 'bz_default_hidden');
+ }
+}
function onFilterProductChange() {
selectProduct(Dom.get('product'), Dom.get('component'), null, null, '__Any__');
Dom.get('component').disabled = Dom.get('product').value == '';
}
-function onFilterActionChange() {
- var value = Dom.get('action').value;
- Dom.get('add_filter').disabled = value == '';
+function setFilterAddEnabled() {
+ Dom.get('add_filter').disabled =
+ (
+ Dom.get('field').value == '~'
+ && Dom.get('field_contains').value == ''
+ )
+ || Dom.get('action').value == '';
}
-function onRemoveChange() {
+function onFilterRemoveChange() {
var cbs = Dom.get('filters_table').getElementsByTagName('input');
for (var i = 0, l = cbs.length; i < l; i++) {
if (cbs[i].checked) {
@@ -34,9 +48,13 @@ function showAllFlags() {
Dom.removeClass('all_flags', 'bz_default_hidden');
}
-Event.onDOMReady(function() {
- Event.on('action', 'change', onFilterActionChange);
+YAHOO.util.Event.onDOMReady(function() {
+ YAHOO.util.Event.on('field', 'change', onFilterFieldChange);
+ YAHOO.util.Event.on('field_contains', 'keyup', setFilterAddEnabled);
+ YAHOO.util.Event.on('product', 'change', onFilterProductChange);
+ YAHOO.util.Event.on('action', 'change', setFilterAddEnabled);
+ onFilterFieldChange();
onFilterProductChange();
- onFilterActionChange();
- onRemoveChange();
+ onFilterRemoveChange();
+ setFilterAddEnabled();
});