summaryrefslogtreecommitdiffstats
path: root/extensions/TrackingFlags/lib/Flag
diff options
context:
space:
mode:
authorDavid Lawrence <dlawrence@mozilla.com>2013-07-25 08:55:15 +0200
committerByron Jones <bjones@mozilla.com>2013-07-25 08:55:15 +0200
commit701e41d96429e68d8989b2063d02cd0d20a229ea (patch)
treec462cc86f7ade39cd11a5b27a279dbb4b4dba48e /extensions/TrackingFlags/lib/Flag
parentb84db39863cd55166bff584265d255a0f882e64a (diff)
downloadbugzilla-701e41d96429e68d8989b2063d02cd0d20a229ea.tar.gz
bugzilla-701e41d96429e68d8989b2063d02cd0d20a229ea.tar.xz
Bug 750742: Create new BMO extension called TrackingFlags to move current tracking flags away from custom fields
Diffstat (limited to 'extensions/TrackingFlags/lib/Flag')
-rw-r--r--extensions/TrackingFlags/lib/Flag/Bug.pm171
-rw-r--r--extensions/TrackingFlags/lib/Flag/Value.pm130
-rw-r--r--extensions/TrackingFlags/lib/Flag/Visibility.pm172
3 files changed, 473 insertions, 0 deletions
diff --git a/extensions/TrackingFlags/lib/Flag/Bug.pm b/extensions/TrackingFlags/lib/Flag/Bug.pm
new file mode 100644
index 000000000..5e2886e66
--- /dev/null
+++ b/extensions/TrackingFlags/lib/Flag/Bug.pm
@@ -0,0 +1,171 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::TrackingFlags::Flag::Bug;
+
+use base qw(Bugzilla::Object);
+
+use strict;
+use warnings;
+
+use Bugzilla::Extension::TrackingFlags::Flag;
+
+use Bugzilla::Bug;
+use Bugzilla::Error;
+
+use Scalar::Util qw(blessed);
+
+###############################
+#### Initialization ####
+###############################
+
+use constant DEFAULT_FLAG_BUG => {
+ 'id' => 0,
+ 'tracking_flag_id' => 0,
+ 'bug_id' => '',
+ 'value' => '---',
+};
+
+use constant DB_TABLE => 'tracking_flags_bugs';
+
+use constant DB_COLUMNS => qw(
+ id
+ tracking_flag_id
+ bug_id
+ value
+);
+
+use constant LIST_ORDER => 'id';
+
+use constant UPDATE_COLUMNS => qw(
+ value
+);
+
+use constant VALIDATORS => {
+ tracking_flag_id => \&_check_tracking_flag,
+ value => \&_check_value,
+};
+
+use constant AUDIT_CREATES => 0;
+use constant AUDIT_UPDATES => 0;
+use constant AUDIT_REMOVES => 0;
+
+###############################
+#### Object Methods ####
+###############################
+
+sub new {
+ my $invocant = shift;
+ my $class = ref($invocant) || $invocant;
+ my ($param) = @_;
+
+ my $self;
+ if ($param) {
+ $self = $class->SUPER::new(@_);
+ if (!$self) {
+ $self = DEFAULT_FLAG_BUG;
+ bless($self, $class);
+ }
+ }
+ else {
+ $self = DEFAULT_FLAG_BUG;
+ bless($self, $class);
+ }
+
+ return $self
+}
+
+sub match {
+ my $class = shift;
+ my $bug_flags = $class->SUPER::match(@_);
+ preload_all_the_things($bug_flags);
+ return $bug_flags;
+}
+
+sub remove_from_db {
+ my ($self) = @_;
+ $self->SUPER::remove_from_db();
+ $self->{'id'} = $self->{'tracking_flag_id'} = $self->{'bug_id'} = 0;
+ $self->{'value'} = '---';
+}
+
+sub preload_all_the_things {
+ my ($bug_flags) = @_;
+ my $cache = Bugzilla->request_cache;
+
+ # Preload tracking flag objects
+ my @tracking_flag_ids;
+ foreach my $bug_flag (@$bug_flags) {
+ if (exists $cache->{'tracking_flags'}
+ && $cache->{'tracking_flags'}->{$bug_flag->tracking_flag_id})
+ {
+ $bug_flag->{'tracking_flag'}
+ = $cache->{'tracking_flags'}->{$bug_flag->tracking_flag_id};
+ next;
+ }
+ push(@tracking_flag_ids, $bug_flag->tracking_flag_id);
+ }
+
+ return unless @tracking_flag_ids;
+
+ my $tracking_flags
+ = Bugzilla::Extension::TrackingFlags::Flag->match({ id => \@tracking_flag_ids });
+ my %tracking_flag_hash = map { $_->flag_id => $_ } @$tracking_flags;
+
+ foreach my $bug_flag (@$bug_flags) {
+ next if exists $bug_flag->{'tracking_flag'};
+ $bug_flag->{'tracking_flag'} = $tracking_flag_hash{$bug_flag->tracking_flag_id};
+ }
+}
+
+###############################
+#### Validators ####
+###############################
+
+sub _check_value {
+ my ($invocant, $value) = @_;
+ $value || ThrowCodeError('param_required', { param => 'value' });
+ return $value;
+}
+
+sub _check_tracking_flag {
+ my ($invocant, $flag) = @_;
+ if (blessed $flag) {
+ return $flag->flag_id;
+ }
+ $flag = Bugzilla::Extension::TrackingFlags::Flag->new({ id => $flag, cache => 1 })
+ || ThrowCodeError('tracking_flags_invalid_param', { name => 'flag_id', value => $flag });
+ return $flag->flag_id;
+}
+
+###############################
+#### Setters ####
+###############################
+
+sub set_value { $_[0]->set('value', $_[1]); }
+
+###############################
+#### Accessors ####
+###############################
+
+sub tracking_flag_id { return $_[0]->{'tracking_flag_id'}; }
+sub bug_id { return $_[0]->{'bug_id'}; }
+sub value { return $_[0]->{'value'}; }
+
+sub bug {
+ return $_[0]->{'bug'} ||= Bugzilla::Bug->new({
+ id => $_[0]->bug_id, cache => 1
+ });
+}
+
+sub tracking_flag {
+ return $_[0]->{'tracking_flag'} ||= Bugzilla::Extension::TrackingFlags::Flag->new({
+ id => $_[0]->tracking_flag_id, cache => 1
+ });
+}
+
+1;
diff --git a/extensions/TrackingFlags/lib/Flag/Value.pm b/extensions/TrackingFlags/lib/Flag/Value.pm
new file mode 100644
index 000000000..4023e191d
--- /dev/null
+++ b/extensions/TrackingFlags/lib/Flag/Value.pm
@@ -0,0 +1,130 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::TrackingFlags::Flag::Value;
+
+use base qw(Bugzilla::Object);
+
+use strict;
+use warnings;
+
+use Bugzilla::Error;
+use Bugzilla::Group;
+use Bugzilla::Util qw(detaint_natural);
+use Scalar::Util qw(blessed);
+
+###############################
+#### Initialization ####
+###############################
+
+use constant DB_TABLE => 'tracking_flags_values';
+
+use constant DB_COLUMNS => qw(
+ id
+ tracking_flag_id
+ setter_group_id
+ value
+ sortkey
+ is_active
+);
+
+use constant LIST_ORDER => 'sortkey';
+
+use constant UPDATE_COLUMNS => qw(
+ setter_group_id
+ value
+ sortkey
+ is_active
+);
+
+use constant VALIDATORS => {
+ tracking_flag_id => \&_check_tracking_flag,
+ setter_group_id => \&_check_setter_group,
+ value => \&_check_value,
+ sortkey => \&_check_sortkey,
+ is_active => \&Bugzilla::Object::check_boolean,
+};
+
+###############################
+#### Validators ####
+###############################
+
+sub _check_value {
+ my ($invocant, $value) = @_;
+ defined $value || ThrowCodeError('param_required', { param => 'value' });
+ return $value;
+}
+
+sub _check_tracking_flag {
+ my ($invocant, $flag) = @_;
+ if (blessed $flag) {
+ return $flag->flag_id;
+ }
+ $flag = Bugzilla::Extension::TrackingFlags::Flag->new({ id => $flag, cache => 1 })
+ || ThrowCodeError('tracking_flags_invalid_param', { name => 'flag_id', value => $flag });
+ return $flag->flag_id;
+}
+
+sub _check_setter_group {
+ my ($invocant, $group) = @_;
+ if (blessed $group) {
+ return $group->id;
+ }
+ $group = Bugzilla::Group->new({ id => $group, cache => 1 })
+ || ThrowCodeError('tracking_flags_invalid_param', { name => 'setter_group_id', value => $group });
+ return $group->id;
+}
+
+sub _check_sortkey {
+ my ($invocant, $sortkey) = @_;
+ detaint_natural($sortkey)
+ || ThrowUserError('field_invalid_sortkey', { sortkey => $sortkey });
+ return $sortkey;
+}
+
+###############################
+#### Setters ####
+###############################
+
+sub set_setter_group_id { $_[0]->set('setter_group_id', $_[1]); }
+sub set_value { $_[0]->set('value', $_[1]); }
+sub set_sortkey { $_[0]->set('sortkey', $_[1]); }
+sub set_is_active { $_[0]->set('is_active', $_[1]); }
+
+###############################
+#### Accessors ####
+###############################
+
+sub tracking_flag_id { return $_[0]->{'tracking_flag_id'}; }
+sub setter_group_id { return $_[0]->{'setter_group_id'}; }
+sub value { return $_[0]->{'value'}; }
+sub sortkey { return $_[0]->{'sortkey'}; }
+sub is_active { return $_[0]->{'is_active'}; }
+
+sub tracking_flag {
+ return $_[0]->{'tracking_flag'} ||= Bugzilla::Extension::TrackingFlags::Flag->new({
+ id => $_[0]->tracking_flag_id, cache => 1
+ });
+}
+
+sub setter_group {
+ if ($_[0]->setter_group_id) {
+ $_[0]->{'setter_group'} ||= Bugzilla::Group->new({
+ id => $_[0]->setter_group_id, cache => 1
+ });
+ }
+ return $_[0]->{'setter_group'};
+}
+
+########################################
+## Compatibility with Bugzilla::Field ##
+########################################
+
+sub name { return $_[0]->{'value'}; }
+sub is_visible_on_bug { return 1; }
+
+1;
diff --git a/extensions/TrackingFlags/lib/Flag/Visibility.pm b/extensions/TrackingFlags/lib/Flag/Visibility.pm
new file mode 100644
index 000000000..7600d71bd
--- /dev/null
+++ b/extensions/TrackingFlags/lib/Flag/Visibility.pm
@@ -0,0 +1,172 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::TrackingFlags::Flag::Visibility;
+
+use base qw(Bugzilla::Object);
+
+use strict;
+use warnings;
+
+use Bugzilla::Error;
+use Bugzilla::Product;
+use Bugzilla::Component;
+use Scalar::Util qw(blessed);
+
+###############################
+#### Initialization ####
+###############################
+
+use constant DB_TABLE => 'tracking_flags_visibility';
+
+use constant DB_COLUMNS => qw(
+ id
+ tracking_flag_id
+ product_id
+ component_id
+);
+
+use constant LIST_ORDER => 'id';
+
+use constant UPDATE_COLUMNS => (); # imutable
+
+use constant VALIDATORS => {
+ tracking_flag_id => \&_check_tracking_flag,
+ product_id => \&_check_product,
+ component_id => \&_check_component,
+};
+
+###############################
+#### Methods ####
+###############################
+
+sub match {
+ my $class= shift;
+ my ($params) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ # Allow matching component and product by name
+ # (in addition to matching by ID).
+ # Borrowed from Bugzilla::Bug::match
+ my %translate_fields = (
+ product => 'Bugzilla::Product',
+ component => 'Bugzilla::Component',
+ );
+
+ foreach my $field (keys %translate_fields) {
+ my @ids;
+ # Convert names to ids. We use "exists" everywhere since people can
+ # legally specify "undef" to mean IS NULL
+ if (exists $params->{$field}) {
+ my $names = $params->{$field};
+ my $type = $translate_fields{$field};
+ my $objects = Bugzilla::Object::match($type, { name => $names });
+ push(@ids, map { $_->id } @$objects);
+ }
+ # You can also specify ids directly as arguments to this function,
+ # so include them in the list if they have been specified.
+ if (exists $params->{"${field}_id"}) {
+ my $current_ids = $params->{"${field}_id"};
+ my @id_array = ref $current_ids ? @$current_ids : ($current_ids);
+ push(@ids, @id_array);
+ }
+ # We do this "or" instead of a "scalar(@ids)" to handle the case
+ # when people passed only invalid object names. Otherwise we'd
+ # end up with a SUPER::match call with zero criteria (which dies).
+ if (exists $params->{$field} or exists $params->{"${field}_id"}) {
+ delete $params->{$field};
+ $params->{"${field}_id"} = scalar(@ids) == 1 ? [ $ids[0] ] : \@ids;
+ }
+ }
+
+ # If we aren't matching on the product, use the default matching code
+ if (!exists $params->{product_id}) {
+ return $class->SUPER::match(@_);
+ }
+
+ my @criteria = ("1=1");
+
+ if ($params->{product_id}) {
+ push(@criteria, $dbh->sql_in('product_id', $params->{'product_id'}));
+ if ($params->{component_id}) {
+ my $component_id = $params->{component_id};
+ push(@criteria, "(" . $dbh->sql_in('component_id', $params->{'component_id'}) .
+ " OR component_id IS NULL)");
+ }
+ }
+
+ my $where = join(' AND ', @criteria);
+ my $flag_ids = $dbh->selectcol_arrayref("SELECT id
+ FROM tracking_flags_visibility
+ WHERE $where");
+
+ return Bugzilla::Extension::TrackingFlags::Flag::Visibility->new_from_list($flag_ids);
+}
+
+###############################
+#### Validators ####
+###############################
+
+sub _check_tracking_flag {
+ my ($invocant, $flag) = @_;
+ if (blessed $flag) {
+ return $flag->flag_id;
+ }
+ $flag = Bugzilla::Extension::TrackingFlags::Flag->new($flag)
+ || ThrowCodeError('tracking_flags_invalid_param', { name => 'flag_id', value => $flag });
+ return $flag->flag_id;
+}
+
+sub _check_product {
+ my ($invocant, $product) = @_;
+ if (blessed $product) {
+ return $product->id;
+ }
+ $product = Bugzilla::Product->new($product)
+ || ThrowCodeError('tracking_flags_invalid_param', { name => 'product_id', value => $product });
+ return $product->id;
+}
+
+sub _check_component {
+ my ($invocant, $component) = @_;
+ return undef unless defined $component;
+ if (blessed $component) {
+ return $component->id;
+ }
+ $component = Bugzilla::Component->new($component)
+ || ThrowCodeError('tracking_flags_invalid_param', { name => 'component_id', value => $component });
+ return $component->id;
+}
+
+###############################
+#### Accessors ####
+###############################
+
+sub tracking_flag_id { return $_[0]->{'tracking_flag_id'}; }
+sub product_id { return $_[0]->{'product_id'}; }
+sub component_id { return $_[0]->{'component_id'}; }
+
+sub tracking_flag {
+ my ($self) = @_;
+ $self->{'tracking_flag'} ||= Bugzilla::Extension::TrackingFlags::Flag->new($self->tracking_flag_id);
+ return $self->{'tracking_flag'};
+}
+
+sub product {
+ my ($self) = @_;
+ $self->{'product'} ||= Bugzilla::Product->new($self->product_id);
+ return $self->{'product'};
+}
+
+sub component {
+ my ($self) = @_;
+ return undef unless $self->component_id;
+ $self->{'component'} ||= Bugzilla::Component->new($self->component_id);
+ return $self->{'component'};
+}
+
+1;