diff options
Diffstat (limited to 'extensions/TrackingFlags')
-rw-r--r-- | extensions/TrackingFlags/Config.pm | 12 | ||||
-rw-r--r-- | extensions/TrackingFlags/Extension.pm | 1224 | ||||
-rwxr-xr-x | extensions/TrackingFlags/bin/bug_825946.pl | 25 | ||||
-rwxr-xr-x | extensions/TrackingFlags/bin/bulk_flag_clear.pl | 80 | ||||
-rwxr-xr-x | extensions/TrackingFlags/bin/migrate_tracking_flags.pl | 407 | ||||
-rw-r--r-- | extensions/TrackingFlags/lib/Admin.pm | 721 | ||||
-rw-r--r-- | extensions/TrackingFlags/lib/Constants.pm | 44 | ||||
-rw-r--r-- | extensions/TrackingFlags/lib/Flag.pm | 632 | ||||
-rw-r--r-- | extensions/TrackingFlags/lib/Flag/Bug.pm | 168 | ||||
-rw-r--r-- | extensions/TrackingFlags/lib/Flag/Value.pm | 129 | ||||
-rw-r--r-- | extensions/TrackingFlags/lib/Flag/Visibility.pm | 195 |
11 files changed, 1777 insertions, 1860 deletions
diff --git a/extensions/TrackingFlags/Config.pm b/extensions/TrackingFlags/Config.pm index d0bc5ca20..6f5b9be39 100644 --- a/extensions/TrackingFlags/Config.pm +++ b/extensions/TrackingFlags/Config.pm @@ -13,15 +13,9 @@ use warnings; use constant NAME => 'TrackingFlags'; -use constant REQUIRED_MODULES => [ - { - package => 'JSON-XS', - module => 'JSON::XS', - version => '2.0' - }, -]; +use constant REQUIRED_MODULES => + [{package => 'JSON-XS', module => 'JSON::XS', version => '2.0'},]; -use constant OPTIONAL_MODULES => [ -]; +use constant OPTIONAL_MODULES => []; __PACKAGE__->NAME; diff --git a/extensions/TrackingFlags/Extension.pm b/extensions/TrackingFlags/Extension.pm index 5f6715fc8..fea0240c8 100644 --- a/extensions/TrackingFlags/Extension.pm +++ b/extensions/TrackingFlags/Extension.pm @@ -34,823 +34,701 @@ our $VERSION = '1'; our @FLAG_CACHE; BEGIN { - *Bugzilla::tracking_flags = \&_tracking_flags; - *Bugzilla::tracking_flag_names = \&_tracking_flag_names; + *Bugzilla::tracking_flags = \&_tracking_flags; + *Bugzilla::tracking_flag_names = \&_tracking_flag_names; } sub _tracking_flags { - return Bugzilla::Extension::TrackingFlags::Flag->get_all(); + return Bugzilla::Extension::TrackingFlags::Flag->get_all(); } sub _tracking_flag_names { - return Bugzilla::Extension::TrackingFlags::Flag->get_all_names(); + return Bugzilla::Extension::TrackingFlags::Flag->get_all_names(); } 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); - } + 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') { - my $flags = Bugzilla::Extension::TrackingFlags::Flag->match({ - product => $vars->{'product'}->name, - enter_bug => 1, - is_active => 1, - }); - - $vars->{tracking_flags} = $flags; - $vars->{tracking_flags_json} = _flags_to_json($flags); - $vars->{tracking_flag_types} = FLAG_TYPES; - $vars->{tracking_flag_components} = _flags_to_components($flags, $vars->{product}); - $vars->{highest_status_firefox} = _get_highest_status_firefox($flags); - } - elsif ($file eq 'bug/edit.html.tmpl'|| $file eq 'bug/show.xml.tmpl' - || $file eq 'email/bugmail.html.tmpl' || $file eq 'email/bugmail.txt.tmpl') - { - # note: bug/edit.html.tmpl doesn't support multiple bugs - my $bug = exists $vars->{'bugs'} ? $vars->{'bugs'}[0] : $vars->{'bug'}; - - if ($bug && !$bug->{error}) { - my $flags = Bugzilla::Extension::TrackingFlags::Flag->match({ - product => $bug->product, - component => $bug->component, - bug_id => $bug->id, - is_active => 1, - }); - - $vars->{tracking_flags} = $flags; - $vars->{tracking_flags_json} = _flags_to_json($flags); - } + my ($self, $args) = @_; + my $file = $args->{'file'}; + my $vars = $args->{'vars'}; + + if ($file eq 'bug/create/create.html.tmpl') { + my $flags + = Bugzilla::Extension::TrackingFlags::Flag->match({ + product => $vars->{'product'}->name, enter_bug => 1, is_active => 1, + }); + + $vars->{tracking_flags} = $flags; + $vars->{tracking_flags_json} = _flags_to_json($flags); + $vars->{tracking_flag_types} = FLAG_TYPES; + $vars->{tracking_flag_components} + = _flags_to_components($flags, $vars->{product}); + $vars->{highest_status_firefox} = _get_highest_status_firefox($flags); + } + elsif ($file eq 'bug/edit.html.tmpl' + || $file eq 'bug/show.xml.tmpl' + || $file eq 'email/bugmail.html.tmpl' + || $file eq 'email/bugmail.txt.tmpl') + { + # note: bug/edit.html.tmpl doesn't support multiple bugs + my $bug = exists $vars->{'bugs'} ? $vars->{'bugs'}[0] : $vars->{'bug'}; + + if ($bug && !$bug->{error}) { + my $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 - }); + $vars->{tracking_flags} = $flags; + $vars->{tracking_flags_json} = _flags_to_json($flags); } + + $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 _flags_to_json { - my ($flags) = @_; + my ($flags) = @_; - my $json = { - flags => {}, - types => [], - comments => {}, - }; + my $json = {flags => {}, types => [], comments => {},}; - my %type_map = map { $_->{name} => $_ } @{ FLAG_TYPES() }; - foreach my $flag (@$flags) { - my $flag_type = $flag->flag_type; + my %type_map = map { $_->{name} => $_ } @{FLAG_TYPES()}; + foreach my $flag (@$flags) { + my $flag_type = $flag->flag_type; - $json->{flags}->{$flag_type}->{$flag->name} = $flag->bug_flag->value; + $json->{flags}->{$flag_type}->{$flag->name} = $flag->bug_flag->value; - if ($type_map{$flag_type}->{collapsed} - && !grep { $_ eq $flag_type } @{ $json->{types} }) - { - push @{ $json->{types} }, $flag_type; - } + if ($type_map{$flag_type}->{collapsed} && !grep { $_ eq $flag_type } + @{$json->{types}}) + { + push @{$json->{types}}, $flag_type; + } - foreach my $value (@{ $flag->values }) { - if (defined($value->comment) && $value->comment ne '') { - $json->{comments}->{$flag->name}->{$value->value} = $value->comment; - } - } + foreach my $value (@{$flag->values}) { + if (defined($value->comment) && $value->comment ne '') { + $json->{comments}->{$flag->name}->{$value->value} = $value->comment; + } } + } - return encode_json($json); + return encode_json($json); } sub _flags_to_components { - my ($flags, $product) = @_; - - # for each component, generate a list of visible tracking flags - my $json = {}; - foreach my $component (@{ $product->components }) { - next unless $component->is_active; - foreach my $flag (@$flags) { - foreach my $visibility (@{ $flag->visibility }) { - if ($visibility->product_id == $product->id - && (!$visibility->component_id || $visibility->component_id == $component->id)) - { - $json->{$component->name} //= []; - push @{ $json->{$component->name} }, $flag->name; - } - } + my ($flags, $product) = @_; + + # for each component, generate a list of visible tracking flags + my $json = {}; + foreach my $component (@{$product->components}) { + next unless $component->is_active; + foreach my $flag (@$flags) { + foreach my $visibility (@{$flag->visibility}) { + if ($visibility->product_id == $product->id + && (!$visibility->component_id || $visibility->component_id == $component->id)) + { + $json->{$component->name} //= []; + push @{$json->{$component->name}}, $flag->name; } + } } - return encode_json($json); + } + return encode_json($json); } sub _get_highest_status_firefox { - my ($flags) = @_; - - my @status_flags = - sort { $b <=> $a } - map { $_->name =~ /(\d+)$/; $1 } - grep { $_->is_active && $_->name =~ /^cf_status_firefox\d/ } - @$flags; - return @status_flags ? $status_flags[0] : undef; + my ($flags) = @_; + + my @status_flags + = sort { $b <=> $a } + map { $_->name =~ /(\d+)$/; $1 } + grep { $_->is_active && $_->name =~ /^cf_status_firefox\d/ } @$flags; + return @status_flags ? $status_flags[0] : undef; } sub db_schema_abstract_schema { - my ($self, $args) = @_; - $args->{'schema'}->{'tracking_flags'} = { - FIELDS => [ - id => { - TYPE => 'MEDIUMSERIAL', - NOTNULL => 1, - PRIMARYKEY => 1, - }, - field_id => { - TYPE => 'INT3', - NOTNULL => 1, - REFERENCES => { - TABLE => 'fielddefs', - COLUMN => 'id', - DELETE => 'CASCADE' - } - }, - name => { - TYPE => 'varchar(64)', - NOTNULL => 1, - }, - description => { - TYPE => 'varchar(64)', - NOTNULL => 1, - }, - type => { - TYPE => 'varchar(64)', - NOTNULL => 1, - }, - sortkey => { - TYPE => 'INT2', - NOTNULL => 1, - DEFAULT => '0', - }, - enter_bug => { - TYPE => 'BOOLEAN', - NOTNULL => 1, - DEFAULT => 'TRUE', - }, - is_active => { - TYPE => 'BOOLEAN', - NOTNULL => 1, - DEFAULT => 'TRUE', - }, - ], - INDEXES => [ - tracking_flags_idx => { - FIELDS => ['name'], - TYPE => 'UNIQUE', - }, - ], - }; - $args->{'schema'}->{'tracking_flags_values'} = { - FIELDS => [ - id => { - TYPE => 'MEDIUMSERIAL', - NOTNULL => 1, - PRIMARYKEY => 1, - }, - tracking_flag_id => { - TYPE => 'INT3', - NOTNULL => 1, - REFERENCES => { - TABLE => 'tracking_flags', - COLUMN => 'id', - DELETE => 'CASCADE', - }, - }, - setter_group_id => { - TYPE => 'INT3', - NOTNULL => 0, - REFERENCES => { - TABLE => 'groups', - COLUMN => 'id', - DELETE => 'SET NULL', - }, - }, - value => { - TYPE => 'varchar(64)', - NOTNULL => 1, - }, - sortkey => { - TYPE => 'INT2', - NOTNULL => 1, - DEFAULT => '0', - }, - enter_bug => { - TYPE => 'BOOLEAN', - NOTNULL => 1, - DEFAULT => 'TRUE', - }, - is_active => { - TYPE => 'BOOLEAN', - NOTNULL => 1, - DEFAULT => 'TRUE', - }, - comment => { - TYPE => 'TEXT', - NOTNULL => 0, - }, - ], - INDEXES => [ - tracking_flags_values_idx => { - FIELDS => ['tracking_flag_id', 'value'], - TYPE => 'UNIQUE', - }, - ], - }; - $args->{'schema'}->{'tracking_flags_bugs'} = { - FIELDS => [ - id => { - TYPE => 'MEDIUMSERIAL', - NOTNULL => 1, - PRIMARYKEY => 1, - }, - tracking_flag_id => { - TYPE => 'INT3', - NOTNULL => 1, - REFERENCES => { - TABLE => 'tracking_flags', - COLUMN => 'id', - DELETE => 'CASCADE', - }, - }, - bug_id => { - TYPE => 'INT3', - NOTNULL => 1, - REFERENCES => { - TABLE => 'bugs', - COLUMN => 'bug_id', - DELETE => 'CASCADE', - }, - }, - value => { - TYPE => 'varchar(64)', - NOTNULL => 1, - }, - ], - INDEXES => [ - tracking_flags_bugs_idx => { - FIELDS => ['tracking_flag_id', 'bug_id'], - TYPE => 'UNIQUE', - }, - ], - }; - $args->{'schema'}->{'tracking_flags_visibility'} = { - FIELDS => [ - id => { - TYPE => 'MEDIUMSERIAL', - NOTNULL => 1, - PRIMARYKEY => 1, - }, - tracking_flag_id => { - TYPE => 'INT3', - NOTNULL => 1, - REFERENCES => { - TABLE => 'tracking_flags', - COLUMN => 'id', - DELETE => 'CASCADE', - }, - }, - product_id => { - TYPE => 'INT2', - NOTNULL => 1, - REFERENCES => { - TABLE => 'products', - COLUMN => 'id', - DELETE => 'CASCADE', - }, - }, - component_id => { - TYPE => 'INT2', - NOTNULL => 0, - REFERENCES => { - TABLE => 'components', - COLUMN => 'id', - DELETE => 'CASCADE', - }, - }, - ], - INDEXES => [ - tracking_flags_visibility_idx => { - FIELDS => ['tracking_flag_id', 'product_id', 'component_id'], - TYPE => 'UNIQUE', - }, - ], - }; + my ($self, $args) = @_; + $args->{'schema'}->{'tracking_flags'} = { + FIELDS => [ + id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,}, + field_id => { + TYPE => 'INT3', + NOTNULL => 1, + REFERENCES => {TABLE => 'fielddefs', COLUMN => 'id', DELETE => 'CASCADE'} + }, + name => {TYPE => 'varchar(64)', NOTNULL => 1,}, + description => {TYPE => 'varchar(64)', NOTNULL => 1,}, + type => {TYPE => 'varchar(64)', NOTNULL => 1,}, + sortkey => {TYPE => 'INT2', NOTNULL => 1, DEFAULT => '0',}, + enter_bug => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'TRUE',}, + is_active => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'TRUE',}, + ], + INDEXES => [tracking_flags_idx => {FIELDS => ['name'], TYPE => 'UNIQUE',},], + }; + $args->{'schema'}->{'tracking_flags_values'} = { + FIELDS => [ + id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,}, + tracking_flag_id => { + TYPE => 'INT3', + NOTNULL => 1, + REFERENCES => {TABLE => 'tracking_flags', COLUMN => 'id', DELETE => 'CASCADE',}, + }, + setter_group_id => { + TYPE => 'INT3', + NOTNULL => 0, + REFERENCES => {TABLE => 'groups', COLUMN => 'id', DELETE => 'SET NULL',}, + }, + value => {TYPE => 'varchar(64)', NOTNULL => 1,}, + sortkey => {TYPE => 'INT2', NOTNULL => 1, DEFAULT => '0',}, + enter_bug => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'TRUE',}, + is_active => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'TRUE',}, + comment => {TYPE => 'TEXT', NOTNULL => 0,}, + ], + INDEXES => [ + tracking_flags_values_idx => + {FIELDS => ['tracking_flag_id', 'value'], TYPE => 'UNIQUE',}, + ], + }; + $args->{'schema'}->{'tracking_flags_bugs'} = { + FIELDS => [ + id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,}, + tracking_flag_id => { + TYPE => 'INT3', + NOTNULL => 1, + REFERENCES => {TABLE => 'tracking_flags', COLUMN => 'id', DELETE => 'CASCADE',}, + }, + bug_id => { + TYPE => 'INT3', + NOTNULL => 1, + REFERENCES => {TABLE => 'bugs', COLUMN => 'bug_id', DELETE => 'CASCADE',}, + }, + value => {TYPE => 'varchar(64)', NOTNULL => 1,}, + ], + INDEXES => [ + tracking_flags_bugs_idx => + {FIELDS => ['tracking_flag_id', 'bug_id'], TYPE => 'UNIQUE',}, + ], + }; + $args->{'schema'}->{'tracking_flags_visibility'} = { + FIELDS => [ + id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,}, + tracking_flag_id => { + TYPE => 'INT3', + NOTNULL => 1, + REFERENCES => {TABLE => 'tracking_flags', COLUMN => 'id', DELETE => 'CASCADE',}, + }, + product_id => { + TYPE => 'INT2', + NOTNULL => 1, + REFERENCES => {TABLE => 'products', COLUMN => 'id', DELETE => 'CASCADE',}, + }, + component_id => { + TYPE => 'INT2', + NOTNULL => 0, + REFERENCES => {TABLE => 'components', COLUMN => 'id', DELETE => 'CASCADE',}, + }, + ], + INDEXES => [ + tracking_flags_visibility_idx => { + FIELDS => ['tracking_flag_id', 'product_id', 'component_id'], + TYPE => 'UNIQUE', + }, + ], + }; } sub install_update_db { - my $dbh = Bugzilla->dbh; - - my $fk = $dbh->bz_fk_info('tracking_flags', 'field_id'); - if ($fk and !defined $fk->{DELETE}) { - $fk->{DELETE} = 'CASCADE'; - $dbh->bz_alter_fk('tracking_flags', 'field_id', $fk); - } - - $dbh->bz_add_column( - 'tracking_flags', - 'enter_bug', - { - TYPE => 'BOOLEAN', - NOTNULL => 1, - DEFAULT => 'TRUE', - } - ); - $dbh->bz_add_column( - 'tracking_flags_values', - 'comment', - { - TYPE => 'TEXT', - NOTNULL => 0, - }, - ); + my $dbh = Bugzilla->dbh; + + my $fk = $dbh->bz_fk_info('tracking_flags', 'field_id'); + if ($fk and !defined $fk->{DELETE}) { + $fk->{DELETE} = 'CASCADE'; + $dbh->bz_alter_fk('tracking_flags', 'field_id', $fk); + } + + $dbh->bz_add_column('tracking_flags', 'enter_bug', + {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'TRUE',}); + $dbh->bz_add_column('tracking_flags_values', 'comment', + {TYPE => 'TEXT', NOTNULL => 0,}, + ); } sub install_filesystem { - my ($self, $args) = @_; - my $files = $args->{files}; - my $extensions_dir = bz_locations()->{extensionsdir}; - $files->{"$extensions_dir/TrackingFlags/bin/bulk_flag_clear.pl"} = { - perms => Bugzilla::Install::Filesystem::OWNER_EXECUTE - }; + my ($self, $args) = @_; + my $files = $args->{files}; + my $extensions_dir = bz_locations()->{extensionsdir}; + $files->{"$extensions_dir/TrackingFlags/bin/bulk_flag_clear.pl"} + = {perms => Bugzilla::Install::Filesystem::OWNER_EXECUTE}; } sub active_custom_fields { - my ($self, $args) = @_; - my $fields = $args->{'fields'}; - my $params = $args->{'params'}; - my $product = $params->{'product'}; - my $component = $params->{'component'}; - - return if $params->{skip_extensions}; - # Create a hash of current fields based on field names - my %field_hash = map { $_->name => $_ } @$$fields; - - my @tracking_flags; - if ($product) { - $params->{'product_id'} = $product->id; - $params->{'component_id'} = $component->id if $component; - $params->{'is_active'} = 1; - @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 = sort { $a->sortkey <=> $b->sortkey } values %field_hash; + my ($self, $args) = @_; + my $fields = $args->{'fields'}; + my $params = $args->{'params'}; + my $product = $params->{'product'}; + my $component = $params->{'component'}; + + return if $params->{skip_extensions}; + + # Create a hash of current fields based on field names + my %field_hash = map { $_->name => $_ } @$$fields; + + my @tracking_flags; + if ($product) { + $params->{'product_id'} = $product->id; + $params->{'component_id'} = $component->id if $component; + $params->{'is_active'} = 1; + @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 = sort { $a->sortkey <=> $b->sortkey } 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 - }; - } + 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) = @_; - # if there are elements in the tracking_flags array, then they have been - # removed from the query, so we mustn't generate joins - return if scalar @{ $args->{search}->{tracking_flags} }; - - 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 ] - }; - } + my ($self, $args) = @_; + + # if there are elements in the tracking_flags array, then they have been + # removed from the query, so we mustn't generate joins + return if scalar @{$args->{search}->{tracking_flags}}; + + 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; - if (!Bugzilla::Bug->can($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} ||= '---'; - }; - no strict 'refs'; - *{"Bugzilla::Bug::$flag_name"} = $accessor; - } - if (!Bugzilla::Bug->can("set_$flag_name")) { - my $setter = sub { - my ($self, $value) = @_; - $value = ref($value) eq 'ARRAY' - ? $value->[0] - : $value; - $self->set($flag_name, $value); - }; - no strict 'refs'; - *{"Bugzilla::Bug::set_$flag_name"} = $setter; + 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; + if (!Bugzilla::Bug->can($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} ||= '---'; + }; + no strict 'refs'; + *{"Bugzilla::Bug::$flag_name"} = $accessor; + } + if (!Bugzilla::Bug->can("set_$flag_name")) { + my $setter = sub { + my ($self, $value) = @_; + $value = ref($value) eq 'ARRAY' ? $value->[0] : $value; + $self->set($flag_name, $value); + }; + no strict 'refs'; + *{"Bugzilla::Bug::set_$flag_name"} = $setter; } + } } 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); - } + 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, @_) - } - }; - } + 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); - - if ($operator eq 'isempty' or $operator eq 'isnotempty') { - $args->{'full_field'} = "$bugs_alias.value"; - } - else { - $args->{'full_field'} = "COALESCE($bugs_alias.value, '---')"; - } + 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); + + if ($operator eq 'isempty' or $operator eq 'isnotempty') { + $args->{'full_field'} = "$bugs_alias.value"; + } + else { + $args->{'full_field'} = "COALESCE($bugs_alias.value, '---')"; + } } sub request_cleanup { - foreach my $flag (@FLAG_CACHE) { - my $bug_flag = delete $flag->{bug_flag}; - if ($bug_flag) { - delete $bug_flag->{bug}; - delete $bug_flag->{tracking_flag}; - } - foreach my $value (@{ $flag->{values} }) { - delete $value->{tracking_flag}; - } + foreach my $flag (@FLAG_CACHE) { + my $bug_flag = delete $flag->{bug_flag}; + if ($bug_flag) { + delete $bug_flag->{bug}; + delete $bug_flag->{tracking_flag}; + } + foreach my $value (@{$flag->{values}}) { + delete $value->{tracking_flag}; } - @FLAG_CACHE = (); + } + @FLAG_CACHE = (); } sub bug_end_of_create { - my ($self, $args) = @_; - my $bug = $args->{'bug'}; - my $timestamp = $args->{'timestamp'}; - my $user = Bugzilla->user; + my ($self, $args) = @_; + my $bug = $args->{'bug'}; + my $timestamp = $args->{'timestamp'}; + my $user = Bugzilla->user; - my $params = Bugzilla->request_cache->{tracking_flags_create_params}; - return if !$params; + my $params = Bugzilla->request_cache->{tracking_flags_create_params}; + return if !$params; - my $tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->match({ - product => $bug->product, - component => $bug->component, - is_active => 1, + 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; - } + 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 object_end_of_set_all { - my ($self, $args) = @_; - my $object = $args->{object}; - my $params = $args->{params}; + my ($self, $args) = @_; + my $object = $args->{object}; + my $params = $args->{params}; - return unless $object->isa('Bugzilla::Bug'); + return unless $object->isa('Bugzilla::Bug'); - # Do not filter by product/component as we may be changing those - my $tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->match({ - bug_id => $object->id, - is_active => 1, + # Do not filter by product/component as we may be changing those + my $tracking_flags + = Bugzilla::Extension::TrackingFlags::Flag->match({ + bug_id => $object->id, is_active => 1, }); - foreach my $flag (@$tracking_flags) { - my $flag_name = $flag->name; - if (exists $params->{$flag_name}) { - my $value = ref($params->{$flag_name}) eq 'ARRAY' - ? $params->{$flag_name}->[0] - : $params->{$flag_name}; - $object->set($flag_name, $value); - } + foreach my $flag (@$tracking_flags) { + my $flag_name = $flag->name; + if (exists $params->{$flag_name}) { + my $value + = ref($params->{$flag_name}) eq 'ARRAY' + ? $params->{$flag_name}->[0] + : $params->{$flag_name}; + $object->set($flag_name, $value); } + } } sub bug_check_can_change_field { - my ($self, $args) = @_; - my ($bug, $field, $old_value, $new_value, $priv_results) - = @$args{qw(bug field old_value new_value priv_results)}; - - return if $field !~ /^cf_/ or $old_value eq $new_value; - return unless my $flag = Bugzilla::Extension::TrackingFlags::Flag->new({ name => $field }); - - if ($flag->can_set_value($new_value)) { - push @$priv_results, PRIVILEGES_REQUIRED_NONE; - } - else { - push @$priv_results, PRIVILEGES_REQUIRED_EMPOWERED; - } + my ($self, $args) = @_; + my ($bug, $field, $old_value, $new_value, $priv_results) + = @$args{qw(bug field old_value new_value priv_results)}; + + return if $field !~ /^cf_/ or $old_value eq $new_value; + return + unless my $flag + = Bugzilla::Extension::TrackingFlags::Flag->new({name => $field}); + + if ($flag->can_set_value($new_value)) { + push @$priv_results, PRIVILEGES_REQUIRED_NONE; + } + else { + push @$priv_results, PRIVILEGES_REQUIRED_EMPOWERED; + } } sub bug_end_of_update { - my ($self, $args) = @_; - my ($bug, $old_bug, $timestamp, $changes) - = @$args{qw(bug old_bug timestamp changes)}; - 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 ($self, $args) = @_; + my ($bug, $old_bug, $timestamp, $changes) + = @$args{qw(bug old_bug timestamp changes)}; + 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 $product_id = $bug->product_id; - my $component_id = $bug->component_id; - my $is_visible = sub { - $_->product_id == $product_id && (!$_->component_id || $_->component_id == $component_id); - }; + my $product_id = $bug->product_id; + my $component_id = $bug->component_id; + my $is_visible = sub { + $_->product_id == $product_id + && (!$_->component_id || $_->component_id == $component_id); + }; + + my (@flag_changes); + foreach my $flag (@$tracking_flags) { + my $flag_name = $flag->name; + my $new_value = $bug->$flag_name; + my $old_value = $old_bug->$flag_name; + + if ($flag->bug_flag->id) { + my $visibility = $flag->visibility; + if (none { $is_visible->() } @$visibility) { + push(@flag_changes, {flag => $flag, added => '---', removed => $new_value}); + next; + } + } - my (@flag_changes); - foreach my $flag (@$tracking_flags) { - my $flag_name = $flag->name; - my $new_value = $bug->$flag_name; - my $old_value = $old_bug->$flag_name; - - if ($flag->bug_flag->id) { - my $visibility = $flag->visibility; - if (none { $is_visible->() } @$visibility) { - push(@flag_changes, { flag => $flag, - added => '---', - removed => $new_value }); - next; - } - } + if ($new_value ne $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 }); - } + # 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'}; + 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); - } + 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); + $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; - } + # Update the name/value pair in the bug object + $bug->{$flag->name} = $added; + } } sub bug_end_of_create_validators { - my ($self, $args) = @_; - my $params = $args->{params}; - - # We need to stash away any params that are setting/updating tracking - # flags early on. Otherwise set_all or insert_create_data will complain. - my @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all; - my $cache = Bugzilla->request_cache->{tracking_flags_create_params} ||= {}; - foreach my $flag (@tracking_flags) { - my $flag_name = $flag->name; - if (defined $params->{$flag_name}) { - $cache->{$flag_name} = delete $params->{$flag_name}; - } + my ($self, $args) = @_; + my $params = $args->{params}; + + # We need to stash away any params that are setting/updating tracking + # flags early on. Otherwise set_all or insert_create_data will complain. + my @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all; + my $cache = Bugzilla->request_cache->{tracking_flags_create_params} ||= {}; + foreach my $flag (@tracking_flags) { + my $flag_name = $flag->name; + if (defined $params->{$flag_name}) { + $cache->{$flag_name} = delete $params->{$flag_name}; } + } } sub mailer_before_send { - my ($self, $args) = @_; - my $email = $args->{email}; + 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'); + # 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 $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); - } + 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); - } + 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); } + } } # Purpose: generically handle generating pretty blocking/status "flags" from # custom field names. sub quicksearch_map { - my ($self, $args) = @_; - my $map = $args->{'map'}; - - foreach my $name (keys %$map) { - if ($name =~ /^cf_(blocking|tracking|status)_([a-z]+)?(\d+)?$/) { - my $type = $1; - my $product = $2; - my $version = $3; - - if ($version) { - $version = join('.', split(//, $version)); - } - - my $pretty_name = $type; - if ($product) { - $pretty_name .= "-" . $product; - } - if ($version) { - $pretty_name .= $version; - } - - $map->{$pretty_name} = $name; - } + my ($self, $args) = @_; + my $map = $args->{'map'}; + + foreach my $name (keys %$map) { + if ($name =~ /^cf_(blocking|tracking|status)_([a-z]+)?(\d+)?$/) { + my $type = $1; + my $product = $2; + my $version = $3; + + if ($version) { + $version = join('.', split(//, $version)); + } + + my $pretty_name = $type; + if ($product) { + $pretty_name .= "-" . $product; + } + if ($version) { + $pretty_name .= $version; + } + + $map->{$pretty_name} = $name; } + } } sub reorg_move_component { - my ($self, $args) = @_; - my $new_product = $args->{new_product}; - my $component = $args->{component}; - - Bugzilla->dbh->do( - "UPDATE tracking_flags_visibility SET product_id=? WHERE component_id=?", - undef, - $new_product->id, $component->id, - ); + my ($self, $args) = @_; + my $new_product = $args->{new_product}; + my $component = $args->{component}; + + Bugzilla->dbh->do( + "UPDATE tracking_flags_visibility SET product_id=? WHERE component_id=?", + undef, $new_product->id, $component->id,); } sub sanitycheck_check { - my ($self, $args) = @_; - my $status = $args->{status}; + my ($self, $args) = @_; + my $status = $args->{status}; - $status->('tracking_flags_check'); + $status->('tracking_flags_check'); - my ($count) = Bugzilla->dbh->selectrow_array(" + my ($count) = Bugzilla->dbh->selectrow_array(" SELECT COUNT(*) FROM tracking_flags_visibility INNER JOIN components ON components.id = tracking_flags_visibility.component_id WHERE tracking_flags_visibility.product_id <> components.product_id "); - if ($count) { - $status->('tracking_flags_alert', undef, 'alert'); - $status->('tracking_flags_repair'); - } + if ($count) { + $status->('tracking_flags_alert', undef, 'alert'); + $status->('tracking_flags_repair'); + } } sub sanitycheck_repair { - my ($self, $args) = @_; - return unless Bugzilla->cgi->param('tracking_flags_repair'); + my ($self, $args) = @_; + return unless Bugzilla->cgi->param('tracking_flags_repair'); - my $status = $args->{'status'}; - my $dbh = Bugzilla->dbh; - $status->('tracking_flags_repairing'); + my $status = $args->{'status'}; + my $dbh = Bugzilla->dbh; + $status->('tracking_flags_repairing'); - my $rows = $dbh->selectall_arrayref(" + my $rows = $dbh->selectall_arrayref(" SELECT DISTINCT tracking_flags_visibility.product_id AS bad_product_id, components.product_id AS good_product_id, tracking_flags_visibility.component_id FROM tracking_flags_visibility INNER JOIN components ON components.id = tracking_flags_visibility.component_id WHERE tracking_flags_visibility.product_id <> components.product_id - ", - { Slice => {} } - ); - foreach my $row (@$rows) { - $dbh->do(" + ", {Slice => {}}); + foreach my $row (@$rows) { + $dbh->do(" UPDATE tracking_flags_visibility SET product_id=? WHERE product_id=? AND component_id=? - ", undef, - $row->{good_product_id}, - $row->{bad_product_id}, - $row->{component_id}, - ); - } + ", undef, $row->{good_product_id}, $row->{bad_product_id}, + $row->{component_id},); + } } __PACKAGE__->NAME; diff --git a/extensions/TrackingFlags/bin/bug_825946.pl b/extensions/TrackingFlags/bin/bug_825946.pl index 896dc5448..8a340175b 100755 --- a/extensions/TrackingFlags/bin/bug_825946.pl +++ b/extensions/TrackingFlags/bin/bug_825946.pl @@ -13,8 +13,8 @@ use 5.10.1; use lib qw(. lib local/lib/perl5); BEGIN { - use Bugzilla; - Bugzilla->extensions; + use Bugzilla; + Bugzilla->extensions; } use Bugzilla::Constants qw( USAGE_MODE_CMDLINE ); @@ -51,8 +51,8 @@ SQL my %visible; foreach my $row (@$tf_vis) { - my ($tracking_flag_id, $product_id, $component_id) = @$row; - $visible{$tracking_flag_id}{$product_id}{$component_id // 'ALL'} = 1; + my ($tracking_flag_id, $product_id, $component_id) = @$row; + $visible{$tracking_flag_id}{$product_id}{$component_id // 'ALL'} = 1; } my %bugs = map { $_->[0] => 1 } @$tf_bugs; @@ -66,13 +66,16 @@ my $removed = 0; $dbh->bz_start_transaction(); my ($timestamp) = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'); foreach my $tf_bug (@$tf_bugs) { - my ($flag_name, $value, $bug_id, $tf_id, $product_id, $component_id) = @$tf_bug; - unless ($visible{$tf_id}{$product_id}{$component_id} || $visible{$tf_id}{$product_id}{ALL}) { - $dbh->do("DELETE FROM tracking_flags_bugs WHERE tracking_flag_id = ? AND bug_id = ?", - undef, $tf_id, $bug_id); - LogActivityEntry($bug_id, $flag_name, $value, '---', $user->id, $timestamp); - $removed++; - } + my ($flag_name, $value, $bug_id, $tf_id, $product_id, $component_id) = @$tf_bug; + unless ($visible{$tf_id}{$product_id}{$component_id} + || $visible{$tf_id}{$product_id}{ALL}) + { + $dbh->do( + "DELETE FROM tracking_flags_bugs WHERE tracking_flag_id = ? AND bug_id = ?", + undef, $tf_id, $bug_id); + LogActivityEntry($bug_id, $flag_name, $value, '---', $user->id, $timestamp); + $removed++; + } } $dbh->bz_commit_transaction(); diff --git a/extensions/TrackingFlags/bin/bulk_flag_clear.pl b/extensions/TrackingFlags/bin/bulk_flag_clear.pl index 305fbf883..470269f13 100755 --- a/extensions/TrackingFlags/bin/bulk_flag_clear.pl +++ b/extensions/TrackingFlags/bin/bulk_flag_clear.pl @@ -13,8 +13,8 @@ use 5.10.1; use lib qw(. lib local/lib/perl5); BEGIN { - use Bugzilla; - Bugzilla->extensions; + use Bugzilla; + Bugzilla->extensions; } use Bugzilla::Constants; @@ -27,21 +27,14 @@ use Getopt::Long; Bugzilla->usage_mode(USAGE_MODE_CMDLINE); my $config = {}; -GetOptions( - $config, - "trace=i", - "update_db", - "flag=s", - "modified_before=s", - "modified_after=s", - "value=s" -) or exit; +GetOptions($config, "trace=i", "update_db", "flag=s", "modified_before=s", + "modified_after=s", "value=s") + or exit; unless ($config->{flag} - && ($config->{modified_before} - || $config->{modified_after} - || $config->{value})) + && ($config->{modified_before} || $config->{modified_after} || $config->{value}) + ) { - die <<EOF; + die <<EOF; $0 clears tracking flags matching the specified criteria. the last-modified will be updated, however bugmail will not be generated. @@ -64,23 +57,24 @@ EOF my (@where, @values); -my $flag = Bugzilla::Extension::TrackingFlags::Flag->check({ name => $config->{flag} }); -push @where, 'tracking_flags_bugs.tracking_flag_id = ?'; +my $flag + = Bugzilla::Extension::TrackingFlags::Flag->check({name => $config->{flag}}); +push @where, 'tracking_flags_bugs.tracking_flag_id = ?'; push @values, $flag->flag_id; if ($config->{modified_before}) { - push @where, 'bugs.delta_ts < ?'; - push @values, $config->{modified_before}; + push @where, 'bugs.delta_ts < ?'; + push @values, $config->{modified_before}; } if ($config->{modified_after}) { - push @where, 'bugs.delta_ts > ?'; - push @values, $config->{modified_after}; + push @where, 'bugs.delta_ts > ?'; + push @values, $config->{modified_after}; } if ($config->{value}) { - push @where, 'tracking_flags_bugs.value = ?'; - push @values, $config->{value}; + push @where, 'tracking_flags_bugs.value = ?'; + push @values, $config->{value}; } my $sql = " @@ -99,37 +93,35 @@ $dbh->{TraceLevel} = $config->{trace} if $config->{trace}; my $bug_ids = $dbh->selectcol_arrayref($sql, undef, @values); if (!@$bug_ids) { - die "no matching bugs found\n"; + die "no matching bugs found\n"; } if (!$config->{update_db}) { - print "bugs found: ", scalar(@$bug_ids), "\n\n", join(',', @$bug_ids), "\n\n"; - print "--update_db not provided, no changes made to the database\n"; - exit; + print "bugs found: ", scalar(@$bug_ids), "\n\n", join(',', @$bug_ids), "\n\n"; + print "--update_db not provided, no changes made to the database\n"; + exit; } # update bugs -my $nobody = Bugzilla::User->check({ name => Bugzilla->params->{'nobody_user'} }); +my $nobody = Bugzilla::User->check({name => Bugzilla->params->{'nobody_user'}}); + # put our nobody user into all groups to avoid permissions issues $nobody->{groups} = [Bugzilla::Group->get_all]; Bugzilla->set_user($nobody); foreach my $bug_id (@$bug_ids) { - print "updating bug $bug_id\n"; - $dbh->bz_start_transaction; - - # update the bug - # this will deal with history for us but not send bugmail - my $bug = Bugzilla::Bug->check({ id => $bug_id }); - $bug->set_all({ $flag->name => '---' }); - $bug->update; - - # update lastdiffed to skip bugmail for this change - $dbh->do( - "UPDATE bugs SET lastdiffed = delta_ts WHERE bug_id = ?", - undef, - $bug->id - ); - $dbh->bz_commit_transaction; + print "updating bug $bug_id\n"; + $dbh->bz_start_transaction; + + # update the bug + # this will deal with history for us but not send bugmail + my $bug = Bugzilla::Bug->check({id => $bug_id}); + $bug->set_all({$flag->name => '---'}); + $bug->update; + + # update lastdiffed to skip bugmail for this change + $dbh->do("UPDATE bugs SET lastdiffed = delta_ts WHERE bug_id = ?", + undef, $bug->id); + $dbh->bz_commit_transaction; } diff --git a/extensions/TrackingFlags/bin/migrate_tracking_flags.pl b/extensions/TrackingFlags/bin/migrate_tracking_flags.pl index cd55f5f83..97b8eccd5 100755 --- a/extensions/TrackingFlags/bin/migrate_tracking_flags.pl +++ b/extensions/TrackingFlags/bin/migrate_tracking_flags.pl @@ -16,8 +16,8 @@ use 5.10.1; use lib qw(. lib local/lib/perl5); BEGIN { - use Bugzilla; - Bugzilla->extensions; + use Bugzilla; + Bugzilla->extensions; } use Bugzilla::Constants; @@ -39,10 +39,7 @@ use Data::Dumper; Bugzilla->usage_mode(USAGE_MODE_CMDLINE); my ($dry_run, $trace) = (0, 0); -GetOptions( - "dry-run" => \$dry_run, - "trace" => \$trace, -) or exit; +GetOptions("dry-run" => \$dry_run, "trace" => \$trace,) or exit; my $dbh = Bugzilla->dbh; @@ -52,263 +49,271 @@ my %product_cache; my %component_cache; sub migrate_flag_visibility { - my ($new_flag, $products) = @_; + my ($new_flag, $products) = @_; + + # Create product/component visibility + foreach my $prod_name (keys %$products) { + $product_cache{$prod_name} ||= Bugzilla::Product->new({name => $prod_name}); + if (!$product_cache{$prod_name}) { + warn "No such product $prod_name\n"; + next; + } - # Create product/component visibility - foreach my $prod_name (keys %$products) { - $product_cache{$prod_name} ||= Bugzilla::Product->new({ name => $prod_name }); - if (!$product_cache{$prod_name}) { - warn "No such product $prod_name\n"; - next; + # If no components specified then we do Product/__any__ + # otherwise, we enter an entry for each Product/Component + my $components = $products->{$prod_name}; + if (!@$components) { + Bugzilla::Extension::TrackingFlags::Flag::Visibility->create({ + tracking_flag_id => $new_flag->flag_id, + product_id => $product_cache{$prod_name}->id, + component_id => undef + }); + } + else { + foreach my $comp_name (@$components) { + my $comp_matches = []; + + # If the component is a regexp, we need to find all components + # matching the regex and insert each individually + if (ref $comp_name eq 'Regexp') { + my $comp_re = $comp_name; + $comp_re =~ s/\?\-xism://; + $comp_re =~ s/\(//; + $comp_re =~ s/\)//; + $comp_matches = $dbh->selectcol_arrayref( + 'SELECT components.name FROM components + WHERE components.product_id = ? + AND ' + . $dbh->sql_regexp('components.name', $dbh->quote($comp_re)) . ' + ORDER BY components.name', undef, $product_cache{$prod_name}->id + ); + } + else { + $comp_matches = [$comp_name]; } - # If no components specified then we do Product/__any__ - # otherwise, we enter an entry for each Product/Component - my $components = $products->{$prod_name}; - if (!@$components) { - Bugzilla::Extension::TrackingFlags::Flag::Visibility->create({ - tracking_flag_id => $new_flag->flag_id, - product_id => $product_cache{$prod_name}->id, - component_id => undef + foreach my $comp_match (@$comp_matches) { + $component_cache{"${prod_name}:${comp_match}"} + ||= Bugzilla::Component->new({ + name => $comp_match, product => $product_cache{$prod_name} }); + if (!$component_cache{"${prod_name}:${comp_match}"}) { + warn "No such product $prod_name and component $comp_match\n"; + next; + } + + Bugzilla::Extension::TrackingFlags::Flag::Visibility->create({ + tracking_flag_id => $new_flag->flag_id, + product_id => $product_cache{$prod_name}->id, + component_id => $component_cache{"${prod_name}:${comp_match}"}->id, + }); } - else { - foreach my $comp_name (@$components) { - my $comp_matches = []; - # If the component is a regexp, we need to find all components - # matching the regex and insert each individually - if (ref $comp_name eq 'Regexp') { - my $comp_re = $comp_name; - $comp_re =~ s/\?\-xism://; - $comp_re =~ s/\(//; - $comp_re =~ s/\)//; - $comp_matches = $dbh->selectcol_arrayref( - 'SELECT components.name FROM components - WHERE components.product_id = ? - AND ' . $dbh->sql_regexp('components.name', $dbh->quote($comp_re)) . ' - ORDER BY components.name', - undef, - $product_cache{$prod_name}->id); - } - else { - $comp_matches = [ $comp_name ]; - } - - foreach my $comp_match (@$comp_matches) { - $component_cache{"${prod_name}:${comp_match}"} - ||= Bugzilla::Component->new({ name => $comp_match, - product => $product_cache{$prod_name} }); - if (!$component_cache{"${prod_name}:${comp_match}"}) { - warn "No such product $prod_name and component $comp_match\n"; - next; - } - - Bugzilla::Extension::TrackingFlags::Flag::Visibility->create({ - tracking_flag_id => $new_flag->flag_id, - product_id => $product_cache{$prod_name}->id, - component_id => $component_cache{"${prod_name}:${comp_match}"}->id, - }); - } - } - } + } } + } } sub migrate_flag_values { - my ($new_flag, $field) = @_; - - print "Migrating flag values..."; - - my %blocking_trusted_requesters - = %{$Bugzilla::Extension::BMO::Data::blocking_trusted_requesters}; - my %blocking_trusted_setters - = %{$Bugzilla::Extension::BMO::Data::blocking_trusted_setters}; - my %status_trusted_wanters - = %{$Bugzilla::Extension::BMO::Data::status_trusted_wanters}; - my %status_trusted_setters - = %{$Bugzilla::Extension::BMO::Data::status_trusted_setters}; - - my %group_cache; - foreach my $value (@{ $field->legal_values }) { - my $group_name = 'everyone'; - - if ($field->name =~ /^cf_(blocking|tracking)_/) { - if ($value->name ne '---' && $value->name !~ '\?$') { - $group_name = get_setter_group($field->name, \%blocking_trusted_setters); - } - if ($value->name eq '?') { - $group_name = get_setter_group($field->name, \%blocking_trusted_requesters); - } - } elsif ($field->name =~ /^cf_status_/) { - if ($value->name eq 'wanted') { - $group_name = get_setter_group($field->name, \%status_trusted_wanters); - } elsif ($value->name ne '---' && $value->name ne '?') { - $group_name = get_setter_group($field->name, \%status_trusted_setters); - } - } + my ($new_flag, $field) = @_; + + print "Migrating flag values..."; + + my %blocking_trusted_requesters + = %{$Bugzilla::Extension::BMO::Data::blocking_trusted_requesters}; + my %blocking_trusted_setters + = %{$Bugzilla::Extension::BMO::Data::blocking_trusted_setters}; + my %status_trusted_wanters + = %{$Bugzilla::Extension::BMO::Data::status_trusted_wanters}; + my %status_trusted_setters + = %{$Bugzilla::Extension::BMO::Data::status_trusted_setters}; + + my %group_cache; + foreach my $value (@{$field->legal_values}) { + my $group_name = 'everyone'; + + if ($field->name =~ /^cf_(blocking|tracking)_/) { + if ($value->name ne '---' && $value->name !~ '\?$') { + $group_name = get_setter_group($field->name, \%blocking_trusted_setters); + } + if ($value->name eq '?') { + $group_name = get_setter_group($field->name, \%blocking_trusted_requesters); + } + } + elsif ($field->name =~ /^cf_status_/) { + if ($value->name eq 'wanted') { + $group_name = get_setter_group($field->name, \%status_trusted_wanters); + } + elsif ($value->name ne '---' && $value->name ne '?') { + $group_name = get_setter_group($field->name, \%status_trusted_setters); + } + } - $group_cache{$group_name} ||= Bugzilla::Group->new({ name => $group_name }); - $group_cache{$group_name} || die "Setter group '$group_name' does not exist"; + $group_cache{$group_name} ||= Bugzilla::Group->new({name => $group_name}); + $group_cache{$group_name} || die "Setter group '$group_name' does not exist"; - Bugzilla::Extension::TrackingFlags::Flag::Value->create({ - tracking_flag_id => $new_flag->flag_id, - value => $value->name, - setter_group_id => $group_cache{$group_name}->id, - sortkey => $value->sortkey, - is_active => $value->is_active - }); - } + Bugzilla::Extension::TrackingFlags::Flag::Value->create({ + tracking_flag_id => $new_flag->flag_id, + value => $value->name, + setter_group_id => $group_cache{$group_name}->id, + sortkey => $value->sortkey, + is_active => $value->is_active + }); + } - print "done.\n"; + print "done.\n"; } sub get_setter_group { - my ($field, $trusted) = @_; - my $setter_group = $trusted->{'_default'} || ""; - foreach my $dfield (keys %$trusted) { - if ($field =~ $dfield) { - $setter_group = $trusted->{$dfield}; - } + my ($field, $trusted) = @_; + my $setter_group = $trusted->{'_default'} || ""; + foreach my $dfield (keys %$trusted) { + if ($field =~ $dfield) { + $setter_group = $trusted->{$dfield}; } - return $setter_group; + } + return $setter_group; } sub migrate_flag_bugs { - my ($new_flag, $field) = @_; + my ($new_flag, $field) = @_; - print "Migrating bug values..."; + print "Migrating bug values..."; - my $bugs = $dbh->selectall_arrayref("SELECT bug_id, " . $field->name . " + my $bugs = $dbh->selectall_arrayref( + "SELECT bug_id, " . $field->name . " FROM bugs WHERE " . $field->name . " != '---' - ORDER BY bug_id"); - local $| = 1; - my $count = 1; - my $total = scalar @$bugs; - foreach my $row (@$bugs) { - my ($id, $value) = @$row; - indicate_progress({ current => $count++, total => $total, every => 25 }); - Bugzilla::Extension::TrackingFlags::Flag::Bug->create({ - tracking_flag_id => $new_flag->flag_id, - bug_id => $id, - value => $value, - - }); - } - - print "done.\n"; + ORDER BY bug_id" + ); + local $| = 1; + my $count = 1; + my $total = scalar @$bugs; + foreach my $row (@$bugs) { + my ($id, $value) = @$row; + indicate_progress({current => $count++, total => $total, every => 25}); + Bugzilla::Extension::TrackingFlags::Flag::Bug->create({ + tracking_flag_id => $new_flag->flag_id, + bug_id => $id, + value => $value, + + }); + } + + print "done.\n"; } sub migrate_flag_activity { - my ($new_flag, $field) = @_; + my ($new_flag, $field) = @_; - print "Migating flag activity..."; + print "Migating flag activity..."; - my $new_field = Bugzilla::Field->new({ name => $new_flag->name }); - $dbh->do("UPDATE bugs_activity SET fieldid = ? WHERE fieldid = ?", - undef, $new_field->id, $field->id); + my $new_field = Bugzilla::Field->new({name => $new_flag->name}); + $dbh->do("UPDATE bugs_activity SET fieldid = ? WHERE fieldid = ?", + undef, $new_field->id, $field->id); - print "done.\n"; + print "done.\n"; } sub do_migration { - my $bmo_tracking_flags = $Bugzilla::Extension::BMO::Data::cf_visible_in_products; - my $bmo_project_flags = $Bugzilla::Extension::BMO::Data::cf_project_flags; - my $bmo_disabled_flags = $Bugzilla::Extension::BMO::Data::cf_disabled_flags; + my $bmo_tracking_flags + = $Bugzilla::Extension::BMO::Data::cf_visible_in_products; + my $bmo_project_flags = $Bugzilla::Extension::BMO::Data::cf_project_flags; + my $bmo_disabled_flags = $Bugzilla::Extension::BMO::Data::cf_disabled_flags; - my $fields = Bugzilla::Field->match({ custom => 1, - type => FIELD_TYPE_SINGLE_SELECT }); + my $fields + = Bugzilla::Field->match({custom => 1, type => FIELD_TYPE_SINGLE_SELECT}); - my @drop_columns; - foreach my $field (@$fields) { - next if $field->name !~ /^cf_(blocking|tracking|status)_/; + my @drop_columns; + foreach my $field (@$fields) { + next if $field->name !~ /^cf_(blocking|tracking|status)_/; - foreach my $field_re (keys %$bmo_tracking_flags) { - next if $field->name !~ $field_re; + foreach my $field_re (keys %$bmo_tracking_flags) { + next if $field->name !~ $field_re; - # Create the new tracking flag if not exists - my $new_flag - = Bugzilla::Extension::TrackingFlags::Flag->new({ name => $field->name }); + # Create the new tracking flag if not exists + my $new_flag + = Bugzilla::Extension::TrackingFlags::Flag->new({name => $field->name}); - next if $new_flag; + next if $new_flag; - print "----------------------------------\n" . - "Migrating custom tracking field " . $field->name . "...\n"; + print "----------------------------------\n" + . "Migrating custom tracking field " + . $field->name . "...\n"; - my $new_flag_name = $field->name . "_new"; # Temporary name til we delete the old + my $new_flag_name = $field->name . "_new"; # Temporary name til we delete the old - my $type = grep($field->name =~ $_, @$bmo_project_flags) - ? 'project' - : 'tracking'; + my $type + = grep($field->name =~ $_, @$bmo_project_flags) ? 'project' : 'tracking'; - my $is_active = grep($_ eq $field->name, @$bmo_disabled_flags) ? 0 : 1; + my $is_active = grep($_ eq $field->name, @$bmo_disabled_flags) ? 0 : 1; - $new_flag = Bugzilla::Extension::TrackingFlags::Flag->create({ - name => $new_flag_name, - description => $field->description, - type => $type, - sortkey => $field->sortkey, - is_active => $is_active, - enter_bug => $field->enter_bug, - }); + $new_flag = Bugzilla::Extension::TrackingFlags::Flag->create({ + name => $new_flag_name, + description => $field->description, + type => $type, + sortkey => $field->sortkey, + is_active => $is_active, + enter_bug => $field->enter_bug, + }); - migrate_flag_visibility($new_flag, $bmo_tracking_flags->{$field_re}); + migrate_flag_visibility($new_flag, $bmo_tracking_flags->{$field_re}); - migrate_flag_values($new_flag, $field); + migrate_flag_values($new_flag, $field); - migrate_flag_bugs($new_flag, $field); + migrate_flag_bugs($new_flag, $field); - migrate_flag_activity($new_flag, $field); + migrate_flag_activity($new_flag, $field); - push(@drop_columns, $field->name); + push(@drop_columns, $field->name); - # Remove the old flag entry from fielddefs - $dbh->do("DELETE FROM fielddefs WHERE name = ?", - undef, $field->name); + # Remove the old flag entry from fielddefs + $dbh->do("DELETE FROM fielddefs WHERE name = ?", undef, $field->name); - # Rename the new flag - $dbh->do("UPDATE fielddefs SET name = ? WHERE name = ?", - undef, $field->name, $new_flag_name); + # Rename the new flag + $dbh->do("UPDATE fielddefs SET name = ? WHERE name = ?", + undef, $field->name, $new_flag_name); - $new_flag->set_name($field->name); - $new_flag->update; + $new_flag->set_name($field->name); + $new_flag->update; - # more than one regex could possibly match but we only want the first one - last; - } + # more than one regex could possibly match but we only want the first one + last; } + } - # Drop each custom flag's value table and the column from the bz schema object - if (!$dry_run && @drop_columns) { - print "Dropping value tables and updating bz schema object...\n"; + # Drop each custom flag's value table and the column from the bz schema object + if (!$dry_run && @drop_columns) { + print "Dropping value tables and updating bz schema object...\n"; - foreach my $column (@drop_columns) { - # Drop the values table - $dbh->bz_drop_table($column); + foreach my $column (@drop_columns) { - # Drop the bugs table column from the bz schema object - $dbh->_bz_real_schema->delete_column('bugs', $column); - $dbh->_bz_store_real_schema; - } + # Drop the values table + $dbh->bz_drop_table($column); - # Do the one alter table to drop all columns at once - $dbh->do("ALTER TABLE bugs DROP COLUMN " . join(", DROP COLUMN ", @drop_columns)); + # Drop the bugs table column from the bz schema object + $dbh->_bz_real_schema->delete_column('bugs', $column); + $dbh->_bz_store_real_schema; } + + # Do the one alter table to drop all columns at once + $dbh->do( + "ALTER TABLE bugs DROP COLUMN " . join(", DROP COLUMN ", @drop_columns)); + } } # Start Main eval { - if ($dry_run) { - print "** dry run : no changes to the database will be made **\n"; - $dbh->bz_start_transaction(); - } - print "Starting migration...\n"; - do_migration(); - $dbh->bz_rollback_transaction() if $dry_run; - print "All done!\n"; + if ($dry_run) { + print "** dry run : no changes to the database will be made **\n"; + $dbh->bz_start_transaction(); + } + print "Starting migration...\n"; + do_migration(); + $dbh->bz_rollback_transaction() if $dry_run; + print "All done!\n"; }; if ($@) { - $dbh->bz_rollback_transaction() if $dry_run; - die "$@" if $@; + $dbh->bz_rollback_transaction() if $dry_run; + die "$@" if $@; } diff --git a/extensions/TrackingFlags/lib/Admin.pm b/extensions/TrackingFlags/lib/Admin.pm index 50a0e0a61..e6ff9a31a 100644 --- a/extensions/TrackingFlags/lib/Admin.pm +++ b/extensions/TrackingFlags/lib/Admin.pm @@ -30,8 +30,8 @@ use Scalar::Util qw(blessed); use base qw(Exporter); our @EXPORT = qw( - admin_list - admin_edit + admin_list + admin_edit ); # @@ -39,177 +39,189 @@ our @EXPORT = qw( # sub admin_list { - my ($vars) = @_; - $vars->{show_bug_counts} = Bugzilla->input_params->{show_bug_counts}; - $vars->{flags} = [ Bugzilla::Extension::TrackingFlags::Flag->get_all() ]; + my ($vars) = @_; + $vars->{show_bug_counts} = Bugzilla->input_params->{show_bug_counts}; + $vars->{flags} = [Bugzilla::Extension::TrackingFlags::Flag->get_all()]; } sub admin_edit { - my ($vars, $page) = @_; - my $input = Bugzilla->input_params; - - $vars->{groups} = _groups_to_json(); - $vars->{mode} = $input->{mode} || 'new'; - $vars->{flag_id} = $input->{flag_id} || 0; - $vars->{tracking_flag_types} = FLAG_TYPES; - - if ($input->{delete}) { - my $token = $input->{token}; - check_hash_token($token, ['tracking_flags_edit']); - delete_token($token); - - my $flag = Bugzilla::Extension::TrackingFlags::Flag->new($vars->{flag_id}) - || ThrowCodeError('tracking_flags_invalid_item_id', { item => 'flag', id => $vars->{flag_id} }); - $flag->remove_from_db(); - - $vars->{message} = 'tracking_flag_deleted'; - $vars->{flag} = $flag; - $vars->{flags} = [ Bugzilla::Extension::TrackingFlags::Flag->get_all() ]; - - print Bugzilla->cgi->header; - my $template = Bugzilla->template; - $template->process('pages/tracking_flags_admin_list.html.tmpl', $vars) - || ThrowTemplateError($template->error()); - exit; - - } elsif ($input->{save}) { - my $token = $input->{token}; - check_hash_token($token, ['tracking_flags_edit']); - delete_token($token); - - my ($flag, $values, $visibilities) = _load_from_input($input, $vars); - _validate($flag, $values, $visibilities); - my $flag_obj = _update_db($flag, $values, $visibilities); - - $vars->{flag} = $flag_obj; - $vars->{values} = _flag_values_to_json($values); - $vars->{visibility} = _flag_visibility_to_json($visibilities); - - if ($vars->{mode} eq 'new') { - $vars->{message} = 'tracking_flag_created'; - } else { - $vars->{message} = 'tracking_flag_updated'; - } - $vars->{mode} = 'edit'; - - } else { - # initial load - - if ($vars->{mode} eq 'edit') { - # edit - straight load - my $flag = Bugzilla::Extension::TrackingFlags::Flag->new($vars->{flag_id}) - || ThrowCodeError('tracking_flags_invalid_item_id', { item => 'flag', id => $vars->{flag_id} }); - $vars->{flag} = $flag; - $vars->{values} = _flag_values_to_json($flag->values); - $vars->{visibility} = _flag_visibility_to_json($flag->visibility); - $vars->{can_delete} = !$flag->bug_count; - - } elsif ($vars->{mode} eq 'copy') { - # copy - load the source flag - $vars->{mode} = 'new'; - my $flag = Bugzilla::Extension::TrackingFlags::Flag->new($input->{copy_from}) - || ThrowCodeError('tracking_flags_invalid_item_id', { item => 'flag', id => $vars->{copy_from} }); - - # increment the number at the end of the name and description - if ($flag->name =~ /^(\D+)(\d+)$/) { - $flag->set_name("$1" . ($2 + 1)); - } - if ($flag->description =~ /^(\D+)([\d\.]+)$/) { - my $description = $1; - my $version = $2; - if ($version =~ /\./) { - my ($major, $minor) = split(/\./, $version); - $minor++; - $version = "$major.$minor"; - } - else { - $version++; - } - $flag->set_description($description . $version); - } - $flag->set_sortkey(_next_unique_sortkey($flag->sortkey)); - $flag->set_type($flag->flag_type); - $flag->set_enter_bug($flag->enter_bug); - # always default new flags as active, even when copying an inactive one - $flag->set_is_active(1); - - $vars->{flag} = $flag; - $vars->{values} = _flag_values_to_json($flag->values, 1); - $vars->{visibility} = _flag_visibility_to_json($flag->visibility, 1); - $vars->{can_delete} = 0; - - } else { - $vars->{mode} = 'new'; - $vars->{flag} = { - sortkey => 0, - enter_bug => 1, - is_active => 1, - }; - $vars->{values} = _flag_values_to_json([ - { - id => 0, - value => '---', - setter_group_id => '', - is_active => 1, - comment => '', - }, - ]); - $vars->{visibility} = ''; - $vars->{can_delete} = 0; - } + my ($vars, $page) = @_; + my $input = Bugzilla->input_params; + + $vars->{groups} = _groups_to_json(); + $vars->{mode} = $input->{mode} || 'new'; + $vars->{flag_id} = $input->{flag_id} || 0; + $vars->{tracking_flag_types} = FLAG_TYPES; + + if ($input->{delete}) { + my $token = $input->{token}; + check_hash_token($token, ['tracking_flags_edit']); + delete_token($token); + + my $flag + = Bugzilla::Extension::TrackingFlags::Flag->new($vars->{flag_id}) + || ThrowCodeError('tracking_flags_invalid_item_id', + {item => 'flag', id => $vars->{flag_id}}); + $flag->remove_from_db(); + + $vars->{message} = 'tracking_flag_deleted'; + $vars->{flag} = $flag; + $vars->{flags} = [Bugzilla::Extension::TrackingFlags::Flag->get_all()]; + + print Bugzilla->cgi->header; + my $template = Bugzilla->template; + $template->process('pages/tracking_flags_admin_list.html.tmpl', $vars) + || ThrowTemplateError($template->error()); + exit; + + } + elsif ($input->{save}) { + my $token = $input->{token}; + check_hash_token($token, ['tracking_flags_edit']); + delete_token($token); + + my ($flag, $values, $visibilities) = _load_from_input($input, $vars); + _validate($flag, $values, $visibilities); + my $flag_obj = _update_db($flag, $values, $visibilities); + + $vars->{flag} = $flag_obj; + $vars->{values} = _flag_values_to_json($values); + $vars->{visibility} = _flag_visibility_to_json($visibilities); + + if ($vars->{mode} eq 'new') { + $vars->{message} = 'tracking_flag_created'; } -} + else { + $vars->{message} = 'tracking_flag_updated'; + } + $vars->{mode} = 'edit'; + + } + else { + # initial load + + if ($vars->{mode} eq 'edit') { + + # edit - straight load + my $flag + = Bugzilla::Extension::TrackingFlags::Flag->new($vars->{flag_id}) + || ThrowCodeError('tracking_flags_invalid_item_id', + {item => 'flag', id => $vars->{flag_id}}); + $vars->{flag} = $flag; + $vars->{values} = _flag_values_to_json($flag->values); + $vars->{visibility} = _flag_visibility_to_json($flag->visibility); + $vars->{can_delete} = !$flag->bug_count; -sub _load_from_input { - my ($input, $vars) = @_; - - # flag - - my $flag = { - id => ($input->{mode} eq 'edit' ? $input->{flag_id} : 0), - name => trim($input->{flag_name} || ''), - description => trim($input->{flag_desc} || ''), - sortkey => $input->{flag_sort} || 0, - type => trim($input->{flag_type} || ''), - enter_bug => $input->{flag_enter_bug} ? 1 : 0, - is_active => $input->{flag_active} ? 1 : 0, - }; - detaint_natural($flag->{id}); - detaint_natural($flag->{sortkey}); - detaint_natural($flag->{enter_bug}); - detaint_natural($flag->{is_active}); - - # values - - my $values = decode_json($input->{values} || '[]'); - foreach my $value (@$values) { - $value->{value} = '' unless exists $value->{value} && defined $value->{value}; - $value->{setter_group_id} = '' unless $value->{setter_group_id}; - $value->{is_active} = $value->{is_active} ? 1 : 0; } + elsif ($vars->{mode} eq 'copy') { + + # copy - load the source flag + $vars->{mode} = 'new'; + my $flag + = Bugzilla::Extension::TrackingFlags::Flag->new($input->{copy_from}) + || ThrowCodeError('tracking_flags_invalid_item_id', + {item => 'flag', id => $vars->{copy_from}}); + + # increment the number at the end of the name and description + if ($flag->name =~ /^(\D+)(\d+)$/) { + $flag->set_name("$1" . ($2 + 1)); + } + if ($flag->description =~ /^(\D+)([\d\.]+)$/) { + my $description = $1; + my $version = $2; + if ($version =~ /\./) { + my ($major, $minor) = split(/\./, $version); + $minor++; + $version = "$major.$minor"; + } + else { + $version++; + } + $flag->set_description($description . $version); + } + $flag->set_sortkey(_next_unique_sortkey($flag->sortkey)); + $flag->set_type($flag->flag_type); + $flag->set_enter_bug($flag->enter_bug); + + # always default new flags as active, even when copying an inactive one + $flag->set_is_active(1); - # vibility + $vars->{flag} = $flag; + $vars->{values} = _flag_values_to_json($flag->values, 1); + $vars->{visibility} = _flag_visibility_to_json($flag->visibility, 1); + $vars->{can_delete} = 0; - my $visibilities = decode_json($input->{visibility} || '[]'); - foreach my $visibility (@$visibilities) { - $visibility->{product} = '' unless exists $visibility->{product} && defined $visibility->{product}; - $visibility->{component} = '' unless exists $visibility->{component} && defined $visibility->{component}; } + else { + $vars->{mode} = 'new'; + $vars->{flag} = {sortkey => 0, enter_bug => 1, is_active => 1,}; + $vars->{values} = _flag_values_to_json([ + { + id => 0, + value => '---', + setter_group_id => '', + is_active => 1, + comment => '', + }, + ]); + $vars->{visibility} = ''; + $vars->{can_delete} = 0; + } + } +} - return ($flag, $values, $visibilities); +sub _load_from_input { + my ($input, $vars) = @_; + + # flag + + my $flag = { + id => ($input->{mode} eq 'edit' ? $input->{flag_id} : 0), + name => trim($input->{flag_name} || ''), + description => trim($input->{flag_desc} || ''), + sortkey => $input->{flag_sort} || 0, + type => trim($input->{flag_type} || ''), + enter_bug => $input->{flag_enter_bug} ? 1 : 0, + is_active => $input->{flag_active} ? 1 : 0, + }; + detaint_natural($flag->{id}); + detaint_natural($flag->{sortkey}); + detaint_natural($flag->{enter_bug}); + detaint_natural($flag->{is_active}); + + # values + + my $values = decode_json($input->{values} || '[]'); + foreach my $value (@$values) { + $value->{value} = '' unless exists $value->{value} && defined $value->{value}; + $value->{setter_group_id} = '' unless $value->{setter_group_id}; + $value->{is_active} = $value->{is_active} ? 1 : 0; + } + + # vibility + + my $visibilities = decode_json($input->{visibility} || '[]'); + foreach my $visibility (@$visibilities) { + $visibility->{product} = '' + unless exists $visibility->{product} && defined $visibility->{product}; + $visibility->{component} = '' + unless exists $visibility->{component} && defined $visibility->{component}; + } + + return ($flag, $values, $visibilities); } sub _next_unique_sortkey { - my ($sortkey) = @_; + my ($sortkey) = @_; - my %current; - foreach my $flag (Bugzilla::Extension::TrackingFlags::Flag->get_all()) { - $current{$flag->sortkey} = 1; - } + my %current; + foreach my $flag (Bugzilla::Extension::TrackingFlags::Flag->get_all()) { + $current{$flag->sortkey} = 1; + } - $sortkey += 5; - $sortkey += 5 while exists $current{$sortkey}; - return $sortkey; + $sortkey += 5; + $sortkey += 5 while exists $current{$sortkey}; + return $sortkey; } # @@ -217,77 +229,83 @@ sub _next_unique_sortkey { # sub _validate { - my ($flag, $values, $visibilities) = @_; - - # flag - - my @missing; - push @missing, 'Field Name' if $flag->{name} eq ''; - push @missing, 'Field Description' if $flag->{description} eq ''; - push @missing, 'Field Sort Key' if $flag->{sortkey} eq ''; - scalar(@missing) - && ThrowUserError('tracking_flags_missing_mandatory', { fields => \@missing }); - - $flag->{name} =~ /^cf_/ - || ThrowUserError('tracking_flags_cf_prefix'); - - if ($flag->{id}) { - my $old_flag = Bugzilla::Extension::TrackingFlags::Flag->new($flag->{id}) - || ThrowCodeError('tracking_flags_invalid_item_id', { item => 'flag', id => $flag->{id} }); - if ($flag->{name} ne $old_flag->name) { - Bugzilla::Field->new({ name => $flag->{name} }) - && ThrowUserError('field_already_exists', { field => { name => $flag->{name} }}); - } - } else { - Bugzilla::Field->new({ name => $flag->{name} }) - && ThrowUserError('field_already_exists', { field => { name => $flag->{name} }}); + my ($flag, $values, $visibilities) = @_; + + # flag + + my @missing; + push @missing, 'Field Name' if $flag->{name} eq ''; + push @missing, 'Field Description' if $flag->{description} eq ''; + push @missing, 'Field Sort Key' if $flag->{sortkey} eq ''; + scalar(@missing) + && ThrowUserError('tracking_flags_missing_mandatory', {fields => \@missing}); + + $flag->{name} =~ /^cf_/ || ThrowUserError('tracking_flags_cf_prefix'); + + if ($flag->{id}) { + my $old_flag + = Bugzilla::Extension::TrackingFlags::Flag->new($flag->{id}) + || ThrowCodeError('tracking_flags_invalid_item_id', + {item => 'flag', id => $flag->{id}}); + if ($flag->{name} ne $old_flag->name) { + Bugzilla::Field->new({name => $flag->{name}}) + && ThrowUserError('field_already_exists', {field => {name => $flag->{name}}}); } + } + else { + Bugzilla::Field->new({name => $flag->{name}}) + && ThrowUserError('field_already_exists', {field => {name => $flag->{name}}}); + } - # values + # values - scalar(@$values) - || ThrowUserError('tracking_flags_missing_values'); + scalar(@$values) || ThrowUserError('tracking_flags_missing_values'); - my %seen; - foreach my $value (@$values) { - my $v = $value->{value}; + my %seen; + foreach my $value (@$values) { + my $v = $value->{value}; - $v eq '' - && ThrowUserError('tracking_flags_missing_value'); + $v eq '' && ThrowUserError('tracking_flags_missing_value'); - exists $seen{$v} - && ThrowUserError('tracking_flags_duplicate_value', { value => $v }); - $seen{$v} = 1; + exists $seen{$v} + && ThrowUserError('tracking_flags_duplicate_value', {value => $v}); + $seen{$v} = 1; - push @missing, "Setter for $v" if !$value->{setter_group_id}; - } - scalar(@missing) - && ThrowUserError('tracking_flags_missing_mandatory', { fields => \@missing }); + push @missing, "Setter for $v" if !$value->{setter_group_id}; + } + scalar(@missing) + && ThrowUserError('tracking_flags_missing_mandatory', {fields => \@missing}); - # visibility + # visibility - scalar(@$visibilities) - || ThrowUserError('tracking_flags_missing_visibility'); + scalar(@$visibilities) || ThrowUserError('tracking_flags_missing_visibility'); - %seen = (); - foreach my $visibility (@$visibilities) { - my $name = $visibility->{product} . ':' . $visibility->{component}; + %seen = (); + foreach my $visibility (@$visibilities) { + my $name = $visibility->{product} . ':' . $visibility->{component}; - exists $seen{$name} - && ThrowUserError('tracking_flags_duplicate_visibility', { name => $name }); + exists $seen{$name} + && ThrowUserError('tracking_flags_duplicate_visibility', {name => $name}); - $visibility->{product_obj} = Bugzilla::Product->new({ name => $visibility->{product} }) - || ThrowCodeError('tracking_flags_invalid_product', { product => $visibility->{product} }); + $visibility->{product_obj} + = Bugzilla::Product->new({name => $visibility->{product}}) + || ThrowCodeError('tracking_flags_invalid_product', + {product => $visibility->{product}}); - if ($visibility->{component} ne '') { - $visibility->{component_obj} = Bugzilla::Component->new({ product => $visibility->{product_obj}, - name => $visibility->{component} }) - || ThrowCodeError('tracking_flags_invalid_component', { - product => $visibility->{product}, - component_name => $visibility->{component}, - }); + if ($visibility->{component} ne '') { + $visibility->{component_obj} + = Bugzilla::Component->new({ + product => $visibility->{product_obj}, name => $visibility->{component} + }) + || ThrowCodeError( + 'tracking_flags_invalid_component', + { + product => $visibility->{product}, + component_name => $visibility->{component}, } + ); } + } } @@ -296,106 +314,115 @@ sub _validate { # sub _update_db { - my ($flag, $values, $visibilities) = @_; - my $dbh = Bugzilla->dbh; + my ($flag, $values, $visibilities) = @_; + my $dbh = Bugzilla->dbh; - $dbh->bz_start_transaction(); - my $flag_obj = _update_db_flag($flag); - _update_db_values($flag_obj, $flag, $values); - _update_db_visibility($flag_obj, $flag, $visibilities); - $dbh->bz_commit_transaction(); + $dbh->bz_start_transaction(); + my $flag_obj = _update_db_flag($flag); + _update_db_values($flag_obj, $flag, $values); + _update_db_visibility($flag_obj, $flag, $visibilities); + $dbh->bz_commit_transaction(); - return $flag_obj; + return $flag_obj; } sub _update_db_flag { - my ($flag) = @_; + my ($flag) = @_; + + my $object_set = { + name => $flag->{name}, + description => $flag->{description}, + sortkey => $flag->{sortkey}, + type => $flag->{type}, + enter_bug => $flag->{enter_bug}, + is_active => $flag->{is_active}, + }; + + my $flag_obj; + if ($flag->{id}) { + + # update existing flag + $flag_obj + = Bugzilla::Extension::TrackingFlags::Flag->new($flag->{id}) + || ThrowCodeError('tracking_flags_invalid_item_id', + {item => 'flag', id => $flag->{id}}); + $flag_obj->set_all($object_set); + $flag_obj->update(); + + } + else { + # create new flag + $flag_obj = Bugzilla::Extension::TrackingFlags::Flag->create($object_set); + } + + return $flag_obj; +} - my $object_set = { - name => $flag->{name}, - description => $flag->{description}, - sortkey => $flag->{sortkey}, - type => $flag->{type}, - enter_bug => $flag->{enter_bug}, - is_active => $flag->{is_active}, - }; +sub _update_db_values { + my ($flag_obj, $flag, $values) = @_; - my $flag_obj; - if ($flag->{id}) { - # update existing flag - $flag_obj = Bugzilla::Extension::TrackingFlags::Flag->new($flag->{id}) - || ThrowCodeError('tracking_flags_invalid_item_id', { item => 'flag', id => $flag->{id} }); - $flag_obj->set_all($object_set); - $flag_obj->update(); - - } else { - # create new flag - $flag_obj = Bugzilla::Extension::TrackingFlags::Flag->create($object_set); + # delete + foreach my $current_value (@{$flag_obj->values}) { + if (!grep { $_->{id} == $current_value->id } @$values) { + $current_value->remove_from_db(); } + } - return $flag_obj; -} + # add/update + my $sortkey = 0; + foreach my $value (@{$values}) { + $sortkey += 10; -sub _update_db_values { - my ($flag_obj, $flag, $values) = @_; + my $object_set = { + value => $value->{value}, + setter_group_id => $value->{setter_group_id}, + is_active => $value->{is_active}, + sortkey => $sortkey, + comment => $value->{comment}, + }; - # delete - foreach my $current_value (@{ $flag_obj->values }) { - if (!grep { $_->{id} == $current_value->id } @$values) { - $current_value->remove_from_db(); - } + if ($value->{id}) { + my $value_obj + = Bugzilla::Extension::TrackingFlags::Flag::Value->new($value->{id}) + || ThrowCodeError('tracking_flags_invalid_item_id', + {item => 'flag value', id => $flag->{id}}); + my $old_value = $value_obj->value; + $value_obj->set_all($object_set); + $value_obj->update(); + Bugzilla::Extension::TrackingFlags::Flag::Bug->update_all_values({ + value_obj => $value_obj, + old_value => $old_value, + new_value => $value_obj->value, + }); } - - # add/update - my $sortkey = 0; - foreach my $value (@{ $values }) { - $sortkey += 10; - - my $object_set = { - value => $value->{value}, - setter_group_id => $value->{setter_group_id}, - is_active => $value->{is_active}, - sortkey => $sortkey, - comment => $value->{comment}, - }; - - if ($value->{id}) { - my $value_obj = Bugzilla::Extension::TrackingFlags::Flag::Value->new($value->{id}) - || ThrowCodeError('tracking_flags_invalid_item_id', { item => 'flag value', id => $flag->{id} }); - my $old_value = $value_obj->value; - $value_obj->set_all($object_set); - $value_obj->update(); - Bugzilla::Extension::TrackingFlags::Flag::Bug->update_all_values({ - value_obj => $value_obj, - old_value => $old_value, - new_value => $value_obj->value, - }); - } else { - $object_set->{tracking_flag_id} = $flag_obj->flag_id; - Bugzilla::Extension::TrackingFlags::Flag::Value->create($object_set); - } + else { + $object_set->{tracking_flag_id} = $flag_obj->flag_id; + Bugzilla::Extension::TrackingFlags::Flag::Value->create($object_set); } + } } sub _update_db_visibility { - my ($flag_obj, $flag, $visibilities) = @_; + my ($flag_obj, $flag, $visibilities) = @_; - # delete - foreach my $current_visibility (@{ $flag_obj->visibility }) { - if (!grep { $_->{id} == $current_visibility->id } @$visibilities) { - $current_visibility->remove_from_db(); - } - } - - # add - foreach my $visibility (@{ $visibilities }) { - next if $visibility->{id}; - Bugzilla::Extension::TrackingFlags::Flag::Visibility->create({ - tracking_flag_id => $flag_obj->flag_id, - product_id => $visibility->{product_obj}->id, - component_id => $visibility->{component} ? $visibility->{component_obj}->id : undef, - }); + # delete + foreach my $current_visibility (@{$flag_obj->visibility}) { + if (!grep { $_->{id} == $current_visibility->id } @$visibilities) { + $current_visibility->remove_from_db(); } + } + + # add + foreach my $visibility (@{$visibilities}) { + next if $visibility->{id}; + Bugzilla::Extension::TrackingFlags::Flag::Visibility->create({ + tracking_flag_id => $flag_obj->flag_id, + product_id => $visibility->{product_obj}->id, + component_id => $visibility->{component} + ? $visibility->{component_obj}->id + : undef, + }); + } } # @@ -403,62 +430,66 @@ sub _update_db_visibility { # sub _groups_to_json { - my @data; - foreach my $group (sort { $a->name cmp $b->name } Bugzilla::Group->get_all()) { - push @data, { - id => $group->id, - name => $group->name, - }; - } - return encode_json(\@data); + my @data; + foreach my $group (sort { $a->name cmp $b->name } Bugzilla::Group->get_all()) { + push @data, {id => $group->id, name => $group->name,}; + } + return encode_json(\@data); } sub _flag_values_to_json { - my ($values, $is_copy) = @_; - # setting is_copy will set the id's to zero, to force new values rather - # than editing existing ones - my @data; - foreach my $value (@$values) { - push @data, { - id => $is_copy ? 0 : $value->{id}, - value => $value->{value}, - setter_group_id => $value->{setter_group_id}, - is_active => $value->{is_active} ? JSON::true : JSON::false, - comment => $value->{comment} // '', - }; - } - return encode_json(\@data); + my ($values, $is_copy) = @_; + + # setting is_copy will set the id's to zero, to force new values rather + # than editing existing ones + my @data; + foreach my $value (@$values) { + push @data, + { + id => $is_copy ? 0 : $value->{id}, + value => $value->{value}, + setter_group_id => $value->{setter_group_id}, + is_active => $value->{is_active} ? JSON::true : JSON::false, + comment => $value->{comment} // '', + }; + } + return encode_json(\@data); } sub _flag_visibility_to_json { - my ($visibilities, $is_copy) = @_; - # setting is_copy will set the id's to zero, to force new visibilites - # rather than editing existing ones - my @data; - - foreach my $visibility (@$visibilities) { - my $product = exists $visibility->{product_id} - ? $visibility->product->name - : $visibility->{product}; - my $component; - if (exists $visibility->{component_id} && $visibility->{component_id}) { - $component = $visibility->component->name; - } elsif (exists $visibility->{component}) { - $component = $visibility->{component}; - } else { - $component = undef; - } - push @data, { - id => $is_copy ? 0 : $visibility->{id}, - product => $product, - component => $component, - }; + my ($visibilities, $is_copy) = @_; + + # setting is_copy will set the id's to zero, to force new visibilites + # rather than editing existing ones + my @data; + + foreach my $visibility (@$visibilities) { + my $product + = exists $visibility->{product_id} + ? $visibility->product->name + : $visibility->{product}; + my $component; + if (exists $visibility->{component_id} && $visibility->{component_id}) { + $component = $visibility->component->name; + } + elsif (exists $visibility->{component}) { + $component = $visibility->{component}; + } + else { + $component = undef; } - @data = sort { - lc($a->{product}) cmp lc($b->{product}) - || lc($a->{component}) cmp lc($b->{component}) - } @data; - return encode_json(\@data); + push @data, + { + id => $is_copy ? 0 : $visibility->{id}, + product => $product, + component => $component, + }; + } + @data = sort { + lc($a->{product}) cmp lc($b->{product}) + || lc($a->{component}) cmp lc($b->{component}) + } @data; + return encode_json(\@data); } 1; diff --git a/extensions/TrackingFlags/lib/Constants.pm b/extensions/TrackingFlags/lib/Constants.pm index 00827aa7a..842e7501d 100644 --- a/extensions/TrackingFlags/lib/Constants.pm +++ b/extensions/TrackingFlags/lib/Constants.pm @@ -14,31 +14,31 @@ use warnings; use base qw(Exporter); our @EXPORT = qw( - FLAG_TYPES + FLAG_TYPES ); sub FLAG_TYPES { - my @flag_types = ( - { - name => 'project', - description => 'Project Flags', - collapsed => 0, - sortkey => 0 - }, - { - name => 'tracking', - description => 'Tracking Flags', - collapsed => 1, - sortkey => 1 - }, - { - name => 'blocking', - description => 'Blocking Flags', - collapsed => 1, - sortkey => 2 - }, - ); - return [ sort { $a->{'sortkey'} <=> $b->{'sortkey'} } @flag_types ]; + my @flag_types = ( + { + name => 'project', + description => 'Project Flags', + collapsed => 0, + sortkey => 0 + }, + { + name => 'tracking', + description => 'Tracking Flags', + collapsed => 1, + sortkey => 1 + }, + { + name => 'blocking', + description => 'Blocking Flags', + collapsed => 1, + sortkey => 2 + }, + ); + return [sort { $a->{'sortkey'} <=> $b->{'sortkey'} } @flag_types]; } 1; diff --git a/extensions/TrackingFlags/lib/Flag.pm b/extensions/TrackingFlags/lib/Flag.pm index 82c0314e3..c1ecbc4ad 100644 --- a/extensions/TrackingFlags/lib/Flag.pm +++ b/extensions/TrackingFlags/lib/Flag.pm @@ -30,43 +30,43 @@ use Bugzilla::Extension::TrackingFlags::Flag::Visibility; use constant DB_TABLE => 'tracking_flags'; use constant DB_COLUMNS => qw( - id - field_id - name - description - type - sortkey - enter_bug - is_active + id + field_id + name + description + type + sortkey + enter_bug + is_active ); use constant LIST_ORDER => 'sortkey'; use constant UPDATE_COLUMNS => qw( - name - description - type - sortkey - enter_bug - is_active + name + description + type + sortkey + enter_bug + is_active ); use constant VALIDATORS => { - name => \&_check_name, - description => \&_check_description, - type => \&_check_type, - sortkey => \&_check_sortkey, - enter_bug => \&Bugzilla::Object::check_boolean, - is_active => \&Bugzilla::Object::check_boolean, + name => \&_check_name, + description => \&_check_description, + type => \&_check_type, + sortkey => \&_check_sortkey, + enter_bug => \&Bugzilla::Object::check_boolean, + is_active => \&Bugzilla::Object::check_boolean, }; use constant UPDATE_VALIDATORS => { - name => \&_check_name, - description => \&_check_description, - type => \&_check_type, - sortkey => \&_check_sortkey, - enter_bug => \&Bugzilla::Object::check_boolean, - is_active => \&Bugzilla::Object::check_boolean, + name => \&_check_name, + description => \&_check_description, + type => \&_check_type, + sortkey => \&_check_sortkey, + enter_bug => \&Bugzilla::Object::check_boolean, + is_active => \&Bugzilla::Object::check_boolean, }; ############################### @@ -74,257 +74,260 @@ use constant UPDATE_VALIDATORS => { ############################### sub new { - my $class = shift; - my $param = shift; - my $cache = Bugzilla->request_cache; - - if (!ref $param - && exists $cache->{'tracking_flags'} - && exists $cache->{'tracking_flags'}->{$param}) - { - return $cache->{'tracking_flags'}->{$param}; - } - - return $class->SUPER::new($param); + my $class = shift; + my $param = shift; + my $cache = Bugzilla->request_cache; + + if (!ref $param + && exists $cache->{'tracking_flags'} + && exists $cache->{'tracking_flags'}->{$param}) + { + return $cache->{'tracking_flags'}->{$param}; + } + + return $class->SUPER::new($param); } sub new_from_hash { - my $class = shift; - my $cache = Bugzilla->request_cache->{'tracking_flags'} //= {}; - my $flag = $class->SUPER::new_from_hash(@_); - if ($flag) { - push @Bugzilla::Extension::TrackingFlags::FLAG_CACHE, $flag; - } - return $flag; + my $class = shift; + my $cache = Bugzilla->request_cache->{'tracking_flags'} //= {}; + my $flag = $class->SUPER::new_from_hash(@_); + if ($flag) { + push @Bugzilla::Extension::TrackingFlags::FLAG_CACHE, $flag; + } + return $flag; } sub create { - my $class = shift; - my $params = shift; - my $dbh = Bugzilla->dbh; - my $flag; - - # Disable bug updates temporarily to avoid conflicts. - SetParam('disable_bug_updates', 1); - write_params(); - - eval { - $dbh->bz_start_transaction(); - - $params = $class->run_create_validators($params); - - # We have to create an entry for this new flag - # in the fielddefs table for use elsewhere. We cannot - # use Bugzilla::Field->create as it will create the - # additional tables needed by custom fields which we - # do not need. Also we do this so as not to add a - # another column to the bugs table. - # We will create the entry as a custom field with a - # type of FIELD_TYPE_EXTENSION so Bugzilla will skip - # these field types in certain parts of the core code. - $dbh->do("INSERT INTO fielddefs + my $class = shift; + my $params = shift; + my $dbh = Bugzilla->dbh; + my $flag; + + # Disable bug updates temporarily to avoid conflicts. + SetParam('disable_bug_updates', 1); + write_params(); + + eval { + $dbh->bz_start_transaction(); + + $params = $class->run_create_validators($params); + + # We have to create an entry for this new flag + # in the fielddefs table for use elsewhere. We cannot + # use Bugzilla::Field->create as it will create the + # additional tables needed by custom fields which we + # do not need. Also we do this so as not to add a + # another column to the bugs table. + # We will create the entry as a custom field with a + # type of FIELD_TYPE_EXTENSION so Bugzilla will skip + # these field types in certain parts of the core code. + $dbh->do( + "INSERT INTO fielddefs (name, description, sortkey, type, custom, obsolete, buglist) VALUES - (?, ?, ?, ?, ?, ?, ?)", - undef, - $params->{'name'}, - $params->{'description'}, - $params->{'sortkey'}, - FIELD_TYPE_EXTENSION, - 1, 0, 1); - $params->{'field_id'} = $dbh->bz_last_key; - - $flag = $class->SUPER::create($params); - - $dbh->bz_commit_transaction(); - }; - my $error = "$@"; - SetParam('disable_bug_updates', 0); - write_params(); - die $error if $error; + (?, ?, ?, ?, ?, ?, ?)", undef, $params->{'name'}, + $params->{'description'}, $params->{'sortkey'}, FIELD_TYPE_EXTENSION, 1, 0, 1 + ); + $params->{'field_id'} = $dbh->bz_last_key; + + $flag = $class->SUPER::create($params); - # fielddefs has been changed so we need to clear global config - Bugzilla->memcached->clear_config(); + $dbh->bz_commit_transaction(); + }; + my $error = "$@"; + SetParam('disable_bug_updates', 0); + write_params(); + die $error if $error; - return $flag; + # fielddefs has been changed so we need to clear global config + Bugzilla->memcached->clear_config(); + + return $flag; } sub update { - my $self = shift; - my $dbh = Bugzilla->dbh; + my $self = shift; + my $dbh = Bugzilla->dbh; - my $old_self = $self->new($self->flag_id); + my $old_self = $self->new($self->flag_id); - # HACK! Bugzilla::Object::update uses hardcoded $self->id - # instead of $self->{ID_FIELD} so we need to reverse field_id - # and the real id temporarily - my $field_id = $self->id; - $self->{'field_id'} = $self->{'id'}; + # HACK! Bugzilla::Object::update uses hardcoded $self->id + # instead of $self->{ID_FIELD} so we need to reverse field_id + # and the real id temporarily + my $field_id = $self->id; + $self->{'field_id'} = $self->{'id'}; - my $changes = $self->SUPER::update(@_); + my $changes = $self->SUPER::update(@_); - $self->{'field_id'} = $field_id; + $self->{'field_id'} = $field_id; - # Update the fielddefs entry - $dbh->do("UPDATE fielddefs SET name = ?, description = ? WHERE name = ?", - undef, - $self->name, $self->description, $old_self->name); + # Update the fielddefs entry + $dbh->do("UPDATE fielddefs SET name = ?, description = ? WHERE name = ?", + undef, $self->name, $self->description, $old_self->name); - # Update request_cache - my $cache = Bugzilla->request_cache; - if (exists $cache->{'tracking_flags'}) { - $cache->{'tracking_flags'}->{$self->flag_id} = $self; - } + # Update request_cache + my $cache = Bugzilla->request_cache; + if (exists $cache->{'tracking_flags'}) { + $cache->{'tracking_flags'}->{$self->flag_id} = $self; + } - # fielddefs has been changed so we need to clear global config - Bugzilla->memcached->clear_config(); + # fielddefs has been changed so we need to clear global config + Bugzilla->memcached->clear_config(); - return $changes; + return $changes; } sub match { - my $class = shift; - my ($params) = @_; - - # Use later for preload - my $bug_id = delete $params->{'bug_id'}; - - # Retrieve all flags relevant for the given product and component - if (!exists $params->{'id'} - && ($params->{'component'} || $params->{'component_id'} - || $params->{'product'} || $params->{'product_id'})) - { - my $visible_flags - = Bugzilla::Extension::TrackingFlags::Flag::Visibility->match(@_); - my @flag_ids = map { $_->tracking_flag_id } @$visible_flags; - - delete $params->{'component'} if exists $params->{'component'}; - delete $params->{'component_id'} if exists $params->{'component_id'}; - delete $params->{'product'} if exists $params->{'product'}; - delete $params->{'product_id'} if exists $params->{'product_id'}; - - $params->{'id'} = \@flag_ids; - } - - # We need to return inactive flags if a value has been set - my $is_active_filter = delete $params->{is_active}; - - my $flags = $class->SUPER::match($params); - preload_all_the_things($flags, { bug_id => $bug_id }); - - if ($is_active_filter) { - $flags = [ grep { $_->is_active || exists $_->{bug_flag} } @$flags ]; - } - return [ sort { $a->sortkey <=> $b->sortkey } @$flags ]; + my $class = shift; + my ($params) = @_; + + # Use later for preload + my $bug_id = delete $params->{'bug_id'}; + + # Retrieve all flags relevant for the given product and component + if ( + !exists $params->{'id'} + && ( $params->{'component'} + || $params->{'component_id'} + || $params->{'product'} + || $params->{'product_id'}) + ) + { + my $visible_flags + = Bugzilla::Extension::TrackingFlags::Flag::Visibility->match(@_); + my @flag_ids = map { $_->tracking_flag_id } @$visible_flags; + + delete $params->{'component'} if exists $params->{'component'}; + delete $params->{'component_id'} if exists $params->{'component_id'}; + delete $params->{'product'} if exists $params->{'product'}; + delete $params->{'product_id'} if exists $params->{'product_id'}; + + $params->{'id'} = \@flag_ids; + } + + # We need to return inactive flags if a value has been set + my $is_active_filter = delete $params->{is_active}; + + my $flags = $class->SUPER::match($params); + preload_all_the_things($flags, {bug_id => $bug_id}); + + if ($is_active_filter) { + $flags = [grep { $_->is_active || exists $_->{bug_flag} } @$flags]; + } + return [sort { $a->sortkey <=> $b->sortkey } @$flags]; } sub get_all { - my $self = shift; - my $cache = Bugzilla->request_cache; - if (!exists $cache->{'tracking_flags'}) { - my @tracking_flags = $self->SUPER::get_all(@_); - preload_all_the_things(\@tracking_flags); - my %tracking_flags_hash = map { $_->flag_id => $_ } @tracking_flags; - $cache->{'tracking_flags'} = \%tracking_flags_hash; - } - return sort { $a->flag_type cmp $b->flag_type || $a->sortkey <=> $b->sortkey } - values %{ $cache->{'tracking_flags'} }; + my $self = shift; + my $cache = Bugzilla->request_cache; + if (!exists $cache->{'tracking_flags'}) { + my @tracking_flags = $self->SUPER::get_all(@_); + preload_all_the_things(\@tracking_flags); + my %tracking_flags_hash = map { $_->flag_id => $_ } @tracking_flags; + $cache->{'tracking_flags'} = \%tracking_flags_hash; + } + return + sort { $a->flag_type cmp $b->flag_type || $a->sortkey <=> $b->sortkey } + values %{$cache->{'tracking_flags'}}; } # avoids the overhead of pre-loading if just the field names are required sub get_all_names { - my $self = shift; - my $cache = Bugzilla->request_cache; - if (!exists $cache->{'tracking_flags_names'}) { - $cache->{'tracking_flags_names'} = - Bugzilla->dbh->selectcol_arrayref("SELECT name FROM tracking_flags ORDER BY name"); - } - return @{ $cache->{'tracking_flags_names'} }; + my $self = shift; + my $cache = Bugzilla->request_cache; + if (!exists $cache->{'tracking_flags_names'}) { + $cache->{'tracking_flags_names'} = Bugzilla->dbh->selectcol_arrayref( + "SELECT name FROM tracking_flags ORDER BY name"); + } + return @{$cache->{'tracking_flags_names'}}; } sub remove_from_db { - my $self = shift; - my $dbh = Bugzilla->dbh; + my $self = shift; + my $dbh = Bugzilla->dbh; - # Check to see if tracking_flags_bugs table has records - if ($self->bug_count) { - ThrowUserError('tracking_flag_has_contents', { flag => $self }); - } + # Check to see if tracking_flags_bugs table has records + if ($self->bug_count) { + ThrowUserError('tracking_flag_has_contents', {flag => $self}); + } - # Disable bug updates temporarily to avoid conflicts. - SetParam('disable_bug_updates', 1); - write_params(); + # Disable bug updates temporarily to avoid conflicts. + SetParam('disable_bug_updates', 1); + write_params(); - eval { - $dbh->bz_start_transaction(); + eval { + $dbh->bz_start_transaction(); - $dbh->do('DELETE FROM bugs_activity WHERE fieldid = ?', undef, $self->id); - $dbh->do('DELETE FROM fielddefs WHERE name = ?', undef, $self->name); + $dbh->do('DELETE FROM bugs_activity WHERE fieldid = ?', undef, $self->id); + $dbh->do('DELETE FROM fielddefs WHERE name = ?', undef, $self->name); - $dbh->bz_commit_transaction(); + $dbh->bz_commit_transaction(); - # Remove from request cache - my $cache = Bugzilla->request_cache; - if (exists $cache->{'tracking_flags'}) { - delete $cache->{'tracking_flags'}->{$self->flag_id}; - } - }; - my $error = "$@"; - SetParam('disable_bug_updates', 0); - write_params(); - die $error if $error; + # Remove from request cache + my $cache = Bugzilla->request_cache; + if (exists $cache->{'tracking_flags'}) { + delete $cache->{'tracking_flags'}->{$self->flag_id}; + } + }; + my $error = "$@"; + SetParam('disable_bug_updates', 0); + write_params(); + die $error if $error; } sub preload_all_the_things { - my ($flags, $params) = @_; + my ($flags, $params) = @_; - my %flag_hash = map { $_->flag_id => $_ } @$flags; - my @flag_ids = keys %flag_hash; - return unless @flag_ids; + my %flag_hash = map { $_->flag_id => $_ } @$flags; + my @flag_ids = keys %flag_hash; + return unless @flag_ids; - # Preload values - my $value_objects - = Bugzilla::Extension::TrackingFlags::Flag::Value->match({ tracking_flag_id => \@flag_ids }); + # Preload values + my $value_objects = Bugzilla::Extension::TrackingFlags::Flag::Value->match( + {tracking_flag_id => \@flag_ids}); - # Now populate the tracking flags with this set of value objects. - foreach my $obj (@$value_objects) { - my $flag_id = $obj->tracking_flag_id; + # Now populate the tracking flags with this set of value objects. + foreach my $obj (@$value_objects) { + my $flag_id = $obj->tracking_flag_id; - # Prepopulate the tracking flag object in the value object - $obj->{'tracking_flag'} = $flag_hash{$flag_id}; + # Prepopulate the tracking flag object in the value object + $obj->{'tracking_flag'} = $flag_hash{$flag_id}; - # Prepopulate the current value objects for this tracking flag - $flag_hash{$flag_id}->{'values'} ||= []; - push(@{$flag_hash{$flag_id}->{'values'}}, $obj); - } + # Prepopulate the current value objects for this tracking flag + $flag_hash{$flag_id}->{'values'} ||= []; + push(@{$flag_hash{$flag_id}->{'values'}}, $obj); + } - # Preload bug values if a bug_id is passed - if ($params && exists $params->{'bug_id'} && $params->{'bug_id'}) { - # We don't want to use @flag_ids here as we want all flags attached to this bug - # even if they are inactive. - my $bug_objects - = Bugzilla::Extension::TrackingFlags::Flag::Bug->match({ bug_id => $params->{'bug_id'} }); - # Now populate the tracking flags with this set of objects. - # Also we add them to the flag hash since we want them to be visible even if - # they are not longer applicable to this product/component. - foreach my $obj (@$bug_objects) { - my $flag_id = $obj->tracking_flag_id; - - # Load the flag object if it does not yet exist. - # This can happen if the bug value tracking flag - # is no longer visible for the product/component - $flag_hash{$flag_id} - ||= Bugzilla::Extension::TrackingFlags::Flag->new($flag_id); - - # Prepopulate the tracking flag object in the bug flag object - $obj->{'tracking_flag'} = $flag_hash{$flag_id}; - - # Prepopulate the the current bug flag object for the tracking flag - $flag_hash{$flag_id}->{'bug_flag'} = $obj; - } + # Preload bug values if a bug_id is passed + if ($params && exists $params->{'bug_id'} && $params->{'bug_id'}) { + + # We don't want to use @flag_ids here as we want all flags attached to this bug + # even if they are inactive. + my $bug_objects = Bugzilla::Extension::TrackingFlags::Flag::Bug->match( + {bug_id => $params->{'bug_id'}}); + + # Now populate the tracking flags with this set of objects. + # Also we add them to the flag hash since we want them to be visible even if + # they are not longer applicable to this product/component. + foreach my $obj (@$bug_objects) { + my $flag_id = $obj->tracking_flag_id; + + # Load the flag object if it does not yet exist. + # This can happen if the bug value tracking flag + # is no longer visible for the product/component + $flag_hash{$flag_id} + ||= Bugzilla::Extension::TrackingFlags::Flag->new($flag_id); + + # Prepopulate the tracking flag object in the bug flag object + $obj->{'tracking_flag'} = $flag_hash{$flag_id}; + + # Prepopulate the the current bug flag object for the tracking flag + $flag_hash{$flag_id}->{'bug_flag'} = $obj; } + } - @$flags = values %flag_hash; + @$flags = values %flag_hash; } ############################### @@ -332,125 +335,126 @@ sub preload_all_the_things { ############################### sub _check_name { - my ($invocant, $name) = @_; - $name = trim($name); - $name || ThrowCodeError('param_required', { param => 'name' }); - return $name; + my ($invocant, $name) = @_; + $name = trim($name); + $name || ThrowCodeError('param_required', {param => 'name'}); + return $name; } sub _check_description { - my ($invocant, $description) = @_; - $description = trim($description); - $description || ThrowCodeError( 'param_required', { param => 'description' } ); - return $description; + my ($invocant, $description) = @_; + $description = trim($description); + $description || ThrowCodeError('param_required', {param => 'description'}); + return $description; } sub _check_type { - my ($invocant, $type) = @_; - $type = trim($type); - $type || ThrowCodeError( 'param_required', { param => 'type' } ); - grep($_->{name} eq $type, @{FLAG_TYPES()}) - || ThrowUserError('tracking_flags_invalid_flag_type', { type => $type }); - return $type; + my ($invocant, $type) = @_; + $type = trim($type); + $type || ThrowCodeError('param_required', {param => 'type'}); + grep($_->{name} eq $type, @{FLAG_TYPES()}) + || ThrowUserError('tracking_flags_invalid_flag_type', {type => $type}); + return $type; } sub _check_sortkey { - my ($invocant, $sortkey) = @_; - detaint_natural($sortkey) - || ThrowUserError('field_invalid_sortkey', { sortkey => $sortkey }); - return $sortkey; + my ($invocant, $sortkey) = @_; + detaint_natural($sortkey) + || ThrowUserError('field_invalid_sortkey', {sortkey => $sortkey}); + return $sortkey; } ############################### #### Setters #### ############################### -sub set_name { $_[0]->set('name', $_[1]); } +sub set_name { $_[0]->set('name', $_[1]); } sub set_description { $_[0]->set('description', $_[1]); } -sub set_type { $_[0]->set('type', $_[1]); } -sub set_sortkey { $_[0]->set('sortkey', $_[1]); } -sub set_enter_bug { $_[0]->set('enter_bug', $_[1]); } -sub set_is_active { $_[0]->set('is_active', $_[1]); } +sub set_type { $_[0]->set('type', $_[1]); } +sub set_sortkey { $_[0]->set('sortkey', $_[1]); } +sub set_enter_bug { $_[0]->set('enter_bug', $_[1]); } +sub set_is_active { $_[0]->set('is_active', $_[1]); } ############################### #### Accessors #### ############################### -sub flag_id { return $_[0]->{'id'}; } -sub name { return $_[0]->{'name'}; } +sub flag_id { return $_[0]->{'id'}; } +sub name { return $_[0]->{'name'}; } sub description { return $_[0]->{'description'}; } -sub flag_type { return $_[0]->{'type'}; } -sub sortkey { return $_[0]->{'sortkey'}; } -sub enter_bug { return $_[0]->{'enter_bug'}; } -sub is_active { return $_[0]->{'is_active'}; } +sub flag_type { return $_[0]->{'type'}; } +sub sortkey { return $_[0]->{'sortkey'}; } +sub enter_bug { return $_[0]->{'enter_bug'}; } +sub is_active { return $_[0]->{'is_active'}; } sub values { - return $_[0]->{'values'} ||= Bugzilla::Extension::TrackingFlags::Flag::Value->match({ - tracking_flag_id => $_[0]->flag_id - }); + return $_[0]->{'values'} + ||= Bugzilla::Extension::TrackingFlags::Flag::Value->match( + {tracking_flag_id => $_[0]->flag_id}); } sub visibility { - return $_[0]->{'visibility'} ||= Bugzilla::Extension::TrackingFlags::Flag::Visibility->match({ - tracking_flag_id => $_[0]->flag_id - }); + return $_[0]->{'visibility'} + ||= Bugzilla::Extension::TrackingFlags::Flag::Visibility->match( + {tracking_flag_id => $_[0]->flag_id}); } sub can_set_value { - my ($self, $new_value, $user) = @_; - $user ||= Bugzilla->user; - my $new_value_obj; - foreach my $value (@{$self->values}) { - if ($value->value eq $new_value) { - $new_value_obj = $value; - last; - } + my ($self, $new_value, $user) = @_; + $user ||= Bugzilla->user; + my $new_value_obj; + foreach my $value (@{$self->values}) { + if ($value->value eq $new_value) { + $new_value_obj = $value; + last; } - return $new_value_obj - && $new_value_obj->setter_group - && $user->in_group($new_value_obj->setter_group->name) - ? 1 - : 0; + } + return + $new_value_obj + && $new_value_obj->setter_group + && $user->in_group($new_value_obj->setter_group->name) ? 1 : 0; } sub bug_flag { - my ($self, $bug_id) = @_; - # Return the current bug value object if defined unless the passed bug_id does - # not equal the current bug value objects id. - if (defined $self->{'bug_flag'} - && (!$bug_id || $self->{'bug_flag'}->bug->id == $bug_id)) - { - return $self->{'bug_flag'}; - } - - # Flag::Bug->new will return a default bug value object if $params undefined - my $params = !$bug_id - ? undef - : { condition => "tracking_flag_id = ? AND bug_id = ?", - values => [ $self->flag_id, $bug_id ] }; - return $self->{'bug_flag'} = Bugzilla::Extension::TrackingFlags::Flag::Bug->new($params); + my ($self, $bug_id) = @_; + + # Return the current bug value object if defined unless the passed bug_id does + # not equal the current bug value objects id. + if (defined $self->{'bug_flag'} + && (!$bug_id || $self->{'bug_flag'}->bug->id == $bug_id)) + { + return $self->{'bug_flag'}; + } + + # Flag::Bug->new will return a default bug value object if $params undefined + my $params = !$bug_id + ? undef + : { + condition => "tracking_flag_id = ? AND bug_id = ?", + values => [$self->flag_id, $bug_id] + }; + return $self->{'bug_flag'} + = Bugzilla::Extension::TrackingFlags::Flag::Bug->new($params); } sub bug_count { - my ($self) = @_; - return $self->{'bug_count'} if defined $self->{'bug_count'}; - my $dbh = Bugzilla->dbh; - return $self->{'bug_count'} = scalar $dbh->selectrow_array(" + my ($self) = @_; + return $self->{'bug_count'} if defined $self->{'bug_count'}; + my $dbh = Bugzilla->dbh; + return $self->{'bug_count'} = scalar $dbh->selectrow_array(" SELECT COUNT(bug_id) FROM tracking_flags_bugs - WHERE tracking_flag_id = ?", - undef, $self->flag_id); + WHERE tracking_flag_id = ?", undef, $self->flag_id); } sub activity_count { - my ($self) = @_; - return $self->{'activity_count'} if defined $self->{'activity_count'}; - my $dbh = Bugzilla->dbh; - return $self->{'activity_count'} = scalar $dbh->selectrow_array(" + my ($self) = @_; + return $self->{'activity_count'} if defined $self->{'activity_count'}; + my $dbh = Bugzilla->dbh; + return $self->{'activity_count'} = scalar $dbh->selectrow_array(" SELECT COUNT(bug_id) FROM bugs_activity - WHERE fieldid = ?", - undef, $self->id); + WHERE fieldid = ?", undef, $self->id); } ###################################### @@ -460,25 +464,25 @@ sub activity_count { # Here we return 'field_id' instead of the real # id as we want other Bugzilla code to treat this # as a Bugzilla::Field object in certain places. -sub id { return $_[0]->{'field_id'}; } +sub id { return $_[0]->{'field_id'}; } sub type { return FIELD_TYPE_EXTENSION; } -sub legal_values { return $_[0]->values; } -sub custom { return 1; } -sub in_new_bugmail { return 1; } +sub legal_values { return $_[0]->values; } +sub custom { return 1; } +sub in_new_bugmail { return 1; } sub obsolete { return $_[0]->is_active ? 0 : 1; } -sub buglist { return 1; } -sub is_select { return 1; } -sub is_abnormal { return 1; } -sub is_timetracking { return 0; } +sub buglist { return 1; } +sub is_select { return 1; } +sub is_abnormal { return 1; } +sub is_timetracking { return 0; } sub visibility_field { return undef; } sub visibility_values { return undef; } sub controls_visibility_of { return undef; } sub value_field { return undef; } sub controls_values_of { return undef; } -sub is_visible_on_bug { return 1; } -sub is_relationship { return 0; } -sub reverse_desc { return ''; } -sub is_mandatory { return 0; } -sub is_numeric { return 0; } +sub is_visible_on_bug { return 1; } +sub is_relationship { return 0; } +sub reverse_desc { return ''; } +sub is_mandatory { return 0; } +sub is_numeric { return 0; } 1; diff --git a/extensions/TrackingFlags/lib/Flag/Bug.pm b/extensions/TrackingFlags/lib/Flag/Bug.pm index 7be661720..9d9c5ce8c 100644 --- a/extensions/TrackingFlags/lib/Flag/Bug.pm +++ b/extensions/TrackingFlags/lib/Flag/Bug.pm @@ -24,32 +24,26 @@ use Scalar::Util qw(blessed); #### Initialization #### ############################### -use constant DEFAULT_FLAG_BUG => { - 'id' => 0, - 'tracking_flag_id' => 0, - 'bug_id' => 0, - 'value' => '---', -}; +use constant DEFAULT_FLAG_BUG => + {'id' => 0, 'tracking_flag_id' => 0, 'bug_id' => 0, 'value' => '---',}; use constant DB_TABLE => 'tracking_flags_bugs'; use constant DB_COLUMNS => qw( - id - tracking_flag_id - bug_id - value + id + tracking_flag_id + bug_id + value ); use constant LIST_ORDER => 'id'; use constant UPDATE_COLUMNS => qw( - value + value ); -use constant VALIDATORS => { - tracking_flag_id => \&_check_tracking_flag, - value => \&_check_value, -}; +use constant VALIDATORS => + {tracking_flag_id => \&_check_tracking_flag, value => \&_check_value,}; use constant AUDIT_CREATES => 0; use constant AUDIT_UPDATES => 0; @@ -60,67 +54,67 @@ use constant AUDIT_REMOVES => 0; ############################### 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); + 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 + return $self; } sub match { - my $class = shift; - my $bug_flags = $class->SUPER::match(@_); - preload_all_the_things($bug_flags); - return $bug_flags; + 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'} = '---'; + 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); + 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; + 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; + 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}; - } + 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}; + } } ############################## @@ -128,15 +122,15 @@ sub preload_all_the_things { ############################## sub update_all_values { - my ($invocant, $params) = @_; - my $dbh = Bugzilla->dbh; - $dbh->do( - "UPDATE tracking_flags_bugs SET value=? WHERE tracking_flag_id=? AND value=?", - undef, - $params->{new_value}, - $params->{value_obj}->tracking_flag_id, - $params->{old_value}, - ); + my ($invocant, $params) = @_; + my $dbh = Bugzilla->dbh; + $dbh->do( + "UPDATE tracking_flags_bugs SET value=? WHERE tracking_flag_id=? AND value=?", + undef, + $params->{new_value}, + $params->{value_obj}->tracking_flag_id, + $params->{old_value}, + ); } ############################### @@ -144,19 +138,21 @@ sub update_all_values { ############################### sub _check_value { - my ($invocant, $value) = @_; - $value || ThrowCodeError('param_required', { param => 'value' }); - return $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 }); + 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; } ############################### @@ -170,19 +166,17 @@ sub set_value { $_[0]->set('value', $_[1]); } ############################### sub tracking_flag_id { return $_[0]->{'tracking_flag_id'}; } -sub bug_id { return $_[0]->{'bug_id'}; } -sub value { return $_[0]->{'value'}; } +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 - }); + 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 - }); + 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 index 4f2aacc3a..52d63970d 100644 --- a/extensions/TrackingFlags/lib/Flag/Value.pm +++ b/extensions/TrackingFlags/lib/Flag/Value.pm @@ -25,32 +25,32 @@ use Scalar::Util qw(blessed weaken); use constant DB_TABLE => 'tracking_flags_values'; use constant DB_COLUMNS => qw( - id - tracking_flag_id - setter_group_id - value - sortkey - is_active - comment + id + tracking_flag_id + setter_group_id + value + sortkey + is_active + comment ); use constant LIST_ORDER => 'sortkey'; use constant UPDATE_COLUMNS => qw( - setter_group_id - value - sortkey - is_active - comment + setter_group_id + value + sortkey + is_active + comment ); 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, - comment => \&_check_comment, + tracking_flag_id => \&_check_tracking_flag, + setter_group_id => \&_check_setter_group, + value => \&_check_value, + sortkey => \&_check_sortkey, + is_active => \&Bugzilla::Object::check_boolean, + comment => \&_check_comment, }; ############################### @@ -58,43 +58,47 @@ use constant VALIDATORS => { ############################### sub _check_value { - my ($invocant, $value) = @_; - defined $value || ThrowCodeError('param_required', { param => 'value' }); - return $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 }); + 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 }); + 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; + my ($invocant, $sortkey) = @_; + detaint_natural($sortkey) + || ThrowUserError('field_invalid_sortkey', {sortkey => $sortkey}); + return $sortkey; } sub _check_comment { - my ($invocant, $value) = @_; - return undef unless defined $value; - $value = trim($value); - return $value eq '' ? undef : $value; + my ($invocant, $value) = @_; + return undef unless defined $value; + $value = trim($value); + return $value eq '' ? undef : $value; } ############################### @@ -102,38 +106,37 @@ sub _check_comment { ############################### 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]); } -sub set_comment { $_[0]->set('comment', $_[1]); } +sub set_value { $_[0]->set('value', $_[1]); } +sub set_sortkey { $_[0]->set('sortkey', $_[1]); } +sub set_is_active { $_[0]->set('is_active', $_[1]); } +sub set_comment { $_[0]->set('comment', $_[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 comment { return $_[0]->{'comment'}; } +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 comment { return $_[0]->{'comment'}; } sub tracking_flag { - return $_[0]->{'tracking_flag'} if $_[0]->{'tracking_flag'}; - my $tf = $_[0]->{'tracking_flag'} = Bugzilla::Extension::TrackingFlags::Flag->new({ - id => $_[0]->tracking_flag_id, cache => 1 - }); - weaken($_[0]->{'tracking_flag'}); - return $tf; + return $_[0]->{'tracking_flag'} if $_[0]->{'tracking_flag'}; + my $tf = $_[0]->{'tracking_flag'} + = Bugzilla::Extension::TrackingFlags::Flag->new( + {id => $_[0]->tracking_flag_id, cache => 1}); + weaken($_[0]->{'tracking_flag'}); + return $tf; } 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'}; + if ($_[0]->setter_group_id) { + $_[0]->{'setter_group'} + ||= Bugzilla::Group->new({id => $_[0]->setter_group_id, cache => 1}); + } + return $_[0]->{'setter_group'}; } ######################################## @@ -141,6 +144,6 @@ sub setter_group { ######################################## sub name { return $_[0]->{'value'}; } -sub is_visible_on_bug { return 1; } +sub is_visible_on_bug { return 1; } 1; diff --git a/extensions/TrackingFlags/lib/Flag/Visibility.pm b/extensions/TrackingFlags/lib/Flag/Visibility.pm index 878c16f99..e90c0bf22 100644 --- a/extensions/TrackingFlags/lib/Flag/Visibility.pm +++ b/extensions/TrackingFlags/lib/Flag/Visibility.pm @@ -25,20 +25,20 @@ use Scalar::Util qw(blessed); use constant DB_TABLE => 'tracking_flags_visibility'; use constant DB_COLUMNS => qw( - id - tracking_flag_id - product_id - component_id + id + tracking_flag_id + product_id + component_id ); use constant LIST_ORDER => 'id'; -use constant UPDATE_COLUMNS => (); # imutable +use constant UPDATE_COLUMNS => (); # imutable use constant VALIDATORS => { - tracking_flag_id => \&_check_tracking_flag, - product_id => \&_check_product, - component_id => \&_check_component, + tracking_flag_id => \&_check_tracking_flag, + product_id => \&_check_product, + component_id => \&_check_component, }; ############################### @@ -46,66 +46,72 @@ use constant VALIDATORS => { ############################### 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; - } + 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); } - # If we aren't matching on the product, use the default matching code - if (!exists $params->{product_id}) { - return $class->SUPER::match(@_); + # 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); } - 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)"); - } + # 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 + my $where = join(' AND ', @criteria); + my $flag_ids = $dbh->selectcol_arrayref( + "SELECT id FROM tracking_flags_visibility - WHERE $where"); + WHERE $where" + ); - return Bugzilla::Extension::TrackingFlags::Flag::Visibility->new_from_list($flag_ids); + return Bugzilla::Extension::TrackingFlags::Flag::Visibility->new_from_list( + $flag_ids); } ############################### @@ -113,34 +119,40 @@ sub match { ############################### 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 }); + 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 }); + 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 }); + 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; } ############################### @@ -148,26 +160,27 @@ sub _check_component { ############################### sub tracking_flag_id { return $_[0]->{'tracking_flag_id'}; } -sub product_id { return $_[0]->{'product_id'}; } -sub component_id { return $_[0]->{'component_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'}; + 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'}; + 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'}; + my ($self) = @_; + return undef unless $self->component_id; + $self->{'component'} ||= Bugzilla::Component->new($self->component_id); + return $self->{'component'}; } 1; |