aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'extensions')
-rw-r--r--extensions/BmpConvert/Config.pm9
-rw-r--r--extensions/BmpConvert/Extension.pm49
-rw-r--r--extensions/Example/Config.pm21
-rw-r--r--extensions/Example/Extension.pm1512
-rw-r--r--extensions/Example/lib/Auth/Login.pm2
-rw-r--r--extensions/Example/lib/Auth/Verify.pm2
-rw-r--r--extensions/Example/lib/Config.pm13
-rw-r--r--extensions/Example/lib/WebService.pm4
-rw-r--r--extensions/Example/template/en/default/setup/strings.txt.pl4
-rw-r--r--extensions/Gentoo/Extension.pm66
-rw-r--r--extensions/InlineHistory/Extension.pm341
-rw-r--r--extensions/MoreBugUrl/Config.pm6
-rw-r--r--extensions/MoreBugUrl/Extension.pm53
-rw-r--r--extensions/MoreBugUrl/lib/BitBucket.pm20
-rw-r--r--extensions/MoreBugUrl/lib/GetSatisfaction.pm22
-rw-r--r--extensions/MoreBugUrl/lib/PHP.pm26
-rw-r--r--extensions/MoreBugUrl/lib/Phabricator.pm23
-rw-r--r--extensions/MoreBugUrl/lib/RT.pm25
-rw-r--r--extensions/MoreBugUrl/lib/Redmine.pm23
-rw-r--r--extensions/MoreBugUrl/lib/ReviewBoard.pm31
-rw-r--r--extensions/MoreBugUrl/lib/Rietveld.pm48
-rw-r--r--extensions/MoreBugUrl/lib/Savane.pm27
-rw-r--r--extensions/OldBugMove/Extension.pm248
-rw-r--r--extensions/OldBugMove/lib/Params.pm22
-rw-r--r--extensions/SecureMail/Config.pm32
-rw-r--r--extensions/SecureMail/Extension.pm992
-rw-r--r--extensions/TypeSniffer/Config.pm14
-rw-r--r--extensions/TypeSniffer/Extension.pm73
-rw-r--r--extensions/Voting/Config.pm6
-rw-r--r--extensions/Voting/Extension.pm1354
-rwxr-xr-xextensions/create.pl45
31 files changed, 2593 insertions, 2520 deletions
diff --git a/extensions/BmpConvert/Config.pm b/extensions/BmpConvert/Config.pm
index 4984f19a9..ed2df6197 100644
--- a/extensions/BmpConvert/Config.pm
+++ b/extensions/BmpConvert/Config.pm
@@ -12,12 +12,7 @@ use strict;
use warnings;
use constant NAME => 'BmpConvert';
-use constant REQUIRED_MODULES => [
- {
- package => 'PerlMagick',
- module => 'Image::Magick',
- version => 0,
- },
-];
+use constant REQUIRED_MODULES =>
+ [{package => 'PerlMagick', module => 'Image::Magick', version => 0,},];
__PACKAGE__->NAME;
diff --git a/extensions/BmpConvert/Extension.pm b/extensions/BmpConvert/Extension.pm
index b8201f106..d9e3d0ef4 100644
--- a/extensions/BmpConvert/Extension.pm
+++ b/extensions/BmpConvert/Extension.pm
@@ -18,29 +18,30 @@ use Image::Magick;
our $VERSION = '1.0';
sub attachment_process_data {
- my ($self, $args) = @_;
- return unless $args->{attributes}->{mimetype} eq 'image/bmp';
-
- my $data = ${$args->{data}};
- my $img = Image::Magick->new(magick => 'bmp');
-
- # $data is a filehandle.
- if (ref $data) {
- $img->Read(file => \*$data);
- $img->set(magick => 'png');
- $img->Write(file => \*$data);
- }
- # $data is a blob.
- else {
- $img->BlobToImage($data);
- $img->set(magick => 'png');
- $data = $img->ImageToBlob();
- }
- undef $img;
-
- ${$args->{data}} = $data;
- $args->{attributes}->{mimetype} = 'image/png';
- $args->{attributes}->{filename} =~ s/^(.+)\.bmp$/$1.png/i;
+ my ($self, $args) = @_;
+ return unless $args->{attributes}->{mimetype} eq 'image/bmp';
+
+ my $data = ${$args->{data}};
+ my $img = Image::Magick->new(magick => 'bmp');
+
+ # $data is a filehandle.
+ if (ref $data) {
+ $img->Read(file => \*$data);
+ $img->set(magick => 'png');
+ $img->Write(file => \*$data);
+ }
+
+ # $data is a blob.
+ else {
+ $img->BlobToImage($data);
+ $img->set(magick => 'png');
+ $data = $img->ImageToBlob();
+ }
+ undef $img;
+
+ ${$args->{data}} = $data;
+ $args->{attributes}->{mimetype} = 'image/png';
+ $args->{attributes}->{filename} =~ s/^(.+)\.bmp$/$1.png/i;
}
- __PACKAGE__->NAME;
+__PACKAGE__->NAME;
diff --git a/extensions/Example/Config.pm b/extensions/Example/Config.pm
index e7782ef6c..696da2de9 100644
--- a/extensions/Example/Config.pm
+++ b/extensions/Example/Config.pm
@@ -12,21 +12,16 @@ use strict;
use warnings;
use constant NAME => 'Example';
-use constant REQUIRED_MODULES => [
- {
- package => 'Data-Dumper',
- module => 'Data::Dumper',
- version => 0,
- },
-];
+use constant REQUIRED_MODULES =>
+ [{package => 'Data-Dumper', module => 'Data::Dumper', version => 0,},];
use constant OPTIONAL_MODULES => [
- {
- package => 'Acme',
- module => 'Acme',
- version => 1.11,
- feature => ['example_acme'],
- },
+ {
+ package => 'Acme',
+ module => 'Acme',
+ version => 1.11,
+ feature => ['example_acme'],
+ },
];
__PACKAGE__->NAME;
diff --git a/extensions/Example/Extension.pm b/extensions/Example/Extension.pm
index c4fabe656..d76c71c06 100644
--- a/extensions/Example/Extension.pm
+++ b/extensions/Example/Extension.pm
@@ -35,357 +35,374 @@ use constant REL_EXAMPLE => -127;
our $VERSION = '1.0';
sub user_can_administer {
- my ($self, $args) = @_;
- my $can_administer = $args->{can_administer};
+ my ($self, $args) = @_;
+ my $can_administer = $args->{can_administer};
- # If you add an option to the admin pages (e.g. by using the Hooks in
- # template/en/default/admin/admin.html.tmpl), you may want to allow
- # users in another group view admin.cgi
- #if (Bugzilla->user->in_group('other_group')) {
- # $$can_administer = 1;
- #}
+ # If you add an option to the admin pages (e.g. by using the Hooks in
+ # template/en/default/admin/admin.html.tmpl), you may want to allow
+ # users in another group view admin.cgi
+ #if (Bugzilla->user->in_group('other_group')) {
+ # $$can_administer = 1;
+ #}
}
sub admin_editusers_action {
- my ($self, $args) = @_;
- my ($vars, $action, $user) = @$args{qw(vars action user)};
- my $template = Bugzilla->template;
-
- if ($action eq 'my_action') {
- # Allow to restrict the search to any group the user is allowed to bless.
- $vars->{'restrictablegroups'} = $user->bless_groups();
- $template->process('admin/users/search.html.tmpl', $vars)
- || ThrowTemplateError($template->error());
- exit;
- }
+ my ($self, $args) = @_;
+ my ($vars, $action, $user) = @$args{qw(vars action user)};
+ my $template = Bugzilla->template;
+
+ if ($action eq 'my_action') {
+
+ # Allow to restrict the search to any group the user is allowed to bless.
+ $vars->{'restrictablegroups'} = $user->bless_groups();
+ $template->process('admin/users/search.html.tmpl', $vars)
+ || ThrowTemplateError($template->error());
+ exit;
+ }
}
sub attachment_process_data {
- my ($self, $args) = @_;
- my $type = $args->{attributes}->{mimetype};
- my $filename = $args->{attributes}->{filename};
-
- # Make sure images have the correct extension.
- # Uncomment the two lines below to make this check effective.
- if ($type =~ /^image\/(\w+)$/) {
- my $format = $1;
- if ($filename =~ /^(.+)(:?\.[^\.]+)$/) {
- my $name = $1;
- #$args->{attributes}->{filename} = "${name}.$format";
- }
- else {
- # The file has no extension. We append it.
- #$args->{attributes}->{filename} .= ".$format";
- }
+ my ($self, $args) = @_;
+ my $type = $args->{attributes}->{mimetype};
+ my $filename = $args->{attributes}->{filename};
+
+ # Make sure images have the correct extension.
+ # Uncomment the two lines below to make this check effective.
+ if ($type =~ /^image\/(\w+)$/) {
+ my $format = $1;
+ if ($filename =~ /^(.+)(:?\.[^\.]+)$/) {
+ my $name = $1;
+
+ #$args->{attributes}->{filename} = "${name}.$format";
+ }
+ else {
+ # The file has no extension. We append it.
+ #$args->{attributes}->{filename} .= ".$format";
}
+ }
}
sub auth_login_methods {
- my ($self, $args) = @_;
- my $modules = $args->{modules};
- if (exists $modules->{Example}) {
- $modules->{Example} = 'Bugzilla/Extension/Example/Auth/Login.pm';
- }
+ my ($self, $args) = @_;
+ my $modules = $args->{modules};
+ if (exists $modules->{Example}) {
+ $modules->{Example} = 'Bugzilla/Extension/Example/Auth/Login.pm';
+ }
}
sub auth_verify_methods {
- my ($self, $args) = @_;
- my $modules = $args->{modules};
- if (exists $modules->{Example}) {
- $modules->{Example} = 'Bugzilla/Extension/Example/Auth/Verify.pm';
- }
+ my ($self, $args) = @_;
+ my $modules = $args->{modules};
+ if (exists $modules->{Example}) {
+ $modules->{Example} = 'Bugzilla/Extension/Example/Auth/Verify.pm';
+ }
}
sub bug_check_can_change_field {
- my ($self, $args) = @_;
-
- my ($bug, $field, $new_value, $old_value, $priv_results)
- = @$args{qw(bug field new_value old_value priv_results)};
-
- my $user = Bugzilla->user;
-
- # Disallow a bug from being reopened if currently closed unless user
- # is in 'admin' group
- if ($field eq 'bug_status' && $bug->product_obj->name eq 'Example') {
- if (!is_open_state($old_value) && is_open_state($new_value)
- && !$user->in_group('admin'))
- {
- push(@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
- return;
- }
- }
+ my ($self, $args) = @_;
- # Disallow a bug's keywords from being edited unless user is the
- # reporter of the bug
- if ($field eq 'keywords' && $bug->product_obj->name eq 'Example'
- && $user->login ne $bug->reporter->login)
- {
- push(@$priv_results, PRIVILEGES_REQUIRED_REPORTER);
- return;
- }
+ my ($bug, $field, $new_value, $old_value, $priv_results)
+ = @$args{qw(bug field new_value old_value priv_results)};
+
+ my $user = Bugzilla->user;
- # Allow updating of priority even if user cannot normally edit the bug
- # and they are in group 'engineering'
- if ($field eq 'priority' && $bug->product_obj->name eq 'Example'
- && $user->in_group('engineering'))
+ # Disallow a bug from being reopened if currently closed unless user
+ # is in 'admin' group
+ if ($field eq 'bug_status' && $bug->product_obj->name eq 'Example') {
+ if (!is_open_state($old_value)
+ && is_open_state($new_value)
+ && !$user->in_group('admin'))
{
- push(@$priv_results, PRIVILEGES_REQUIRED_NONE);
- return;
+ push(@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
+ return;
}
+ }
+
+ # Disallow a bug's keywords from being edited unless user is the
+ # reporter of the bug
+ if ( $field eq 'keywords'
+ && $bug->product_obj->name eq 'Example'
+ && $user->login ne $bug->reporter->login)
+ {
+ push(@$priv_results, PRIVILEGES_REQUIRED_REPORTER);
+ return;
+ }
+
+ # Allow updating of priority even if user cannot normally edit the bug
+ # and they are in group 'engineering'
+ if ( $field eq 'priority'
+ && $bug->product_obj->name eq 'Example'
+ && $user->in_group('engineering'))
+ {
+ push(@$priv_results, PRIVILEGES_REQUIRED_NONE);
+ return;
+ }
}
sub bug_columns {
- my ($self, $args) = @_;
- my $columns = $args->{'columns'};
- push (@$columns, "delta_ts AS example")
+ my ($self, $args) = @_;
+ my $columns = $args->{'columns'};
+ push(@$columns, "delta_ts AS example");
}
sub bug_end_of_create {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
+
+ # This code doesn't actually *do* anything, it's just here to show you
+ # how to use this hook.
+ my $bug = $args->{'bug'};
+ my $timestamp = $args->{'timestamp'};
+
+ my $bug_id = $bug->id;
- # This code doesn't actually *do* anything, it's just here to show you
- # how to use this hook.
- my $bug = $args->{'bug'};
- my $timestamp = $args->{'timestamp'};
-
- my $bug_id = $bug->id;
- # Uncomment this line to see a line in your webserver's error log whenever
- # you file a bug.
- # warn "Bug $bug_id has been filed!";
+ # Uncomment this line to see a line in your webserver's error log whenever
+ # you file a bug.
+ # warn "Bug $bug_id has been filed!";
}
sub bug_end_of_create_validators {
- my ($self, $args) = @_;
-
- # This code doesn't actually *do* anything, it's just here to show you
- # how to use this hook.
- my $bug_params = $args->{'params'};
-
- # Uncomment this line below to see a line in your webserver's error log
- # containing all validated bug field values every time you file a bug.
- # warn Dumper($bug_params);
-
- # This would remove all ccs from the bug, preventing ANY ccs from being
- # added on bug creation.
- # $bug_params->{cc} = [];
-}
+ my ($self, $args) = @_;
-sub bug_start_of_update {
- my ($self, $args) = @_;
+ # This code doesn't actually *do* anything, it's just here to show you
+ # how to use this hook.
+ my $bug_params = $args->{'params'};
- # This code doesn't actually *do* anything, it's just here to show you
- # how to use this hook.
- my ($bug, $old_bug, $timestamp, $changes) =
- @$args{qw(bug old_bug timestamp changes)};
+ # Uncomment this line below to see a line in your webserver's error log
+ # containing all validated bug field values every time you file a bug.
+ # warn Dumper($bug_params);
- foreach my $field (keys %$changes) {
- my $used_to_be = $changes->{$field}->[0];
- my $now_it_is = $changes->{$field}->[1];
- }
+ # This would remove all ccs from the bug, preventing ANY ccs from being
+ # added on bug creation.
+ # $bug_params->{cc} = [];
+}
- my $old_summary = $old_bug->short_desc;
-
- my $status_message;
- if (my $status_change = $changes->{'bug_status'}) {
- my $old_status = new Bugzilla::Status({ name => $status_change->[0] });
- my $new_status = new Bugzilla::Status({ name => $status_change->[1] });
- if ($new_status->is_open && !$old_status->is_open) {
- $status_message = "Bug re-opened!";
- }
- if (!$new_status->is_open && $old_status->is_open) {
- $status_message = "Bug closed!";
- }
+sub bug_start_of_update {
+ my ($self, $args) = @_;
+
+ # This code doesn't actually *do* anything, it's just here to show you
+ # how to use this hook.
+ my ($bug, $old_bug, $timestamp, $changes)
+ = @$args{qw(bug old_bug timestamp changes)};
+
+ foreach my $field (keys %$changes) {
+ my $used_to_be = $changes->{$field}->[0];
+ my $now_it_is = $changes->{$field}->[1];
+ }
+
+ my $old_summary = $old_bug->short_desc;
+
+ my $status_message;
+ if (my $status_change = $changes->{'bug_status'}) {
+ my $old_status = new Bugzilla::Status({name => $status_change->[0]});
+ my $new_status = new Bugzilla::Status({name => $status_change->[1]});
+ if ($new_status->is_open && !$old_status->is_open) {
+ $status_message = "Bug re-opened!";
+ }
+ if (!$new_status->is_open && $old_status->is_open) {
+ $status_message = "Bug closed!";
}
+ }
+
+ my $bug_id = $bug->id;
+ my $num_changes = scalar keys %$changes;
+ my $result = "There were $num_changes changes to fields on bug $bug_id"
+ . " at $timestamp.";
- my $bug_id = $bug->id;
- my $num_changes = scalar keys %$changes;
- my $result = "There were $num_changes changes to fields on bug $bug_id"
- . " at $timestamp.";
- # Uncomment this line to see $result in your webserver's error log whenever
- # you update a bug.
- # warn $result;
+ # Uncomment this line to see $result in your webserver's error log whenever
+ # you update a bug.
+ # warn $result;
}
sub bug_end_of_update {
- my ($self, $args) = @_;
-
- # This code doesn't actually *do* anything, it's just here to show you
- # how to use this hook.
- my ($bug, $old_bug, $timestamp, $changes) =
- @$args{qw(bug old_bug timestamp changes)};
-
- foreach my $field (keys %$changes) {
- my $used_to_be = $changes->{$field}->[0];
- my $now_it_is = $changes->{$field}->[1];
+ my ($self, $args) = @_;
+
+ # This code doesn't actually *do* anything, it's just here to show you
+ # how to use this hook.
+ my ($bug, $old_bug, $timestamp, $changes)
+ = @$args{qw(bug old_bug timestamp changes)};
+
+ foreach my $field (keys %$changes) {
+ my $used_to_be = $changes->{$field}->[0];
+ my $now_it_is = $changes->{$field}->[1];
+ }
+
+ my $old_summary = $old_bug->short_desc;
+
+ my $status_message;
+ if (my $status_change = $changes->{'bug_status'}) {
+ my $old_status = new Bugzilla::Status({name => $status_change->[0]});
+ my $new_status = new Bugzilla::Status({name => $status_change->[1]});
+ if ($new_status->is_open && !$old_status->is_open) {
+ $status_message = "Bug re-opened!";
}
-
- my $old_summary = $old_bug->short_desc;
-
- my $status_message;
- if (my $status_change = $changes->{'bug_status'}) {
- my $old_status = new Bugzilla::Status({ name => $status_change->[0] });
- my $new_status = new Bugzilla::Status({ name => $status_change->[1] });
- if ($new_status->is_open && !$old_status->is_open) {
- $status_message = "Bug re-opened!";
- }
- if (!$new_status->is_open && $old_status->is_open) {
- $status_message = "Bug closed!";
- }
+ if (!$new_status->is_open && $old_status->is_open) {
+ $status_message = "Bug closed!";
}
-
- my $bug_id = $bug->id;
- my $num_changes = scalar keys %$changes;
- my $result = "There were $num_changes changes to fields on bug $bug_id"
- . " at $timestamp.";
- # Uncomment this line to see $result in your webserver's error log whenever
- # you update a bug.
- # warn $result;
+ }
+
+ my $bug_id = $bug->id;
+ my $num_changes = scalar keys %$changes;
+ my $result = "There were $num_changes changes to fields on bug $bug_id"
+ . " at $timestamp.";
+
+ # Uncomment this line to see $result in your webserver's error log whenever
+ # you update a bug.
+ # warn $result;
}
sub bug_fields {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my $fields = $args->{'fields'};
- push (@$fields, "example")
+ my $fields = $args->{'fields'};
+ push(@$fields, "example");
}
sub bug_format_comment {
- my ($self, $args) = @_;
-
- # This replaces every occurrence of the word "foo" with the word
- # "bar"
-
- my $regexes = $args->{'regexes'};
- push(@$regexes, { match => qr/\bfoo\b/, replace => 'bar' });
-
- # And this links every occurrence of the word "bar" to example.com,
- # but it won't affect "foo"s that have already been turned into "bar"
- # above (because each regex is run in order, and later regexes don't modify
- # earlier matches, due to some cleverness in Bugzilla's internals).
- #
- # For example, the phrase "foo bar" would become:
- # bar <a href="http://example.com/bar">bar</a>
- my $bar_match = qr/\b(bar)\b/;
- push(@$regexes, { match => $bar_match, replace => \&_replace_bar });
+ my ($self, $args) = @_;
+
+ # This replaces every occurrence of the word "foo" with the word
+ # "bar"
+
+ my $regexes = $args->{'regexes'};
+ push(@$regexes, {match => qr/\bfoo\b/, replace => 'bar'});
+
+ # And this links every occurrence of the word "bar" to example.com,
+ # but it won't affect "foo"s that have already been turned into "bar"
+ # above (because each regex is run in order, and later regexes don't modify
+ # earlier matches, due to some cleverness in Bugzilla's internals).
+ #
+ # For example, the phrase "foo bar" would become:
+ # bar <a href="http://example.com/bar">bar</a>
+ my $bar_match = qr/\b(bar)\b/;
+ push(@$regexes, {match => $bar_match, replace => \&_replace_bar});
}
# Used by bug_format_comment--see its code for an explanation.
sub _replace_bar {
- my $args = shift;
- # $match is the first parentheses match in the $bar_match regex
- # in bug-format_comment.pl. We get up to 10 regex matches as
- # arguments to this function.
- my $match = $args->{matches}->[0];
- # Remember, you have to HTML-escape any data that you are returning!
- $match = html_quote($match);
- return qq{<a href="http://example.com/">$match</a>};
-};
+ my $args = shift;
+
+ # $match is the first parentheses match in the $bar_match regex
+ # in bug-format_comment.pl. We get up to 10 regex matches as
+ # arguments to this function.
+ my $match = $args->{matches}->[0];
+
+ # Remember, you have to HTML-escape any data that you are returning!
+ $match = html_quote($match);
+ return qq{<a href="http://example.com/">$match</a>};
+}
sub buglist_columns {
- my ($self, $args) = @_;
-
- my $columns = $args->{'columns'};
- $columns->{'example'} = { 'name' => 'bugs.delta_ts' , 'title' => 'Example' };
- $columns->{'product_desc'} = { 'name' => 'prod_desc.description',
- 'title' => 'Product Description' };
+ my ($self, $args) = @_;
+
+ my $columns = $args->{'columns'};
+ $columns->{'example'} = {'name' => 'bugs.delta_ts', 'title' => 'Example'};
+ $columns->{'product_desc'}
+ = {'name' => 'prod_desc.description', 'title' => 'Product Description'};
}
sub buglist_column_joins {
- my ($self, $args) = @_;
- my $joins = $args->{'column_joins'};
+ my ($self, $args) = @_;
+ my $joins = $args->{'column_joins'};
- # This column is added using the "buglist_columns" hook
- $joins->{'product_desc'} = {
- from => 'product_id',
- to => 'id',
- table => 'products',
- as => 'prod_desc',
- join => 'INNER',
- };
+ # This column is added using the "buglist_columns" hook
+ $joins->{'product_desc'} = {
+ from => 'product_id',
+ to => 'id',
+ table => 'products',
+ as => 'prod_desc',
+ join => 'INNER',
+ };
}
sub search_operator_field_override {
- my ($self, $args) = @_;
-
- my $operators = $args->{'operators'};
+ my ($self, $args) = @_;
- my $original = $operators->{component}->{_non_changed};
- $operators->{component} = {
- _non_changed => sub { _component_nonchanged($original, @_) }
- };
+ my $operators = $args->{'operators'};
+
+ my $original = $operators->{component}->{_non_changed};
+ $operators->{component} = {
+ _non_changed => sub { _component_nonchanged($original, @_) }
+ };
}
sub _component_nonchanged {
- my $original = shift;
- my ($invocant, $args) = @_;
+ my $original = shift;
+ my ($invocant, $args) = @_;
+
+ $invocant->$original($args);
- $invocant->$original($args);
- # Actually, it does not change anything in the result,
- # just an example.
- $args->{term} = $args->{term} . " OR 1=2";
+ # Actually, it does not change anything in the result,
+ # just an example.
+ $args->{term} = $args->{term} . " OR 1=2";
}
sub bugmail_recipients {
- my ($self, $args) = @_;
- my $recipients = $args->{recipients};
- my $bug = $args->{bug};
-
- my $user =
- new Bugzilla::User({ name => Bugzilla->params->{'maintainer'} });
-
- if ($bug->id == 1) {
- # Uncomment the line below to add the maintainer to the recipients
- # list of every bugmail from bug 1 as though that the maintainer
- # were on the CC list.
- #$recipients->{$user->id}->{+REL_CC} = 1;
-
- # And this line adds the maintainer as though they had the
- # "REL_EXAMPLE" relationship from the bugmail_relationships hook below.
- #$recipients->{$user->id}->{+REL_EXAMPLE} = 1;
- }
+ my ($self, $args) = @_;
+ my $recipients = $args->{recipients};
+ my $bug = $args->{bug};
+
+ my $user = new Bugzilla::User({name => Bugzilla->params->{'maintainer'}});
+
+ if ($bug->id == 1) {
+
+ # Uncomment the line below to add the maintainer to the recipients
+ # list of every bugmail from bug 1 as though that the maintainer
+ # were on the CC list.
+ #$recipients->{$user->id}->{+REL_CC} = 1;
+
+ # And this line adds the maintainer as though they had the
+ # "REL_EXAMPLE" relationship from the bugmail_relationships hook below.
+ #$recipients->{$user->id}->{+REL_EXAMPLE} = 1;
+ }
}
sub bugmail_relationships {
- my ($self, $args) = @_;
- my $relationships = $args->{relationships};
- $relationships->{+REL_EXAMPLE} = 'Example';
+ my ($self, $args) = @_;
+ my $relationships = $args->{relationships};
+ $relationships->{+REL_EXAMPLE} = 'Example';
}
sub cgi_headers {
- my ($self, $args) = @_;
- my $headers = $args->{'headers'};
+ my ($self, $args) = @_;
+ my $headers = $args->{'headers'};
- $headers->{'-x_test_header'} = "Test header from Example extension";
+ $headers->{'-x_test_header'} = "Test header from Example extension";
}
sub config_add_panels {
- my ($self, $args) = @_;
-
- my $modules = $args->{panel_modules};
- $modules->{Example} = "Bugzilla::Extension::Example::Config";
+ my ($self, $args) = @_;
+
+ my $modules = $args->{panel_modules};
+ $modules->{Example} = "Bugzilla::Extension::Example::Config";
}
sub config_modify_panels {
- my ($self, $args) = @_;
-
- my $panels = $args->{panels};
-
- # Add the "Example" auth methods.
- my $auth_params = $panels->{'auth'}->{params};
- my ($info_class) = grep($_->{name} eq 'user_info_class', @$auth_params);
- my ($verify_class) = grep($_->{name} eq 'user_verify_class', @$auth_params);
+ my ($self, $args) = @_;
+
+ my $panels = $args->{panels};
- push(@{ $info_class->{choices} }, 'CGI,Example');
- push(@{ $verify_class->{choices} }, 'Example');
+ # Add the "Example" auth methods.
+ my $auth_params = $panels->{'auth'}->{params};
+ my ($info_class) = grep($_->{name} eq 'user_info_class', @$auth_params);
+ my ($verify_class) = grep($_->{name} eq 'user_verify_class', @$auth_params);
- push(@$auth_params, { name => 'param_example',
- type => 't',
- default => 0,
- checker => \&check_numeric });
+ push(@{$info_class->{choices}}, 'CGI,Example');
+ push(@{$verify_class->{choices}}, 'Example');
+
+ push(
+ @$auth_params,
+ {
+ name => 'param_example',
+ type => 't',
+ default => 0,
+ checker => \&check_numeric
+ }
+ );
}
sub db_schema_abstract_schema {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
+
# $args->{'schema'}->{'example_table'} = {
# FIELDS => [
# id => {TYPE => 'SMALLSERIAL', NOTNULL => 1,
@@ -404,672 +421,695 @@ sub db_schema_abstract_schema {
}
sub email_in_before_parse {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my $subject = $args->{mail}->header('Subject');
- # Correctly extract the bug ID from email subjects of the form [Bug comp/NNN].
- if ($subject =~ /\[.*(\d+)\].*/) {
- $args->{fields}->{bug_id} = $1;
- }
+ my $subject = $args->{mail}->header('Subject');
+
+ # Correctly extract the bug ID from email subjects of the form [Bug comp/NNN].
+ if ($subject =~ /\[.*(\d+)\].*/) {
+ $args->{fields}->{bug_id} = $1;
+ }
}
sub email_in_after_parse {
- my ($self, $args) = @_;
- my $reporter = $args->{fields}->{reporter};
- my $dbh = Bugzilla->dbh;
-
- # No other check needed if this is a valid regular user.
- return if login_to_id($reporter);
-
- # The reporter is not a regular user. We create an account for them,
- # but they can only comment on existing bugs.
- # This is useful for people who reply by email to bugmails received
- # in mailing-lists.
- if ($args->{fields}->{bug_id}) {
- # WARNING: we return now to skip the remaining code below.
- # You must understand that removing this line would make the code
- # below effective! Do it only if you are OK with the behavior
- # described here.
- return;
-
- Bugzilla::User->create({ login_name => $reporter, cryptpassword => '*' });
-
- # For security reasons, delete all fields unrelated to comments.
- foreach my $field (keys %{$args->{fields}}) {
- next if $field =~ /^(?:bug_id|comment|reporter)$/;
- delete $args->{fields}->{$field};
- }
- }
- else {
- ThrowUserError('invalid_username', { name => $reporter });
+ my ($self, $args) = @_;
+ my $reporter = $args->{fields}->{reporter};
+ my $dbh = Bugzilla->dbh;
+
+ # No other check needed if this is a valid regular user.
+ return if login_to_id($reporter);
+
+ # The reporter is not a regular user. We create an account for them,
+ # but they can only comment on existing bugs.
+ # This is useful for people who reply by email to bugmails received
+ # in mailing-lists.
+ if ($args->{fields}->{bug_id}) {
+
+ # WARNING: we return now to skip the remaining code below.
+ # You must understand that removing this line would make the code
+ # below effective! Do it only if you are OK with the behavior
+ # described here.
+ return;
+
+ Bugzilla::User->create({login_name => $reporter, cryptpassword => '*'});
+
+ # For security reasons, delete all fields unrelated to comments.
+ foreach my $field (keys %{$args->{fields}}) {
+ next if $field =~ /^(?:bug_id|comment|reporter)$/;
+ delete $args->{fields}->{$field};
}
+ }
+ else {
+ ThrowUserError('invalid_username', {name => $reporter});
+ }
}
sub enter_bug_entrydefaultvars {
- my ($self, $args) = @_;
-
- my $vars = $args->{vars};
- $vars->{'example'} = 1;
+ my ($self, $args) = @_;
+
+ my $vars = $args->{vars};
+ $vars->{'example'} = 1;
}
sub error_catch {
- my ($self, $args) = @_;
- # Customize the error message displayed when someone tries to access
- # page.cgi with an invalid page ID, and keep track of this attempt
- # in the web server log.
- return unless Bugzilla->error_mode == ERROR_MODE_WEBPAGE;
- return unless $args->{error} eq 'bad_page_cgi_id';
-
- my $page_id = $args->{vars}->{page_id};
- my $login = Bugzilla->user->identity || "Someone";
- warn "$login attempted to access page.cgi with id = $page_id";
-
- my $page = $args->{message};
- my $new_error_msg = "Ah ah, you tried to access $page_id? Good try!";
- $new_error_msg = html_quote($new_error_msg);
- # There are better tools to parse an HTML page, but it's just an example.
- # Since Perl 5.16, we can no longer write "class" inside look-behind
- # assertions, because "ss" is also seen as the german ß character, which
- # makes Perl 5.16 complain. The right fix is to use the /aa modifier,
- # but it's only understood since Perl 5.14. So the workaround is to write
- # "clas[s]" instead of "class". Stupid and ugly hack, but it works with
- # all Perl versions.
- $$page =~ s/(?<=<td id="error_msg" clas[s]="throw_error">).*(?=<\/td>)/$new_error_msg/si;
+ my ($self, $args) = @_;
+
+ # Customize the error message displayed when someone tries to access
+ # page.cgi with an invalid page ID, and keep track of this attempt
+ # in the web server log.
+ return unless Bugzilla->error_mode == ERROR_MODE_WEBPAGE;
+ return unless $args->{error} eq 'bad_page_cgi_id';
+
+ my $page_id = $args->{vars}->{page_id};
+ my $login = Bugzilla->user->identity || "Someone";
+ warn "$login attempted to access page.cgi with id = $page_id";
+
+ my $page = $args->{message};
+ my $new_error_msg = "Ah ah, you tried to access $page_id? Good try!";
+ $new_error_msg = html_quote($new_error_msg);
+
+ # There are better tools to parse an HTML page, but it's just an example.
+ # Since Perl 5.16, we can no longer write "class" inside look-behind
+ # assertions, because "ss" is also seen as the german ß character, which
+ # makes Perl 5.16 complain. The right fix is to use the /aa modifier,
+ # but it's only understood since Perl 5.14. So the workaround is to write
+ # "clas[s]" instead of "class". Stupid and ugly hack, but it works with
+ # all Perl versions.
+ $$page
+ =~ s/(?<=<td id="error_msg" clas[s]="throw_error">).*(?=<\/td>)/$new_error_msg/si;
}
sub flag_end_of_update {
- my ($self, $args) = @_;
-
- # This code doesn't actually *do* anything, it's just here to show you
- # how to use this hook.
- my $flag_params = $args;
- my ($object, $timestamp, $old_flags, $new_flags) =
- @$flag_params{qw(object timestamp old_flags new_flags)};
- my ($removed, $added) = diff_arrays($old_flags, $new_flags);
- my ($granted, $denied) = (0, 0);
- foreach my $new_flag (@$added) {
- $granted++ if $new_flag =~ /\+$/;
- $denied++ if $new_flag =~ /-$/;
- }
- my $bug_id = $object->isa('Bugzilla::Bug') ? $object->id
- : $object->bug_id;
- my $result = "$granted flags were granted and $denied flags were denied"
- . " on bug $bug_id at $timestamp.";
- # Uncomment this line to see $result in your webserver's error log whenever
- # you update flags.
- # warn $result;
+ my ($self, $args) = @_;
+
+ # This code doesn't actually *do* anything, it's just here to show you
+ # how to use this hook.
+ my $flag_params = $args;
+ my ($object, $timestamp, $old_flags, $new_flags)
+ = @$flag_params{qw(object timestamp old_flags new_flags)};
+ my ($removed, $added) = diff_arrays($old_flags, $new_flags);
+ my ($granted, $denied) = (0, 0);
+ foreach my $new_flag (@$added) {
+ $granted++ if $new_flag =~ /\+$/;
+ $denied++ if $new_flag =~ /-$/;
+ }
+ my $bug_id = $object->isa('Bugzilla::Bug') ? $object->id : $object->bug_id;
+ my $result = "$granted flags were granted and $denied flags were denied"
+ . " on bug $bug_id at $timestamp.";
+
+ # Uncomment this line to see $result in your webserver's error log whenever
+ # you update flags.
+ # warn $result;
}
sub group_before_delete {
- my ($self, $args) = @_;
- # This code doesn't actually *do* anything, it's just here to show you
- # how to use this hook.
+ my ($self, $args) = @_;
+
+ # This code doesn't actually *do* anything, it's just here to show you
+ # how to use this hook.
+
+ my $group = $args->{'group'};
+ my $group_id = $group->id;
- my $group = $args->{'group'};
- my $group_id = $group->id;
- # Uncomment this line to see a line in your webserver's error log whenever
- # you file a bug.
- # warn "Group $group_id is about to be deleted!";
+ # Uncomment this line to see a line in your webserver's error log whenever
+ # you file a bug.
+ # warn "Group $group_id is about to be deleted!";
}
sub group_end_of_create {
- my ($self, $args) = @_;
- # This code doesn't actually *do* anything, it's just here to show you
- # how to use this hook.
- my $group = $args->{'group'};
+ my ($self, $args) = @_;
- my $group_id = $group->id;
- # Uncomment this line to see a line in your webserver's error log whenever
- # you create a new group.
- #warn "Group $group_id has been created!";
+ # This code doesn't actually *do* anything, it's just here to show you
+ # how to use this hook.
+ my $group = $args->{'group'};
+
+ my $group_id = $group->id;
+
+ # Uncomment this line to see a line in your webserver's error log whenever
+ # you create a new group.
+ #warn "Group $group_id has been created!";
}
sub group_end_of_update {
- my ($self, $args) = @_;
- # This code doesn't actually *do* anything, it's just here to show you
- # how to use this hook.
+ my ($self, $args) = @_;
- my ($group, $changes) = @$args{qw(group changes)};
+ # This code doesn't actually *do* anything, it's just here to show you
+ # how to use this hook.
- foreach my $field (keys %$changes) {
- my $used_to_be = $changes->{$field}->[0];
- my $now_it_is = $changes->{$field}->[1];
- }
+ my ($group, $changes) = @$args{qw(group changes)};
+
+ foreach my $field (keys %$changes) {
+ my $used_to_be = $changes->{$field}->[0];
+ my $now_it_is = $changes->{$field}->[1];
+ }
- my $group_id = $group->id;
- my $num_changes = scalar keys %$changes;
- my $result =
- "There were $num_changes changes to fields on group $group_id.";
- # Uncomment this line to see $result in your webserver's error log whenever
- # you update a group.
- #warn $result;
+ my $group_id = $group->id;
+ my $num_changes = scalar keys %$changes;
+ my $result = "There were $num_changes changes to fields on group $group_id.";
+
+ # Uncomment this line to see $result in your webserver's error log whenever
+ # you update a group.
+ #warn $result;
}
sub install_before_final_checks {
- my ($self, $args) = @_;
- print "Install-before_final_checks hook\n" unless $args->{silent};
-
- # Add a new user setting like this:
- #
- # add_setting('product_chooser', # setting name
- # ['pretty', 'full', 'small'], # options
- # 'pretty'); # default
- #
- # To add descriptions for the setting and choices, add extra values to
- # the hash defined in global/setting-descs.none.tmpl. Do this in a hook:
- # hook/global/setting-descs-settings.none.tmpl .
+ my ($self, $args) = @_;
+ print "Install-before_final_checks hook\n" unless $args->{silent};
+
+ # Add a new user setting like this:
+ #
+ # add_setting('product_chooser', # setting name
+ # ['pretty', 'full', 'small'], # options
+ # 'pretty'); # default
+ #
+ # To add descriptions for the setting and choices, add extra values to
+ # the hash defined in global/setting-descs.none.tmpl. Do this in a hook:
+ # hook/global/setting-descs-settings.none.tmpl .
}
sub install_filesystem {
- my ($self, $args) = @_;
- my $create_dirs = $args->{'create_dirs'};
- my $recurse_dirs = $args->{'recurse_dirs'};
- my $htaccess = $args->{'htaccess'};
-
- # Create a new directory in datadir specifically for this extension.
- # The directory will need to allow files to be created by the extension
- # code as well as allow the webserver to server content from it.
- # my $data_path = bz_locations->{'datadir'} . "/" . __PACKAGE__->NAME;
- # $create_dirs->{$data_path} = Bugzilla::Install::Filesystem::DIR_CGI_WRITE;
-
- # Update the permissions of any files and directories that currently reside
- # in the extension's directory.
- # $recurse_dirs->{$data_path} = {
- # files => Bugzilla::Install::Filesystem::CGI_READ,
- # dirs => Bugzilla::Install::Filesystem::DIR_CGI_WRITE
- # };
-
- # Create a htaccess file that allows specific content to be served from the
- # extension's directory.
- # $htaccess->{"$data_path/.htaccess"} = {
- # perms => Bugzilla::Install::Filesystem::WS_SERVE,
- # contents => Bugzilla::Install::Filesystem::HT_DEFAULT_DENY
- # };
+ my ($self, $args) = @_;
+ my $create_dirs = $args->{'create_dirs'};
+ my $recurse_dirs = $args->{'recurse_dirs'};
+ my $htaccess = $args->{'htaccess'};
+
+ # Create a new directory in datadir specifically for this extension.
+ # The directory will need to allow files to be created by the extension
+ # code as well as allow the webserver to server content from it.
+ # my $data_path = bz_locations->{'datadir'} . "/" . __PACKAGE__->NAME;
+ # $create_dirs->{$data_path} = Bugzilla::Install::Filesystem::DIR_CGI_WRITE;
+
+ # Update the permissions of any files and directories that currently reside
+ # in the extension's directory.
+ # $recurse_dirs->{$data_path} = {
+ # files => Bugzilla::Install::Filesystem::CGI_READ,
+ # dirs => Bugzilla::Install::Filesystem::DIR_CGI_WRITE
+ # };
+
+ # Create a htaccess file that allows specific content to be served from the
+ # extension's directory.
+ # $htaccess->{"$data_path/.htaccess"} = {
+ # perms => Bugzilla::Install::Filesystem::WS_SERVE,
+ # contents => Bugzilla::Install::Filesystem::HT_DEFAULT_DENY
+ # };
}
sub install_update_db {
- my $dbh = Bugzilla->dbh;
+ my $dbh = Bugzilla->dbh;
+
# $dbh->bz_add_column('example', 'new_column',
# {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
# $dbh->bz_add_index('example', 'example_new_column_idx', [qw(value)]);
}
sub install_update_db_fielddefs {
- my $dbh = Bugzilla->dbh;
-# $dbh->bz_add_column('fielddefs', 'example_column',
+ my $dbh = Bugzilla->dbh;
+
+# $dbh->bz_add_column('fielddefs', 'example_column',
# {TYPE => 'MEDIUMTEXT', NOTNULL => 1, DEFAULT => ''});
}
sub job_map {
- my ($self, $args) = @_;
-
- my $job_map = $args->{job_map};
-
- # This adds the named class (an instance of TheSchwartz::Worker) as a
- # handler for when a job is added with the name "some_task".
- $job_map->{'some_task'} = 'Bugzilla::Extension::Example::Job::SomeClass';
-
- # Schedule a job like this:
- # my $queue = Bugzilla->job_queue();
- # $queue->insert('some_task', { some_parameter => $some_variable });
+ my ($self, $args) = @_;
+
+ my $job_map = $args->{job_map};
+
+ # This adds the named class (an instance of TheSchwartz::Worker) as a
+ # handler for when a job is added with the name "some_task".
+ $job_map->{'some_task'} = 'Bugzilla::Extension::Example::Job::SomeClass';
+
+ # Schedule a job like this:
+ # my $queue = Bugzilla->job_queue();
+ # $queue->insert('some_task', { some_parameter => $some_variable });
}
sub mailer_before_send {
- my ($self, $args) = @_;
-
- my $email = $args->{email};
- # If you add a header to an email, it's best to start it with
- # 'X-Bugzilla-<Extension>' so that you don't conflict with
- # other extensions.
- $email->header_set('X-Bugzilla-Example-Header', 'Example');
+ my ($self, $args) = @_;
+
+ my $email = $args->{email};
+
+ # If you add a header to an email, it's best to start it with
+ # 'X-Bugzilla-<Extension>' so that you don't conflict with
+ # other extensions.
+ $email->header_set('X-Bugzilla-Example-Header', 'Example');
}
sub object_before_create {
- my ($self, $args) = @_;
-
- my $class = $args->{'class'};
- my $object_params = $args->{'params'};
-
- # Note that this is a made-up class, for this example.
- if ($class->isa('Bugzilla::ExampleObject')) {
- warn "About to create an ExampleObject!";
- warn "Got the following parameters: "
- . join(', ', keys(%$object_params));
- }
+ my ($self, $args) = @_;
+
+ my $class = $args->{'class'};
+ my $object_params = $args->{'params'};
+
+ # Note that this is a made-up class, for this example.
+ if ($class->isa('Bugzilla::ExampleObject')) {
+ warn "About to create an ExampleObject!";
+ warn "Got the following parameters: " . join(', ', keys(%$object_params));
+ }
}
sub object_before_delete {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my $object = $args->{'object'};
+ my $object = $args->{'object'};
- # Note that this is a made-up class, for this example.
- if ($object->isa('Bugzilla::ExampleObject')) {
- my $id = $object->id;
- warn "An object with id $id is about to be deleted!";
- }
+ # Note that this is a made-up class, for this example.
+ if ($object->isa('Bugzilla::ExampleObject')) {
+ my $id = $object->id;
+ warn "An object with id $id is about to be deleted!";
+ }
}
sub object_before_set {
- my ($self, $args) = @_;
-
- my ($object, $field, $value) = @$args{qw(object field value)};
-
- # Note that this is a made-up class, for this example.
- if ($object->isa('Bugzilla::ExampleObject')) {
- warn "The field $field is changing from " . $object->{$field}
- . " to $value!";
- }
+ my ($self, $args) = @_;
+
+ my ($object, $field, $value) = @$args{qw(object field value)};
+
+ # Note that this is a made-up class, for this example.
+ if ($object->isa('Bugzilla::ExampleObject')) {
+ warn "The field $field is changing from " . $object->{$field} . " to $value!";
+ }
}
sub object_columns {
- my ($self, $args) = @_;
- my ($class, $columns) = @$args{qw(class columns)};
+ my ($self, $args) = @_;
+ my ($class, $columns) = @$args{qw(class columns)};
- if ($class->isa('Bugzilla::ExampleObject')) {
- push(@$columns, 'example');
- }
+ if ($class->isa('Bugzilla::ExampleObject')) {
+ push(@$columns, 'example');
+ }
}
sub object_end_of_create {
- my ($self, $args) = @_;
-
- my $class = $args->{'class'};
- my $object = $args->{'object'};
+ my ($self, $args) = @_;
+
+ my $class = $args->{'class'};
+ my $object = $args->{'object'};
- warn "Created a new $class object!";
+ warn "Created a new $class object!";
}
sub object_end_of_create_validators {
- my ($self, $args) = @_;
-
- my $class = $args->{'class'};
- my $object_params = $args->{'params'};
-
- # Note that this is a made-up class, for this example.
- if ($class->isa('Bugzilla::ExampleObject')) {
- # Always set example_field to 1, even if the validators said otherwise.
- $object_params->{example_field} = 1;
- }
-
+ my ($self, $args) = @_;
+
+ my $class = $args->{'class'};
+ my $object_params = $args->{'params'};
+
+ # Note that this is a made-up class, for this example.
+ if ($class->isa('Bugzilla::ExampleObject')) {
+
+ # Always set example_field to 1, even if the validators said otherwise.
+ $object_params->{example_field} = 1;
+ }
+
}
sub object_end_of_set {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my ($object, $field) = @$args{qw(object field)};
+ my ($object, $field) = @$args{qw(object field)};
- # Note that this is a made-up class, for this example.
- if ($object->isa('Bugzilla::ExampleObject')) {
- warn "The field $field has changed to " . $object->{$field};
- }
+ # Note that this is a made-up class, for this example.
+ if ($object->isa('Bugzilla::ExampleObject')) {
+ warn "The field $field has changed to " . $object->{$field};
+ }
}
sub object_end_of_set_all {
- my ($self, $args) = @_;
-
- my $object = $args->{'object'};
- my $object_params = $args->{'params'};
-
- # Note that this is a made-up class, for this example.
- if ($object->isa('Bugzilla::ExampleObject')) {
- if ($object_params->{example_field} == 1) {
- $object->{example_field} = 1;
- }
+ my ($self, $args) = @_;
+
+ my $object = $args->{'object'};
+ my $object_params = $args->{'params'};
+
+ # Note that this is a made-up class, for this example.
+ if ($object->isa('Bugzilla::ExampleObject')) {
+ if ($object_params->{example_field} == 1) {
+ $object->{example_field} = 1;
}
-
+ }
+
}
sub object_end_of_update {
- my ($self, $args) = @_;
-
- my ($object, $old_object, $changes) =
- @$args{qw(object old_object changes)};
-
- # Note that this is a made-up class, for this example.
- if ($object->isa('Bugzilla::ExampleObject')) {
- if (defined $changes->{'name'}) {
- my ($old, $new) = @{ $changes->{'name'} };
- print "The name field changed from $old to $new!";
- }
+ my ($self, $args) = @_;
+
+ my ($object, $old_object, $changes) = @$args{qw(object old_object changes)};
+
+ # Note that this is a made-up class, for this example.
+ if ($object->isa('Bugzilla::ExampleObject')) {
+ if (defined $changes->{'name'}) {
+ my ($old, $new) = @{$changes->{'name'}};
+ print "The name field changed from $old to $new!";
}
+ }
}
sub object_update_columns {
- my ($self, $args) = @_;
- my ($object, $columns) = @$args{qw(object columns)};
+ my ($self, $args) = @_;
+ my ($object, $columns) = @$args{qw(object columns)};
- if ($object->isa('Bugzilla::ExampleObject')) {
- push(@$columns, 'example');
- }
+ if ($object->isa('Bugzilla::ExampleObject')) {
+ push(@$columns, 'example');
+ }
}
sub object_validators {
- my ($self, $args) = @_;
- my ($class, $validators) = @$args{qw(class validators)};
-
- if ($class->isa('Bugzilla::Bug')) {
- # This is an example of adding a new validator.
- # See the _check_example subroutine below.
- $validators->{example} = \&_check_example;
-
- # This is an example of overriding an existing validator.
- # See the check_short_desc validator below.
- my $original = $validators->{short_desc};
- $validators->{short_desc} = sub { _check_short_desc($original, @_) };
- }
+ my ($self, $args) = @_;
+ my ($class, $validators) = @$args{qw(class validators)};
+
+ if ($class->isa('Bugzilla::Bug')) {
+
+ # This is an example of adding a new validator.
+ # See the _check_example subroutine below.
+ $validators->{example} = \&_check_example;
+
+ # This is an example of overriding an existing validator.
+ # See the check_short_desc validator below.
+ my $original = $validators->{short_desc};
+ $validators->{short_desc} = sub { _check_short_desc($original, @_) };
+ }
}
sub _check_example {
- my ($invocant, $value, $field) = @_;
- warn "I was called to validate the value of $field.";
- warn "The value of $field that I was passed in is: $value";
+ my ($invocant, $value, $field) = @_;
+ warn "I was called to validate the value of $field.";
+ warn "The value of $field that I was passed in is: $value";
- # Make the value always be 1.
- my $fixed_value = 1;
- return $fixed_value;
+ # Make the value always be 1.
+ my $fixed_value = 1;
+ return $fixed_value;
}
sub _check_short_desc {
- my $original = shift;
- my $invocant = shift;
- my $value = $invocant->$original(@_);
- if ($value !~ /example/i) {
- # Use this line to make Bugzilla throw an error every time
- # you try to file a bug or update a bug without the word "example"
- # in the summary.
- if (0) {
- ThrowUserError('example_short_desc_invalid');
- }
+ my $original = shift;
+ my $invocant = shift;
+ my $value = $invocant->$original(@_);
+ if ($value !~ /example/i) {
+
+ # Use this line to make Bugzilla throw an error every time
+ # you try to file a bug or update a bug without the word "example"
+ # in the summary.
+ if (0) {
+ ThrowUserError('example_short_desc_invalid');
}
- return $value;
+ }
+ return $value;
}
sub page_before_template {
- my ($self, $args) = @_;
-
- my ($vars, $page) = @$args{qw(vars page_id)};
-
- # You can see this hook in action by loading page.cgi?id=example.html
- if ($page eq 'example.html') {
- $vars->{cgi_variables} = { Bugzilla->cgi->Vars };
- }
+ my ($self, $args) = @_;
+
+ my ($vars, $page) = @$args{qw(vars page_id)};
+
+ # You can see this hook in action by loading page.cgi?id=example.html
+ if ($page eq 'example.html') {
+ $vars->{cgi_variables} = {Bugzilla->cgi->Vars};
+ }
}
sub path_info_whitelist {
- my ($self, $args) = @_;
- my $whitelist = $args->{whitelist};
- push(@$whitelist, "page.cgi");
+ my ($self, $args) = @_;
+ my $whitelist = $args->{whitelist};
+ push(@$whitelist, "page.cgi");
}
sub post_bug_after_creation {
- my ($self, $args) = @_;
-
- my $vars = $args->{vars};
- $vars->{'example'} = 1;
+ my ($self, $args) = @_;
+
+ my $vars = $args->{vars};
+ $vars->{'example'} = 1;
}
sub product_confirm_delete {
- my ($self, $args) = @_;
-
- my $vars = $args->{vars};
- $vars->{'example'} = 1;
+ my ($self, $args) = @_;
+
+ my $vars = $args->{vars};
+ $vars->{'example'} = 1;
}
sub product_end_of_create {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
+
+ my $product = $args->{product};
- my $product = $args->{product};
+ # For this example, any lines of code that actually make changes to your
+ # database have been commented out.
- # For this example, any lines of code that actually make changes to your
- # database have been commented out.
+ # This section will take a group that exists in your installation
+ # (possible called test_group) and automatically makes the new
+ # product hidden to only members of the group. Just remove
+ # the restriction if you want the new product to be public.
- # This section will take a group that exists in your installation
- # (possible called test_group) and automatically makes the new
- # product hidden to only members of the group. Just remove
- # the restriction if you want the new product to be public.
+ my $example_group = new Bugzilla::Group({name => 'example_group'});
- my $example_group = new Bugzilla::Group({ name => 'example_group' });
+ if ($example_group) {
+ $product->set_group_controls(
+ $example_group,
+ {
+ entry => 1,
+ membercontrol => CONTROLMAPMANDATORY,
+ othercontrol => CONTROLMAPMANDATORY
+ }
+ );
- if ($example_group) {
- $product->set_group_controls($example_group,
- { entry => 1,
- membercontrol => CONTROLMAPMANDATORY,
- othercontrol => CONTROLMAPMANDATORY });
# $product->update();
- }
+ }
- # This section will automatically add a default component
- # to the new product called 'No Component'.
+ # This section will automatically add a default component
+ # to the new product called 'No Component'.
- my $default_assignee = new Bugzilla::User(
- { name => Bugzilla->params->{maintainer} });
+ my $default_assignee
+ = new Bugzilla::User({name => Bugzilla->params->{maintainer}});
+
+ if ($default_assignee) {
- if ($default_assignee) {
# Bugzilla::Component->create(
# { name => 'No Component',
# product => $product,
-# description => 'Select this component if one does not ' .
+# description => 'Select this component if one does not ' .
# 'exist in the current list of components',
# initialowner => $default_assignee });
- }
+ }
}
sub quicksearch_map {
- my ($self, $args) = @_;
- my $map = $args->{'map'};
+ my ($self, $args) = @_;
+ my $map = $args->{'map'};
- # This demonstrates adding a shorter alias for a long custom field name.
- $map->{'impact'} = $map->{'cf_long_field_name_for_impact_field'};
+ # This demonstrates adding a shorter alias for a long custom field name.
+ $map->{'impact'} = $map->{'cf_long_field_name_for_impact_field'};
}
sub sanitycheck_check {
- my ($self, $args) = @_;
-
- my $dbh = Bugzilla->dbh;
- my $sth;
-
- my $status = $args->{'status'};
-
- # Check that all users are Australian
- $status->('example_check_au_user');
-
- $sth = $dbh->prepare("SELECT userid, login_name
+ my ($self, $args) = @_;
+
+ my $dbh = Bugzilla->dbh;
+ my $sth;
+
+ my $status = $args->{'status'};
+
+ # Check that all users are Australian
+ $status->('example_check_au_user');
+
+ $sth = $dbh->prepare(
+ "SELECT userid, login_name
FROM profiles
- WHERE login_name NOT LIKE '%.au'");
- $sth->execute;
-
- my $seen_nonau = 0;
- while (my ($userid, $login, $numgroups) = $sth->fetchrow_array) {
- $status->('example_check_au_user_alert',
- { userid => $userid, login => $login },
- 'alert');
- $seen_nonau = 1;
- }
-
- $status->('example_check_au_user_prompt') if $seen_nonau;
+ WHERE login_name NOT LIKE '%.au'"
+ );
+ $sth->execute;
+
+ my $seen_nonau = 0;
+ while (my ($userid, $login, $numgroups) = $sth->fetchrow_array) {
+ $status->(
+ 'example_check_au_user_alert', {userid => $userid, login => $login}, 'alert'
+ );
+ $seen_nonau = 1;
+ }
+
+ $status->('example_check_au_user_prompt') if $seen_nonau;
}
sub sanitycheck_repair {
- my ($self, $args) = @_;
-
- my $cgi = Bugzilla->cgi;
- my $dbh = Bugzilla->dbh;
-
- my $status = $args->{'status'};
-
- if ($cgi->param('example_repair_au_user')) {
- $status->('example_repair_au_user_start');
-
- #$dbh->do("UPDATE profiles
- # SET login_name = CONCAT(login_name, '.au')
- # WHERE login_name NOT LIKE '%.au'");
-
- $status->('example_repair_au_user_end');
- }
+ my ($self, $args) = @_;
+
+ my $cgi = Bugzilla->cgi;
+ my $dbh = Bugzilla->dbh;
+
+ my $status = $args->{'status'};
+
+ if ($cgi->param('example_repair_au_user')) {
+ $status->('example_repair_au_user_start');
+
+ #$dbh->do("UPDATE profiles
+ # SET login_name = CONCAT(login_name, '.au')
+ # WHERE login_name NOT LIKE '%.au'");
+
+ $status->('example_repair_au_user_end');
+ }
}
sub template_before_create {
- my ($self, $args) = @_;
-
- my $config = $args->{'config'};
- # This will be accessible as "example_global_variable" in every
- # template in Bugzilla. See Bugzilla/Template.pm's create() function
- # for more things that you can set.
- $config->{VARIABLES}->{example_global_variable} = sub { return 'value' };
+ my ($self, $args) = @_;
+
+ my $config = $args->{'config'};
+
+ # This will be accessible as "example_global_variable" in every
+ # template in Bugzilla. See Bugzilla/Template.pm's create() function
+ # for more things that you can set.
+ $config->{VARIABLES}->{example_global_variable} = sub { return 'value' };
}
sub template_after_create {
- my ( $self, $args ) = @_;
- my $context = $args->{template}->context;
-
- # define a pluck method on template toolkit lists.
- $context->define_vmethod(
- list => pluck => sub {
- my ( $list, $field ) = @_;
- return [ map { $_->$field } @$list ];
- }
- );
+ my ($self, $args) = @_;
+ my $context = $args->{template}->context;
+
+ # define a pluck method on template toolkit lists.
+ $context->define_vmethod(
+ list => pluck => sub {
+ my ($list, $field) = @_;
+ return [map { $_->$field } @$list];
+ }
+ );
}
sub template_before_process {
- my ($self, $args) = @_;
-
- my ($vars, $file, $context) = @$args{qw(vars file context)};
+ my ($self, $args) = @_;
- if ($file eq 'bug/edit.html.tmpl') {
- $vars->{'viewing_the_bug_form'} = 1;
- }
+ my ($vars, $file, $context) = @$args{qw(vars file context)};
+
+ if ($file eq 'bug/edit.html.tmpl') {
+ $vars->{'viewing_the_bug_form'} = 1;
+ }
}
sub user_check_account_creation {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my $login = $args->{login};
- my $ip = remote_ip();
+ my $login = $args->{login};
+ my $ip = remote_ip();
- # Log all requests.
- warn "USER ACCOUNT CREATION REQUEST FOR $login ($ip)";
+ # Log all requests.
+ warn "USER ACCOUNT CREATION REQUEST FOR $login ($ip)";
- # Reject requests based on their email address.
- if ($login =~ /\@evil\.com$/) {
- ThrowUserError('account_creation_restricted');
- }
+ # Reject requests based on their email address.
+ if ($login =~ /\@evil\.com$/) {
+ ThrowUserError('account_creation_restricted');
+ }
- # Reject requests based on their IP address.
- if ($ip =~ /^192\.168\./) {
- ThrowUserError('account_creation_restricted');
- }
+ # Reject requests based on their IP address.
+ if ($ip =~ /^192\.168\./) {
+ ThrowUserError('account_creation_restricted');
+ }
}
sub user_preferences {
- my ($self, $args) = @_;
- my $tab = $args->{current_tab};
- my $save = $args->{save_changes};
- my $handled = $args->{handled};
+ my ($self, $args) = @_;
+ my $tab = $args->{current_tab};
+ my $save = $args->{save_changes};
+ my $handled = $args->{handled};
- return unless $tab eq 'my_tab';
+ return unless $tab eq 'my_tab';
- my $value = Bugzilla->input_params->{'example_pref'};
- if ($save) {
- # Validate your data and update the DB accordingly.
- $value =~ s/\s+/:/g;
- }
- $args->{'vars'}->{example_pref} = $value;
+ my $value = Bugzilla->input_params->{'example_pref'};
+ if ($save) {
+
+ # Validate your data and update the DB accordingly.
+ $value =~ s/\s+/:/g;
+ }
+ $args->{'vars'}->{example_pref} = $value;
- # Set the 'handled' scalar reference to true so that the caller
- # knows the panel name is valid and that an extension took care of it.
- $$handled = 1;
+ # Set the 'handled' scalar reference to true so that the caller
+ # knows the panel name is valid and that an extension took care of it.
+ $$handled = 1;
}
sub webservice {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my $dispatch = $args->{dispatch};
- $dispatch->{Example} = "Bugzilla::Extension::Example::WebService";
+ my $dispatch = $args->{dispatch};
+ $dispatch->{Example} = "Bugzilla::Extension::Example::WebService";
}
sub webservice_error_codes {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my $error_map = $args->{error_map};
- $error_map->{'example_my_error'} = 10001;
+ my $error_map = $args->{error_map};
+ $error_map->{'example_my_error'} = 10001;
}
sub webservice_status_code_map {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my $status_code_map = $args->{status_code_map};
- # Uncomment this line to override the status code for the
- # error 'object_does_not_exist' to STATUS_BAD_REQUEST
- #$status_code_map->{51} = STATUS_BAD_REQUEST;
+ my $status_code_map = $args->{status_code_map};
+
+ # Uncomment this line to override the status code for the
+ # error 'object_does_not_exist' to STATUS_BAD_REQUEST
+ #$status_code_map->{51} = STATUS_BAD_REQUEST;
}
sub webservice_before_call {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- # This code doesn't actually *do* anything, it's just here to show you
- # how to use this hook.
- my $method = $args->{method};
- my $full_method = $args->{full_method};
+ # This code doesn't actually *do* anything, it's just here to show you
+ # how to use this hook.
+ my $method = $args->{method};
+ my $full_method = $args->{full_method};
- # Uncomment this line to see a line in your webserver's error log whenever
- # a webservice call is made
- #warn "RPC call $full_method made by ",
- # Bugzilla->user->login || 'an anonymous user', "\n";
+ # Uncomment this line to see a line in your webserver's error log whenever
+ # a webservice call is made
+ #warn "RPC call $full_method made by ",
+ # Bugzilla->user->login || 'an anonymous user', "\n";
}
sub webservice_fix_credentials {
- my ($self, $args) = @_;
- my $rpc = $args->{'rpc'};
- my $params = $args->{'params'};
- # Allow user to pass in username=foo&password=bar
- if (exists $params->{'username'} && exists $params->{'password'}) {
- $params->{'Bugzilla_login'} = $params->{'username'};
- $params->{'Bugzilla_password'} = $params->{'password'};
- }
+ my ($self, $args) = @_;
+ my $rpc = $args->{'rpc'};
+ my $params = $args->{'params'};
+
+ # Allow user to pass in username=foo&password=bar
+ if (exists $params->{'username'} && exists $params->{'password'}) {
+ $params->{'Bugzilla_login'} = $params->{'username'};
+ $params->{'Bugzilla_password'} = $params->{'password'};
+ }
}
sub webservice_rest_request {
- my ($self, $args) = @_;
- my $rpc = $args->{'rpc'};
- my $params = $args->{'params'};
- # Internally we may have a field called 'cf_test_field' but we allow users
- # to use the shorter 'test_field' name.
- if (exists $params->{'test_field'}) {
- $params->{'test_field'} = delete $params->{'cf_test_field'};
- }
+ my ($self, $args) = @_;
+ my $rpc = $args->{'rpc'};
+ my $params = $args->{'params'};
+
+ # Internally we may have a field called 'cf_test_field' but we allow users
+ # to use the shorter 'test_field' name.
+ if (exists $params->{'test_field'}) {
+ $params->{'test_field'} = delete $params->{'cf_test_field'};
+ }
}
sub webservice_rest_resources {
- my ($self, $args) = @_;
- my $rpc = $args->{'rpc'};
- my $resources = $args->{'resources'};
- # Add a new resource that allows for /rest/example/hello
- # to call Example.hello
- $resources->{'Bugzilla::Extension::Example::WebService'} = [
- qr{^/example/hello$}, {
- GET => {
- method => 'hello',
- }
- }
- ];
+ my ($self, $args) = @_;
+ my $rpc = $args->{'rpc'};
+ my $resources = $args->{'resources'};
+
+ # Add a new resource that allows for /rest/example/hello
+ # to call Example.hello
+ $resources->{'Bugzilla::Extension::Example::WebService'}
+ = [qr{^/example/hello$}, {GET => {method => 'hello',}}];
}
sub webservice_rest_response {
- my ($self, $args) = @_;
- my $rpc = $args->{'rpc'};
- my $result = $args->{'result'};
- my $response = $args->{'response'};
- # Convert a list of bug hashes to a single bug hash if only one is
- # being returned.
- if (ref $$result eq 'HASH'
- && exists $$result->{'bugs'}
- && scalar @{ $$result->{'bugs'} } == 1)
- {
- $$result = $$result->{'bugs'}->[0];
- }
+ my ($self, $args) = @_;
+ my $rpc = $args->{'rpc'};
+ my $result = $args->{'result'};
+ my $response = $args->{'response'};
+
+ # Convert a list of bug hashes to a single bug hash if only one is
+ # being returned.
+ if ( ref $$result eq 'HASH'
+ && exists $$result->{'bugs'}
+ && scalar @{$$result->{'bugs'}} == 1)
+ {
+ $$result = $$result->{'bugs'}->[0];
+ }
}
# This must be the last line of your extension.
diff --git a/extensions/Example/lib/Auth/Login.pm b/extensions/Example/lib/Auth/Login.pm
index 15c58a881..49af42c22 100644
--- a/extensions/Example/lib/Auth/Login.pm
+++ b/extensions/Example/lib/Auth/Login.pm
@@ -17,7 +17,7 @@ use Bugzilla::Constants;
# Always returns no data.
sub get_login_info {
- return { failure => AUTH_NODATA };
+ return {failure => AUTH_NODATA};
}
1;
diff --git a/extensions/Example/lib/Auth/Verify.pm b/extensions/Example/lib/Auth/Verify.pm
index 49fd9fbb7..591c1d4f8 100644
--- a/extensions/Example/lib/Auth/Verify.pm
+++ b/extensions/Example/lib/Auth/Verify.pm
@@ -16,7 +16,7 @@ use Bugzilla::Constants;
# A verifier that always fails.
sub check_credentials {
- return { failure => AUTH_NO_SUCH_USER };
+ return {failure => AUTH_NO_SUCH_USER};
}
1;
diff --git a/extensions/Example/lib/Config.pm b/extensions/Example/lib/Config.pm
index fac0046af..360a57510 100644
--- a/extensions/Example/lib/Config.pm
+++ b/extensions/Example/lib/Config.pm
@@ -16,16 +16,11 @@ use Bugzilla::Config::Common;
our $sortkey = 5000;
sub get_param_list {
- my ($class) = @_;
+ my ($class) = @_;
- my @param_list = (
- {
- name => 'example_string',
- type => 't',
- default => 'EXAMPLE',
- },
- );
- return @param_list;
+ my @param_list
+ = ({name => 'example_string', type => 't', default => 'EXAMPLE',},);
+ return @param_list;
}
1;
diff --git a/extensions/Example/lib/WebService.pm b/extensions/Example/lib/WebService.pm
index d8a96b5f5..f461fee6f 100644
--- a/extensions/Example/lib/WebService.pm
+++ b/extensions/Example/lib/WebService.pm
@@ -14,8 +14,8 @@ use parent qw(Bugzilla::WebService);
use Bugzilla::Error;
use constant PUBLIC_METHODS => qw(
- hello
- throw_an_error
+ hello
+ throw_an_error
);
# This can be called as Example.hello() from the WebService.
diff --git a/extensions/Example/template/en/default/setup/strings.txt.pl b/extensions/Example/template/en/default/setup/strings.txt.pl
index 98550547e..e2fc52452 100644
--- a/extensions/Example/template/en/default/setup/strings.txt.pl
+++ b/extensions/Example/template/en/default/setup/strings.txt.pl
@@ -5,8 +5,6 @@
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.
-%strings = (
- feature_example_acme => 'Example Extension: Acme Feature',
-);
+%strings = (feature_example_acme => 'Example Extension: Acme Feature',);
1;
diff --git a/extensions/Gentoo/Extension.pm b/extensions/Gentoo/Extension.pm
index 936026678..97402e88d 100644
--- a/extensions/Gentoo/Extension.pm
+++ b/extensions/Gentoo/Extension.pm
@@ -11,62 +11,58 @@ use POSIX qw(uname);
our $VERSION = '1.0';
sub install_filesystem {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my $dirs = $args->{'create_dirs'};
- my $files = $args->{'files'};
- my $recurse_dirs = $args->{'recurse_dirs'};
- my $htaccess = $args->{'htaccess'};
+ my $dirs = $args->{'create_dirs'};
+ my $files = $args->{'files'};
+ my $recurse_dirs = $args->{'recurse_dirs'};
+ my $htaccess = $args->{'htaccess'};
- my $datadir = bz_locations()->{'datadir'};
+ my $datadir = bz_locations()->{'datadir'};
- $dirs->{"${datadir}/cached"} = { perms => 0750 };
+ $dirs->{"${datadir}/cached"} = {perms => 0750};
- $files->{"zzz.txt"} = { perms => 0644 };
- $files->{"robots-ssl.txt"} = { perms => 0644 };
- $files->{"bots.html"} = { perms => 0644 };
- $files->{"favicon.ico"} = { perms => 0644 };
- $files->{"runstats.sh"} = { perms => 0700 };
- $files->{"recompile.sh"} = { perms => 0700 };
+ $files->{"zzz.txt"} = {perms => 0644};
+ $files->{"robots-ssl.txt"} = {perms => 0644};
+ $files->{"bots.html"} = {perms => 0644};
+ $files->{"favicon.ico"} = {perms => 0644};
+ $files->{"runstats.sh"} = {perms => 0700};
+ $files->{"recompile.sh"} = {perms => 0700};
- $recurse_dirs->{"$datadir/cached"} = {
- files => 0640, dirs => 0750
- };
+ $recurse_dirs->{"$datadir/cached"} = {files => 0640, dirs => 0750};
- $recurse_dirs->{"images/ranks"} = {
- files => 0640, dirs => 0750
- };
+ $recurse_dirs->{"images/ranks"} = {files => 0640, dirs => 0750};
- $htaccess->{"$datadir/cached/.htaccess"} = {
- perms => 0640, contents => <<EOT
+ $htaccess->{"$datadir/cached/.htaccess"} = {
+ perms => 0640,
+ contents => <<EOT
# Allow access to the cached stuff
Allow from all
EOT
- };
+ };
}
sub template_before_create {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my $config = $args->{config};
- my $constants = $config->{CONSTANTS};
+ my $config = $args->{config};
+ my $constants = $config->{CONSTANTS};
- my %nodemap = (
- 'yellowbishop' => 'bugs-web1',
- 'yellowleg' => 'bugs-web2'
- );
+ my %nodemap = ('yellowbishop' => 'bugs-web1', 'yellowleg' => 'bugs-web2');
- my $hostname = (uname())[1];
- $constants->{GENTOO_NODE} = $nodemap{$hostname} ? $nodemap{$hostname} : "[$hostname]";
- $constants->{GENTOO_APPEND_VERSION} = "+";
+ my $hostname = (uname())[1];
+ $constants->{GENTOO_NODE}
+ = $nodemap{$hostname} ? $nodemap{$hostname} : "[$hostname]";
+ $constants->{GENTOO_APPEND_VERSION} = "+";
}
sub user_check_account_creation {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my $login = $args->{login};
+ my $login = $args->{login};
- ThrowUserError('restricted_email_address', {addr => $login}) if $login =~ m/.+\@gentoo\.org$/;
+ ThrowUserError('restricted_email_address', {addr => $login})
+ if $login =~ m/.+\@gentoo\.org$/;
}
__PACKAGE__->NAME;
diff --git a/extensions/InlineHistory/Extension.pm b/extensions/InlineHistory/Extension.pm
index c88ff74ed..87d0674ca 100644
--- a/extensions/InlineHistory/Extension.pm
+++ b/extensions/InlineHistory/Extension.pm
@@ -19,200 +19,207 @@ our $VERSION = '1.6';
use constant MAXIMUM_ACTIVITY_COUNT => 500;
# don't show really long values
-use constant MAXIMUM_VALUE_LENGTH => 256;
+use constant MAXIMUM_VALUE_LENGTH => 256;
sub template_before_create {
- my ($self, $args) = @_;
- $args->{config}->{FILTERS}->{ih_short_value} = sub {
- my ($str) = @_;
- return length($str) <= MAXIMUM_VALUE_LENGTH
- ? $str
- : substr($str, 0, MAXIMUM_VALUE_LENGTH - 3) . '...';
- };
+ my ($self, $args) = @_;
+ $args->{config}->{FILTERS}->{ih_short_value} = sub {
+ my ($str) = @_;
+ return
+ length($str) <= MAXIMUM_VALUE_LENGTH
+ ? $str
+ : substr($str, 0, MAXIMUM_VALUE_LENGTH - 3) . '...';
+ };
}
sub template_before_process {
- my ($self, $args) = @_;
- my $file = $args->{'file'};
- my $vars = $args->{'vars'};
-
- return if $file ne 'bug/edit.html.tmpl';
-
- my $user = Bugzilla->user;
- my $dbh = Bugzilla->dbh;
- return unless $user->id && $user->settings->{'inline_history'}->{'value'} eq 'on';
-
- # note: bug/edit.html.tmpl doesn't support multiple bugs
- my $bug = exists $vars->{'bugs'} ? $vars->{'bugs'}[0] : $vars->{'bug'};
- my $bug_id = $bug->id;
-
- # build bug activity
- my ($activity) = $bug->can('get_activity')
- ? $bug->get_activity()
- : Bugzilla::Bug::GetBugActivity($bug_id);
- $activity = _add_duplicates($bug_id, $activity);
-
- if (scalar @$activity > MAXIMUM_ACTIVITY_COUNT) {
- $activity = [];
- $vars->{'ih_activity'} = 0;
- $vars->{'ih_activity_max'} = 1;
- return;
- }
-
- # prime caches with objects already loaded
- my %user_cache;
- foreach my $comment (@{$bug->comments}) {
- $user_cache{$comment->{author}->login} = $comment->{author};
- }
-
- my %attachment_cache;
- foreach my $attachment (@{$bug->attachments}) {
- $attachment_cache{$attachment->id} = $attachment;
- }
-
- # build a list of bugs we need to check visibility of, so we can check with a single query
- my %visible_bug_ids;
-
- # augment and tweak
- foreach my $operation (@$activity) {
- # make operation.who an object
- $user_cache{$operation->{who}} ||= Bugzilla::User->new({ name => $operation->{who} });
- $operation->{who} = $user_cache{$operation->{who}};
-
- for (my $i = 0; $i < scalar(@{$operation->{changes}}); $i++) {
- my $change = $operation->{changes}->[$i];
-
- # make an attachment object
- if ($change->{attachid}) {
- $change->{attach} = $attachment_cache{$change->{attachid}};
- }
-
- # empty resolutions are displayed as --- by default
- # make it explicit here to enable correct display of the change
- if ($change->{fieldname} eq 'resolution') {
- $change->{removed} = '---' if $change->{removed} eq '';
- $change->{added} = '---' if $change->{added} eq '';
- }
-
- # make boolean fields true/false instead of 1/0
- my ($table, $field) = ('bugs', $change->{fieldname});
- if ($field =~ /^([^\.]+)\.(.+)$/) {
- ($table, $field) = ($1, $2);
- }
- my $column = $dbh->bz_column_info($table, $field);
- if ($column && $column->{TYPE} eq 'BOOLEAN') {
- $change->{removed} = '';
- $change->{added} = $change->{added} ? 'true' : 'false';
- }
-
- my $field_obj;
- if ($change->{fieldname} =~ /^cf_/) {
- $field_obj = Bugzilla::Field->new({ name => $change->{fieldname}, cache => 1 });
- }
-
- # identify buglist changes
- if ($change->{fieldname} eq 'blocked' ||
- $change->{fieldname} eq 'dependson' ||
- $change->{fieldname} eq 'dupe' ||
- ($field_obj && $field_obj->type == FIELD_TYPE_BUG_ID)
- ) {
- $change->{buglist} = 1;
- foreach my $what (qw(removed added)) {
- my @buglist = split(/[\s,]+/, $change->{$what});
- foreach my $id (@buglist) {
- if ($id && $id =~ /^\d+$/) {
- $visible_bug_ids{$id} = 1;
- }
- }
- }
+ my ($self, $args) = @_;
+ my $file = $args->{'file'};
+ my $vars = $args->{'vars'};
+
+ return if $file ne 'bug/edit.html.tmpl';
+
+ my $user = Bugzilla->user;
+ my $dbh = Bugzilla->dbh;
+ return
+ unless $user->id && $user->settings->{'inline_history'}->{'value'} eq 'on';
+
+ # note: bug/edit.html.tmpl doesn't support multiple bugs
+ my $bug = exists $vars->{'bugs'} ? $vars->{'bugs'}[0] : $vars->{'bug'};
+ my $bug_id = $bug->id;
+
+ # build bug activity
+ my ($activity)
+ = $bug->can('get_activity')
+ ? $bug->get_activity()
+ : Bugzilla::Bug::GetBugActivity($bug_id);
+ $activity = _add_duplicates($bug_id, $activity);
+
+ if (scalar @$activity > MAXIMUM_ACTIVITY_COUNT) {
+ $activity = [];
+ $vars->{'ih_activity'} = 0;
+ $vars->{'ih_activity_max'} = 1;
+ return;
+ }
+
+ # prime caches with objects already loaded
+ my %user_cache;
+ foreach my $comment (@{$bug->comments}) {
+ $user_cache{$comment->{author}->login} = $comment->{author};
+ }
+
+ my %attachment_cache;
+ foreach my $attachment (@{$bug->attachments}) {
+ $attachment_cache{$attachment->id} = $attachment;
+ }
+
+# build a list of bugs we need to check visibility of, so we can check with a single query
+ my %visible_bug_ids;
+
+ # augment and tweak
+ foreach my $operation (@$activity) {
+
+ # make operation.who an object
+ $user_cache{$operation->{who}}
+ ||= Bugzilla::User->new({name => $operation->{who}});
+ $operation->{who} = $user_cache{$operation->{who}};
+
+ for (my $i = 0; $i < scalar(@{$operation->{changes}}); $i++) {
+ my $change = $operation->{changes}->[$i];
+
+ # make an attachment object
+ if ($change->{attachid}) {
+ $change->{attach} = $attachment_cache{$change->{attachid}};
+ }
+
+ # empty resolutions are displayed as --- by default
+ # make it explicit here to enable correct display of the change
+ if ($change->{fieldname} eq 'resolution') {
+ $change->{removed} = '---' if $change->{removed} eq '';
+ $change->{added} = '---' if $change->{added} eq '';
+ }
+
+ # make boolean fields true/false instead of 1/0
+ my ($table, $field) = ('bugs', $change->{fieldname});
+ if ($field =~ /^([^\.]+)\.(.+)$/) {
+ ($table, $field) = ($1, $2);
+ }
+ my $column = $dbh->bz_column_info($table, $field);
+ if ($column && $column->{TYPE} eq 'BOOLEAN') {
+ $change->{removed} = '';
+ $change->{added} = $change->{added} ? 'true' : 'false';
+ }
+
+ my $field_obj;
+ if ($change->{fieldname} =~ /^cf_/) {
+ $field_obj = Bugzilla::Field->new({name => $change->{fieldname}, cache => 1});
+ }
+
+ # identify buglist changes
+ if ( $change->{fieldname} eq 'blocked'
+ || $change->{fieldname} eq 'dependson'
+ || $change->{fieldname} eq 'dupe'
+ || ($field_obj && $field_obj->type == FIELD_TYPE_BUG_ID))
+ {
+ $change->{buglist} = 1;
+ foreach my $what (qw(removed added)) {
+ my @buglist = split(/[\s,]+/, $change->{$what});
+ foreach my $id (@buglist) {
+ if ($id && $id =~ /^\d+$/) {
+ $visible_bug_ids{$id} = 1;
}
+ }
+ }
+ }
+
+ # split multiple flag changes (must be processed last)
+ if ($change->{fieldname} eq 'flagtypes.name') {
+ my @added = split(/, /, $change->{added});
+ my @removed = split(/, /, $change->{removed});
+ next if scalar(@added) <= 1 && scalar(@removed) <= 1;
+
+ # remove current change
+ splice(@{$operation->{changes}}, $i, 1);
+
+ # restructure into added/removed for each flag
+ my %flags;
+ foreach my $added (@added) {
+ my ($value, $name) = $added =~ /^((.+).)$/;
+ $flags{$name}{added} = $value;
+ $flags{$name}{removed} |= '';
+ }
+ foreach my $removed (@removed) {
+ my ($value, $name) = $removed =~ /^((.+).)$/;
+ $flags{$name}{added} |= '';
+ $flags{$name}{removed} = $value;
+ }
- # split multiple flag changes (must be processed last)
- if ($change->{fieldname} eq 'flagtypes.name') {
- my @added = split(/, /, $change->{added});
- my @removed = split(/, /, $change->{removed});
- next if scalar(@added) <= 1 && scalar(@removed) <= 1;
- # remove current change
- splice(@{$operation->{changes}}, $i, 1);
- # restructure into added/removed for each flag
- my %flags;
- foreach my $added (@added) {
- my ($value, $name) = $added =~ /^((.+).)$/;
- $flags{$name}{added} = $value;
- $flags{$name}{removed} |= '';
- }
- foreach my $removed (@removed) {
- my ($value, $name) = $removed =~ /^((.+).)$/;
- $flags{$name}{added} |= '';
- $flags{$name}{removed} = $value;
- }
- # clone current change, modify and insert
- foreach my $flag (sort keys %flags) {
- my $flag_change = {};
- foreach my $key (keys %$change) {
- $flag_change->{$key} = $change->{$key};
- }
- $flag_change->{removed} = $flags{$flag}{removed};
- $flag_change->{added} = $flags{$flag}{added};
- splice(@{$operation->{changes}}, $i, 0, $flag_change);
- }
- $i--;
- }
+ # clone current change, modify and insert
+ foreach my $flag (sort keys %flags) {
+ my $flag_change = {};
+ foreach my $key (keys %$change) {
+ $flag_change->{$key} = $change->{$key};
+ }
+ $flag_change->{removed} = $flags{$flag}{removed};
+ $flag_change->{added} = $flags{$flag}{added};
+ splice(@{$operation->{changes}}, $i, 0, $flag_change);
}
+ $i--;
+ }
}
+ }
- $user->visible_bugs([keys %visible_bug_ids]);
+ $user->visible_bugs([keys %visible_bug_ids]);
- $vars->{'ih_activity'} = $activity;
+ $vars->{'ih_activity'} = $activity;
}
sub _add_duplicates {
- # insert 'is a dupe of this bug' comment to allow js to display
- # as activity
- my ($bug_id, $activity) = @_;
+ # insert 'is a dupe of this bug' comment to allow js to display
+ # as activity
- # we're ignoring pre-bugzilla 3.0 ".. has been marked as a duplicate .."
- # comments because searching each comment's text is expensive. these
- # legacy comments will not be visible at all in the bug's comment/activity
- # stream. bug 928786 deals with migrating those comments to be stored as
- # CMT_HAS_DUPE instead.
+ my ($bug_id, $activity) = @_;
- my $dbh = Bugzilla->dbh;
- my $sth = $dbh->prepare("
- SELECT profiles.login_name, " .
- $dbh->sql_date_format('bug_when', '%Y.%m.%d %H:%i:%s') . ",
+ # we're ignoring pre-bugzilla 3.0 ".. has been marked as a duplicate .."
+ # comments because searching each comment's text is expensive. these
+ # legacy comments will not be visible at all in the bug's comment/activity
+ # stream. bug 928786 deals with migrating those comments to be stored as
+ # CMT_HAS_DUPE instead.
+
+ my $dbh = Bugzilla->dbh;
+ my $sth = $dbh->prepare("
+ SELECT profiles.login_name, "
+ . $dbh->sql_date_format('bug_when', '%Y.%m.%d %H:%i:%s') . ",
extra_data
FROM longdescs
INNER JOIN profiles ON profiles.userid = longdescs.who
WHERE bug_id = ? AND type = ?
ORDER BY bug_when
");
- $sth->execute($bug_id, CMT_HAS_DUPE);
-
- while (my($who, $when, $dupe_id) = $sth->fetchrow_array) {
- my $entry = {
- 'when' => $when,
- 'who' => $who,
- 'changes' => [
- {
- 'removed' => '',
- 'added' => $dupe_id,
- 'attachid' => undef,
- 'fieldname' => 'dupe',
- 'dupe' => 1,
- }
- ],
- };
- push @$activity, $entry;
- }
+ $sth->execute($bug_id, CMT_HAS_DUPE);
+
+ while (my ($who, $when, $dupe_id) = $sth->fetchrow_array) {
+ my $entry = {
+ 'when' => $when,
+ 'who' => $who,
+ 'changes' => [{
+ 'removed' => '',
+ 'added' => $dupe_id,
+ 'attachid' => undef,
+ 'fieldname' => 'dupe',
+ 'dupe' => 1,
+ }],
+ };
+ push @$activity, $entry;
+ }
- return [ sort { $a->{when} cmp $b->{when} } @$activity ];
+ return [sort { $a->{when} cmp $b->{when} } @$activity];
}
sub install_before_final_checks {
- my ($self, $args) = @_;
- add_setting('inline_history', ['on', 'off'], 'off');
+ my ($self, $args) = @_;
+ add_setting('inline_history', ['on', 'off'], 'off');
}
__PACKAGE__->NAME;
diff --git a/extensions/MoreBugUrl/Config.pm b/extensions/MoreBugUrl/Config.pm
index e0eac5f8a..f8175bb4c 100644
--- a/extensions/MoreBugUrl/Config.pm
+++ b/extensions/MoreBugUrl/Config.pm
@@ -13,10 +13,8 @@ use warnings;
use constant NAME => 'MoreBugUrl';
-use constant REQUIRED_MODULES => [
-];
+use constant REQUIRED_MODULES => [];
-use constant OPTIONAL_MODULES => [
-];
+use constant OPTIONAL_MODULES => [];
__PACKAGE__->NAME;
diff --git a/extensions/MoreBugUrl/Extension.pm b/extensions/MoreBugUrl/Extension.pm
index 0a4223e19..0fc29b2aa 100644
--- a/extensions/MoreBugUrl/Extension.pm
+++ b/extensions/MoreBugUrl/Extension.pm
@@ -14,41 +14,44 @@ use warnings;
use parent qw(Bugzilla::Extension);
use constant MORE_SUB_CLASSES => qw(
- Bugzilla::Extension::MoreBugUrl::BitBucket
- Bugzilla::Extension::MoreBugUrl::ReviewBoard
- Bugzilla::Extension::MoreBugUrl::Rietveld
- Bugzilla::Extension::MoreBugUrl::RT
- Bugzilla::Extension::MoreBugUrl::GetSatisfaction
- Bugzilla::Extension::MoreBugUrl::PHP
- Bugzilla::Extension::MoreBugUrl::Redmine
- Bugzilla::Extension::MoreBugUrl::Savane
- Bugzilla::Extension::MoreBugUrl::Phabricator
+ Bugzilla::Extension::MoreBugUrl::BitBucket
+ Bugzilla::Extension::MoreBugUrl::ReviewBoard
+ Bugzilla::Extension::MoreBugUrl::Rietveld
+ Bugzilla::Extension::MoreBugUrl::RT
+ Bugzilla::Extension::MoreBugUrl::GetSatisfaction
+ Bugzilla::Extension::MoreBugUrl::PHP
+ Bugzilla::Extension::MoreBugUrl::Redmine
+ Bugzilla::Extension::MoreBugUrl::Savane
+ Bugzilla::Extension::MoreBugUrl::Phabricator
);
# We need to update bug_see_also table because both
# Rietveld and ReviewBoard were originally under Bugzilla/BugUrl/.
sub install_update_db {
- my $dbh = Bugzilla->dbh;
+ my $dbh = Bugzilla->dbh;
- my $should_rename = $dbh->selectrow_array(
- q{SELECT 1 FROM bug_see_also
+ my $should_rename = $dbh->selectrow_array(
+ q{SELECT 1 FROM bug_see_also
WHERE class IN ('Bugzilla::BugUrl::Rietveld',
- 'Bugzilla::BugUrl::ReviewBoard')});
-
- if ($should_rename) {
- my $sth = $dbh->prepare('UPDATE bug_see_also SET class = ?
- WHERE class = ?');
- $sth->execute('Bugzilla::Extension::MoreBugUrl::ReviewBoard',
- 'Bugzilla::BugUrl::ReviewBoard');
-
- $sth->execute('Bugzilla::Extension::MoreBugUrl::Rietveld',
- 'Bugzilla::BugUrl::Rietveld');
- }
+ 'Bugzilla::BugUrl::ReviewBoard')}
+ );
+
+ if ($should_rename) {
+ my $sth = $dbh->prepare(
+ 'UPDATE bug_see_also SET class = ?
+ WHERE class = ?'
+ );
+ $sth->execute('Bugzilla::Extension::MoreBugUrl::ReviewBoard',
+ 'Bugzilla::BugUrl::ReviewBoard');
+
+ $sth->execute('Bugzilla::Extension::MoreBugUrl::Rietveld',
+ 'Bugzilla::BugUrl::Rietveld');
+ }
}
sub bug_url_sub_classes {
- my ($self, $args) = @_;
- push @{ $args->{sub_classes} }, MORE_SUB_CLASSES;
+ my ($self, $args) = @_;
+ push @{$args->{sub_classes}}, MORE_SUB_CLASSES;
}
__PACKAGE__->NAME;
diff --git a/extensions/MoreBugUrl/lib/BitBucket.pm b/extensions/MoreBugUrl/lib/BitBucket.pm
index dcc85992d..683f9aeb3 100644
--- a/extensions/MoreBugUrl/lib/BitBucket.pm
+++ b/extensions/MoreBugUrl/lib/BitBucket.pm
@@ -18,23 +18,23 @@ use parent qw(Bugzilla::BugUrl);
###############################
sub should_handle {
- my ($class, $uri) = @_;
+ my ($class, $uri) = @_;
- # BitBucket issues have the form of
- # bitbucket.org/user/project/issue/1234
- return (lc($uri->authority) eq "bitbucket.org"
- && $uri->path =~ m|[^/]+/[^/]+/issue/\d+|i) ? 1 : 0;
+ # BitBucket issues have the form of
+ # bitbucket.org/user/project/issue/1234
+ return (lc($uri->authority) eq "bitbucket.org"
+ && $uri->path =~ m|[^/]+/[^/]+/issue/\d+|i) ? 1 : 0;
}
sub _check_value {
- my $class = shift;
+ my $class = shift;
- my $uri = $class->SUPER::_check_value(@_);
+ my $uri = $class->SUPER::_check_value(@_);
- my ($path) = $uri->path =~ m|([^/]+/[^/]+/issue/\d+)|i;
- $uri = new URI("https://bitbucket.org/$path");
+ my ($path) = $uri->path =~ m|([^/]+/[^/]+/issue/\d+)|i;
+ $uri = new URI("https://bitbucket.org/$path");
- return $uri;
+ return $uri;
}
1;
diff --git a/extensions/MoreBugUrl/lib/GetSatisfaction.pm b/extensions/MoreBugUrl/lib/GetSatisfaction.pm
index 74951735b..34c86b65f 100644
--- a/extensions/MoreBugUrl/lib/GetSatisfaction.pm
+++ b/extensions/MoreBugUrl/lib/GetSatisfaction.pm
@@ -18,24 +18,24 @@ use parent qw(Bugzilla::BugUrl);
###############################
sub should_handle {
- my ($class, $uri) = @_;
+ my ($class, $uri) = @_;
- # GetSatisfaction URLs only have one form:
- # http(s)://getsatisfaction.com/PROJECT_NAME/topics/TOPIC_NAME
- return (lc($uri->authority) eq 'getsatisfaction.com'
- and $uri->path =~ m|^/[^/]+/topics/[^/]+$|) ? 1 : 0;
+ # GetSatisfaction URLs only have one form:
+ # http(s)://getsatisfaction.com/PROJECT_NAME/topics/TOPIC_NAME
+ return (lc($uri->authority) eq 'getsatisfaction.com'
+ and $uri->path =~ m|^/[^/]+/topics/[^/]+$|) ? 1 : 0;
}
sub _check_value {
- my ($class, $uri) = @_;
+ my ($class, $uri) = @_;
- $uri = $class->SUPER::_check_value($uri);
+ $uri = $class->SUPER::_check_value($uri);
- # GetSatisfaction HTTP URLs redirect to HTTPS, so just use the HTTPS
- # scheme.
- $uri->scheme('https');
+ # GetSatisfaction HTTP URLs redirect to HTTPS, so just use the HTTPS
+ # scheme.
+ $uri->scheme('https');
- return $uri;
+ return $uri;
}
1;
diff --git a/extensions/MoreBugUrl/lib/PHP.pm b/extensions/MoreBugUrl/lib/PHP.pm
index 6f201d7b1..9419bcd14 100644
--- a/extensions/MoreBugUrl/lib/PHP.pm
+++ b/extensions/MoreBugUrl/lib/PHP.pm
@@ -18,27 +18,27 @@ use parent qw(Bugzilla::BugUrl);
###############################
sub should_handle {
- my ($class, $uri) = @_;
+ my ($class, $uri) = @_;
- # PHP Bug URLs have only one form:
- # https://bugs.php.net/bug.php?id=1234
- return (lc($uri->authority) eq 'bugs.php.net'
- and $uri->path =~ m|/bug\.php$|
- and $uri->query_param('id') =~ /^\d+$/) ? 1 : 0;
+ # PHP Bug URLs have only one form:
+ # https://bugs.php.net/bug.php?id=1234
+ return (lc($uri->authority) eq 'bugs.php.net'
+ and $uri->path =~ m|/bug\.php$|
+ and $uri->query_param('id') =~ /^\d+$/) ? 1 : 0;
}
sub _check_value {
- my $class = shift;
+ my $class = shift;
- my $uri = $class->SUPER::_check_value(@_);
+ my $uri = $class->SUPER::_check_value(@_);
- # PHP Bug URLs redirect to HTTPS, so just use the HTTPS scheme.
- $uri->scheme('https');
+ # PHP Bug URLs redirect to HTTPS, so just use the HTTPS scheme.
+ $uri->scheme('https');
- # And remove any # part if there is one.
- $uri->fragment(undef);
+ # And remove any # part if there is one.
+ $uri->fragment(undef);
- return $uri;
+ return $uri;
}
1;
diff --git a/extensions/MoreBugUrl/lib/Phabricator.pm b/extensions/MoreBugUrl/lib/Phabricator.pm
index 818d9af8f..0822663ad 100644
--- a/extensions/MoreBugUrl/lib/Phabricator.pm
+++ b/extensions/MoreBugUrl/lib/Phabricator.pm
@@ -18,24 +18,25 @@ use parent qw(Bugzilla::BugUrl);
###############################
sub should_handle {
- my ($class, $uri) = @_;
- return ($uri->path =~ m|^/T\d+$|) ? 1 : 0;
+ my ($class, $uri) = @_;
+ return ($uri->path =~ m|^/T\d+$|) ? 1 : 0;
}
sub _check_value {
- my $class = shift;
+ my $class = shift;
- my $uri = $class->SUPER::_check_value(@_);
+ my $uri = $class->SUPER::_check_value(@_);
- # Phabricator URLs have only one form:
- # http://example.com/T111
+ # Phabricator URLs have only one form:
+ # http://example.com/T111
- # Make sure there are no query parameters.
- $uri->query(undef);
- # And remove any # part if there is one.
- $uri->fragment(undef);
+ # Make sure there are no query parameters.
+ $uri->query(undef);
- return $uri;
+ # And remove any # part if there is one.
+ $uri->fragment(undef);
+
+ return $uri;
}
1;
diff --git a/extensions/MoreBugUrl/lib/RT.pm b/extensions/MoreBugUrl/lib/RT.pm
index acb90cf39..54dc2dd4c 100644
--- a/extensions/MoreBugUrl/lib/RT.pm
+++ b/extensions/MoreBugUrl/lib/RT.pm
@@ -18,24 +18,25 @@ use parent qw(Bugzilla::BugUrl);
###############################
sub should_handle {
- my ($class, $uri) = @_;
-
- # RT URLs can look like various things:
- # http://example.com/rt/Ticket/Display.html?id=1234
- # https://example.com/Public/Bug/Display.html?id=1234
- return ($uri->path =~ m|/Display\.html$|
- and $uri->query_param('id') =~ /^\d+$/) ? 1 : 0;
+ my ($class, $uri) = @_;
+
+ # RT URLs can look like various things:
+ # http://example.com/rt/Ticket/Display.html?id=1234
+ # https://example.com/Public/Bug/Display.html?id=1234
+ return ($uri->path =~ m|/Display\.html$| and $uri->query_param('id') =~ /^\d+$/)
+ ? 1
+ : 0;
}
sub _check_value {
- my $class = shift;
+ my $class = shift;
- my $uri = $class->SUPER::_check_value(@_);
+ my $uri = $class->SUPER::_check_value(@_);
- # And remove any # part if there is one.
- $uri->fragment(undef);
+ # And remove any # part if there is one.
+ $uri->fragment(undef);
- return $uri;
+ return $uri;
}
1;
diff --git a/extensions/MoreBugUrl/lib/Redmine.pm b/extensions/MoreBugUrl/lib/Redmine.pm
index 57a071239..712dab197 100644
--- a/extensions/MoreBugUrl/lib/Redmine.pm
+++ b/extensions/MoreBugUrl/lib/Redmine.pm
@@ -18,24 +18,25 @@ use parent qw(Bugzilla::BugUrl);
###############################
sub should_handle {
- my ($class, $uri) = @_;
- return ($uri->path =~ m|/issues/\d+$|) ? 1 : 0;
+ my ($class, $uri) = @_;
+ return ($uri->path =~ m|/issues/\d+$|) ? 1 : 0;
}
sub _check_value {
- my $class = shift;
+ my $class = shift;
- my $uri = $class->SUPER::_check_value(@_);
+ my $uri = $class->SUPER::_check_value(@_);
- # Redmine URLs have only one form:
- # http://demo.redmine.com/issues/111
+ # Redmine URLs have only one form:
+ # http://demo.redmine.com/issues/111
- # Make sure there are no query parameters.
- $uri->query(undef);
- # And remove any # part if there is one.
- $uri->fragment(undef);
+ # Make sure there are no query parameters.
+ $uri->query(undef);
- return $uri;
+ # And remove any # part if there is one.
+ $uri->fragment(undef);
+
+ return $uri;
}
1;
diff --git a/extensions/MoreBugUrl/lib/ReviewBoard.pm b/extensions/MoreBugUrl/lib/ReviewBoard.pm
index af5ff0684..48cbecf11 100644
--- a/extensions/MoreBugUrl/lib/ReviewBoard.pm
+++ b/extensions/MoreBugUrl/lib/ReviewBoard.pm
@@ -18,29 +18,30 @@ use parent qw(Bugzilla::BugUrl);
###############################
sub should_handle {
- my ($class, $uri) = @_;
- return ($uri->path =~ m|/r/\d+/?$|) ? 1 : 0;
+ my ($class, $uri) = @_;
+ return ($uri->path =~ m|/r/\d+/?$|) ? 1 : 0;
}
sub _check_value {
- my $class = shift;
+ my $class = shift;
- my $uri = $class->SUPER::_check_value(@_);
+ my $uri = $class->SUPER::_check_value(@_);
- # Review Board URLs have only one form (the trailing slash is optional):
- # http://reviews.reviewboard.org/r/111/
+ # Review Board URLs have only one form (the trailing slash is optional):
+ # http://reviews.reviewboard.org/r/111/
- # Make sure there are no query parameters.
- $uri->query(undef);
- # And remove any # part if there is one.
- $uri->fragment(undef);
+ # Make sure there are no query parameters.
+ $uri->query(undef);
- # make sure the trailing slash is present
- if ($uri->path !~ m|/$|) {
- $uri->path($uri->path . '/');
- }
+ # And remove any # part if there is one.
+ $uri->fragment(undef);
- return $uri;
+ # make sure the trailing slash is present
+ if ($uri->path !~ m|/$|) {
+ $uri->path($uri->path . '/');
+ }
+
+ return $uri;
}
1;
diff --git a/extensions/MoreBugUrl/lib/Rietveld.pm b/extensions/MoreBugUrl/lib/Rietveld.pm
index a4bf08492..55a18b0b2 100644
--- a/extensions/MoreBugUrl/lib/Rietveld.pm
+++ b/extensions/MoreBugUrl/lib/Rietveld.pm
@@ -18,32 +18,34 @@ use parent qw(Bugzilla::BugUrl);
###############################
sub should_handle {
- my ($class, $uri) = @_;
- return ($uri->authority =~ /\.appspot\.com$/i
- and $uri->path =~ m#^/\d+(?:/|/show)?$#) ? 1 : 0;
+ my ($class, $uri) = @_;
+ return ($uri->authority =~ /\.appspot\.com$/i
+ and $uri->path =~ m#^/\d+(?:/|/show)?$#) ? 1 : 0;
}
sub _check_value {
- my ($class, $uri) = @_;
-
- $uri = $class->SUPER::_check_value($uri);
-
- # Rietveld URLs have three forms:
- # http(s)://example.appspot.com/1234
- # http(s)://example.appspot.com/1234/
- # http(s)://example.appspot.com/1234/show
- if ($uri->path =~ m#^/(\d+)(?:/|/show)$#) {
- # This is the shortest standard URL form for Rietveld issues,
- # and so we reduce all URLs to this.
- $uri->path('/' . $1);
- }
-
- # Make sure there are no query parameters.
- $uri->query(undef);
- # And remove any # part if there is one.
- $uri->fragment(undef);
-
- return $uri;
+ my ($class, $uri) = @_;
+
+ $uri = $class->SUPER::_check_value($uri);
+
+ # Rietveld URLs have three forms:
+ # http(s)://example.appspot.com/1234
+ # http(s)://example.appspot.com/1234/
+ # http(s)://example.appspot.com/1234/show
+ if ($uri->path =~ m#^/(\d+)(?:/|/show)$#) {
+
+ # This is the shortest standard URL form for Rietveld issues,
+ # and so we reduce all URLs to this.
+ $uri->path('/' . $1);
+ }
+
+ # Make sure there are no query parameters.
+ $uri->query(undef);
+
+ # And remove any # part if there is one.
+ $uri->fragment(undef);
+
+ return $uri;
}
1;
diff --git a/extensions/MoreBugUrl/lib/Savane.pm b/extensions/MoreBugUrl/lib/Savane.pm
index 5b35bbf7d..717878b33 100644
--- a/extensions/MoreBugUrl/lib/Savane.pm
+++ b/extensions/MoreBugUrl/lib/Savane.pm
@@ -18,24 +18,27 @@ use parent qw(Bugzilla::BugUrl);
###############################
sub should_handle {
- my ($class, $uri) = @_;
- # Savane URLs look like the following (the index.php is optional):
- # https://savannah.gnu.org/bugs/index.php?107657
- # https://savannah.gnu.org/patch/index.php?107657
- # https://savannah.gnu.org/support/index.php?107657
- # https://savannah.gnu.org/task/index.php?107657
- return ($uri->as_string =~ m|/(bugs\|patch\|support\|task)/(index\.php)?\?\d+$|) ? 1 : 0;
+ my ($class, $uri) = @_;
+
+ # Savane URLs look like the following (the index.php is optional):
+ # https://savannah.gnu.org/bugs/index.php?107657
+ # https://savannah.gnu.org/patch/index.php?107657
+ # https://savannah.gnu.org/support/index.php?107657
+ # https://savannah.gnu.org/task/index.php?107657
+ return ($uri->as_string =~ m|/(bugs\|patch\|support\|task)/(index\.php)?\?\d+$|)
+ ? 1
+ : 0;
}
sub _check_value {
- my $class = shift;
+ my $class = shift;
- my $uri = $class->SUPER::_check_value(@_);
+ my $uri = $class->SUPER::_check_value(@_);
- # And remove any # part if there is one.
- $uri->fragment(undef);
+ # And remove any # part if there is one.
+ $uri->fragment(undef);
- return $uri;
+ return $uri;
}
1;
diff --git a/extensions/OldBugMove/Extension.pm b/extensions/OldBugMove/Extension.pm
index 375707fd2..53c26fc8d 100644
--- a/extensions/OldBugMove/Extension.pm
+++ b/extensions/OldBugMove/Extension.pm
@@ -29,172 +29,172 @@ use constant VERSION => BUGZILLA_VERSION;
use constant CMT_MOVED_TO => 4;
sub install_update_db {
- my $reso_type = Bugzilla::Field::Choice->type('resolution');
- my $moved_reso = $reso_type->new({ name => 'MOVED' });
- # We make the MOVED resolution inactive, so that it doesn't show up
- # as a valid drop-down option.
- if ($moved_reso) {
- $moved_reso->set_is_active(0);
- $moved_reso->update();
- }
- else {
- print "Creating the MOVED resolution...\n";
- $reso_type->create(
- { value => 'MOVED', sortkey => '30000', isactive => 0 });
- }
+ my $reso_type = Bugzilla::Field::Choice->type('resolution');
+ my $moved_reso = $reso_type->new({name => 'MOVED'});
+
+ # We make the MOVED resolution inactive, so that it doesn't show up
+ # as a valid drop-down option.
+ if ($moved_reso) {
+ $moved_reso->set_is_active(0);
+ $moved_reso->update();
+ }
+ else {
+ print "Creating the MOVED resolution...\n";
+ $reso_type->create({value => 'MOVED', sortkey => '30000', isactive => 0});
+ }
}
sub config_add_panels {
- my ($self, $args) = @_;
- my $modules = $args->{'panel_modules'};
- $modules->{'OldBugMove'} = 'Bugzilla::Extension::OldBugMove::Params';
+ my ($self, $args) = @_;
+ my $modules = $args->{'panel_modules'};
+ $modules->{'OldBugMove'} = 'Bugzilla::Extension::OldBugMove::Params';
}
sub template_before_create {
- my ($self, $args) = @_;
- my $config = $args->{config};
+ my ($self, $args) = @_;
+ my $config = $args->{config};
- my $constants = $config->{CONSTANTS};
- $constants->{CMT_MOVED_TO} = CMT_MOVED_TO;
+ my $constants = $config->{CONSTANTS};
+ $constants->{CMT_MOVED_TO} = CMT_MOVED_TO;
- my $vars = $config->{VARIABLES};
- $vars->{oldbugmove_user_is_mover} = \&_user_is_mover;
+ my $vars = $config->{VARIABLES};
+ $vars->{oldbugmove_user_is_mover} = \&_user_is_mover;
}
sub object_before_delete {
- my ($self, $args) = @_;
- my $object = $args->{'object'};
- if ($object->isa('Bugzilla::Field::Choice::resolution')) {
- if ($object->name eq 'MOVED') {
- ThrowUserError('oldbugmove_no_delete_moved');
- }
+ my ($self, $args) = @_;
+ my $object = $args->{'object'};
+ if ($object->isa('Bugzilla::Field::Choice::resolution')) {
+ if ($object->name eq 'MOVED') {
+ ThrowUserError('oldbugmove_no_delete_moved');
}
+ }
}
sub object_before_set {
- my ($self, $args) = @_;
- my ($object, $field) = @$args{qw(object field)};
- if ($field eq 'resolution' and $object->isa('Bugzilla::Bug')) {
- # Store the old value so that end_of_set can check it.
- $object->{'_oldbugmove_old_resolution'} = $object->resolution;
- }
+ my ($self, $args) = @_;
+ my ($object, $field) = @$args{qw(object field)};
+ if ($field eq 'resolution' and $object->isa('Bugzilla::Bug')) {
+
+ # Store the old value so that end_of_set can check it.
+ $object->{'_oldbugmove_old_resolution'} = $object->resolution;
+ }
}
sub object_end_of_set {
- my ($self, $args) = @_;
- my ($object, $field) = @$args{qw(object field)};
- if ($field eq 'resolution' and $object->isa('Bugzilla::Bug')) {
- my $old_value = delete $object->{'_oldbugmove_old_resolution'};
- return if $old_value eq $object->resolution;
- if ($object->resolution eq 'MOVED') {
- $object->add_comment('', { type => CMT_MOVED_TO,
- extra_data => Bugzilla->user->login });
- }
+ my ($self, $args) = @_;
+ my ($object, $field) = @$args{qw(object field)};
+ if ($field eq 'resolution' and $object->isa('Bugzilla::Bug')) {
+ my $old_value = delete $object->{'_oldbugmove_old_resolution'};
+ return if $old_value eq $object->resolution;
+ if ($object->resolution eq 'MOVED') {
+ $object->add_comment('',
+ {type => CMT_MOVED_TO, extra_data => Bugzilla->user->login});
}
+ }
}
sub object_end_of_set_all {
- my ($self, $args) = @_;
- my $object = $args->{'object'};
+ my ($self, $args) = @_;
+ my $object = $args->{'object'};
- if ($object->isa('Bugzilla::Bug') and Bugzilla->input_params->{'oldbugmove'}) {
- my $new_status = Bugzilla->params->{'duplicate_or_move_bug_status'};
- $object->set_bug_status($new_status, { resolution => 'MOVED' });
- }
+ if ($object->isa('Bugzilla::Bug') and Bugzilla->input_params->{'oldbugmove'}) {
+ my $new_status = Bugzilla->params->{'duplicate_or_move_bug_status'};
+ $object->set_bug_status($new_status, {resolution => 'MOVED'});
+ }
}
sub object_validators {
- my ($self, $args) = @_;
- my ($class, $validators) = @$args{qw(class validators)};
- if ($class->isa('Bugzilla::Comment')) {
- my $extra_data_validator = $validators->{extra_data};
- $validators->{extra_data} =
- sub { _check_comment_extra_data($extra_data_validator, @_) };
- }
- elsif ($class->isa('Bugzilla::Bug')) {
- my $reso_validator = $validators->{resolution};
- $validators->{resolution} =
- sub { _check_bug_resolution($reso_validator, @_) };
- }
+ my ($self, $args) = @_;
+ my ($class, $validators) = @$args{qw(class validators)};
+ if ($class->isa('Bugzilla::Comment')) {
+ my $extra_data_validator = $validators->{extra_data};
+ $validators->{extra_data}
+ = sub { _check_comment_extra_data($extra_data_validator, @_) };
+ }
+ elsif ($class->isa('Bugzilla::Bug')) {
+ my $reso_validator = $validators->{resolution};
+ $validators->{resolution} = sub { _check_bug_resolution($reso_validator, @_) };
+ }
}
sub _check_bug_resolution {
- my $original_validator = shift;
- my ($invocant, $resolution) = @_;
-
- if ($resolution eq 'MOVED' && $invocant->resolution ne 'MOVED'
- && !Bugzilla->input_params->{'oldbugmove'})
- {
- # MOVED has a special meaning and can only be used when
- # really moving bugs to another installation.
- ThrowUserError('oldbugmove_no_manual_move');
- }
-
- return $original_validator->(@_);
+ my $original_validator = shift;
+ my ($invocant, $resolution) = @_;
+
+ if ( $resolution eq 'MOVED'
+ && $invocant->resolution ne 'MOVED'
+ && !Bugzilla->input_params->{'oldbugmove'})
+ {
+ # MOVED has a special meaning and can only be used when
+ # really moving bugs to another installation.
+ ThrowUserError('oldbugmove_no_manual_move');
+ }
+
+ return $original_validator->(@_);
}
sub _check_comment_extra_data {
- my $original_validator = shift;
- my ($invocant, $extra_data, undef, $params) = @_;
- my $type = blessed($invocant) ? $invocant->type : $params->{type};
-
- if ($type == CMT_MOVED_TO) {
- return Bugzilla::User->check($extra_data)->login;
- }
- return $original_validator->(@_);
+ my $original_validator = shift;
+ my ($invocant, $extra_data, undef, $params) = @_;
+ my $type = blessed($invocant) ? $invocant->type : $params->{type};
+
+ if ($type == CMT_MOVED_TO) {
+ return Bugzilla::User->check($extra_data)->login;
+ }
+ return $original_validator->(@_);
}
sub bug_end_of_update {
- my ($self, $args) = @_;
- my ($bug, $old_bug, $changes) = @$args{qw(bug old_bug changes)};
- if (defined $changes->{'resolution'}
- and $changes->{'resolution'}->[1] eq 'MOVED')
- {
- $self->_move_bug($bug, $old_bug);
- }
+ my ($self, $args) = @_;
+ my ($bug, $old_bug, $changes) = @$args{qw(bug old_bug changes)};
+ if (defined $changes->{'resolution'}
+ and $changes->{'resolution'}->[1] eq 'MOVED')
+ {
+ $self->_move_bug($bug, $old_bug);
+ }
}
sub _move_bug {
- my ($self, $bug, $old_bug) = @_;
-
- my $dbh = Bugzilla->dbh;
- my $template = Bugzilla->template;
-
- _user_is_mover(Bugzilla->user)
- or ThrowUserError("auth_failure", { action => 'move',
- object => 'bugs' });
-
- # Don't export the new status and resolution. We want the current
- # ones.
- local $Storable::forgive_me = 1;
- my $export_me = dclone($bug);
- $export_me->{bug_status} = $old_bug->bug_status;
- delete $export_me->{status};
- $export_me->{resolution} = $old_bug->resolution;
-
- # Prepare and send all data about these bugs to the new database
- my $to = Bugzilla->params->{'move-to-address'};
- $to =~ s/@/\@/;
- my $from = Bugzilla->params->{'mailfrom'};
- $from =~ s/@/\@/;
- my $msg = "To: $to\n";
- $msg .= "From: Bugzilla <" . $from . ">\n";
- $msg .= "Subject: Moving bug " . $bug->id . "\n\n";
- my @fieldlist = (Bugzilla::Bug->fields, 'group', 'long_desc',
- 'attachment', 'attachmentdata');
- my %displayfields = map { $_ => 1 } @fieldlist;
- my $vars = { bugs => [$export_me], displayfields => \%displayfields };
- $template->process("bug/show.xml.tmpl", $vars, \$msg)
- || ThrowTemplateError($template->error());
- $msg .= "\n";
- MessageToMTA($msg);
+ my ($self, $bug, $old_bug) = @_;
+
+ my $dbh = Bugzilla->dbh;
+ my $template = Bugzilla->template;
+
+ _user_is_mover(Bugzilla->user)
+ or ThrowUserError("auth_failure", {action => 'move', object => 'bugs'});
+
+ # Don't export the new status and resolution. We want the current
+ # ones.
+ local $Storable::forgive_me = 1;
+ my $export_me = dclone($bug);
+ $export_me->{bug_status} = $old_bug->bug_status;
+ delete $export_me->{status};
+ $export_me->{resolution} = $old_bug->resolution;
+
+ # Prepare and send all data about these bugs to the new database
+ my $to = Bugzilla->params->{'move-to-address'};
+ $to =~ s/@/\@/;
+ my $from = Bugzilla->params->{'mailfrom'};
+ $from =~ s/@/\@/;
+ my $msg = "To: $to\n";
+ $msg .= "From: Bugzilla <" . $from . ">\n";
+ $msg .= "Subject: Moving bug " . $bug->id . "\n\n";
+ my @fieldlist = (Bugzilla::Bug->fields, 'group', 'long_desc', 'attachment',
+ 'attachmentdata');
+ my %displayfields = map { $_ => 1 } @fieldlist;
+ my $vars = {bugs => [$export_me], displayfields => \%displayfields};
+ $template->process("bug/show.xml.tmpl", $vars, \$msg)
+ || ThrowTemplateError($template->error());
+ $msg .= "\n";
+ MessageToMTA($msg);
}
sub _user_is_mover {
- my $user = shift;
+ my $user = shift;
- my @movers = map { trim($_) } split(',', Bugzilla->params->{'movers'});
- return ($user->id and grep($_ eq $user->login, @movers)) ? 1 : 0;
+ my @movers = map { trim($_) } split(',', Bugzilla->params->{'movers'});
+ return ($user->id and grep($_ eq $user->login, @movers)) ? 1 : 0;
}
__PACKAGE__->NAME;
diff --git a/extensions/OldBugMove/lib/Params.pm b/extensions/OldBugMove/lib/Params.pm
index 05e3ed277..e1bab048b 100644
--- a/extensions/OldBugMove/lib/Params.pm
+++ b/extensions/OldBugMove/lib/Params.pm
@@ -16,23 +16,11 @@ use Bugzilla::Config::Common;
our $sortkey = 700;
use constant get_param_list => (
- {
- name => 'move-to-url',
- type => 't',
- default => ''
- },
-
- {
- name => 'move-to-address',
- type => 't',
- default => 'bugzilla-import'
- },
-
- {
- name => 'movers',
- type => 't',
- default => ''
- },
+ {name => 'move-to-url', type => 't', default => ''},
+
+ {name => 'move-to-address', type => 't', default => 'bugzilla-import'},
+
+ {name => 'movers', type => 't', default => ''},
);
1;
diff --git a/extensions/SecureMail/Config.pm b/extensions/SecureMail/Config.pm
index f1975c1c1..69074573f 100644
--- a/extensions/SecureMail/Config.pm
+++ b/extensions/SecureMail/Config.pm
@@ -25,25 +25,19 @@ use strict;
use constant NAME => 'SecureMail';
use constant REQUIRED_MODULES => [
- {
- package => 'Crypt-OpenPGP',
- module => 'Crypt::OpenPGP',
- # 1.02 added the ability for new() to take KeyRing objects for the
- # PubRing argument.
- version => '1.02',
- # 1.04 hangs - https://rt.cpan.org/Public/Bug/Display.html?id=68018
- # blacklist => [ '1.04' ],
- },
- {
- package => 'Crypt-SMIME',
- module => 'Crypt::SMIME',
- version => 0,
- },
- {
- package => 'HTML-Tree',
- module => 'HTML::Tree',
- version => 0,
- }
+ {
+ package => 'Crypt-OpenPGP',
+ module => 'Crypt::OpenPGP',
+
+ # 1.02 added the ability for new() to take KeyRing objects for the
+ # PubRing argument.
+ version => '1.02',
+
+ # 1.04 hangs - https://rt.cpan.org/Public/Bug/Display.html?id=68018
+ # blacklist => [ '1.04' ],
+ },
+ {package => 'Crypt-SMIME', module => 'Crypt::SMIME', version => 0,},
+ {package => 'HTML-Tree', module => 'HTML::Tree', version => 0,}
];
__PACKAGE__->NAME;
diff --git a/extensions/SecureMail/Extension.pm b/extensions/SecureMail/Extension.pm
index 492ce0cb6..e1d7468f4 100644
--- a/extensions/SecureMail/Extension.pm
+++ b/extensions/SecureMail/Extension.pm
@@ -53,12 +53,12 @@ use constant SECURE_ALL => 2;
# public_key text in the 'profiles' table - stores public key
##############################################################################
sub install_update_db {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my $dbh = Bugzilla->dbh;
- $dbh->bz_add_column('groups', 'secure_mail',
- {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 0});
- $dbh->bz_add_column('profiles', 'public_key', { TYPE => 'LONGTEXT' });
+ my $dbh = Bugzilla->dbh;
+ $dbh->bz_add_column('groups', 'secure_mail',
+ {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 0});
+ $dbh->bz_add_column('profiles', 'public_key', {TYPE => 'LONGTEXT'});
}
##############################################################################
@@ -66,516 +66,530 @@ sub install_update_db {
##############################################################################
BEGIN {
- *Bugzilla::Group::secure_mail = \&_group_secure_mail;
- *Bugzilla::User::public_key = \&_user_public_key;
+ *Bugzilla::Group::secure_mail = \&_group_secure_mail;
+ *Bugzilla::User::public_key = \&_user_public_key;
}
sub _group_secure_mail { return $_[0]->{'secure_mail'}; }
# We want to lazy-load the public_key.
sub _user_public_key {
- my $self = shift;
- if (!exists $self->{public_key}) {
- ($self->{public_key}) = Bugzilla->dbh->selectrow_array(
- "SELECT public_key FROM profiles WHERE userid = ?",
- undef,
- $self->id
- );
- }
- return $self->{public_key};
+ my $self = shift;
+ if (!exists $self->{public_key}) {
+ ($self->{public_key})
+ = Bugzilla->dbh->selectrow_array(
+ "SELECT public_key FROM profiles WHERE userid = ?",
+ undef, $self->id);
+ }
+ return $self->{public_key};
}
# Make sure generic functions know about the additional fields in the user
# and group objects.
sub object_columns {
- my ($self, $args) = @_;
- my $class = $args->{'class'};
- my $columns = $args->{'columns'};
+ my ($self, $args) = @_;
+ my $class = $args->{'class'};
+ my $columns = $args->{'columns'};
- if ($class->isa('Bugzilla::Group')) {
- push(@$columns, 'secure_mail');
- }
+ if ($class->isa('Bugzilla::Group')) {
+ push(@$columns, 'secure_mail');
+ }
}
# Plug appropriate validators so we can check the validity of the two
# fields created by this extension, when new values are submitted.
sub object_validators {
- my ($self, $args) = @_;
- my %args = %{ $args };
- my ($invocant, $validators) = @args{qw(class validators)};
+ my ($self, $args) = @_;
+ my %args = %{$args};
+ my ($invocant, $validators) = @args{qw(class validators)};
+
+ if ($invocant->isa('Bugzilla::Group')) {
+ $validators->{'secure_mail'} = \&Bugzilla::Object::check_boolean;
+ }
+ elsif ($invocant->isa('Bugzilla::User')) {
+ $validators->{'public_key'} = sub {
+ my ($self, $value) = @_;
+ $value = trim($value) || '';
+
+ return $value if $value eq '';
+
+ if ($value =~ /PUBLIC KEY/) {
+
+ # PGP keys must be ASCII-armoured.
+ if (!Crypt::OpenPGP::Armour->unarmour($value)) {
+ ThrowUserError('securemail_invalid_key',
+ {errstr => Crypt::OpenPGP::Armour->errstr});
+ }
+ }
+ elsif ($value =~ /BEGIN CERTIFICATE/) {
- if ($invocant->isa('Bugzilla::Group')) {
- $validators->{'secure_mail'} = \&Bugzilla::Object::check_boolean;
- }
- elsif ($invocant->isa('Bugzilla::User')) {
- $validators->{'public_key'} = sub {
- my ($self, $value) = @_;
- $value = trim($value) || '';
-
- return $value if $value eq '';
-
- if ($value =~ /PUBLIC KEY/) {
- # PGP keys must be ASCII-armoured.
- if (!Crypt::OpenPGP::Armour->unarmour($value)) {
- ThrowUserError('securemail_invalid_key',
- { errstr => Crypt::OpenPGP::Armour->errstr });
- }
- }
- elsif ($value =~ /BEGIN CERTIFICATE/) {
- # S/MIME Keys must be in PEM format (Base64-encoded X.509)
- #
- # Crypt::SMIME seems not to like tainted values - it claims
- # they aren't scalars!
- trick_taint($value);
-
- my $smime = Crypt::SMIME->new();
- eval {
- $smime->setPublicKey([$value]);
- };
- if ($@) {
- ThrowUserError('securemail_invalid_key',
- { errstr => $@ });
- }
- }
- else {
- ThrowUserError('securemail_invalid_key');
- }
-
- return $value;
- };
- }
+ # S/MIME Keys must be in PEM format (Base64-encoded X.509)
+ #
+ # Crypt::SMIME seems not to like tainted values - it claims
+ # they aren't scalars!
+ trick_taint($value);
+
+ my $smime = Crypt::SMIME->new();
+ eval { $smime->setPublicKey([$value]); };
+ if ($@) {
+ ThrowUserError('securemail_invalid_key', {errstr => $@});
+ }
+ }
+ else {
+ ThrowUserError('securemail_invalid_key');
+ }
+
+ return $value;
+ };
+ }
}
# When creating a 'group' object, set up the secure_mail field appropriately.
sub object_before_create {
- my ($self, $args) = @_;
- my $class = $args->{'class'};
- my $params = $args->{'params'};
+ my ($self, $args) = @_;
+ my $class = $args->{'class'};
+ my $params = $args->{'params'};
- if ($class->isa('Bugzilla::Group')) {
- $params->{secure_mail} = Bugzilla->cgi->param('secure_mail');
- }
+ if ($class->isa('Bugzilla::Group')) {
+ $params->{secure_mail} = Bugzilla->cgi->param('secure_mail');
+ }
}
# On update, make sure the updating process knows about our new columns.
sub object_update_columns {
- my ($self, $args) = @_;
- my $object = $args->{'object'};
- my $columns = $args->{'columns'};
+ my ($self, $args) = @_;
+ my $object = $args->{'object'};
+ my $columns = $args->{'columns'};
- if ($object->isa('Bugzilla::Group')) {
- # This seems like a convenient moment to extract this value...
- $object->set('secure_mail', Bugzilla->cgi->param('secure_mail'));
+ if ($object->isa('Bugzilla::Group')) {
- push(@$columns, 'secure_mail');
- }
- elsif ($object->isa('Bugzilla::User')) {
- push(@$columns, 'public_key');
- }
+ # This seems like a convenient moment to extract this value...
+ $object->set('secure_mail', Bugzilla->cgi->param('secure_mail'));
+
+ push(@$columns, 'secure_mail');
+ }
+ elsif ($object->isa('Bugzilla::User')) {
+ push(@$columns, 'public_key');
+ }
}
# Handle the setting and changing of the public key.
sub user_preferences {
- my ($self, $args) = @_;
- my $tab = $args->{'current_tab'};
- my $save = $args->{'save_changes'};
- my $handled = $args->{'handled'};
- my $vars = $args->{'vars'};
- my $params = Bugzilla->input_params;
-
- return unless $tab eq 'securemail';
-
- # Create a new user object so we don't mess with the main one, as we
- # don't know where it's been...
- my $user = new Bugzilla::User(Bugzilla->user->id);
-
- if ($save) {
- $user->set('public_key', $params->{'public_key'});
- $user->update();
-
- # Send user a test email
- if ($user->public_key) {
- _send_test_email($user);
- $vars->{'test_email_sent'} = 1;
- }
+ my ($self, $args) = @_;
+ my $tab = $args->{'current_tab'};
+ my $save = $args->{'save_changes'};
+ my $handled = $args->{'handled'};
+ my $vars = $args->{'vars'};
+ my $params = Bugzilla->input_params;
+
+ return unless $tab eq 'securemail';
+
+ # Create a new user object so we don't mess with the main one, as we
+ # don't know where it's been...
+ my $user = new Bugzilla::User(Bugzilla->user->id);
+
+ if ($save) {
+ $user->set('public_key', $params->{'public_key'});
+ $user->update();
+
+ # Send user a test email
+ if ($user->public_key) {
+ _send_test_email($user);
+ $vars->{'test_email_sent'} = 1;
}
+ }
- $vars->{'public_key'} = $user->public_key;
+ $vars->{'public_key'} = $user->public_key;
- # Set the 'handled' scalar reference to true so that the caller
- # knows the panel name is valid and that an extension took care of it.
- $$handled = 1;
+ # Set the 'handled' scalar reference to true so that the caller
+ # knows the panel name is valid and that an extension took care of it.
+ $$handled = 1;
}
sub template_before_process {
- my ($self, $args) = @_;
- my $file = $args->{'file'};
- my $vars = $args->{'vars'};
-
- # Bug dependency emails contain the subject of the dependent bug
- # right before the diffs when a status has gone from open/closed
- # or closed/open. We need to sanitize the subject of change.blocker
- # similar to how we do referenced bugs
- return unless
- $file eq 'email/bugmail.html.tmpl'
- || $file eq 'email/bugmail.txt.tmpl';
-
- if (defined $vars->{diffs}) {
- foreach my $change (@{ $vars->{diffs} }) {
- next if !defined $change->{blocker};
- if (grep($_->secure_mail, @{ $change->{blocker}->groups_in })) {
- $change->{blocker}->{short_desc} = "(Secure bug)";
- }
- }
+ my ($self, $args) = @_;
+ my $file = $args->{'file'};
+ my $vars = $args->{'vars'};
+
+ # Bug dependency emails contain the subject of the dependent bug
+ # right before the diffs when a status has gone from open/closed
+ # or closed/open. We need to sanitize the subject of change.blocker
+ # similar to how we do referenced bugs
+ return
+ unless $file eq 'email/bugmail.html.tmpl'
+ || $file eq 'email/bugmail.txt.tmpl';
+
+ if (defined $vars->{diffs}) {
+ foreach my $change (@{$vars->{diffs}}) {
+ next if !defined $change->{blocker};
+ if (grep($_->secure_mail, @{$change->{blocker}->groups_in})) {
+ $change->{blocker}->{short_desc} = "(Secure bug)";
+ }
}
+ }
}
sub _send_test_email {
- my ($user) = @_;
- my $template = Bugzilla->template_inner($user->settings->{'lang'}->{'value'});
+ my ($user) = @_;
+ my $template = Bugzilla->template_inner($user->settings->{'lang'}->{'value'});
- my $vars = {
- to_user => $user->email,
- };
+ my $vars = {to_user => $user->email,};
- my $msg = "";
- $template->process("account/email/securemail-test.txt.tmpl", $vars, \$msg)
- || ThrowTemplateError($template->error());
+ my $msg = "";
+ $template->process("account/email/securemail-test.txt.tmpl", $vars, \$msg)
+ || ThrowTemplateError($template->error());
- MessageToMTA($msg);
+ MessageToMTA($msg);
}
##############################################################################
# Encrypting the email
##############################################################################
sub mailer_before_send {
- my ($self, $args) = @_;
-
- my $email = $args->{'email'};
- my $body = $email->body;
-
- # Decide whether to make secure.
- # This is a bit of a hack; it would be nice if it were more clear
- # what sort a particular email is.
- my $is_bugmail = $email->header('X-Bugzilla-Status') ||
- $email->header('X-Bugzilla-Type') eq 'request';
- my $is_passwordmail = !$is_bugmail && ($body =~ /cfmpw.*cxlpw/s);
- my $is_test_email = $email->header('X-Bugzilla-Type') =~ /securemail-test/ ? 1 : 0;
- my $is_whine_email = $email->header('X-Bugzilla-Type') eq 'whine' ? 1 : 0;
- my $encrypt_header = $email->header('X-Bugzilla-Encrypt') ? 1 : 0;
-
- if ($is_bugmail
- || $is_passwordmail
- || $is_test_email
- || $is_whine_email
- || $encrypt_header
- ) {
- # Convert the email's To address into a User object
- my $login = $email->header('To');
- my $emailsuffix = Bugzilla->params->{'emailsuffix'};
- $login =~ s/$emailsuffix$//;
- my $user = new Bugzilla::User({ name => $login });
-
- # Default to secure. (Of course, this means if this extension has a
- # bug, lots of people are going to get bugmail falsely claiming their
- # bugs are secure and they need to add a key...)
- my $make_secure = SECURE_ALL;
-
- if ($is_bugmail) {
- # This is also a bit of a hack, but there's no header with the
- # bug ID in. So we take the first number in the subject.
- my ($bug_id) = ($email->header('Subject') =~ /\[\D+(\d+)\]/);
- my $bug = new Bugzilla::Bug($bug_id);
- if (!_should_secure_bug($bug)) {
- $make_secure = SECURE_NONE;
- }
- # If the insider group has securemail enabled..
- my $insider_group = Bugzilla::Group->new({ name => Bugzilla->params->{'insidergroup'} });
- if ($insider_group
- && $insider_group->secure_mail
- && $make_secure == SECURE_NONE)
- {
- my $comment_is_private = Bugzilla->dbh->selectcol_arrayref(
- "SELECT isprivate FROM longdescs WHERE bug_id=? ORDER BY bug_when",
- undef, $bug_id);
- # Encrypt if there are private comments on an otherwise public bug
- while ($body =~ /[\r\n]--- Comment #(\d+)/g) {
- my $comment_number = $1;
- if ($comment_number && $comment_is_private->[$comment_number]) {
- $make_secure = SECURE_BODY;
- last;
- }
- }
- # Encrypt if updating a private attachment without a comment
- if ($email->header('X-Bugzilla-Changed-Fields')
- && $email->header('X-Bugzilla-Changed-Fields') =~ /Attachment #(\d+)/)
- {
- my $attachment = Bugzilla::Attachment->new($1);
- if ($attachment && $attachment->isprivate) {
- $make_secure = SECURE_BODY;
- }
- }
- }
- }
- elsif ($is_passwordmail) {
- # Mail is made unsecure only if the user does not have a public
- # key and is not in any security groups. So specifying a public
- # key OR being in a security group means the mail is kept secure
- # (but, as noted above, the check is the other way around because
- # we default to secure).
- if ($user &&
- !$user->public_key &&
- !grep($_->secure_mail, @{ $user->groups }))
- {
- $make_secure = SECURE_NONE;
- }
- }
- elsif ($is_whine_email) {
- # When a whine email has one or more secure bugs in the body, then
- # encrypt the entire email body. Subject can be left alone as it
- # comes from the whine settings.
- $make_secure = _should_secure_whine($email) ? SECURE_BODY : SECURE_NONE;
- }
- elsif ($encrypt_header) {
- # Templates or code may set the X-Bugzilla-Encrypt header to
- # trigger encryption of emails. Remove that header from the email.
- $email->header_set('X-Bugzilla-Encrypt');
+ my ($self, $args) = @_;
+
+ my $email = $args->{'email'};
+ my $body = $email->body;
+
+ # Decide whether to make secure.
+ # This is a bit of a hack; it would be nice if it were more clear
+ # what sort a particular email is.
+ my $is_bugmail = $email->header('X-Bugzilla-Status')
+ || $email->header('X-Bugzilla-Type') eq 'request';
+ my $is_passwordmail = !$is_bugmail && ($body =~ /cfmpw.*cxlpw/s);
+ my $is_test_email
+ = $email->header('X-Bugzilla-Type') =~ /securemail-test/ ? 1 : 0;
+ my $is_whine_email = $email->header('X-Bugzilla-Type') eq 'whine' ? 1 : 0;
+ my $encrypt_header = $email->header('X-Bugzilla-Encrypt') ? 1 : 0;
+
+ if ( $is_bugmail
+ || $is_passwordmail
+ || $is_test_email
+ || $is_whine_email
+ || $encrypt_header)
+ {
+ # Convert the email's To address into a User object
+ my $login = $email->header('To');
+ my $emailsuffix = Bugzilla->params->{'emailsuffix'};
+ $login =~ s/$emailsuffix$//;
+ my $user = new Bugzilla::User({name => $login});
+
+ # Default to secure. (Of course, this means if this extension has a
+ # bug, lots of people are going to get bugmail falsely claiming their
+ # bugs are secure and they need to add a key...)
+ my $make_secure = SECURE_ALL;
+
+ if ($is_bugmail) {
+
+ # This is also a bit of a hack, but there's no header with the
+ # bug ID in. So we take the first number in the subject.
+ my ($bug_id) = ($email->header('Subject') =~ /\[\D+(\d+)\]/);
+ my $bug = new Bugzilla::Bug($bug_id);
+ if (!_should_secure_bug($bug)) {
+ $make_secure = SECURE_NONE;
+ }
+
+ # If the insider group has securemail enabled..
+ my $insider_group
+ = Bugzilla::Group->new({name => Bugzilla->params->{'insidergroup'}});
+ if ( $insider_group
+ && $insider_group->secure_mail
+ && $make_secure == SECURE_NONE)
+ {
+ my $comment_is_private
+ = Bugzilla->dbh->selectcol_arrayref(
+ "SELECT isprivate FROM longdescs WHERE bug_id=? ORDER BY bug_when",
+ undef, $bug_id);
+
+ # Encrypt if there are private comments on an otherwise public bug
+ while ($body =~ /[\r\n]--- Comment #(\d+)/g) {
+ my $comment_number = $1;
+ if ($comment_number && $comment_is_private->[$comment_number]) {
+ $make_secure = SECURE_BODY;
+ last;
+ }
}
- # If finding the user fails for some reason, but we determine we
- # should be encrypting, we want to make the mail safe. An empty key
- # does that.
- my $public_key = $user ? $user->public_key : '';
-
- # Check if the new bugmail prefix should be added to the subject.
- my $add_new = ($email->header('X-Bugzilla-Type') eq 'new' &&
- $user &&
- $user->settings->{'bugmail_new_prefix'}->{'value'} eq 'on') ? 1 : 0;
-
- if ($make_secure == SECURE_NONE) {
- # Filter the bug_links in HTML email in case the bugs the links
- # point are "secured" bugs and the user may not be able to see
- # the summaries.
- _filter_bug_links($email);
- }
- else {
- _make_secure($email, $public_key, $is_bugmail && $make_secure == SECURE_ALL, $add_new);
+ # Encrypt if updating a private attachment without a comment
+ if ( $email->header('X-Bugzilla-Changed-Fields')
+ && $email->header('X-Bugzilla-Changed-Fields') =~ /Attachment #(\d+)/)
+ {
+ my $attachment = Bugzilla::Attachment->new($1);
+ if ($attachment && $attachment->isprivate) {
+ $make_secure = SECURE_BODY;
+ }
}
+ }
+ }
+ elsif ($is_passwordmail) {
+
+ # Mail is made unsecure only if the user does not have a public
+ # key and is not in any security groups. So specifying a public
+ # key OR being in a security group means the mail is kept secure
+ # (but, as noted above, the check is the other way around because
+ # we default to secure).
+ if ($user && !$user->public_key && !grep($_->secure_mail, @{$user->groups})) {
+ $make_secure = SECURE_NONE;
+ }
+ }
+ elsif ($is_whine_email) {
+
+ # When a whine email has one or more secure bugs in the body, then
+ # encrypt the entire email body. Subject can be left alone as it
+ # comes from the whine settings.
+ $make_secure = _should_secure_whine($email) ? SECURE_BODY : SECURE_NONE;
+ }
+ elsif ($encrypt_header) {
+
+ # Templates or code may set the X-Bugzilla-Encrypt header to
+ # trigger encryption of emails. Remove that header from the email.
+ $email->header_set('X-Bugzilla-Encrypt');
+ }
+
+ # If finding the user fails for some reason, but we determine we
+ # should be encrypting, we want to make the mail safe. An empty key
+ # does that.
+ my $public_key = $user ? $user->public_key : '';
+
+ # Check if the new bugmail prefix should be added to the subject.
+ my $add_new
+ = ( $email->header('X-Bugzilla-Type') eq 'new'
+ && $user
+ && $user->settings->{'bugmail_new_prefix'}->{'value'} eq 'on') ? 1 : 0;
+
+ if ($make_secure == SECURE_NONE) {
+
+ # Filter the bug_links in HTML email in case the bugs the links
+ # point are "secured" bugs and the user may not be able to see
+ # the summaries.
+ _filter_bug_links($email);
}
+ else {
+ _make_secure($email, $public_key, $is_bugmail && $make_secure == SECURE_ALL,
+ $add_new);
+ }
+ }
}
# Custom hook for bugzilla.mozilla.org (see bug 752400)
sub bugmail_referenced_bugs {
- my ($self, $args) = @_;
- # Sanitise subjects of referenced bugs.
- my $referenced_bugs = $args->{'referenced_bugs'};
- # No need to sanitise subjects if the entire email will be secured.
- return if _should_secure_bug($args->{'updated_bug'});
- # Replace the subject if required
- foreach my $ref (@$referenced_bugs) {
- if (grep($_->secure_mail, @{ $ref->{'bug'}->groups_in })) {
- $ref->{'short_desc'} = "(Secure bug)";
- }
+ my ($self, $args) = @_;
+
+ # Sanitise subjects of referenced bugs.
+ my $referenced_bugs = $args->{'referenced_bugs'};
+
+ # No need to sanitise subjects if the entire email will be secured.
+ return if _should_secure_bug($args->{'updated_bug'});
+
+ # Replace the subject if required
+ foreach my $ref (@$referenced_bugs) {
+ if (grep($_->secure_mail, @{$ref->{'bug'}->groups_in})) {
+ $ref->{'short_desc'} = "(Secure bug)";
}
+ }
}
sub _should_secure_bug {
- my ($bug) = @_;
- # If there's a problem with the bug, err on the side of caution and mark it
- # as secure.
- return
- !$bug
- || $bug->{'error'}
- || grep($_->secure_mail, @{ $bug->groups_in });
+ my ($bug) = @_;
+
+ # If there's a problem with the bug, err on the side of caution and mark it
+ # as secure.
+ return !$bug || $bug->{'error'} || grep($_->secure_mail, @{$bug->groups_in});
}
sub _should_secure_whine {
- my ($email) = @_;
- my $should_secure = 0;
- $email->walk_parts(sub {
- my $part = shift;
- my $content_type = $part->content_type;
- return if !$content_type || $content_type !~ /^text\/plain/;
- my $body = $part->body;
- my @bugids = $body =~ /Bug (\d+):/g;
- foreach my $id (@bugids) {
- $id = trim($id);
- next if !$id;
- my $bug = new Bugzilla::Bug($id);
- if ($bug && _should_secure_bug($bug)) {
- $should_secure = 1;
- last;
- }
- }
- });
- return $should_secure ? 1 : 0;
+ my ($email) = @_;
+ my $should_secure = 0;
+ $email->walk_parts(sub {
+ my $part = shift;
+ my $content_type = $part->content_type;
+ return if !$content_type || $content_type !~ /^text\/plain/;
+ my $body = $part->body;
+ my @bugids = $body =~ /Bug (\d+):/g;
+ foreach my $id (@bugids) {
+ $id = trim($id);
+ next if !$id;
+ my $bug = new Bugzilla::Bug($id);
+ if ($bug && _should_secure_bug($bug)) {
+ $should_secure = 1;
+ last;
+ }
+ }
+ });
+ return $should_secure ? 1 : 0;
}
sub _make_secure {
- my ($email, $key, $sanitise_subject, $add_new) = @_;
-
- # Add header showing this email has been secured
- $email->header_set('X-Bugzilla-Secure-Email', 'Yes');
-
- my $subject = $email->header('Subject');
- my ($bug_id) = $subject =~ /\[\D+(\d+)\]/;
-
- my $key_type = 0;
- if ($key && $key =~ /PUBLIC KEY/) {
- $key_type = 'PGP';
+ my ($email, $key, $sanitise_subject, $add_new) = @_;
+
+ # Add header showing this email has been secured
+ $email->header_set('X-Bugzilla-Secure-Email', 'Yes');
+
+ my $subject = $email->header('Subject');
+ my ($bug_id) = $subject =~ /\[\D+(\d+)\]/;
+
+ my $key_type = 0;
+ if ($key && $key =~ /PUBLIC KEY/) {
+ $key_type = 'PGP';
+ }
+ elsif ($key && $key =~ /BEGIN CERTIFICATE/) {
+ $key_type = 'S/MIME';
+ }
+
+ if ($key_type eq 'PGP') {
+ ##################
+ # PGP Encryption #
+ ##################
+
+ my $pubring = new Crypt::OpenPGP::KeyRing(Data => $key);
+ my $pgp = new Crypt::OpenPGP(PubRing => $pubring);
+
+ if (scalar $email->parts > 1) {
+ my $old_boundary = $email->{ct}{attributes}{boundary};
+ my $to_encrypt = "Content-Type: " . $email->content_type . "\n\n";
+
+ # We need to do some fix up of each part for proper encoding and then
+ # stringify all parts for encrypting. We have to retain the old
+ # boundaries as well so that the email client can reconstruct the
+ # original message properly.
+ $email->walk_parts(\&_fix_encoding);
+
+ $email->walk_parts(sub {
+ my ($part) = @_;
+ if ($sanitise_subject) {
+ _insert_subject($part, $subject);
+ }
+ return if $part->parts > 1; # Top-level
+ $to_encrypt .= "--$old_boundary\n" . $part->as_string . "\n";
+ });
+ $to_encrypt .= "--$old_boundary--";
+
+ # Now create the new properly formatted PGP parts containing the
+ # encrypted original message
+ my @new_parts = (
+ Email::MIME->create(
+ attributes =>
+ {content_type => 'application/pgp-encrypted', encoding => '7bit',},
+ body => "Version: 1\n",
+ ),
+ Email::MIME->create(
+ attributes => {
+ content_type => 'application/octet-stream',
+ filename => 'encrypted.asc',
+ disposition => 'inline',
+ encoding => '7bit',
+ },
+ body => _pgp_encrypt($pgp, $to_encrypt)
+ ),
+ );
+ $email->parts_set(\@new_parts);
+ my $new_boundary = $email->{ct}{attributes}{boundary};
+
+ # Redo the old content type header with the new boundaries
+ # and other information needed for PGP
+ $email->header_set("Content-Type",
+ "multipart/encrypted; "
+ . "protocol=\"application/pgp-encrypted\"; "
+ . "boundary=\"$new_boundary\"");
}
- elsif ($key && $key =~ /BEGIN CERTIFICATE/) {
- $key_type = 'S/MIME';
+ else {
+ _fix_encoding($email);
+ if ($sanitise_subject) {
+ _insert_subject($email, $subject);
+ }
+ $email->body_set(_pgp_encrypt($pgp, $email->body));
}
+ }
- if ($key_type eq 'PGP') {
- ##################
- # PGP Encryption #
- ##################
-
- my $pubring = new Crypt::OpenPGP::KeyRing(Data => $key);
- my $pgp = new Crypt::OpenPGP(PubRing => $pubring);
-
- if (scalar $email->parts > 1) {
- my $old_boundary = $email->{ct}{attributes}{boundary};
- my $to_encrypt = "Content-Type: " . $email->content_type . "\n\n";
-
- # We need to do some fix up of each part for proper encoding and then
- # stringify all parts for encrypting. We have to retain the old
- # boundaries as well so that the email client can reconstruct the
- # original message properly.
- $email->walk_parts(\&_fix_encoding);
-
- $email->walk_parts(sub {
- my ($part) = @_;
- if ($sanitise_subject) {
- _insert_subject($part, $subject);
- }
- return if $part->parts > 1; # Top-level
- $to_encrypt .= "--$old_boundary\n" . $part->as_string . "\n";
- });
- $to_encrypt .= "--$old_boundary--";
-
- # Now create the new properly formatted PGP parts containing the
- # encrypted original message
- my @new_parts = (
- Email::MIME->create(
- attributes => {
- content_type => 'application/pgp-encrypted',
- encoding => '7bit',
- },
- body => "Version: 1\n",
- ),
- Email::MIME->create(
- attributes => {
- content_type => 'application/octet-stream',
- filename => 'encrypted.asc',
- disposition => 'inline',
- encoding => '7bit',
- },
- body => _pgp_encrypt($pgp, $to_encrypt)
- ),
- );
- $email->parts_set(\@new_parts);
- my $new_boundary = $email->{ct}{attributes}{boundary};
- # Redo the old content type header with the new boundaries
- # and other information needed for PGP
- $email->header_set("Content-Type",
- "multipart/encrypted; " .
- "protocol=\"application/pgp-encrypted\"; " .
- "boundary=\"$new_boundary\"");
- }
- else {
- _fix_encoding($email);
- if ($sanitise_subject) {
- _insert_subject($email, $subject);
- }
- $email->body_set(_pgp_encrypt($pgp, $email->body));
- }
+ elsif ($key_type eq 'S/MIME') {
+ #####################
+ # S/MIME Encryption #
+ #####################
+
+ $email->walk_parts(\&_fix_encoding);
+
+ if ($sanitise_subject) {
+ $email->walk_parts(sub { _insert_subject($_[0], $subject) });
}
- elsif ($key_type eq 'S/MIME') {
- #####################
- # S/MIME Encryption #
- #####################
+ my $smime = Crypt::SMIME->new();
+ my $encrypted;
- $email->walk_parts(\&_fix_encoding);
+ eval {
+ $smime->setPublicKey([$key]);
+ $encrypted = $smime->encrypt($email->as_string());
+ };
- if ($sanitise_subject) {
- $email->walk_parts(sub { _insert_subject($_[0], $subject) });
- }
+ if (!$@) {
- my $smime = Crypt::SMIME->new();
- my $encrypted;
-
- eval {
- $smime->setPublicKey([$key]);
- $encrypted = $smime->encrypt($email->as_string());
- };
-
- if (!$@) {
- # We can't replace the Email::MIME object, so we have to swap
- # out its component parts.
- my $enc_obj = new Email::MIME($encrypted);
- $email->header_obj_set($enc_obj->header_obj());
- $email->parts_set([]);
- $email->body_set($enc_obj->body());
- $email->content_type_set('application/pkcs7-mime');
- $email->charset_set('UTF-8') if Bugzilla->params->{'utf8'};
- }
- else {
- $email->body_set('Error during Encryption: ' . $@);
- }
+ # We can't replace the Email::MIME object, so we have to swap
+ # out its component parts.
+ my $enc_obj = new Email::MIME($encrypted);
+ $email->header_obj_set($enc_obj->header_obj());
+ $email->parts_set([]);
+ $email->body_set($enc_obj->body());
+ $email->content_type_set('application/pkcs7-mime');
+ $email->charset_set('UTF-8') if Bugzilla->params->{'utf8'};
}
else {
- # No encryption key provided; send a generic, safe email.
- my $template = Bugzilla->template;
- my $message;
- my $vars = {
- 'urlbase' => correct_urlbase(),
- 'bug_id' => $bug_id,
- 'maintainer' => Bugzilla->params->{'maintainer'}
- };
-
- $template->process('account/email/encryption-required.txt.tmpl',
- $vars, \$message)
- || ThrowTemplateError($template->error());
-
- $email->parts_set([]);
- $email->content_type_set('text/plain');
- $email->body_set($message);
+ $email->body_set('Error during Encryption: ' . $@);
}
+ }
+ else {
+ # No encryption key provided; send a generic, safe email.
+ my $template = Bugzilla->template;
+ my $message;
+ my $vars = {
+ 'urlbase' => correct_urlbase(),
+ 'bug_id' => $bug_id,
+ 'maintainer' => Bugzilla->params->{'maintainer'}
+ };
- if ($sanitise_subject) {
- # This is designed to still work if the admin changes the word
- # 'bug' to something else. However, it could break if they change
- # the format of the subject line in another way.
- my $new = $add_new ? ' New:' : '';
- my $product = $email->header('X-Bugzilla-Product');
- my $component = $email->header('X-Bugzilla-Component');
- # Note: the $bug_id is required within the parentheses in order to keep
- # gmail's threading algorithm happy.
- $subject =~ s/($bug_id\])\s+(.*)$/$1$new (Secure bug $bug_id in $product :: $component)/;
- $email->header_set('Subject', $subject);
- }
+ $template->process('account/email/encryption-required.txt.tmpl',
+ $vars, \$message)
+ || ThrowTemplateError($template->error());
+
+ $email->parts_set([]);
+ $email->content_type_set('text/plain');
+ $email->body_set($message);
+ }
+
+ if ($sanitise_subject) {
+
+ # This is designed to still work if the admin changes the word
+ # 'bug' to something else. However, it could break if they change
+ # the format of the subject line in another way.
+ my $new = $add_new ? ' New:' : '';
+ my $product = $email->header('X-Bugzilla-Product');
+ my $component = $email->header('X-Bugzilla-Component');
+
+ # Note: the $bug_id is required within the parentheses in order to keep
+ # gmail's threading algorithm happy.
+ $subject
+ =~ s/($bug_id\])\s+(.*)$/$1$new (Secure bug $bug_id in $product :: $component)/;
+ $email->header_set('Subject', $subject);
+ }
}
sub _pgp_encrypt {
- my ($pgp, $text) = @_;
- # "@" matches every key in the public key ring, which is fine,
- # because there's only one key in our keyring.
- #
- # We use the CAST5 cipher because the Rijndael (AES) module doesn't
- # like us for some reason I don't have time to debug fully.
- # ("key must be an untainted string scalar")
- my $encrypted = $pgp->encrypt(Data => $text,
- Recipients => "@",
- Cipher => 'CAST5',
- Armour => 1);
- if (!defined $encrypted) {
- return 'Error during Encryption: ' . $pgp->errstr;
- }
- return $encrypted;
+ my ($pgp, $text) = @_;
+
+ # "@" matches every key in the public key ring, which is fine,
+ # because there's only one key in our keyring.
+ #
+ # We use the CAST5 cipher because the Rijndael (AES) module doesn't
+ # like us for some reason I don't have time to debug fully.
+ # ("key must be an untainted string scalar")
+ my $encrypted = $pgp->encrypt(
+ Data => $text,
+ Recipients => "@",
+ Cipher => 'CAST5',
+ Armour => 1
+ );
+ if (!defined $encrypted) {
+ return 'Error during Encryption: ' . $pgp->errstr;
+ }
+ return $encrypted;
}
# Insert the subject into the part's body, as the subject of the message will
@@ -583,77 +597,75 @@ sub _pgp_encrypt {
# XXX this incorrectly assumes all parts of the message are the body
# we should only alter parts who's parent is multipart/alternative
sub _insert_subject {
- my ($part, $subject) = @_;
- my $content_type = $part->content_type or return;
- if ($content_type =~ /^text\/plain/) {
- if (!is_7bit_clean($subject)) {
- $part->encoding_set('quoted-printable');
- }
- $part->body_str_set("Subject: $subject\015\012\015\012" . $part->body_str);
- }
- elsif ($content_type =~ /^text\/html/) {
- my $tree = HTML::Tree->new->parse_content($part->body_str);
- my $body = $tree->look_down(qw(_tag body));
- $body->unshift_content(['div', "Subject: $subject"], ['br']);
- _set_body_from_tree($part, $tree);
+ my ($part, $subject) = @_;
+ my $content_type = $part->content_type or return;
+ if ($content_type =~ /^text\/plain/) {
+ if (!is_7bit_clean($subject)) {
+ $part->encoding_set('quoted-printable');
}
+ $part->body_str_set("Subject: $subject\015\012\015\012" . $part->body_str);
+ }
+ elsif ($content_type =~ /^text\/html/) {
+ my $tree = HTML::Tree->new->parse_content($part->body_str);
+ my $body = $tree->look_down(qw(_tag body));
+ $body->unshift_content(['div', "Subject: $subject"], ['br']);
+ _set_body_from_tree($part, $tree);
+ }
}
sub _fix_encoding {
- my $part = shift;
-
- # don't touch the top-level part of multi-part mail
- return if $part->parts > 1;
-
- # nothing to do if the part already has a charset
- my $ct = parse_content_type($part->content_type);
- my $charset = $ct->{attributes}{charset}
- ? $ct->{attributes}{charset}
- : '';
- return unless !$charset || $charset eq 'us-ascii';
-
- if (Bugzilla->params->{utf8}) {
- $part->charset_set('UTF-8');
- my $raw = $part->body_raw;
- if (utf8::is_utf8($raw)) {
- utf8::encode($raw);
- $part->body_set($raw);
- }
+ my $part = shift;
+
+ # don't touch the top-level part of multi-part mail
+ return if $part->parts > 1;
+
+ # nothing to do if the part already has a charset
+ my $ct = parse_content_type($part->content_type);
+ my $charset = $ct->{attributes}{charset} ? $ct->{attributes}{charset} : '';
+ return unless !$charset || $charset eq 'us-ascii';
+
+ if (Bugzilla->params->{utf8}) {
+ $part->charset_set('UTF-8');
+ my $raw = $part->body_raw;
+ if (utf8::is_utf8($raw)) {
+ utf8::encode($raw);
+ $part->body_set($raw);
}
- $part->encoding_set('quoted-printable');
+ }
+ $part->encoding_set('quoted-printable');
}
sub _filter_bug_links {
- my ($email) = @_;
- $email->walk_parts(sub {
- my $part = shift;
- _fix_encoding($part);
- my $content_type = $part->content_type;
- return if !$content_type || $content_type !~ /text\/html/;
- my $tree = HTML::Tree->new->parse_content($part->body_str);
- my @links = $tree->look_down( _tag => q{a}, class => qr/bz_bug_link/ );
- my $updated = 0;
- foreach my $link (@links) {
- my $href = $link->attr('href');
- my ($bug_id) = $href =~ /\Qshow_bug.cgi?id=\E(\d+)/;
- my $bug = new Bugzilla::Bug($bug_id);
- if ($bug && _should_secure_bug($bug)) {
- $link->attr('title', '(secure bug)');
- $link->attr('class', 'bz_bug_link');
- $updated = 1;
- }
- }
- if ($updated) {
- _set_body_from_tree($part, $tree);
- }
- });
+ my ($email) = @_;
+ $email->walk_parts(sub {
+ my $part = shift;
+ _fix_encoding($part);
+ my $content_type = $part->content_type;
+ return if !$content_type || $content_type !~ /text\/html/;
+ my $tree = HTML::Tree->new->parse_content($part->body_str);
+ my @links = $tree->look_down(_tag => q{a}, class => qr/bz_bug_link/);
+ my $updated = 0;
+ foreach my $link (@links) {
+ my $href = $link->attr('href');
+ my ($bug_id) = $href =~ /\Qshow_bug.cgi?id=\E(\d+)/;
+ my $bug = new Bugzilla::Bug($bug_id);
+ if ($bug && _should_secure_bug($bug)) {
+ $link->attr('title', '(secure bug)');
+ $link->attr('class', 'bz_bug_link');
+ $updated = 1;
+ }
+ }
+ if ($updated) {
+ _set_body_from_tree($part, $tree);
+ }
+ });
}
sub _set_body_from_tree {
- my ($part, $tree) = @_;
- $part->body_set($tree->as_HTML);
- $part->charset_set('UTF-8') if Bugzilla->params->{'utf8'};
- $part->encoding_set('quoted-printable');
+ my ($part, $tree) = @_;
+ $part->body_set($tree->as_HTML);
+ $part->charset_set('UTF-8') if Bugzilla->params->{'utf8'};
+ $part->encoding_set('quoted-printable');
}
__PACKAGE__->NAME;
diff --git a/extensions/TypeSniffer/Config.pm b/extensions/TypeSniffer/Config.pm
index 6ad03b362..72ae2bfce 100644
--- a/extensions/TypeSniffer/Config.pm
+++ b/extensions/TypeSniffer/Config.pm
@@ -25,16 +25,8 @@ use strict;
use constant NAME => 'TypeSniffer';
use constant REQUIRED_MODULES => [
- {
- package => 'File-MimeInfo',
- module => 'File::MimeInfo::Magic',
- version => '0'
- },
- {
- package => 'IO-stringy',
- module => 'IO::Scalar',
- version => '0'
- },
+ {package => 'File-MimeInfo', module => 'File::MimeInfo::Magic', version => '0'},
+ {package => 'IO-stringy', module => 'IO::Scalar', version => '0'},
];
-__PACKAGE__->NAME; \ No newline at end of file
+__PACKAGE__->NAME;
diff --git a/extensions/TypeSniffer/Extension.pm b/extensions/TypeSniffer/Extension.pm
index b788b5426..ed8eadca1 100644
--- a/extensions/TypeSniffer/Extension.pm
+++ b/extensions/TypeSniffer/Extension.pm
@@ -29,47 +29,48 @@ use IO::Scalar;
our $VERSION = '0.02';
################################################################################
# This extension uses magic to guess MIME types for data where the browser has
-# told us it's application/octet-stream (probably because there's no file
+# told us it's application/octet-stream (probably because there's no file
# extension, or it's a text type with a non-.txt file extension).
################################################################################
sub attachment_process_data {
- my ($self, $args) = @_;
- my $attributes = $args->{'attributes'};
- my $params = Bugzilla->input_params;
-
- # If we have autodetected application/octet-stream from the Content-Type
- # header, let's have a better go using a sniffer.
- if ($params->{'contenttypemethod'} eq 'autodetect' &&
- $attributes->{'mimetype'} eq 'application/octet-stream')
- {
- # data attribute can be either scalar data or filehandle
- # bugzilla.org/docs/3.6/en/html/api/Bugzilla/Attachment.html#create
- my $fh = $attributes->{'data'};
- if (!ref($fh)) {
- my $data = $attributes->{'data'};
- $fh = new IO::Scalar \$data;
- }
- else {
- # CGI.pm sends us an Fh that isn't actually an IO::Handle, but
- # has a method for getting an actual handle out of it.
- if (!$fh->isa('IO::Handle')) {
- $fh = $fh->handle;
- # ->handle returns an literal IO::Handle, even though the
- # underlying object is a file. So we rebless it to be a proper
- # IO::File object so that we can call ->seek on it and so on.
- # Just in case CGI.pm fixes this some day, we check ->isa first.
- if (!$fh->isa('IO::File')) {
- bless $fh, 'IO::File';
- }
- }
- }
-
- my $mimetype = mimetype($fh);
- $fh->seek(0, 0);
- if ($mimetype) {
- $attributes->{'mimetype'} = $mimetype;
+ my ($self, $args) = @_;
+ my $attributes = $args->{'attributes'};
+ my $params = Bugzilla->input_params;
+
+ # If we have autodetected application/octet-stream from the Content-Type
+ # header, let's have a better go using a sniffer.
+ if ( $params->{'contenttypemethod'} eq 'autodetect'
+ && $attributes->{'mimetype'} eq 'application/octet-stream')
+ {
+ # data attribute can be either scalar data or filehandle
+ # bugzilla.org/docs/3.6/en/html/api/Bugzilla/Attachment.html#create
+ my $fh = $attributes->{'data'};
+ if (!ref($fh)) {
+ my $data = $attributes->{'data'};
+ $fh = new IO::Scalar \$data;
+ }
+ else {
+ # CGI.pm sends us an Fh that isn't actually an IO::Handle, but
+ # has a method for getting an actual handle out of it.
+ if (!$fh->isa('IO::Handle')) {
+ $fh = $fh->handle;
+
+ # ->handle returns an literal IO::Handle, even though the
+ # underlying object is a file. So we rebless it to be a proper
+ # IO::File object so that we can call ->seek on it and so on.
+ # Just in case CGI.pm fixes this some day, we check ->isa first.
+ if (!$fh->isa('IO::File')) {
+ bless $fh, 'IO::File';
}
+ }
+ }
+
+ my $mimetype = mimetype($fh);
+ $fh->seek(0, 0);
+ if ($mimetype) {
+ $attributes->{'mimetype'} = $mimetype;
}
+ }
}
__PACKAGE__->NAME;
diff --git a/extensions/Voting/Config.pm b/extensions/Voting/Config.pm
index 97c44933e..d72caa3cc 100644
--- a/extensions/Voting/Config.pm
+++ b/extensions/Voting/Config.pm
@@ -13,10 +13,8 @@ use warnings;
use constant NAME => 'Voting';
-use constant REQUIRED_MODULES => [
-];
+use constant REQUIRED_MODULES => [];
-use constant OPTIONAL_MODULES => [
-];
+use constant OPTIONAL_MODULES => [];
__PACKAGE__->NAME;
diff --git a/extensions/Voting/Extension.pm b/extensions/Voting/Extension.pm
index b125933ce..2fd7d2e22 100644
--- a/extensions/Voting/Extension.pm
+++ b/extensions/Voting/Extension.pm
@@ -25,82 +25,84 @@ use Bugzilla::Token;
use List::Util qw(min sum);
-use constant VERSION => BUGZILLA_VERSION;
+use constant VERSION => BUGZILLA_VERSION;
use constant DEFAULT_VOTES_PER_BUG => 1;
+
# These came from Bugzilla itself, so they maintain the old numbers
# they had before.
use constant CMT_POPULAR_VOTES => 3;
-use constant REL_VOTER => 4;
+use constant REL_VOTER => 4;
################
# Installation #
################
BEGIN {
- *Bugzilla::Bug::votes = \&votes;
+ *Bugzilla::Bug::votes = \&votes;
}
sub votes {
- my $self = shift;
- my $dbh = Bugzilla->dbh;
+ my $self = shift;
+ my $dbh = Bugzilla->dbh;
- return $self->{votes} if exists $self->{votes};
+ return $self->{votes} if exists $self->{votes};
- $self->{votes} = $dbh->selectrow_array('SELECT votes FROM bugs WHERE bug_id = ?',
- undef, $self->id);
- return $self->{votes};
+ $self->{votes}
+ = $dbh->selectrow_array('SELECT votes FROM bugs WHERE bug_id = ?',
+ undef, $self->id);
+ return $self->{votes};
}
sub db_schema_abstract_schema {
- my ($self, $args) = @_;
- $args->{'schema'}->{'votes'} = {
- FIELDS => [
- who => {TYPE => 'INT3', NOTNULL => 1,
- REFERENCES => {TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'CASCADE'}},
- bug_id => {TYPE => 'INT3', NOTNULL => 1,
- REFERENCES => {TABLE => 'bugs',
- COLUMN => 'bug_id',
- DELETE => 'CASCADE'}},
- vote_count => {TYPE => 'INT2', NOTNULL => 1},
- ],
- INDEXES => [
- votes_who_idx => ['who'],
- votes_bug_id_idx => ['bug_id'],
- ],
- };
+ my ($self, $args) = @_;
+ $args->{'schema'}->{'votes'} = {
+ FIELDS => [
+ who => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'}
+ },
+ bug_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'bugs', COLUMN => 'bug_id', DELETE => 'CASCADE'}
+ },
+ vote_count => {TYPE => 'INT2', NOTNULL => 1},
+ ],
+ INDEXES => [votes_who_idx => ['who'], votes_bug_id_idx => ['bug_id'],],
+ };
}
sub install_update_db {
- my $dbh = Bugzilla->dbh;
- # Note that before Bugzilla 4.0, voting was a built-in part of Bugzilla,
- # so updates to the columns for old versions of Bugzilla happen in
- # Bugzilla::Install::DB, and can't safely be moved to this extension.
-
- my $field = new Bugzilla::Field({ name => 'votes' });
- if (!$field) {
- Bugzilla::Field->create(
- { name => 'votes', description => 'Votes', buglist => 1 });
- }
-
- $dbh->bz_add_column('products', 'votesperuser',
- {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
- $dbh->bz_add_column('products', 'maxvotesperbug',
- {TYPE => 'INT2', NOTNULL => 1, DEFAULT => DEFAULT_VOTES_PER_BUG});
- $dbh->bz_add_column('products', 'votestoconfirm',
- {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
-
- $dbh->bz_add_column('bugs', 'votes',
- {TYPE => 'INT3', NOTNULL => 1, DEFAULT => 0});
- $dbh->bz_add_index('bugs', 'bugs_votes_idx', ['votes']);
-
- # maxvotesperbug used to default to 10,000, which isn't very sensible.
- my $per_bug = $dbh->bz_column_info('products', 'maxvotesperbug');
- if ($per_bug->{DEFAULT} != DEFAULT_VOTES_PER_BUG) {
- $dbh->bz_alter_column('products', 'maxvotesperbug',
- {TYPE => 'INT2', NOTNULL => 1, DEFAULT => DEFAULT_VOTES_PER_BUG});
- }
+ my $dbh = Bugzilla->dbh;
+
+ # Note that before Bugzilla 4.0, voting was a built-in part of Bugzilla,
+ # so updates to the columns for old versions of Bugzilla happen in
+ # Bugzilla::Install::DB, and can't safely be moved to this extension.
+
+ my $field = new Bugzilla::Field({name => 'votes'});
+ if (!$field) {
+ Bugzilla::Field->create(
+ {name => 'votes', description => 'Votes', buglist => 1});
+ }
+
+ $dbh->bz_add_column('products', 'votesperuser',
+ {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
+ $dbh->bz_add_column('products', 'maxvotesperbug',
+ {TYPE => 'INT2', NOTNULL => 1, DEFAULT => DEFAULT_VOTES_PER_BUG});
+ $dbh->bz_add_column('products', 'votestoconfirm',
+ {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
+
+ $dbh->bz_add_column('bugs', 'votes',
+ {TYPE => 'INT3', NOTNULL => 1, DEFAULT => 0});
+ $dbh->bz_add_index('bugs', 'bugs_votes_idx', ['votes']);
+
+ # maxvotesperbug used to default to 10,000, which isn't very sensible.
+ my $per_bug = $dbh->bz_column_info('products', 'maxvotesperbug');
+ if ($per_bug->{DEFAULT} != DEFAULT_VOTES_PER_BUG) {
+ $dbh->bz_alter_column('products', 'maxvotesperbug',
+ {TYPE => 'INT2', NOTNULL => 1, DEFAULT => DEFAULT_VOTES_PER_BUG});
+ }
}
###########
@@ -108,93 +110,98 @@ sub install_update_db {
###########
sub object_columns {
- my ($self, $args) = @_;
- my ($class, $columns) = @$args{qw(class columns)};
- if ($class->isa('Bugzilla::Bug')) {
- push(@$columns, 'votes');
- }
- elsif ($class->isa('Bugzilla::Product')) {
- push(@$columns, qw(votesperuser maxvotesperbug votestoconfirm));
- }
+ my ($self, $args) = @_;
+ my ($class, $columns) = @$args{qw(class columns)};
+ if ($class->isa('Bugzilla::Bug')) {
+ push(@$columns, 'votes');
+ }
+ elsif ($class->isa('Bugzilla::Product')) {
+ push(@$columns, qw(votesperuser maxvotesperbug votestoconfirm));
+ }
}
sub bug_fields {
- my ($self, $args) = @_;
- my $fields = $args->{fields};
- push(@$fields, 'votes');
+ my ($self, $args) = @_;
+ my $fields = $args->{fields};
+ push(@$fields, 'votes');
}
sub object_update_columns {
- my ($self, $args) = @_;
- my ($object, $columns) = @$args{qw(object columns)};
- if ($object->isa('Bugzilla::Product')) {
- push(@$columns, qw(votesperuser maxvotesperbug votestoconfirm));
- }
+ my ($self, $args) = @_;
+ my ($object, $columns) = @$args{qw(object columns)};
+ if ($object->isa('Bugzilla::Product')) {
+ push(@$columns, qw(votesperuser maxvotesperbug votestoconfirm));
+ }
}
sub object_validators {
- my ($self, $args) = @_;
- my ($class, $validators) = @$args{qw(class validators)};
- if ($class->isa('Bugzilla::Product')) {
- $validators->{'votesperuser'} = \&_check_votesperuser;
- $validators->{'maxvotesperbug'} = \&_check_maxvotesperbug;
- $validators->{'votestoconfirm'} = \&_check_votestoconfirm;
- }
+ my ($self, $args) = @_;
+ my ($class, $validators) = @$args{qw(class validators)};
+ if ($class->isa('Bugzilla::Product')) {
+ $validators->{'votesperuser'} = \&_check_votesperuser;
+ $validators->{'maxvotesperbug'} = \&_check_maxvotesperbug;
+ $validators->{'votestoconfirm'} = \&_check_votestoconfirm;
+ }
}
sub object_before_create {
- my ($self, $args) = @_;
- my ($class, $params) = @$args{qw(class params)};
- if ($class->isa('Bugzilla::Bug')) {
- # Don't ever allow people to directly specify "votes" into the bugs
- # table.
- delete $params->{votes};
- }
- elsif ($class->isa('Bugzilla::Product')) {
- my $input = Bugzilla->input_params;
- $params->{votesperuser} = $input->{'votesperuser'};
- $params->{maxvotesperbug} = $input->{'maxvotesperbug'};
- $params->{votestoconfirm} = $input->{'votestoconfirm'};
- }
+ my ($self, $args) = @_;
+ my ($class, $params) = @$args{qw(class params)};
+ if ($class->isa('Bugzilla::Bug')) {
+
+ # Don't ever allow people to directly specify "votes" into the bugs
+ # table.
+ delete $params->{votes};
+ }
+ elsif ($class->isa('Bugzilla::Product')) {
+ my $input = Bugzilla->input_params;
+ $params->{votesperuser} = $input->{'votesperuser'};
+ $params->{maxvotesperbug} = $input->{'maxvotesperbug'};
+ $params->{votestoconfirm} = $input->{'votestoconfirm'};
+ }
}
sub object_end_of_set_all {
- my ($self, $args) = @_;
- my ($object) = $args->{object};
- if ($object->isa('Bugzilla::Product')) {
- my $input = Bugzilla->input_params;
- $object->set('votesperuser', $input->{'votesperuser'});
- $object->set('maxvotesperbug', $input->{'maxvotesperbug'});
- $object->set('votestoconfirm', $input->{'votestoconfirm'});
- }
+ my ($self, $args) = @_;
+ my ($object) = $args->{object};
+ if ($object->isa('Bugzilla::Product')) {
+ my $input = Bugzilla->input_params;
+ $object->set('votesperuser', $input->{'votesperuser'});
+ $object->set('maxvotesperbug', $input->{'maxvotesperbug'});
+ $object->set('votestoconfirm', $input->{'votestoconfirm'});
+ }
}
sub object_end_of_update {
- my ($self, $args) = @_;
- my ($object, $changes) = @$args{qw(object changes)};
- if ( $object->isa('Bugzilla::Product')
- and ($changes->{maxvotesperbug} or $changes->{votesperuser}
- or $changes->{votestoconfirm}) )
- {
- _modify_bug_votes($object, $changes);
- }
+ my ($self, $args) = @_;
+ my ($object, $changes) = @$args{qw(object changes)};
+ if (
+ $object->isa('Bugzilla::Product')
+ and ($changes->{maxvotesperbug}
+ or $changes->{votesperuser}
+ or $changes->{votestoconfirm})
+ )
+ {
+ _modify_bug_votes($object, $changes);
+ }
}
sub bug_end_of_update {
- my ($self, $args) = @_;
- my ($bug, $changes) = @$args{qw(bug changes)};
-
- if ($changes->{'product'}) {
- my @msgs;
- # If some votes have been removed, RemoveVotes() returns
- # a list of messages to send to voters.
- @msgs = _remove_votes($bug->id, 0, 'votes_bug_moved');
- _confirm_if_vote_confirmed($bug);
-
- foreach my $msg (@msgs) {
- MessageToMTA($msg);
- }
+ my ($self, $args) = @_;
+ my ($bug, $changes) = @$args{qw(bug changes)};
+
+ if ($changes->{'product'}) {
+ my @msgs;
+
+ # If some votes have been removed, RemoveVotes() returns
+ # a list of messages to send to voters.
+ @msgs = _remove_votes($bug->id, 0, 'votes_bug_moved');
+ _confirm_if_vote_confirmed($bug);
+
+ foreach my $msg (@msgs) {
+ MessageToMTA($msg);
}
+ }
}
#############
@@ -202,27 +209,28 @@ sub bug_end_of_update {
#############
sub template_before_create {
- my ($self, $args) = @_;
- my $config = $args->{config};
- my $constants = $config->{CONSTANTS};
- $constants->{REL_VOTER} = REL_VOTER;
- $constants->{CMT_POPULAR_VOTES} = CMT_POPULAR_VOTES;
- $constants->{DEFAULT_VOTES_PER_BUG} = DEFAULT_VOTES_PER_BUG;
+ my ($self, $args) = @_;
+ my $config = $args->{config};
+ my $constants = $config->{CONSTANTS};
+ $constants->{REL_VOTER} = REL_VOTER;
+ $constants->{CMT_POPULAR_VOTES} = CMT_POPULAR_VOTES;
+ $constants->{DEFAULT_VOTES_PER_BUG} = DEFAULT_VOTES_PER_BUG;
}
sub template_before_process {
- my ($self, $args) = @_;
- my ($vars, $file) = @$args{qw(vars file)};
- if ($file eq 'admin/users/confirm-delete.html.tmpl') {
- my $who = $vars->{otheruser};
- my $votes = Bugzilla->dbh->selectrow_array(
- 'SELECT COUNT(*) FROM votes WHERE who = ?', undef, $who->id);
- if ($votes) {
- $vars->{other_safe} = 1;
- $vars->{votes} = $votes;
- }
- }
+ my ($self, $args) = @_;
+ my ($vars, $file) = @$args{qw(vars file)};
+ if ($file eq 'admin/users/confirm-delete.html.tmpl') {
+ my $who = $vars->{otheruser};
+ my $votes
+ = Bugzilla->dbh->selectrow_array('SELECT COUNT(*) FROM votes WHERE who = ?',
+ undef, $who->id);
+ if ($votes) {
+ $vars->{other_safe} = 1;
+ $vars->{votes} = $votes;
+ }
+ }
}
###########
@@ -230,19 +238,19 @@ sub template_before_process {
###########
sub bugmail_recipients {
- my ($self, $args) = @_;
- my ($bug, $recipients) = @$args{qw(bug recipients)};
- my $dbh = Bugzilla->dbh;
+ my ($self, $args) = @_;
+ my ($bug, $recipients) = @$args{qw(bug recipients)};
+ my $dbh = Bugzilla->dbh;
- my $voters = $dbh->selectcol_arrayref(
- "SELECT who FROM votes WHERE bug_id = ?", undef, $bug->id);
- $recipients->{$_}->{+REL_VOTER} = 1 foreach (@$voters);
+ my $voters = $dbh->selectcol_arrayref("SELECT who FROM votes WHERE bug_id = ?",
+ undef, $bug->id);
+ $recipients->{$_}->{+REL_VOTER} = 1 foreach (@$voters);
}
sub bugmail_relationships {
- my ($self, $args) = @_;
- my $relationships = $args->{relationships};
- $relationships->{+REL_VOTER} = 'Voter';
+ my ($self, $args) = @_;
+ my $relationships = $args->{relationships};
+ $relationships->{+REL_VOTER} = 'Voter';
}
###############
@@ -250,59 +258,59 @@ sub bugmail_relationships {
###############
sub sanitycheck_check {
- my ($self, $args) = @_;
- my $status = $args->{status};
-
- # Vote Cache
- $status->('voting_count_start');
- my $dbh = Bugzilla->dbh;
- my %cached_counts = @{ $dbh->selectcol_arrayref(
- 'SELECT bug_id, votes FROM bugs', {Columns=>[1,2]}) };
-
- my %real_counts = @{ $dbh->selectcol_arrayref(
- 'SELECT bug_id, SUM(vote_count) FROM votes '
- . $dbh->sql_group_by('bug_id'), {Columns=>[1,2]}) };
-
- my $needs_rebuild;
- foreach my $id (keys %cached_counts) {
- my $cached_count = $cached_counts{$id};
- my $real_count = $real_counts{$id} || 0;
- if ($cached_count < 0) {
- $status->('voting_count_alert', { id => $id }, 'alert');
- }
- elsif ($cached_count != $real_count) {
- $status->('voting_cache_alert', { id => $id }, 'alert');
- $needs_rebuild = 1;
- }
- }
-
- $status->('voting_cache_rebuild_fix') if $needs_rebuild;
+ my ($self, $args) = @_;
+ my $status = $args->{status};
+
+ # Vote Cache
+ $status->('voting_count_start');
+ my $dbh = Bugzilla->dbh;
+ my %cached_counts = @{$dbh->selectcol_arrayref('SELECT bug_id, votes FROM bugs',
+ {Columns => [1, 2]})};
+
+ my %real_counts = @{
+ $dbh->selectcol_arrayref(
+ 'SELECT bug_id, SUM(vote_count) FROM votes ' . $dbh->sql_group_by('bug_id'),
+ {Columns => [1, 2]})
+ };
+
+ my $needs_rebuild;
+ foreach my $id (keys %cached_counts) {
+ my $cached_count = $cached_counts{$id};
+ my $real_count = $real_counts{$id} || 0;
+ if ($cached_count < 0) {
+ $status->('voting_count_alert', {id => $id}, 'alert');
+ }
+ elsif ($cached_count != $real_count) {
+ $status->('voting_cache_alert', {id => $id}, 'alert');
+ $needs_rebuild = 1;
+ }
+ }
+
+ $status->('voting_cache_rebuild_fix') if $needs_rebuild;
}
sub sanitycheck_repair {
- my ($self, $args) = @_;
- my $status = $args->{status};
- my $input = Bugzilla->input_params;
- my $dbh = Bugzilla->dbh;
-
- return if !$input->{rebuild_vote_cache};
-
- $status->('voting_cache_rebuild_start');
- $dbh->bz_start_transaction();
- $dbh->do('UPDATE bugs SET votes = 0');
-
- my $sth = $dbh->prepare(
- 'SELECT bug_id, SUM(vote_count) FROM votes '
- . $dbh->sql_group_by('bug_id'));
- $sth->execute();
-
- my $sth_update = $dbh->prepare(
- 'UPDATE bugs SET votes = ? WHERE bug_id = ?');
- while (my ($id, $count) = $sth->fetchrow_array) {
- $sth_update->execute($count, $id);
- }
- $dbh->bz_commit_transaction();
- $status->('voting_cache_rebuild_end');
+ my ($self, $args) = @_;
+ my $status = $args->{status};
+ my $input = Bugzilla->input_params;
+ my $dbh = Bugzilla->dbh;
+
+ return if !$input->{rebuild_vote_cache};
+
+ $status->('voting_cache_rebuild_start');
+ $dbh->bz_start_transaction();
+ $dbh->do('UPDATE bugs SET votes = 0');
+
+ my $sth = $dbh->prepare(
+ 'SELECT bug_id, SUM(vote_count) FROM votes ' . $dbh->sql_group_by('bug_id'));
+ $sth->execute();
+
+ my $sth_update = $dbh->prepare('UPDATE bugs SET votes = ? WHERE bug_id = ?');
+ while (my ($id, $count) = $sth->fetchrow_array) {
+ $sth_update->execute($count, $id);
+ }
+ $dbh->bz_commit_transaction();
+ $status->('voting_cache_rebuild_end');
}
@@ -311,35 +319,36 @@ sub sanitycheck_repair {
##############
sub _check_votesperuser {
- return _check_votes(0, @_);
+ return _check_votes(0, @_);
}
sub _check_maxvotesperbug {
- return _check_votes(DEFAULT_VOTES_PER_BUG, @_);
+ return _check_votes(DEFAULT_VOTES_PER_BUG, @_);
}
sub _check_votestoconfirm {
- return _check_votes(0, @_);
+ return _check_votes(0, @_);
}
# This subroutine is only used internally by other _check_votes_* validators.
sub _check_votes {
- my ($default, $invocant, $votes, $field) = @_;
-
- detaint_natural($votes) if defined $votes;
- # On product creation, if the number of votes is not a valid integer,
- # we silently fall back to the given default value.
- # If the product already exists and the change is illegal, we complain.
- if (!defined $votes) {
- if (ref $invocant) {
- ThrowUserError('voting_product_illegal_votes',
- { field => $field, votes => $_[2] });
- }
- else {
- $votes = $default;
- }
+ my ($default, $invocant, $votes, $field) = @_;
+
+ detaint_natural($votes) if defined $votes;
+
+ # On product creation, if the number of votes is not a valid integer,
+ # we silently fall back to the given default value.
+ # If the product already exists and the change is illegal, we complain.
+ if (!defined $votes) {
+ if (ref $invocant) {
+ ThrowUserError('voting_product_illegal_votes',
+ {field => $field, votes => $_[2]});
+ }
+ else {
+ $votes = $default;
}
- return $votes;
+ }
+ return $votes;
}
#########
@@ -347,290 +356,321 @@ sub _check_votes {
#########
sub page_before_template {
- my ($self, $args) = @_;
- my $page = $args->{page_id};
- my $vars = $args->{vars};
-
- if ($page =~ m{^voting/bug\.}) {
- _page_bug($vars);
- }
- elsif ($page =~ m{^voting/user\.}) {
- _page_user($vars);
- }
+ my ($self, $args) = @_;
+ my $page = $args->{page_id};
+ my $vars = $args->{vars};
+
+ if ($page =~ m{^voting/bug\.}) {
+ _page_bug($vars);
+ }
+ elsif ($page =~ m{^voting/user\.}) {
+ _page_user($vars);
+ }
}
sub _page_bug {
- my ($vars) = @_;
- my $dbh = Bugzilla->dbh;
- my $input = Bugzilla->input_params;
+ my ($vars) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $input = Bugzilla->input_params;
- my $bug_id = $input->{bug_id};
- my $bug = Bugzilla::Bug->check($bug_id);
+ my $bug_id = $input->{bug_id};
+ my $bug = Bugzilla::Bug->check($bug_id);
- $vars->{'bug'} = $bug;
- $vars->{'users'} =
- $dbh->selectall_arrayref('SELECT profiles.login_name,
+ $vars->{'bug'} = $bug;
+ $vars->{'users'} = $dbh->selectall_arrayref(
+ 'SELECT profiles.login_name,
profiles.userid AS id,
votes.vote_count
FROM votes
INNER JOIN profiles
ON profiles.userid = votes.who
- WHERE votes.bug_id = ?',
- {Slice=>{}}, $bug->id);
+ WHERE votes.bug_id = ?', {Slice => {}}, $bug->id
+ );
}
sub _page_user {
- my ($vars) = @_;
- my $dbh = Bugzilla->dbh;
- my $user = Bugzilla->user;
- my $input = Bugzilla->input_params;
+ my ($vars) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->user;
+ my $input = Bugzilla->input_params;
- my $action = $input->{action};
- if ($action and $action eq 'vote') {
- _update_votes($vars);
- }
+ my $action = $input->{action};
+ if ($action and $action eq 'vote') {
+ _update_votes($vars);
+ }
- # If a bug_id is given, and we're editing, we'll add it to the votes list.
-
- my $bug_id = $input->{bug_id};
- my $bug = Bugzilla::Bug->check({ id => $bug_id, cache => 1 }) if $bug_id;
- my $who_id = $input->{user_id} || $user->id;
+ # If a bug_id is given, and we're editing, we'll add it to the votes list.
- # Logged-out users must specify a user_id.
- Bugzilla->login(LOGIN_REQUIRED) if !$who_id;
+ my $bug_id = $input->{bug_id};
+ my $bug = Bugzilla::Bug->check({id => $bug_id, cache => 1}) if $bug_id;
+ my $who_id = $input->{user_id} || $user->id;
- my $who = Bugzilla::User->check({ id => $who_id });
+ # Logged-out users must specify a user_id.
+ Bugzilla->login(LOGIN_REQUIRED) if !$who_id;
- my $canedit = $user->id == $who->id;
+ my $who = Bugzilla::User->check({id => $who_id});
- $dbh->bz_start_transaction();
+ my $canedit = $user->id == $who->id;
- if ($canedit && $bug) {
- # Make sure there is an entry for this bug
- # in the vote table, just so that things display right.
- my $has_votes = $dbh->selectrow_array('SELECT vote_count FROM votes
- WHERE bug_id = ? AND who = ?',
- undef, ($bug->id, $who->id));
- if (!$has_votes) {
- $dbh->do('INSERT INTO votes (who, bug_id, vote_count)
- VALUES (?, ?, 0)', undef, ($who->id, $bug->id));
- }
+ $dbh->bz_start_transaction();
+
+ if ($canedit && $bug) {
+
+ # Make sure there is an entry for this bug
+ # in the vote table, just so that things display right.
+ my $has_votes = $dbh->selectrow_array(
+ 'SELECT vote_count FROM votes
+ WHERE bug_id = ? AND who = ?', undef,
+ ($bug->id, $who->id)
+ );
+ if (!$has_votes) {
+ $dbh->do(
+ 'INSERT INTO votes (who, bug_id, vote_count)
+ VALUES (?, ?, 0)', undef, ($who->id, $bug->id)
+ );
}
+ }
- my (@products, @all_bug_ids);
- # Read the votes data for this user for each product.
- foreach my $product (@{ $user->get_selectable_products }) {
- next unless ($product->{votesperuser} > 0);
+ my (@products, @all_bug_ids);
- my $vote_list =
- $dbh->selectall_arrayref('SELECT votes.bug_id, votes.vote_count
+ # Read the votes data for this user for each product.
+ foreach my $product (@{$user->get_selectable_products}) {
+ next unless ($product->{votesperuser} > 0);
+
+ my $vote_list = $dbh->selectall_arrayref(
+ 'SELECT votes.bug_id, votes.vote_count
FROM votes
INNER JOIN bugs
ON votes.bug_id = bugs.bug_id
WHERE votes.who = ?
- AND bugs.product_id = ?',
- undef, ($who->id, $product->id));
-
- my %votes = map { $_->[0] => $_->[1] } @$vote_list;
- my @bug_ids = sort keys %votes;
- # Exclude bugs that the user can no longer see.
- @bug_ids = @{ $user->visible_bugs(\@bug_ids) };
- next unless scalar @bug_ids;
-
- push(@all_bug_ids, @bug_ids);
- my @bugs = @{ Bugzilla::Bug->new_from_list(\@bug_ids) };
- $_->{count} = $votes{$_->id} foreach @bugs;
- # We include votes from bugs that the user can no longer see.
- my $total = sum(values %votes) || 0;
-
- my $onevoteonly = 0;
- $onevoteonly = 1 if (min($product->{votesperuser},
- $product->{maxvotesperbug}) == 1);
-
- push(@products, { name => $product->name,
- bugs => \@bugs,
- bug_ids => \@bug_ids,
- onevoteonly => $onevoteonly,
- total => $total,
- maxvotes => $product->{votesperuser},
- maxperbug => $product->{maxvotesperbug} });
- }
-
- if ($canedit && $bug) {
- $dbh->do('DELETE FROM votes WHERE vote_count = 0 AND who = ?',
- undef, $who->id);
- }
- $dbh->bz_commit_transaction();
-
- $vars->{'canedit'} = $canedit;
- $vars->{'voting_user'} = { "login" => $who->name };
- $vars->{'products'} = \@products;
- $vars->{'this_bug'} = $bug;
- $vars->{'all_bug_ids'} = \@all_bug_ids;
+ AND bugs.product_id = ?', undef,
+ ($who->id, $product->id)
+ );
+
+ my %votes = map { $_->[0] => $_->[1] } @$vote_list;
+ my @bug_ids = sort keys %votes;
+
+ # Exclude bugs that the user can no longer see.
+ @bug_ids = @{$user->visible_bugs(\@bug_ids)};
+ next unless scalar @bug_ids;
+
+ push(@all_bug_ids, @bug_ids);
+ my @bugs = @{Bugzilla::Bug->new_from_list(\@bug_ids)};
+ $_->{count} = $votes{$_->id} foreach @bugs;
+
+ # We include votes from bugs that the user can no longer see.
+ my $total = sum(values %votes) || 0;
+
+ my $onevoteonly = 0;
+ $onevoteonly = 1
+ if (min($product->{votesperuser}, $product->{maxvotesperbug}) == 1);
+
+ push(
+ @products,
+ {
+ name => $product->name,
+ bugs => \@bugs,
+ bug_ids => \@bug_ids,
+ onevoteonly => $onevoteonly,
+ total => $total,
+ maxvotes => $product->{votesperuser},
+ maxperbug => $product->{maxvotesperbug}
+ }
+ );
+ }
+
+ if ($canedit && $bug) {
+ $dbh->do('DELETE FROM votes WHERE vote_count = 0 AND who = ?', undef, $who->id);
+ }
+ $dbh->bz_commit_transaction();
+
+ $vars->{'canedit'} = $canedit;
+ $vars->{'voting_user'} = {"login" => $who->name};
+ $vars->{'products'} = \@products;
+ $vars->{'this_bug'} = $bug;
+ $vars->{'all_bug_ids'} = \@all_bug_ids;
}
sub _update_votes {
- my ($vars) = @_;
-
- ############################################################################
- # Begin Data/Security Validation
- ############################################################################
-
- my $cgi = Bugzilla->cgi;
- my $dbh = Bugzilla->dbh;
- my $template = Bugzilla->template;
- my $user = Bugzilla->login(LOGIN_REQUIRED);
- my $input = Bugzilla->input_params;
-
- # Build a list of bug IDs for which votes have been submitted. Votes
- # are submitted in form fields in which the field names are the bug
- # IDs and the field values are the number of votes.
-
- my @buglist = grep {/^\d+$/} keys %$input;
- my (%bugs, %votes);
-
- # If no bugs are in the buglist, let's make sure the user gets notified
- # that their votes will get nuked if they continue.
- if (scalar(@buglist) == 0) {
- if (!defined $cgi->param('delete_all_votes')) {
- print $cgi->header();
- $template->process("voting/delete-all.html.tmpl", $vars)
- || ThrowTemplateError($template->error());
- exit;
- }
- elsif ($cgi->param('delete_all_votes') == 0) {
- print $cgi->redirect("page.cgi?id=voting/user.html");
- exit;
+ my ($vars) = @_;
+
+ ############################################################################
+ # Begin Data/Security Validation
+ ############################################################################
+
+ my $cgi = Bugzilla->cgi;
+ my $dbh = Bugzilla->dbh;
+ my $template = Bugzilla->template;
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
+ my $input = Bugzilla->input_params;
+
+ # Build a list of bug IDs for which votes have been submitted. Votes
+ # are submitted in form fields in which the field names are the bug
+ # IDs and the field values are the number of votes.
+
+ my @buglist = grep {/^\d+$/} keys %$input;
+ my (%bugs, %votes);
+
+ # If no bugs are in the buglist, let's make sure the user gets notified
+ # that their votes will get nuked if they continue.
+ if (scalar(@buglist) == 0) {
+ if (!defined $cgi->param('delete_all_votes')) {
+ print $cgi->header();
+ $template->process("voting/delete-all.html.tmpl", $vars)
+ || ThrowTemplateError($template->error());
+ exit;
+ }
+ elsif ($cgi->param('delete_all_votes') == 0) {
+ print $cgi->redirect("page.cgi?id=voting/user.html");
+ exit;
+ }
+ }
+ else {
+ $user->visible_bugs(\@buglist);
+ my $bugs_obj = Bugzilla::Bug->new_from_list(\@buglist);
+ $bugs{$_->id} = $_ foreach @$bugs_obj;
+ }
+
+ # Call check_is_visible() on each bug to make sure it is an existing bug
+ # that the user is authorized to access, and make sure the number of votes
+ # submitted is also an integer.
+ foreach my $id (@buglist) {
+ my $bug = $bugs{$id}
+ or ThrowUserError('bug_id_does_not_exist', {bug_id => $id});
+ $bug->check_is_visible;
+ $id = $bug->id;
+ $votes{$id} = $input->{$id};
+ detaint_natural($votes{$id}) || ThrowUserError("voting_must_be_nonnegative");
+ }
+
+ my $token = $cgi->param('token');
+ check_hash_token($token, ['vote']);
+
+ ############################################################################
+ # End Data/Security Validation
+ ############################################################################
+ my $who = $user->id;
+
+ # If the user is voting for bugs, make sure they aren't overstuffing
+ # the ballot box.
+ if (scalar @buglist) {
+ my (%prodcount, %products);
+ foreach my $bug_id (keys %bugs) {
+ my $bug = $bugs{$bug_id};
+ my $prod = $bug->product;
+ $products{$prod} ||= $bug->product_obj;
+ $prodcount{$prod} ||= 0;
+ $prodcount{$prod} += $votes{$bug_id};
+
+ # Make sure we haven't broken the votes-per-bug limit
+ ($votes{$bug_id} <= $products{$prod}->{maxvotesperbug}) || ThrowUserError(
+ "voting_too_many_votes_for_bug",
+ {
+ max => $products{$prod}->{maxvotesperbug},
+ product => $prod,
+ votes => $votes{$bug_id}
}
- }
- else {
- $user->visible_bugs(\@buglist);
- my $bugs_obj = Bugzilla::Bug->new_from_list(\@buglist);
- $bugs{$_->id} = $_ foreach @$bugs_obj;
+ );
}
- # Call check_is_visible() on each bug to make sure it is an existing bug
- # that the user is authorized to access, and make sure the number of votes
- # submitted is also an integer.
- foreach my $id (@buglist) {
- my $bug = $bugs{$id}
- or ThrowUserError('bug_id_does_not_exist', { bug_id => $id });
- $bug->check_is_visible;
- $id = $bug->id;
- $votes{$id} = $input->{$id};
- detaint_natural($votes{$id})
- || ThrowUserError("voting_must_be_nonnegative");
- }
-
- my $token = $cgi->param('token');
- check_hash_token($token, ['vote']);
-
- ############################################################################
- # End Data/Security Validation
- ############################################################################
- my $who = $user->id;
-
- # If the user is voting for bugs, make sure they aren't overstuffing
- # the ballot box.
- if (scalar @buglist) {
- my (%prodcount, %products);
- foreach my $bug_id (keys %bugs) {
- my $bug = $bugs{$bug_id};
- my $prod = $bug->product;
- $products{$prod} ||= $bug->product_obj;
- $prodcount{$prod} ||= 0;
- $prodcount{$prod} += $votes{$bug_id};
-
- # Make sure we haven't broken the votes-per-bug limit
- ($votes{$bug_id} <= $products{$prod}->{maxvotesperbug})
- || ThrowUserError("voting_too_many_votes_for_bug",
- {max => $products{$prod}->{maxvotesperbug},
- product => $prod,
- votes => $votes{$bug_id}});
- }
-
- # Make sure we haven't broken the votes-per-product limit
- foreach my $prod (keys(%prodcount)) {
- ($prodcount{$prod} <= $products{$prod}->{votesperuser})
- || ThrowUserError("voting_too_many_votes_for_product",
- {max => $products{$prod}->{votesperuser},
- product => $prod,
- votes => $prodcount{$prod}});
+ # Make sure we haven't broken the votes-per-product limit
+ foreach my $prod (keys(%prodcount)) {
+ ($prodcount{$prod} <= $products{$prod}->{votesperuser}) || ThrowUserError(
+ "voting_too_many_votes_for_product",
+ {
+ max => $products{$prod}->{votesperuser},
+ product => $prod,
+ votes => $prodcount{$prod}
}
+ );
}
+ }
- # Update the user's votes in the database.
- $dbh->bz_start_transaction();
+ # Update the user's votes in the database.
+ $dbh->bz_start_transaction();
- my $old_list = $dbh->selectall_arrayref('SELECT bug_id, vote_count FROM votes
- WHERE who = ?', undef, $who);
+ my $old_list = $dbh->selectall_arrayref(
+ 'SELECT bug_id, vote_count FROM votes
+ WHERE who = ?', undef, $who
+ );
- my %old_votes = map { $_->[0] => $_->[1] } @$old_list;
+ my %old_votes = map { $_->[0] => $_->[1] } @$old_list;
- my $sth_insertVotes = $dbh->prepare('INSERT INTO votes (who, bug_id, vote_count)
- VALUES (?, ?, ?)');
- my $sth_updateVotes = $dbh->prepare('UPDATE votes SET vote_count = ?
- WHERE bug_id = ? AND who = ?');
+ my $sth_insertVotes = $dbh->prepare(
+ 'INSERT INTO votes (who, bug_id, vote_count)
+ VALUES (?, ?, ?)'
+ );
+ my $sth_updateVotes = $dbh->prepare(
+ 'UPDATE votes SET vote_count = ?
+ WHERE bug_id = ? AND who = ?'
+ );
- my %affected = map { $_ => 1 } (@buglist, keys %old_votes);
- my @deleted_votes;
-
- foreach my $id (keys %affected) {
- if (!$votes{$id}) {
- push(@deleted_votes, $id);
- next;
- }
- if ($votes{$id} == ($old_votes{$id} || 0)) {
- delete $affected{$id};
- next;
- }
- # We use 'defined' in case 0 was accidentally stored in the DB.
- if (defined $old_votes{$id}) {
- $sth_updateVotes->execute($votes{$id}, $id, $who);
- }
- else {
- $sth_insertVotes->execute($who, $id, $votes{$id});
- }
- }
+ my %affected = map { $_ => 1 } (@buglist, keys %old_votes);
+ my @deleted_votes;
- if (@deleted_votes) {
- $dbh->do('DELETE FROM votes WHERE who = ? AND ' .
- $dbh->sql_in('bug_id', \@deleted_votes), undef, $who);
+ foreach my $id (keys %affected) {
+ if (!$votes{$id}) {
+ push(@deleted_votes, $id);
+ next;
}
-
- # Update the cached values in the bugs table
- my @updated_bugs = ();
-
- my $sth_getVotes = $dbh->prepare("SELECT SUM(vote_count) FROM votes
- WHERE bug_id = ?");
-
- $sth_updateVotes = $dbh->prepare('UPDATE bugs SET votes = ? WHERE bug_id = ?');
-
- foreach my $id (keys %affected) {
- $sth_getVotes->execute($id);
- my $v = $sth_getVotes->fetchrow_array || 0;
- $sth_updateVotes->execute($v, $id);
- $bugs{$id}->{votes} = $v if $bugs{$id};
- my $confirmed = _confirm_if_vote_confirmed($bugs{$id} || $id);
- push (@updated_bugs, $id) if $confirmed;
+ if ($votes{$id} == ($old_votes{$id} || 0)) {
+ delete $affected{$id};
+ next;
}
- $dbh->bz_commit_transaction();
-
- print $cgi->header() if scalar @updated_bugs;
- $vars->{'type'} = "votes";
- $vars->{'title_tag'} = 'change_votes';
- foreach my $bug_id (@updated_bugs) {
- $vars->{'id'} = $bug_id;
- $vars->{'sent_bugmail'} =
- Bugzilla::BugMail::Send($bug_id, { 'changer' => $user });
-
- $template->process("bug/process/results.html.tmpl", $vars)
- || ThrowTemplateError($template->error());
- # Set header_done to 1 only after the first bug.
- $vars->{'header_done'} = 1;
+ # We use 'defined' in case 0 was accidentally stored in the DB.
+ if (defined $old_votes{$id}) {
+ $sth_updateVotes->execute($votes{$id}, $id, $who);
}
- $vars->{'message'} = 'votes_recorded';
+ else {
+ $sth_insertVotes->execute($who, $id, $votes{$id});
+ }
+ }
+
+ if (@deleted_votes) {
+ $dbh->do(
+ 'DELETE FROM votes WHERE who = ? AND '
+ . $dbh->sql_in('bug_id', \@deleted_votes),
+ undef, $who
+ );
+ }
+
+ # Update the cached values in the bugs table
+ my @updated_bugs = ();
+
+ my $sth_getVotes = $dbh->prepare(
+ "SELECT SUM(vote_count) FROM votes
+ WHERE bug_id = ?"
+ );
+
+ $sth_updateVotes = $dbh->prepare('UPDATE bugs SET votes = ? WHERE bug_id = ?');
+
+ foreach my $id (keys %affected) {
+ $sth_getVotes->execute($id);
+ my $v = $sth_getVotes->fetchrow_array || 0;
+ $sth_updateVotes->execute($v, $id);
+ $bugs{$id}->{votes} = $v if $bugs{$id};
+ my $confirmed = _confirm_if_vote_confirmed($bugs{$id} || $id);
+ push(@updated_bugs, $id) if $confirmed;
+ }
+
+ $dbh->bz_commit_transaction();
+
+ print $cgi->header() if scalar @updated_bugs;
+ $vars->{'type'} = "votes";
+ $vars->{'title_tag'} = 'change_votes';
+ foreach my $bug_id (@updated_bugs) {
+ $vars->{'id'} = $bug_id;
+ $vars->{'sent_bugmail'}
+ = Bugzilla::BugMail::Send($bug_id, {'changer' => $user});
+
+ $template->process("bug/process/results.html.tmpl", $vars)
+ || ThrowTemplateError($template->error());
+
+ # Set header_done to 1 only after the first bug.
+ $vars->{'header_done'} = 1;
+ }
+ $vars->{'message'} = 'votes_recorded';
}
######################
@@ -638,233 +678,245 @@ sub _update_votes {
######################
sub _modify_bug_votes {
- my ($product, $changes) = @_;
- my $dbh = Bugzilla->dbh;
- my @msgs;
+ my ($product, $changes) = @_;
+ my $dbh = Bugzilla->dbh;
+ my @msgs;
- # 1. too many votes for a single user on a single bug.
- my @toomanyvotes_list;
- if ($product->{maxvotesperbug} < $product->{votesperuser}) {
- my $votes = $dbh->selectall_arrayref(
- 'SELECT votes.who, votes.bug_id
+ # 1. too many votes for a single user on a single bug.
+ my @toomanyvotes_list;
+ if ($product->{maxvotesperbug} < $product->{votesperuser}) {
+ my $votes = $dbh->selectall_arrayref(
+ 'SELECT votes.who, votes.bug_id
FROM votes
INNER JOIN bugs ON bugs.bug_id = votes.bug_id
WHERE bugs.product_id = ?
- AND votes.vote_count > ?',
- undef, ($product->id, $product->{maxvotesperbug}));
-
- foreach my $vote (@$votes) {
- my ($who, $id) = (@$vote);
- # If some votes are removed, _remove_votes() returns a list
- # of messages to send to voters.
- push(@msgs, _remove_votes($id, $who, 'votes_too_many_per_bug'));
- my $name = Bugzilla::User->new($who)->login;
-
- push(@toomanyvotes_list, {id => $id, name => $name});
- }
+ AND votes.vote_count > ?', undef,
+ ($product->id, $product->{maxvotesperbug})
+ );
+
+ foreach my $vote (@$votes) {
+ my ($who, $id) = (@$vote);
+
+ # If some votes are removed, _remove_votes() returns a list
+ # of messages to send to voters.
+ push(@msgs, _remove_votes($id, $who, 'votes_too_many_per_bug'));
+ my $name = Bugzilla::User->new($who)->login;
+
+ push(@toomanyvotes_list, {id => $id, name => $name});
}
+ }
- $changes->{'_too_many_votes'} = \@toomanyvotes_list;
+ $changes->{'_too_many_votes'} = \@toomanyvotes_list;
- # 2. too many total votes for a single user.
- # This part doesn't work in the general case because _remove_votes
- # doesn't enforce votesperuser (except per-bug when it's less
- # than maxvotesperbug). See _remove_votes().
+ # 2. too many total votes for a single user.
+ # This part doesn't work in the general case because _remove_votes
+ # doesn't enforce votesperuser (except per-bug when it's less
+ # than maxvotesperbug). See _remove_votes().
- my $votes = $dbh->selectall_arrayref(
- 'SELECT votes.who, votes.vote_count
+ my $votes = $dbh->selectall_arrayref(
+ 'SELECT votes.who, votes.vote_count
FROM votes
INNER JOIN bugs ON bugs.bug_id = votes.bug_id
- WHERE bugs.product_id = ?',
- undef, $product->id);
+ WHERE bugs.product_id = ?', undef, $product->id
+ );
- my %counts;
- foreach my $vote (@$votes) {
- my ($who, $count) = @$vote;
- if (!defined $counts{$who}) {
- $counts{$who} = $count;
- } else {
- $counts{$who} += $count;
- }
+ my %counts;
+ foreach my $vote (@$votes) {
+ my ($who, $count) = @$vote;
+ if (!defined $counts{$who}) {
+ $counts{$who} = $count;
+ }
+ else {
+ $counts{$who} += $count;
}
+ }
- my @toomanytotalvotes_list;
- foreach my $who (keys(%counts)) {
- if ($counts{$who} > $product->{votesperuser}) {
- my $bug_ids = $dbh->selectcol_arrayref(
- 'SELECT votes.bug_id
+ my @toomanytotalvotes_list;
+ foreach my $who (keys(%counts)) {
+ if ($counts{$who} > $product->{votesperuser}) {
+ my $bug_ids = $dbh->selectcol_arrayref(
+ 'SELECT votes.bug_id
FROM votes
INNER JOIN bugs ON bugs.bug_id = votes.bug_id
WHERE bugs.product_id = ?
- AND votes.who = ?',
- undef, $product->id, $who);
-
- my $name = Bugzilla::User->new($who)->login;
- foreach my $bug_id (@$bug_ids) {
- # _remove_votes returns a list of messages to send
- # in case some voters had too many votes.
- push(@msgs, _remove_votes($bug_id, $who,
- 'votes_too_many_per_user'));
-
- push(@toomanytotalvotes_list, {id => $bug_id, name => $name});
- }
- }
- }
-
- $changes->{'_too_many_total_votes'} = \@toomanytotalvotes_list;
-
- # 3. enough votes to confirm
- my $bug_list = $dbh->selectcol_arrayref(
- 'SELECT bug_id FROM bugs
- WHERE product_id = ? AND bug_status = ? AND votes >= ?',
- undef, ($product->id, 'UNCONFIRMED', $product->{votestoconfirm}));
-
- my @updated_bugs;
- foreach my $bug_id (@$bug_list) {
- my $confirmed = _confirm_if_vote_confirmed($bug_id);
- push (@updated_bugs, $bug_id) if $confirmed;
- }
- $changes->{'_confirmed_bugs'} = \@updated_bugs;
-
- # Now that changes are done, we can send emails to voters.
- foreach my $msg (@msgs) {
- MessageToMTA($msg);
- }
- # And send out emails about changed bugs
- foreach my $bug_id (@updated_bugs) {
- my $sent_bugmail = Bugzilla::BugMail::Send(
- $bug_id, { changer => Bugzilla->user });
- $changes->{'_confirmed_bugs_sent_bugmail'}->{$bug_id} = $sent_bugmail;
- }
+ AND votes.who = ?', undef, $product->id, $who
+ );
+
+ my $name = Bugzilla::User->new($who)->login;
+ foreach my $bug_id (@$bug_ids) {
+
+ # _remove_votes returns a list of messages to send
+ # in case some voters had too many votes.
+ push(@msgs, _remove_votes($bug_id, $who, 'votes_too_many_per_user'));
+
+ push(@toomanytotalvotes_list, {id => $bug_id, name => $name});
+ }
+ }
+ }
+
+ $changes->{'_too_many_total_votes'} = \@toomanytotalvotes_list;
+
+ # 3. enough votes to confirm
+ my $bug_list = $dbh->selectcol_arrayref(
+ 'SELECT bug_id FROM bugs
+ WHERE product_id = ? AND bug_status = ? AND votes >= ?', undef,
+ ($product->id, 'UNCONFIRMED', $product->{votestoconfirm})
+ );
+
+ my @updated_bugs;
+ foreach my $bug_id (@$bug_list) {
+ my $confirmed = _confirm_if_vote_confirmed($bug_id);
+ push(@updated_bugs, $bug_id) if $confirmed;
+ }
+ $changes->{'_confirmed_bugs'} = \@updated_bugs;
+
+ # Now that changes are done, we can send emails to voters.
+ foreach my $msg (@msgs) {
+ MessageToMTA($msg);
+ }
+
+ # And send out emails about changed bugs
+ foreach my $bug_id (@updated_bugs) {
+ my $sent_bugmail
+ = Bugzilla::BugMail::Send($bug_id, {changer => Bugzilla->user});
+ $changes->{'_confirmed_bugs_sent_bugmail'}->{$bug_id} = $sent_bugmail;
+ }
}
# If a bug is moved to a product which allows less votes per bug
# compared to the previous product, extra votes need to be removed.
sub _remove_votes {
- my ($id, $who, $reason) = (@_);
- my $dbh = Bugzilla->dbh;
-
- my $whopart = ($who) ? " AND votes.who = $who" : "";
-
- my $sth = $dbh->prepare("SELECT profiles.login_name, " .
- "profiles.userid, votes.vote_count, " .
- "products.votesperuser, products.maxvotesperbug " .
- "FROM profiles " .
- "LEFT JOIN votes ON profiles.userid = votes.who " .
- "LEFT JOIN bugs ON votes.bug_id = bugs.bug_id " .
- "LEFT JOIN products ON products.id = bugs.product_id " .
- "WHERE votes.bug_id = ? " . $whopart);
- $sth->execute($id);
- my @list;
- while (my ($name, $userid, $oldvotes, $votesperuser, $maxvotesperbug) = $sth->fetchrow_array()) {
- push(@list, [$name, $userid, $oldvotes, $votesperuser, $maxvotesperbug]);
- }
-
- # @messages stores all emails which have to be sent, if any.
- # This array is passed to the caller which will send these emails itself.
- my @messages = ();
-
- if (scalar(@list)) {
- foreach my $ref (@list) {
- my ($name, $userid, $oldvotes, $votesperuser, $maxvotesperbug) = (@$ref);
-
- $maxvotesperbug = min($votesperuser, $maxvotesperbug);
-
- # If this product allows voting and the user's votes are in
- # the acceptable range, then don't do anything.
- next if $votesperuser && $oldvotes <= $maxvotesperbug;
-
- # If the user has more votes on this bug than this product
- # allows, then reduce the number of votes so it fits
- my $newvotes = $maxvotesperbug;
-
- my $removedvotes = $oldvotes - $newvotes;
-
- if ($newvotes) {
- $dbh->do("UPDATE votes SET vote_count = ? " .
- "WHERE bug_id = ? AND who = ?",
- undef, ($newvotes, $id, $userid));
- } else {
- $dbh->do("DELETE FROM votes WHERE bug_id = ? AND who = ?",
- undef, ($id, $userid));
- }
-
- # Notice that we did not make sure that the user fit within the $votesperuser
- # range. This is considered to be an acceptable alternative to losing votes
- # during product moves. Then next time the user attempts to change their votes,
- # they will be forced to fit within the $votesperuser limit.
-
- # Now lets send the e-mail to alert the user to the fact that their votes have
- # been reduced or removed.
- my $vars = {
- 'to' => $name . Bugzilla->params->{'emailsuffix'},
- 'bugid' => $id,
- 'reason' => $reason,
-
- 'votesremoved' => $removedvotes,
- 'votesold' => $oldvotes,
- 'votesnew' => $newvotes,
- };
-
- my $voter = new Bugzilla::User($userid);
- my $template = Bugzilla->template_inner($voter->setting('lang'));
-
- my $msg;
- $template->process("voting/votes-removed.txt.tmpl", $vars, \$msg);
- push(@messages, $msg);
- }
-
- my $votes = $dbh->selectrow_array("SELECT SUM(vote_count) " .
- "FROM votes WHERE bug_id = ?",
- undef, $id) || 0;
- $dbh->do("UPDATE bugs SET votes = ? WHERE bug_id = ?",
- undef, ($votes, $id));
- }
- # Now return the array containing emails to be sent.
- return @messages;
+ my ($id, $who, $reason) = (@_);
+ my $dbh = Bugzilla->dbh;
+
+ my $whopart = ($who) ? " AND votes.who = $who" : "";
+
+ my $sth
+ = $dbh->prepare("SELECT profiles.login_name, "
+ . "profiles.userid, votes.vote_count, "
+ . "products.votesperuser, products.maxvotesperbug "
+ . "FROM profiles "
+ . "LEFT JOIN votes ON profiles.userid = votes.who "
+ . "LEFT JOIN bugs ON votes.bug_id = bugs.bug_id "
+ . "LEFT JOIN products ON products.id = bugs.product_id "
+ . "WHERE votes.bug_id = ? "
+ . $whopart);
+ $sth->execute($id);
+ my @list;
+ while (my ($name, $userid, $oldvotes, $votesperuser, $maxvotesperbug)
+ = $sth->fetchrow_array())
+ {
+ push(@list, [$name, $userid, $oldvotes, $votesperuser, $maxvotesperbug]);
+ }
+
+ # @messages stores all emails which have to be sent, if any.
+ # This array is passed to the caller which will send these emails itself.
+ my @messages = ();
+
+ if (scalar(@list)) {
+ foreach my $ref (@list) {
+ my ($name, $userid, $oldvotes, $votesperuser, $maxvotesperbug) = (@$ref);
+
+ $maxvotesperbug = min($votesperuser, $maxvotesperbug);
+
+ # If this product allows voting and the user's votes are in
+ # the acceptable range, then don't do anything.
+ next if $votesperuser && $oldvotes <= $maxvotesperbug;
+
+ # If the user has more votes on this bug than this product
+ # allows, then reduce the number of votes so it fits
+ my $newvotes = $maxvotesperbug;
+
+ my $removedvotes = $oldvotes - $newvotes;
+
+ if ($newvotes) {
+ $dbh->do("UPDATE votes SET vote_count = ? " . "WHERE bug_id = ? AND who = ?",
+ undef, ($newvotes, $id, $userid));
+ }
+ else {
+ $dbh->do("DELETE FROM votes WHERE bug_id = ? AND who = ?",
+ undef, ($id, $userid));
+ }
+
+ # Notice that we did not make sure that the user fit within the $votesperuser
+ # range. This is considered to be an acceptable alternative to losing votes
+ # during product moves. Then next time the user attempts to change their votes,
+ # they will be forced to fit within the $votesperuser limit.
+
+ # Now lets send the e-mail to alert the user to the fact that their votes have
+ # been reduced or removed.
+ my $vars = {
+ 'to' => $name . Bugzilla->params->{'emailsuffix'},
+ 'bugid' => $id,
+ 'reason' => $reason,
+
+ 'votesremoved' => $removedvotes,
+ 'votesold' => $oldvotes,
+ 'votesnew' => $newvotes,
+ };
+
+ my $voter = new Bugzilla::User($userid);
+ my $template = Bugzilla->template_inner($voter->setting('lang'));
+
+ my $msg;
+ $template->process("voting/votes-removed.txt.tmpl", $vars, \$msg);
+ push(@messages, $msg);
+ }
+
+ my $votes
+ = $dbh->selectrow_array(
+ "SELECT SUM(vote_count) " . "FROM votes WHERE bug_id = ?",
+ undef, $id)
+ || 0;
+ $dbh->do("UPDATE bugs SET votes = ? WHERE bug_id = ?", undef, ($votes, $id));
+ }
+
+ # Now return the array containing emails to be sent.
+ return @messages;
}
# If a user votes for a bug, or the number of votes required to
# confirm a bug has been reduced, check if the bug is now confirmed.
sub _confirm_if_vote_confirmed {
- my $id = shift;
- my $bug = ref $id ? $id : new Bugzilla::Bug({ id => $id, cache => 1 });
-
- my $ret = 0;
- if (!$bug->everconfirmed
- and $bug->product_obj->{votestoconfirm}
- and $bug->votes >= $bug->product_obj->{votestoconfirm})
- {
- $bug->add_comment('', { type => CMT_POPULAR_VOTES });
-
- if ($bug->bug_status eq 'UNCONFIRMED') {
- # Get a valid open state.
- my $new_status;
- foreach my $state (@{$bug->status->can_change_to}) {
- if ($state->is_open && $state->name ne 'UNCONFIRMED') {
- $new_status = $state->name;
- last;
- }
- }
- ThrowCodeError('voting_no_open_bug_status') unless $new_status;
-
- # We cannot call $bug->set_bug_status() here, because a user without
- # canconfirm privs should still be able to confirm a bug by
- # popular vote. We already know the new status is valid, so it's safe.
- $bug->{bug_status} = $new_status;
- $bug->{everconfirmed} = 1;
- delete $bug->{'status'}; # Contains the status object.
- }
- else {
- # If the bug is in a closed state, only set everconfirmed to 1.
- # Do not call $bug->_set_everconfirmed(), for the same reason as above.
- $bug->{everconfirmed} = 1;
+ my $id = shift;
+ my $bug = ref $id ? $id : new Bugzilla::Bug({id => $id, cache => 1});
+
+ my $ret = 0;
+ if ( !$bug->everconfirmed
+ and $bug->product_obj->{votestoconfirm}
+ and $bug->votes >= $bug->product_obj->{votestoconfirm})
+ {
+ $bug->add_comment('', {type => CMT_POPULAR_VOTES});
+
+ if ($bug->bug_status eq 'UNCONFIRMED') {
+
+ # Get a valid open state.
+ my $new_status;
+ foreach my $state (@{$bug->status->can_change_to}) {
+ if ($state->is_open && $state->name ne 'UNCONFIRMED') {
+ $new_status = $state->name;
+ last;
}
- $bug->update();
+ }
+ ThrowCodeError('voting_no_open_bug_status') unless $new_status;
- $ret = 1;
+ # We cannot call $bug->set_bug_status() here, because a user without
+ # canconfirm privs should still be able to confirm a bug by
+ # popular vote. We already know the new status is valid, so it's safe.
+ $bug->{bug_status} = $new_status;
+ $bug->{everconfirmed} = 1;
+ delete $bug->{'status'}; # Contains the status object.
}
- return $ret;
+ else {
+ # If the bug is in a closed state, only set everconfirmed to 1.
+ # Do not call $bug->_set_everconfirmed(), for the same reason as above.
+ $bug->{everconfirmed} = 1;
+ }
+ $bug->update();
+
+ $ret = 1;
+ }
+ return $ret;
}
diff --git a/extensions/create.pl b/extensions/create.pl
index 7c8693e28..f2aa3f933 100755
--- a/extensions/create.pl
+++ b/extensions/create.pl
@@ -27,42 +27,41 @@ my $base_dir = bz_locations()->{'extensionsdir'};
my $name = $ARGV[0] or ThrowUserError('extension_create_no_name');
$name = ucfirst($name);
if ($name !~ /^[A-Z]/) {
- ThrowUserError('extension_first_letter_caps', { name => $name });
+ ThrowUserError('extension_first_letter_caps', {name => $name});
}
-my $extension_dir = "$base_dir/$name";
-mkpath($extension_dir)
+my $extension_dir = "$base_dir/$name";
+mkpath($extension_dir)
|| die "$extension_dir already exists or cannot be created.\n";
my $lcname = lc($name);
foreach my $path (qw(lib docs/en/rst web template/en/default/hook),
- "template/en/default/$lcname")
+ "template/en/default/$lcname")
{
- mkpath("$extension_dir/$path") || die "$extension_dir/$path: $!";
+ mkpath("$extension_dir/$path") || die "$extension_dir/$path: $!";
}
-my $template = Bugzilla->template;
-my $vars = { name => $name, path => $extension_dir };
+my $template = Bugzilla->template;
+my $vars = {name => $name, path => $extension_dir};
my %create_files = (
- 'config.pm.tmpl' => 'Config.pm',
- 'extension.pm.tmpl' => 'Extension.pm',
- 'util.pm.tmpl' => 'lib/Util.pm',
- 'web-readme.txt.tmpl' => 'web/README',
- 'hook-readme.txt.tmpl' => 'template/en/default/hook/README',
- 'name-readme.txt.tmpl' => "template/en/default/$lcname/README",
- 'index-admin.rst.tmpl' => "docs/en/rst/index-admin.rst",
- 'index-user.rst.tmpl' => "docs/en/rst/index-user.rst",
+ 'config.pm.tmpl' => 'Config.pm',
+ 'extension.pm.tmpl' => 'Extension.pm',
+ 'util.pm.tmpl' => 'lib/Util.pm',
+ 'web-readme.txt.tmpl' => 'web/README',
+ 'hook-readme.txt.tmpl' => 'template/en/default/hook/README',
+ 'name-readme.txt.tmpl' => "template/en/default/$lcname/README",
+ 'index-admin.rst.tmpl' => "docs/en/rst/index-admin.rst",
+ 'index-user.rst.tmpl' => "docs/en/rst/index-user.rst",
);
foreach my $template_file (keys %create_files) {
- my $target = $create_files{$template_file};
- my $output;
- $template->process("extensions/$template_file", $vars, \$output)
- or ThrowTemplateError($template->error());
- open(my $fh, '>', "$extension_dir/$target")
- or die "extension_dir/$target: $!";
- print $fh $output;
- close($fh);
+ my $target = $create_files{$template_file};
+ my $output;
+ $template->process("extensions/$template_file", $vars, \$output)
+ or ThrowTemplateError($template->error());
+ open(my $fh, '>', "$extension_dir/$target") or die "extension_dir/$target: $!";
+ print $fh $output;
+ close($fh);
}
say get_text('extension_created', $vars);