summaryrefslogtreecommitdiffstats
path: root/extensions
diff options
context:
space:
mode:
Diffstat (limited to 'extensions')
-rw-r--r--extensions/AntiSpam/Config.pm9
-rw-r--r--extensions/AntiSpam/Extension.pm476
-rw-r--r--extensions/AntiSpam/lib/Config.pm116
-rw-r--r--extensions/BMO/Config.pm21
-rw-r--r--extensions/BMO/Extension.pm4648
-rwxr-xr-xextensions/BMO/bin/bug_1022707.pl6
-rwxr-xr-xextensions/BMO/bin/bug_1093952.pl52
-rwxr-xr-xextensions/BMO/bin/bug_1141452.pl110
-rwxr-xr-xextensions/BMO/bin/migrate-github-pull-requests.pl67
-rw-r--r--extensions/BMO/lib/Constants.pm6
-rw-r--r--extensions/BMO/lib/Data.pm403
-rw-r--r--extensions/BMO/lib/FakeBug.pm28
-rw-r--r--extensions/BMO/lib/Reports/Groups.pm485
-rw-r--r--extensions/BMO/lib/Reports/Internship.pm135
-rw-r--r--extensions/BMO/lib/Reports/ProductSecurity.pm76
-rw-r--r--extensions/BMO/lib/Reports/Recruiting.pm126
-rw-r--r--extensions/BMO/lib/Reports/ReleaseTracking.pm779
-rw-r--r--extensions/BMO/lib/Reports/Triage.pm516
-rw-r--r--extensions/BMO/lib/Reports/UserActivity.pm382
-rw-r--r--extensions/BMO/lib/Util.pm111
-rw-r--r--extensions/BMO/lib/WebService.pm68
-rw-r--r--extensions/BMO/t/bounty_attachment.t93
-rw-r--r--extensions/Bitly/Config.pm2
-rw-r--r--extensions/Bitly/Extension.pm13
-rw-r--r--extensions/Bitly/lib/WebService.pm190
-rw-r--r--extensions/BugModal/Config.pm6
-rw-r--r--extensions/BugModal/Extension.pm551
-rw-r--r--extensions/BugModal/lib/ActivityStream.pm567
-rw-r--r--extensions/BugModal/lib/MonkeyPatches.pm21
-rw-r--r--extensions/BugModal/lib/Util.pm28
-rw-r--r--extensions/BugModal/lib/WebService.pm618
-rw-r--r--extensions/BugmailFilter/Config.pm2
-rw-r--r--extensions/BugmailFilter/Extension.pm807
-rw-r--r--extensions/BugmailFilter/lib/Constants.pm120
-rw-r--r--extensions/BugmailFilter/lib/FakeField.pm50
-rw-r--r--extensions/BugmailFilter/lib/Filter.pm217
-rw-r--r--extensions/BzAPI/Extension.pm339
-rwxr-xr-xextensions/BzAPI/bin/rest.cgi16
-rw-r--r--extensions/BzAPI/lib/Constants.pm230
-rw-r--r--extensions/BzAPI/lib/Resources/Bug.pm1385
-rw-r--r--extensions/BzAPI/lib/Resources/Bugzilla.pm208
-rw-r--r--extensions/BzAPI/lib/Resources/User.pm75
-rw-r--r--extensions/BzAPI/lib/Util.pm652
-rw-r--r--extensions/ComponentWatching/Extension.pm891
-rw-r--r--extensions/ComponentWatching/lib/WebService.pm128
-rw-r--r--extensions/ContributorEngagement/Config.pm6
-rw-r--r--extensions/ContributorEngagement/Extension.pm147
-rw-r--r--extensions/ContributorEngagement/lib/Constants.pm15
-rw-r--r--extensions/EditComments/Config.pm2
-rw-r--r--extensions/EditComments/Extension.pm355
-rw-r--r--extensions/EditComments/lib/WebService.pm89
-rw-r--r--extensions/EditTable/Config.pm2
-rw-r--r--extensions/EditTable/Extension.pm227
-rw-r--r--extensions/Ember/Extension.pm6
-rw-r--r--extensions/Ember/lib/FakeBug.pm92
-rw-r--r--extensions/Ember/lib/WebService.pm1229
-rw-r--r--extensions/Example/Config.pm21
-rw-r--r--extensions/Example/Extension.pm1344
-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/FlagDefaultRequestee/Extension.pm198
-rw-r--r--extensions/FlagDefaultRequestee/lib/Constants.pm8
-rw-r--r--extensions/FlagTypeComment/Extension.pm264
-rw-r--r--extensions/FlagTypeComment/lib/Constants.pm32
-rw-r--r--extensions/GitHubAuth/Extension.pm88
-rw-r--r--extensions/GitHubAuth/lib/Client.pm134
-rw-r--r--extensions/GitHubAuth/lib/Client/Error.pm42
-rw-r--r--extensions/GitHubAuth/lib/Config.pm24
-rw-r--r--extensions/GitHubAuth/lib/Login.pm329
-rw-r--r--extensions/GitHubAuth/lib/Verify.pm6
-rw-r--r--extensions/GoogleAnalytics/Extension.pm6
-rw-r--r--extensions/GoogleAnalytics/lib/Config.pm41
-rw-r--r--extensions/Gravatar/Config.pm2
-rw-r--r--extensions/Gravatar/Extension.pm52
-rw-r--r--extensions/Gravatar/lib/Data.pm6
-rw-r--r--extensions/GuidedBugEntry/Config.pm6
-rw-r--r--extensions/GuidedBugEntry/Extension.pm182
-rw-r--r--extensions/InlineHistory/Extension.pm384
-rw-r--r--extensions/LastResolved/Config.pm6
-rw-r--r--extensions/LastResolved/Extension.pm130
-rw-r--r--extensions/LimitedEmail/Config.pm14
-rw-r--r--extensions/LimitedEmail/Extension.pm62
-rw-r--r--extensions/MozProjectReview/Config.pm6
-rw-r--r--extensions/MozProjectReview/Extension.pm254
-rw-r--r--extensions/MyDashboard/Extension.pm317
-rw-r--r--extensions/MyDashboard/lib/BugInterest.pm54
-rw-r--r--extensions/MyDashboard/lib/Queries.pm566
-rw-r--r--extensions/MyDashboard/lib/Util.pm32
-rw-r--r--extensions/MyDashboard/lib/WebService.pm220
-rw-r--r--extensions/Needinfo/Config.pm6
-rw-r--r--extensions/Needinfo/Extension.pm435
-rw-r--r--extensions/OldBugMove/Extension.pm248
-rw-r--r--extensions/OldBugMove/lib/Params.pm22
-rw-r--r--extensions/OpenGraph/Config.pm6
-rw-r--r--extensions/OrangeFactor/Extension.pm59
-rw-r--r--extensions/PhabBugz/Extension.pm81
-rwxr-xr-xextensions/PhabBugz/bin/phabbugz_feed.pl4
-rw-r--r--extensions/PhabBugz/lib/Config.pm76
-rw-r--r--extensions/PhabBugz/lib/Constants.pm14
-rw-r--r--extensions/PhabBugz/lib/Daemon.pm89
-rw-r--r--extensions/PhabBugz/lib/Feed.pm1206
-rw-r--r--extensions/PhabBugz/lib/Policy.pm133
-rw-r--r--extensions/PhabBugz/lib/Project.pm355
-rw-r--r--extensions/PhabBugz/lib/Revision.pm544
-rw-r--r--extensions/PhabBugz/lib/Types.pm17
-rw-r--r--extensions/PhabBugz/lib/User.pm202
-rw-r--r--extensions/PhabBugz/lib/Util.pm251
-rw-r--r--extensions/PhabBugz/lib/WebService.pm187
-rw-r--r--extensions/PhabBugz/t/basic.t252
-rw-r--r--extensions/PhabBugz/t/feed-daemon-guts.t191
-rw-r--r--extensions/PhabBugz/t/review-flags.t246
-rw-r--r--extensions/ProdCompSearch/Config.pm2
-rw-r--r--extensions/ProdCompSearch/Extension.pm6
-rw-r--r--extensions/ProdCompSearch/lib/WebService.pm237
-rw-r--r--extensions/Profanivore/Config.pm12
-rw-r--r--extensions/Profanivore/Extension.pm247
-rw-r--r--extensions/Push/Config.pm39
-rw-r--r--extensions/Push/Extension.pm876
-rwxr-xr-xextensions/Push/bin/bugzilla-pushd.pl4
-rwxr-xr-xextensions/Push/bin/nagios_push_checker.pl33
-rw-r--r--extensions/Push/lib/Admin.pm171
-rw-r--r--extensions/Push/lib/BacklogMessage.pm135
-rw-r--r--extensions/Push/lib/BacklogQueue.pm138
-rw-r--r--extensions/Push/lib/Backoff.pm82
-rw-r--r--extensions/Push/lib/Config.pm298
-rw-r--r--extensions/Push/lib/Connector.disabled/AMQP.pm344
-rw-r--r--extensions/Push/lib/Connector.disabled/ServiceNow.pm690
-rw-r--r--extensions/Push/lib/Connector/Base.pm99
-rw-r--r--extensions/Push/lib/Connector/File.pm61
-rw-r--r--extensions/Push/lib/Connector/Phabricator.pm153
-rw-r--r--extensions/Push/lib/Connector/Spark.pm228
-rw-r--r--extensions/Push/lib/Connectors.pm129
-rw-r--r--extensions/Push/lib/Constants.pm28
-rw-r--r--extensions/Push/lib/Daemon.pm87
-rw-r--r--extensions/Push/lib/Log.pm32
-rw-r--r--extensions/Push/lib/LogEntry.pm42
-rw-r--r--extensions/Push/lib/Logger.pm50
-rw-r--r--extensions/Push/lib/Message.pm72
-rw-r--r--extensions/Push/lib/Option.pm32
-rw-r--r--extensions/Push/lib/Push.pm434
-rw-r--r--extensions/Push/lib/Queue.pm71
-rw-r--r--extensions/Push/lib/Serialise.pm427
-rw-r--r--extensions/Push/lib/Util.pm165
-rw-r--r--extensions/Push/template/en/default/setup/strings.txt.pl4
-rw-r--r--extensions/REMO/Config.pm6
-rw-r--r--extensions/REMO/Extension.pm547
-rw-r--r--extensions/RequestNagger/Config.pm6
-rw-r--r--extensions/RequestNagger/Extension.pm552
-rwxr-xr-xextensions/RequestNagger/bin/send-request-nags.pl481
-rw-r--r--extensions/RequestNagger/lib/Bug.pm34
-rw-r--r--extensions/RequestNagger/lib/Constants.pm69
-rw-r--r--extensions/RequestNagger/lib/Settings.pm75
-rw-r--r--extensions/RestrictComments/Config.pm2
-rw-r--r--extensions/RestrictComments/Extension.pm98
-rw-r--r--extensions/RestrictComments/lib/Config.pm40
-rw-r--r--extensions/Review/Config.pm2
-rw-r--r--extensions/Review/Extension.pm1651
-rwxr-xr-xextensions/Review/bin/migrate_mentor_from_whiteboard.pl268
-rwxr-xr-xextensions/Review/bin/review_requests_rebuild.pl6
-rw-r--r--extensions/Review/lib/FlagStateActivity.pm110
-rw-r--r--extensions/Review/lib/Util.pm71
-rw-r--r--extensions/Review/lib/WebService.pm468
-rw-r--r--extensions/SecureMail/Config.pm32
-rw-r--r--extensions/SecureMail/Extension.pm1046
-rw-r--r--extensions/SecureMail/lib/TCT.pm112
-rw-r--r--extensions/ShadowBugs/Config.pm2
-rw-r--r--extensions/ShadowBugs/Extension.pm114
-rw-r--r--extensions/SiteMapIndex/Config.pm10
-rw-r--r--extensions/SiteMapIndex/Extension.pm116
-rw-r--r--extensions/SiteMapIndex/lib/Constants.pm8
-rw-r--r--extensions/SiteMapIndex/lib/Util.pm207
-rw-r--r--extensions/Splinter/Extension.pm217
-rw-r--r--extensions/Splinter/lib/Config.pm14
-rw-r--r--extensions/Splinter/lib/Util.pm208
-rw-r--r--extensions/TagNewUsers/Config.pm6
-rw-r--r--extensions/TagNewUsers/Extension.pm346
-rw-r--r--extensions/TrackingFlags/Config.pm12
-rw-r--r--extensions/TrackingFlags/Extension.pm1224
-rwxr-xr-xextensions/TrackingFlags/bin/bug_825946.pl25
-rwxr-xr-xextensions/TrackingFlags/bin/bulk_flag_clear.pl80
-rwxr-xr-xextensions/TrackingFlags/bin/migrate_tracking_flags.pl407
-rw-r--r--extensions/TrackingFlags/lib/Admin.pm721
-rw-r--r--extensions/TrackingFlags/lib/Constants.pm44
-rw-r--r--extensions/TrackingFlags/lib/Flag.pm632
-rw-r--r--extensions/TrackingFlags/lib/Flag/Bug.pm168
-rw-r--r--extensions/TrackingFlags/lib/Flag/Value.pm129
-rw-r--r--extensions/TrackingFlags/lib/Flag/Visibility.pm195
-rw-r--r--extensions/TypeSniffer/Config.pm14
-rw-r--r--extensions/TypeSniffer/Extension.pm103
-rw-r--r--extensions/UserProfile/Config.pm6
-rw-r--r--extensions/UserProfile/Extension.pm796
-rwxr-xr-xextensions/UserProfile/bin/migrate.pl14
-rwxr-xr-xextensions/UserProfile/bin/update.pl62
-rw-r--r--extensions/UserProfile/lib/Util.pm365
-rw-r--r--extensions/UserStory/Config.pm9
-rw-r--r--extensions/UserStory/Extension.pm110
-rw-r--r--extensions/UserStory/lib/Constants.pm2
-rw-r--r--extensions/Voting/Config.pm6
-rw-r--r--extensions/Voting/Extension.pm1367
-rw-r--r--extensions/ZPushNotify/Config.pm2
-rw-r--r--extensions/ZPushNotify/Extension.pm183
-rwxr-xr-xextensions/create.pl38
205 files changed, 22908 insertions, 23636 deletions
diff --git a/extensions/AntiSpam/Config.pm b/extensions/AntiSpam/Config.pm
index e16add9b7..18cd3efa2 100644
--- a/extensions/AntiSpam/Config.pm
+++ b/extensions/AntiSpam/Config.pm
@@ -12,13 +12,8 @@ use strict;
use warnings;
use constant NAME => 'AntiSpam';
-use constant REQUIRED_MODULES => [
- {
- package => 'Email-Address',
- module => 'Email::Address',
- version => 0,
- },
-];
+use constant REQUIRED_MODULES =>
+ [{package => 'Email-Address', module => 'Email::Address', version => 0,},];
use constant OPTIONAL_MODULES => [];
__PACKAGE__->NAME;
diff --git a/extensions/AntiSpam/Extension.pm b/extensions/AntiSpam/Extension.pm
index 19eddb4e7..990130c8e 100644
--- a/extensions/AntiSpam/Extension.pm
+++ b/extensions/AntiSpam/Extension.pm
@@ -26,34 +26,29 @@ our $VERSION = '1';
#
sub _project_honeypot_blocking {
- my ($self, $api_key, $login) = @_;
- my $ip = remote_ip();
- return unless $ip =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
- my $lookup = "$api_key.$4.$3.$2.$1.dnsbl.httpbl.org";
- return unless my $packed = gethostbyname($lookup);
- my $honeypot = inet_ntoa($packed);
- return unless $honeypot =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
- my ($status, $days, $threat, $type) = ($1, $2, $3, $4);
-
- return if $status != 127
- || $threat < Bugzilla->params->{honeypot_threat_threshold};
-
- Bugzilla->audit(sprintf("blocked <%s> from creating %s, honeypot %s", $ip, $login, $honeypot));
- ThrowUserError('account_creation_restricted');
+ my ($self, $api_key, $login) = @_;
+ my $ip = remote_ip();
+ return unless $ip =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
+ my $lookup = "$api_key.$4.$3.$2.$1.dnsbl.httpbl.org";
+ return unless my $packed = gethostbyname($lookup);
+ my $honeypot = inet_ntoa($packed);
+ return unless $honeypot =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
+ my ($status, $days, $threat, $type) = ($1, $2, $3, $4);
+
+ return
+ if $status != 127 || $threat < Bugzilla->params->{honeypot_threat_threshold};
+
+ Bugzilla->audit(
+ sprintf("blocked <%s> from creating %s, honeypot %s", $ip, $login, $honeypot));
+ ThrowUserError('account_creation_restricted');
}
sub config_modify_panels {
- my ($self, $args) = @_;
- push @{ $args->{panels}->{auth}->{params} }, {
- name => 'honeypot_api_key',
- type => 't',
- default => '',
- };
- push @{ $args->{panels}->{auth}->{params} }, {
- name => 'honeypot_threat_threshold',
- type => 't',
- default => '32',
- };
+ my ($self, $args) = @_;
+ push @{$args->{panels}->{auth}->{params}},
+ {name => 'honeypot_api_key', type => 't', default => '',};
+ push @{$args->{panels}->{auth}->{params}},
+ {name => 'honeypot_threat_threshold', type => 't', default => '32',};
}
#
@@ -61,20 +56,22 @@ sub config_modify_panels {
#
sub _comment_blocking {
- my ($self, $params) = @_;
- my $user = Bugzilla->user;
- return if $user->in_group('editbugs');
-
- my $blocklist = Bugzilla->dbh->selectcol_arrayref(
- 'SELECT word FROM antispam_comment_blocklist'
- );
- return unless @$blocklist;
-
- my $regex = '\b(?:' . join('|', map { quotemeta } @$blocklist) . ')\b';
- if ($params->{thetext} =~ /$regex/i) {
- Bugzilla->audit(sprintf("blocked <%s> %s from commenting, blacklisted phrase", remote_ip(), $user->login));
- ThrowUserError('antispam_comment_blocked');
- }
+ my ($self, $params) = @_;
+ my $user = Bugzilla->user;
+ return if $user->in_group('editbugs');
+
+ my $blocklist = Bugzilla->dbh->selectcol_arrayref(
+ 'SELECT word FROM antispam_comment_blocklist');
+ return unless @$blocklist;
+
+ my $regex = '\b(?:' . join('|', map {quotemeta} @$blocklist) . ')\b';
+ if ($params->{thetext} =~ /$regex/i) {
+ Bugzilla->audit(sprintf(
+ "blocked <%s> %s from commenting, blacklisted phrase",
+ remote_ip(), $user->login
+ ));
+ ThrowUserError('antispam_comment_blocked');
+ }
}
#
@@ -82,17 +79,19 @@ sub _comment_blocking {
#
sub _domain_blocking {
- my ($self, $login) = @_;
- my $address = Email::Address->new(undef, $login);
- my $blocked = Bugzilla->dbh->selectrow_array(
- "SELECT 1 FROM antispam_domain_blocklist WHERE domain=?",
- undef,
- $address->host
- );
- if ($blocked) {
- Bugzilla->audit(sprintf("blocked <%s> from creating %s, blacklisted domain", remote_ip(), $login));
- ThrowUserError('account_creation_restricted');
- }
+ my ($self, $login) = @_;
+ my $address = Email::Address->new(undef, $login);
+ my $blocked
+ = Bugzilla->dbh->selectrow_array(
+ "SELECT 1 FROM antispam_domain_blocklist WHERE domain=?",
+ undef, $address->host);
+ if ($blocked) {
+ Bugzilla->audit(sprintf(
+ "blocked <%s> from creating %s, blacklisted domain",
+ remote_ip(), $login
+ ));
+ ThrowUserError('account_creation_restricted');
+ }
}
#
@@ -100,18 +99,18 @@ sub _domain_blocking {
#
sub _ip_blocking {
- my ($self, $login) = @_;
- my $ip = remote_ip();
- trick_taint($ip);
- my $blocked = Bugzilla->dbh->selectrow_array(
- "SELECT 1 FROM antispam_ip_blocklist WHERE ip_address=?",
- undef,
- $ip
- );
- if ($blocked) {
- Bugzilla->audit(sprintf("blocked <%s> from creating %s, blacklisted IP", $ip, $login));
- ThrowUserError('account_creation_restricted');
- }
+ my ($self, $login) = @_;
+ my $ip = remote_ip();
+ trick_taint($ip);
+ my $blocked
+ = Bugzilla->dbh->selectrow_array(
+ "SELECT 1 FROM antispam_ip_blocklist WHERE ip_address=?",
+ undef, $ip);
+ if ($blocked) {
+ Bugzilla->audit(
+ sprintf("blocked <%s> from creating %s, blacklisted IP", $ip, $login));
+ ThrowUserError('account_creation_restricted');
+ }
}
#
@@ -119,44 +118,50 @@ sub _ip_blocking {
#
sub _is_limited_user {
- return Bugzilla->user->creation_age < Bugzilla->params->{antispam_multi_user_limit_age};
+ return Bugzilla->user->creation_age
+ < Bugzilla->params->{antispam_multi_user_limit_age};
}
sub bug_before_create {
- my ($self, $args) = @_;
- $self->_cc_limit($args->{params}, 'cc');
+ my ($self, $args) = @_;
+ $self->_cc_limit($args->{params}, 'cc');
}
sub bug_start_of_set_all {
- my ($self, $args) = @_;
- $self->_cc_limit($args->{params}, 'newcc');
+ my ($self, $args) = @_;
+ $self->_cc_limit($args->{params}, 'newcc');
}
sub _cc_limit {
- my ($self, $params, $cc_field) = @_;
- return unless _is_limited_user();
- return unless exists $params->{$cc_field};
-
- my $cc_count = ref($params->{$cc_field}) ? scalar(@{ $params->{$cc_field} }) : 1;
- if ($cc_count > Bugzilla->params->{antispam_multi_user_limit_count}) {
- Bugzilla->audit(sprintf("blocked <%s> from CC'ing %s users", Bugzilla->user->login, $cc_count));
- delete $params->{$cc_field};
- if (exists $params->{cc} && exists $params->{cc}->{add}) {
- delete $params->{cc}->{add};
- }
+ my ($self, $params, $cc_field) = @_;
+ return unless _is_limited_user();
+ return unless exists $params->{$cc_field};
+
+ my $cc_count = ref($params->{$cc_field}) ? scalar(@{$params->{$cc_field}}) : 1;
+ if ($cc_count > Bugzilla->params->{antispam_multi_user_limit_count}) {
+ Bugzilla->audit(
+ sprintf("blocked <%s> from CC'ing %s users", Bugzilla->user->login, $cc_count));
+ delete $params->{$cc_field};
+ if (exists $params->{cc} && exists $params->{cc}->{add}) {
+ delete $params->{cc}->{add};
}
+ }
}
sub bug_set_flags {
- my ($self, $args) = @_;
- return unless _is_limited_user();
-
- my $flag_count = @{ $args->{new_flags} };
- if ($flag_count > Bugzilla->params->{antispam_multi_user_limit_count}) {
- Bugzilla->audit(sprintf("blocked <%s> from flaging %s users", Bugzilla->user->login, $flag_count));
- # empty the arrayref
- $#{ $args->{new_flags} } = -1;
- }
+ my ($self, $args) = @_;
+ return unless _is_limited_user();
+
+ my $flag_count = @{$args->{new_flags}};
+ if ($flag_count > Bugzilla->params->{antispam_multi_user_limit_count}) {
+ Bugzilla->audit(sprintf(
+ "blocked <%s> from flaging %s users",
+ Bugzilla->user->login, $flag_count
+ ));
+
+ # empty the arrayref
+ $#{$args->{new_flags}} = -1;
+ }
}
#
@@ -164,30 +169,31 @@ sub bug_set_flags {
#
sub comment_after_add_tag {
- my ($self, $args) = @_;
- my $tag = lc($args->{tag});
- return unless $tag eq 'spam' or $tag eq 'abusive' or $tag eq 'abuse';
- my $comment = $args->{comment};
- my $author = $comment->author;
-
- # exclude disabled users
- return if !$author->is_enabled;
-
- # exclude users by group
- return if $author->in_group(Bugzilla->params->{antispam_spammer_exclude_group});
-
- # exclude users who are no longer new
- return if !$author->is_new;
-
- # exclude users who haven't made enough comments
- my $count = $tag eq 'spam'
- ? Bugzilla->params->{antispam_spammer_comment_count}
- : Bugzilla->params->{antispam_abusive_comment_count};
- return if $author->comment_count < $count;
-
- # get user's comments
- trick_taint($tag);
- my $comments = Bugzilla->dbh->selectall_arrayref("
+ my ($self, $args) = @_;
+ my $tag = lc($args->{tag});
+ return unless $tag eq 'spam' or $tag eq 'abusive' or $tag eq 'abuse';
+ my $comment = $args->{comment};
+ my $author = $comment->author;
+
+ # exclude disabled users
+ return if !$author->is_enabled;
+
+ # exclude users by group
+ return if $author->in_group(Bugzilla->params->{antispam_spammer_exclude_group});
+
+ # exclude users who are no longer new
+ return if !$author->is_new;
+
+ # exclude users who haven't made enough comments
+ my $count
+ = $tag eq 'spam'
+ ? Bugzilla->params->{antispam_spammer_comment_count}
+ : Bugzilla->params->{antispam_abusive_comment_count};
+ return if $author->comment_count < $count;
+
+ # get user's comments
+ trick_taint($tag);
+ my $comments = Bugzilla->dbh->selectall_arrayref("
SELECT longdescs.comment_id,longdescs_tags.id
FROM longdescs
LEFT JOIN longdescs_tags
@@ -197,41 +203,39 @@ sub comment_after_add_tag {
ORDER BY longdescs.bug_when
", undef, $tag, $author->id);
- # this comment needs to be counted too
- my $comment_id = $comment->id;
- foreach my $ra (@$comments) {
- if ($ra->[0] == $comment_id) {
- $ra->[1] = 1;
- last;
- }
- }
-
- # throw away comment id and negate bool to make it a list of not-spam/abuse
- $comments = [ map { $_->[1] ? 0 : 1 } @$comments ];
-
- my $reason;
-
- # check if the first N comments are spam/abuse
- if (!scalar(grep { $_ } @$comments[0..($count - 1)])) {
- $reason = "first $count comments are $tag";
- }
-
- # check if the last N comments are spam/abuse
- elsif (!scalar(grep { $_ } @$comments[-$count..-1])) {
- $reason = "last $count comments are $tag";
- }
-
- # disable
- if ($reason) {
- $author->set_disabledtext(
- $tag eq 'spam'
- ? Bugzilla->params->{antispam_spammer_disable_text}
- : Bugzilla->params->{antispam_abusive_disable_text}
- );
- $author->set_disable_mail(1);
- $author->update();
- Bugzilla->audit(sprintf("antispam disabled <%s>: %s", $author->login, $reason));
+ # this comment needs to be counted too
+ my $comment_id = $comment->id;
+ foreach my $ra (@$comments) {
+ if ($ra->[0] == $comment_id) {
+ $ra->[1] = 1;
+ last;
}
+ }
+
+ # throw away comment id and negate bool to make it a list of not-spam/abuse
+ $comments = [map { $_->[1] ? 0 : 1 } @$comments];
+
+ my $reason;
+
+ # check if the first N comments are spam/abuse
+ if (!scalar(grep {$_} @$comments[0 .. ($count - 1)])) {
+ $reason = "first $count comments are $tag";
+ }
+
+ # check if the last N comments are spam/abuse
+ elsif (!scalar(grep {$_} @$comments[-$count .. -1])) {
+ $reason = "last $count comments are $tag";
+ }
+
+ # disable
+ if ($reason) {
+ $author->set_disabledtext($tag eq 'spam'
+ ? Bugzilla->params->{antispam_spammer_disable_text}
+ : Bugzilla->params->{antispam_abusive_disable_text});
+ $author->set_disable_mail(1);
+ $author->update();
+ Bugzilla->audit(sprintf("antispam disabled <%s>: %s", $author->login, $reason));
+ }
}
#
@@ -239,51 +243,54 @@ sub comment_after_add_tag {
#
sub object_end_of_create_validators {
- my ($self, $args) = @_;
- if ($args->{class} eq 'Bugzilla::Comment') {
- $self->_comment_blocking($args->{params});
- }
+ my ($self, $args) = @_;
+ if ($args->{class} eq 'Bugzilla::Comment') {
+ $self->_comment_blocking($args->{params});
+ }
}
sub user_verify_login {
- my ($self, $args) = @_;
- if (my $api_key = Bugzilla->params->{honeypot_api_key}) {
- $self->_project_honeypot_blocking($api_key, $args->{login});
- }
- $self->_ip_blocking($args->{login});
- $self->_domain_blocking($args->{login});
+ my ($self, $args) = @_;
+ if (my $api_key = Bugzilla->params->{honeypot_api_key}) {
+ $self->_project_honeypot_blocking($api_key, $args->{login});
+ }
+ $self->_ip_blocking($args->{login});
+ $self->_domain_blocking($args->{login});
}
sub editable_tables {
- my ($self, $args) = @_;
- my $tables = $args->{tables};
- # allow these tables to be edited with the EditTables extension
- $tables->{antispam_domain_blocklist} = {
- id_field => 'id',
- order_by => 'domain',
- blurb => 'List of fully qualified domain names to block at account creation time.',
- group => 'can_configure_antispam',
- };
- $tables->{antispam_comment_blocklist} = {
- id_field => 'id',
- order_by => 'word',
- blurb => "List of whole words that will cause comments containing \\b\$word\\b to be blocked.\n" .
- "This only applies to comments on bugs which the user didn't report.\n" .
- "Users in the editbugs group are exempt from comment blocking.",
- group => 'can_configure_antispam',
- };
- $tables->{antispam_ip_blocklist} = {
- id_field => 'id',
- order_by => 'ip_address',
- blurb => 'List of IPv4 addresses which are prevented from creating accounts.',
- group => 'can_configure_antispam',
- };
+ my ($self, $args) = @_;
+ my $tables = $args->{tables};
+
+ # allow these tables to be edited with the EditTables extension
+ $tables->{antispam_domain_blocklist} = {
+ id_field => 'id',
+ order_by => 'domain',
+ blurb =>
+ 'List of fully qualified domain names to block at account creation time.',
+ group => 'can_configure_antispam',
+ };
+ $tables->{antispam_comment_blocklist} = {
+ id_field => 'id',
+ order_by => 'word',
+ blurb =>
+ "List of whole words that will cause comments containing \\b\$word\\b to be blocked.\n"
+ . "This only applies to comments on bugs which the user didn't report.\n"
+ . "Users in the editbugs group are exempt from comment blocking.",
+ group => 'can_configure_antispam',
+ };
+ $tables->{antispam_ip_blocklist} = {
+ id_field => 'id',
+ order_by => 'ip_address',
+ blurb => 'List of IPv4 addresses which are prevented from creating accounts.',
+ group => 'can_configure_antispam',
+ };
}
sub config_add_panels {
- my ($self, $args) = @_;
- my $modules = $args->{panel_modules};
- $modules->{AntiSpam} = "Bugzilla::Extension::AntiSpam::Config";
+ my ($self, $args) = @_;
+ my $modules = $args->{panel_modules};
+ $modules->{AntiSpam} = "Bugzilla::Extension::AntiSpam::Config";
}
#
@@ -291,82 +298,43 @@ sub config_add_panels {
#
sub install_before_final_checks {
- if (!Bugzilla::Group->new({ name => 'can_configure_antispam' })) {
- Bugzilla::Group->create({
- name => 'can_configure_antispam',
- description => 'Can configure Anti-Spam measures',
- isbuggroup => 0,
- });
- }
+ if (!Bugzilla::Group->new({name => 'can_configure_antispam'})) {
+ Bugzilla::Group->create({
+ name => 'can_configure_antispam',
+ description => 'Can configure Anti-Spam measures',
+ isbuggroup => 0,
+ });
+ }
}
sub db_schema_abstract_schema {
- my ($self, $args) = @_;
- $args->{'schema'}->{'antispam_domain_blocklist'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- domain => {
- TYPE => 'VARCHAR(255)',
- NOTNULL => 1,
- },
- comment => {
- TYPE => 'VARCHAR(255)',
- NOTNULL => 1,
- },
- ],
- INDEXES => [
- antispam_domain_blocklist_idx => {
- FIELDS => [ 'domain' ],
- TYPE => 'UNIQUE',
- },
- ],
- };
- $args->{'schema'}->{'antispam_comment_blocklist'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- word => {
- TYPE => 'VARCHAR(255)',
- NOTNULL => 1,
- },
- ],
- INDEXES => [
- antispam_comment_blocklist_idx => {
- FIELDS => [ 'word' ],
- TYPE => 'UNIQUE',
- },
- ],
- };
- $args->{'schema'}->{'antispam_ip_blocklist'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- ip_address => {
- TYPE => 'VARCHAR(15)',
- NOTNULL => 1,
- },
- comment => {
- TYPE => 'VARCHAR(255)',
- NOTNULL => 1,
- },
- ],
- INDEXES => [
- antispam_ip_blocklist_idx => {
- FIELDS => [ 'ip_address' ],
- TYPE => 'UNIQUE',
- },
- ],
- };
+ my ($self, $args) = @_;
+ $args->{'schema'}->{'antispam_domain_blocklist'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ domain => {TYPE => 'VARCHAR(255)', NOTNULL => 1,},
+ comment => {TYPE => 'VARCHAR(255)', NOTNULL => 1,},
+ ],
+ INDEXES =>
+ [antispam_domain_blocklist_idx => {FIELDS => ['domain'], TYPE => 'UNIQUE',},],
+ };
+ $args->{'schema'}->{'antispam_comment_blocklist'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ word => {TYPE => 'VARCHAR(255)', NOTNULL => 1,},
+ ],
+ INDEXES =>
+ [antispam_comment_blocklist_idx => {FIELDS => ['word'], TYPE => 'UNIQUE',},],
+ };
+ $args->{'schema'}->{'antispam_ip_blocklist'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ ip_address => {TYPE => 'VARCHAR(15)', NOTNULL => 1,},
+ comment => {TYPE => 'VARCHAR(255)', NOTNULL => 1,},
+ ],
+ INDEXES =>
+ [antispam_ip_blocklist_idx => {FIELDS => ['ip_address'], TYPE => 'UNIQUE',},],
+ };
}
__PACKAGE__->NAME;
diff --git a/extensions/AntiSpam/lib/Config.pm b/extensions/AntiSpam/lib/Config.pm
index 278baea8f..3cddb77ed 100644
--- a/extensions/AntiSpam/lib/Config.pm
+++ b/extensions/AntiSpam/lib/Config.pm
@@ -17,66 +17,66 @@ use Bugzilla::Group;
our $sortkey = 511;
sub get_param_list {
- my ($class) = @_;
+ my ($class) = @_;
- my @param_list = (
- {
- name => 'antispam_spammer_exclude_group',
- type => 's',
- choices => \&get_all_group_names,
- default => 'canconfirm',
- checker => \&check_group
- },
- {
- name => 'antispam_spammer_comment_count',
- type => 't',
- default => '3',
- checker => \&check_numeric
- },
- {
- name => 'antispam_spammer_disable_text',
- type => 'l',
- default =>
- "This account has been automatically disabled as a result of " .
- "a high number of spam comments.<br>\n<br>\n" .
- "Please contact the address at the end of this message if " .
- "you believe this to be an error."
- },
- {
- name => 'antispam_abusive_comment_count',
- type => 't',
- default => '5',
- checker => \&check_numeric
- },
- {
- name => 'antispam_abusive_disable_text',
- type => 'l',
- default =>
- "This account has been automatically disabled as a result of " .
- "a high number of comments tagged as abusive.<br>\n<br>\n" .
- "All interactions on Bugzilla should follow our " .
- "<a href=\"" . Bugzilla->localconfig->{'urlbase'} . "page.cgi?id=etiquette.html\">" .
- "etiquette guidelines</a>.<br>\n<br>\n" .
- "Please contact the address at the end of this message if you " .
- "believe this to be an error, or if you would like your account " .
- "reactivated in order to interact within our etiquette " .
- "guidelines."
- },
- {
- name => 'antispam_multi_user_limit_age',
- type => 't',
- default => '2',
- checker => \&check_numeric,
- },
- {
- name => 'antispam_multi_user_limit_count',
- type => 't',
- default => '5',
- checker => \&check_numeric,
- },
- );
+ my @param_list = (
+ {
+ name => 'antispam_spammer_exclude_group',
+ type => 's',
+ choices => \&get_all_group_names,
+ default => 'canconfirm',
+ checker => \&check_group
+ },
+ {
+ name => 'antispam_spammer_comment_count',
+ type => 't',
+ default => '3',
+ checker => \&check_numeric
+ },
+ {
+ name => 'antispam_spammer_disable_text',
+ type => 'l',
+ default => "This account has been automatically disabled as a result of "
+ . "a high number of spam comments.<br>\n<br>\n"
+ . "Please contact the address at the end of this message if "
+ . "you believe this to be an error."
+ },
+ {
+ name => 'antispam_abusive_comment_count',
+ type => 't',
+ default => '5',
+ checker => \&check_numeric
+ },
+ {
+ name => 'antispam_abusive_disable_text',
+ type => 'l',
+ default => "This account has been automatically disabled as a result of "
+ . "a high number of comments tagged as abusive.<br>\n<br>\n"
+ . "All interactions on Bugzilla should follow our "
+ . "<a href=\""
+ . Bugzilla->localconfig->{'urlbase'}
+ . "page.cgi?id=etiquette.html\">"
+ . "etiquette guidelines</a>.<br>\n<br>\n"
+ . "Please contact the address at the end of this message if you "
+ . "believe this to be an error, or if you would like your account "
+ . "reactivated in order to interact within our etiquette "
+ . "guidelines."
+ },
+ {
+ name => 'antispam_multi_user_limit_age',
+ type => 't',
+ default => '2',
+ checker => \&check_numeric,
+ },
+ {
+ name => 'antispam_multi_user_limit_count',
+ type => 't',
+ default => '5',
+ checker => \&check_numeric,
+ },
+ );
- return @param_list;
+ return @param_list;
}
1;
diff --git a/extensions/BMO/Config.pm b/extensions/BMO/Config.pm
index 153af24cb..e185b0b5d 100644
--- a/extensions/BMO/Config.pm
+++ b/extensions/BMO/Config.pm
@@ -28,24 +28,11 @@ use warnings;
use constant NAME => 'BMO';
use constant REQUIRED_MODULES => [
- {
- package => 'Tie-IxHash',
- module => 'Tie::IxHash',
- version => 0
- },
- {
- package => 'Sys-Syslog',
- module => 'Sys::Syslog',
- version => 0
- },
- {
- package => 'File-MimeInfo',
- module => 'File::MimeInfo::Magic',
- version => '0'
- },
+ {package => 'Tie-IxHash', module => 'Tie::IxHash', version => 0},
+ {package => 'Sys-Syslog', module => 'Sys::Syslog', version => 0},
+ {package => 'File-MimeInfo', module => 'File::MimeInfo::Magic', version => '0'},
];
-use constant OPTIONAL_MODULES => [
-];
+use constant OPTIONAL_MODULES => [];
__PACKAGE__->NAME;
diff --git a/extensions/BMO/Extension.pm b/extensions/BMO/Extension.pm
index 7499f0d1c..f4fb6fa32 100644
--- a/extensions/BMO/Extension.pm
+++ b/extensions/BMO/Extension.pm
@@ -72,296 +72,304 @@ our $VERSION = '0.1';
#
BEGIN {
- *Bugzilla::Bug::last_closed_date = \&_last_closed_date;
- *Bugzilla::Bug::reporters_hw_os = \&_bug_reporters_hw_os;
- *Bugzilla::Bug::is_unassigned = \&_bug_is_unassigned;
- *Bugzilla::Bug::has_current_patch = \&_bug_has_current_patch;
- *Bugzilla::Bug::missing_sec_approval = \&_bug_missing_sec_approval;
- *Bugzilla::Product::default_security_group = \&_default_security_group;
- *Bugzilla::Product::default_security_group_obj = \&_default_security_group_obj;
- *Bugzilla::Product::group_always_settable = \&_group_always_settable;
- *Bugzilla::Product::default_platform_id = \&_product_default_platform_id;
- *Bugzilla::Product::default_op_sys_id = \&_product_default_op_sys_id;
- *Bugzilla::Product::default_platform = \&_product_default_platform;
- *Bugzilla::Product::default_op_sys = \&_product_default_op_sys;
- *Bugzilla::check_default_product_security_group = \&_check_default_product_security_group;
- *Bugzilla::Attachment::is_bounty_attachment = \&_attachment_is_bounty_attachment;
- *Bugzilla::Attachment::bounty_details = \&_attachment_bounty_details;
- *Bugzilla::Attachment::external_redirect = \&_attachment_external_redirect;
- *Bugzilla::Attachment::can_review = \&_attachment_can_review;
- *Bugzilla::Attachment::fetch_github_pr_diff = \&_attachment_fetch_github_pr_diff;
+ *Bugzilla::Bug::last_closed_date = \&_last_closed_date;
+ *Bugzilla::Bug::reporters_hw_os = \&_bug_reporters_hw_os;
+ *Bugzilla::Bug::is_unassigned = \&_bug_is_unassigned;
+ *Bugzilla::Bug::has_current_patch = \&_bug_has_current_patch;
+ *Bugzilla::Bug::missing_sec_approval = \&_bug_missing_sec_approval;
+ *Bugzilla::Product::default_security_group = \&_default_security_group;
+ *Bugzilla::Product::default_security_group_obj = \&_default_security_group_obj;
+ *Bugzilla::Product::group_always_settable = \&_group_always_settable;
+ *Bugzilla::Product::default_platform_id = \&_product_default_platform_id;
+ *Bugzilla::Product::default_op_sys_id = \&_product_default_op_sys_id;
+ *Bugzilla::Product::default_platform = \&_product_default_platform;
+ *Bugzilla::Product::default_op_sys = \&_product_default_op_sys;
+ *Bugzilla::check_default_product_security_group
+ = \&_check_default_product_security_group;
+ *Bugzilla::Attachment::is_bounty_attachment
+ = \&_attachment_is_bounty_attachment;
+ *Bugzilla::Attachment::bounty_details = \&_attachment_bounty_details;
+ *Bugzilla::Attachment::external_redirect = \&_attachment_external_redirect;
+ *Bugzilla::Attachment::can_review = \&_attachment_can_review;
+ *Bugzilla::Attachment::fetch_github_pr_diff
+ = \&_attachment_fetch_github_pr_diff;
}
sub template_before_process {
- my ($self, $args) = @_;
- my $file = $args->{'file'};
- my $vars = $args->{'vars'};
-
- $vars->{'cf_hidden_in_product'} = \&cf_hidden_in_product;
-
- if ($file =~ /^list\/list/) {
- # Purpose: enable correct sorting of list table
- # Matched to changes in list/table.html.tmpl
- my %db_order_column_name_map = (
- 'map_components.name' => 'component',
- 'map_products.name' => 'product',
- 'map_reporter.login_name' => 'reporter',
- 'map_assigned_to.login_name' => 'assigned_to',
- 'delta_ts' => 'opendate',
- 'creation_ts' => 'changeddate',
- );
-
- my @orderstrings = split(/,\s*/, $vars->{'order'});
-
- # contains field names of the columns being used to sort the table.
- my @order_columns;
- foreach my $o (@orderstrings) {
- $o =~ s/bugs.//;
- $o = $db_order_column_name_map{$o} if
- grep($_ eq $o, keys(%db_order_column_name_map));
- next if (grep($_ eq $o, @order_columns));
- push(@order_columns, $o);
- }
+ my ($self, $args) = @_;
+ my $file = $args->{'file'};
+ my $vars = $args->{'vars'};
+
+ $vars->{'cf_hidden_in_product'} = \&cf_hidden_in_product;
+
+ if ($file =~ /^list\/list/) {
+
+ # Purpose: enable correct sorting of list table
+ # Matched to changes in list/table.html.tmpl
+ my %db_order_column_name_map = (
+ 'map_components.name' => 'component',
+ 'map_products.name' => 'product',
+ 'map_reporter.login_name' => 'reporter',
+ 'map_assigned_to.login_name' => 'assigned_to',
+ 'delta_ts' => 'opendate',
+ 'creation_ts' => 'changeddate',
+ );
- $vars->{'order_columns'} = \@order_columns;
+ my @orderstrings = split(/,\s*/, $vars->{'order'});
- # fields that have a custom sortkey. (So they are correctly sorted
- # when using js)
- my @sortkey_fields = qw(bug_status resolution bug_severity priority
- rep_platform op_sys);
+ # contains field names of the columns being used to sort the table.
+ my @order_columns;
+ foreach my $o (@orderstrings) {
+ $o =~ s/bugs.//;
+ $o = $db_order_column_name_map{$o}
+ if grep($_ eq $o, keys(%db_order_column_name_map));
+ next if (grep($_ eq $o, @order_columns));
+ push(@order_columns, $o);
+ }
- my %columns_sortkey;
- foreach my $field (@sortkey_fields) {
- $columns_sortkey{$field} = _get_field_values_sort_key($field);
- }
- $columns_sortkey{'target_milestone'} = _get_field_values_sort_key('milestones');
+ $vars->{'order_columns'} = \@order_columns;
- $vars->{'columns_sortkey'} = \%columns_sortkey;
+ # fields that have a custom sortkey. (So they are correctly sorted
+ # when using js)
+ my @sortkey_fields = qw(bug_status resolution bug_severity priority
+ rep_platform op_sys);
+
+ my %columns_sortkey;
+ foreach my $field (@sortkey_fields) {
+ $columns_sortkey{$field} = _get_field_values_sort_key($field);
}
- elsif ($file =~ /^bug\/create\/create[\.-](.*)/) {
- my $format = $1;
- if (!$vars->{'cloned_bug_id'}) {
- # Allow status whiteboard values to be bookmarked
- $vars->{'status_whiteboard'} =
- Bugzilla->cgi->param('status_whiteboard') || "";
- }
+ $columns_sortkey{'target_milestone'} = _get_field_values_sort_key('milestones');
- # Purpose: for pretty product chooser
- $vars->{'format'} = Bugzilla->cgi->param('format');
+ $vars->{'columns_sortkey'} = \%columns_sortkey;
+ }
+ elsif ($file =~ /^bug\/create\/create[\.-](.*)/) {
+ my $format = $1;
+ if (!$vars->{'cloned_bug_id'}) {
- if ($format eq 'doc.html.tmpl') {
- my $versions = Bugzilla::Product->new({ name => 'Core' })->versions;
- $vars->{'versions'} = [ reverse @$versions ];
- }
- }
- elsif ($file eq 'bug/edit.html.tmpl' || $file eq 'bug_modal/edit.html.tmpl') {
- $vars->{split_cf_crash_signature} = $self->_split_crash_signature($vars);
+ # Allow status whiteboard values to be bookmarked
+ $vars->{'status_whiteboard'} = Bugzilla->cgi->param('status_whiteboard') || "";
}
+ # Purpose: for pretty product chooser
+ $vars->{'format'} = Bugzilla->cgi->param('format');
- if ($file =~ /^list\/list/ || $file =~ /^bug\/create\/create[\.-]/) {
- # hack to allow the bug entry templates to use check_can_change_field
- # to see if various field values should be available to the current user.
- $vars->{'default'} = Bugzilla::Extension::BMO::FakeBug->new($vars->{'default'} || {});
+ if ($format eq 'doc.html.tmpl') {
+ my $versions = Bugzilla::Product->new({name => 'Core'})->versions;
+ $vars->{'versions'} = [reverse @$versions];
}
+ }
+ elsif ($file eq 'bug/edit.html.tmpl' || $file eq 'bug_modal/edit.html.tmpl') {
+ $vars->{split_cf_crash_signature} = $self->_split_crash_signature($vars);
+ }
- if ($file =~ /^attachment\/diff-header\./) {
- my $attachid = $vars->{attachid} ? $vars->{attachid} : $vars->{newid};
- $vars->{attachment} = Bugzilla::Attachment->new({ id => $attachid, cache => 1 })
- if $attachid;
- }
- if ($file =~ /^admin\/products\/(create|edit)\./) {
- my $product = $vars->{product};
- my $security_groups = Bugzilla::Group->match({ isbuggroup => 1, isactive => 1 });
- if ($product) {
- # If set group is not active currently, we add it into the list
- if (!grep($_->name eq $product->default_security_group, @$security_groups)) {
- push(@$security_groups, $product->default_security_group_obj);
- @$security_groups = sort { $a->name cmp $b->name } @$security_groups;
- }
- }
- $vars->{security_groups} = $security_groups;
- }
-}
+ if ($file =~ /^list\/list/ || $file =~ /^bug\/create\/create[\.-]/) {
-sub page_before_template {
- my ($self, $args) = @_;
- my $page = $args->{'page_id'};
- my $vars = $args->{'vars'};
+ # hack to allow the bug entry templates to use check_can_change_field
+ # to see if various field values should be available to the current user.
+ $vars->{'default'}
+ = Bugzilla::Extension::BMO::FakeBug->new($vars->{'default'} || {});
+ }
- if ($page eq 'user_activity.html') {
- require Bugzilla::Extension::BMO::Reports::UserActivity;
- Bugzilla::Extension::BMO::Reports::UserActivity::report($vars);
+ if ($file =~ /^attachment\/diff-header\./) {
+ my $attachid = $vars->{attachid} ? $vars->{attachid} : $vars->{newid};
+ $vars->{attachment} = Bugzilla::Attachment->new({id => $attachid, cache => 1})
+ if $attachid;
+ }
+ if ($file =~ /^admin\/products\/(create|edit)\./) {
+ my $product = $vars->{product};
+ my $security_groups = Bugzilla::Group->match({isbuggroup => 1, isactive => 1});
+ if ($product) {
+
+ # If set group is not active currently, we add it into the list
+ if (!grep($_->name eq $product->default_security_group, @$security_groups)) {
+ push(@$security_groups, $product->default_security_group_obj);
+ @$security_groups = sort { $a->name cmp $b->name } @$security_groups;
+ }
}
- elsif ($page eq 'triage_reports.html') {
- require Bugzilla::Extension::BMO::Reports::Triage;
- Bugzilla::Extension::BMO::Reports::Triage::unconfirmed($vars);
- }
- elsif ($page eq 'triage_owners.html') {
- require Bugzilla::Extension::BMO::Reports::Triage;
- Bugzilla::Extension::BMO::Reports::Triage::owners($vars);
- }
- elsif ($page eq 'group_admins.html') {
- require Bugzilla::Extension::BMO::Reports::Groups;
- Bugzilla::Extension::BMO::Reports::Groups::admins_report($vars);
- }
- elsif ($page eq 'group_membership.html' or $page eq 'group_membership.txt') {
- require Bugzilla::Extension::BMO::Reports::Groups;
- Bugzilla::Extension::BMO::Reports::Groups::membership_report($page, $vars);
- }
- elsif ($page eq 'group_members.html' or $page eq 'group_members.json') {
- require Bugzilla::Extension::BMO::Reports::Groups;
- Bugzilla::Extension::BMO::Reports::Groups::members_report($page, $vars);
- }
- elsif ($page eq 'recruiting_dashboard.html') {
- require Bugzilla::Extension::BMO::Reports::Recruiting;
- Bugzilla::Extension::BMO::Reports::Recruiting::report($vars);
- }
- elsif ($page eq 'internship_dashboard.html') {
- require Bugzilla::Extension::BMO::Reports::Internship;
- Bugzilla::Extension::BMO::Reports::Internship::report($vars);
- }
- elsif ($page eq 'email_queue.html') {
- print Bugzilla->cgi->redirect('view_job_queue.cgi');
- }
- elsif ($page eq 'release_tracking_report.html') {
- require Bugzilla::Extension::BMO::Reports::ReleaseTracking;
- Bugzilla::Extension::BMO::Reports::ReleaseTracking::report($vars);
- }
- elsif ($page eq 'product_security_report.html') {
- require Bugzilla::Extension::BMO::Reports::ProductSecurity;
- Bugzilla::Extension::BMO::Reports::ProductSecurity::report($vars);
- }
- elsif ($page eq 'fields.html') {
- # Recently global/field-descs.none.tmpl and bug/field-help.none.tmpl
- # were changed for better performance and are now only loaded once.
- # I have not found an easy way to allow our hook template to check if
- # it is called from pages/fields.html.tmpl. So we set a value in request_cache
- # that our hook template can see.
- Bugzilla->request_cache->{'bmo_fields_page'} = 1;
- }
- elsif ($page eq 'query_database.html') {
- query_database($vars);
- }
- elsif ($page eq 'attachment_bounty_form.html') {
- bounty_attachment($vars);
- }
- elsif ($page eq 'triage_request.html') {
- triage_request($vars);
- }
+ $vars->{security_groups} = $security_groups;
+ }
+}
+
+sub page_before_template {
+ my ($self, $args) = @_;
+ my $page = $args->{'page_id'};
+ my $vars = $args->{'vars'};
+
+ if ($page eq 'user_activity.html') {
+ require Bugzilla::Extension::BMO::Reports::UserActivity;
+ Bugzilla::Extension::BMO::Reports::UserActivity::report($vars);
+
+ }
+ elsif ($page eq 'triage_reports.html') {
+ require Bugzilla::Extension::BMO::Reports::Triage;
+ Bugzilla::Extension::BMO::Reports::Triage::unconfirmed($vars);
+ }
+ elsif ($page eq 'triage_owners.html') {
+ require Bugzilla::Extension::BMO::Reports::Triage;
+ Bugzilla::Extension::BMO::Reports::Triage::owners($vars);
+ }
+ elsif ($page eq 'group_admins.html') {
+ require Bugzilla::Extension::BMO::Reports::Groups;
+ Bugzilla::Extension::BMO::Reports::Groups::admins_report($vars);
+ }
+ elsif ($page eq 'group_membership.html' or $page eq 'group_membership.txt') {
+ require Bugzilla::Extension::BMO::Reports::Groups;
+ Bugzilla::Extension::BMO::Reports::Groups::membership_report($page, $vars);
+ }
+ elsif ($page eq 'group_members.html' or $page eq 'group_members.json') {
+ require Bugzilla::Extension::BMO::Reports::Groups;
+ Bugzilla::Extension::BMO::Reports::Groups::members_report($page, $vars);
+ }
+ elsif ($page eq 'recruiting_dashboard.html') {
+ require Bugzilla::Extension::BMO::Reports::Recruiting;
+ Bugzilla::Extension::BMO::Reports::Recruiting::report($vars);
+ }
+ elsif ($page eq 'internship_dashboard.html') {
+ require Bugzilla::Extension::BMO::Reports::Internship;
+ Bugzilla::Extension::BMO::Reports::Internship::report($vars);
+ }
+ elsif ($page eq 'email_queue.html') {
+ print Bugzilla->cgi->redirect('view_job_queue.cgi');
+ }
+ elsif ($page eq 'release_tracking_report.html') {
+ require Bugzilla::Extension::BMO::Reports::ReleaseTracking;
+ Bugzilla::Extension::BMO::Reports::ReleaseTracking::report($vars);
+ }
+ elsif ($page eq 'product_security_report.html') {
+ require Bugzilla::Extension::BMO::Reports::ProductSecurity;
+ Bugzilla::Extension::BMO::Reports::ProductSecurity::report($vars);
+ }
+ elsif ($page eq 'fields.html') {
+
+ # Recently global/field-descs.none.tmpl and bug/field-help.none.tmpl
+ # were changed for better performance and are now only loaded once.
+ # I have not found an easy way to allow our hook template to check if
+ # it is called from pages/fields.html.tmpl. So we set a value in request_cache
+ # that our hook template can see.
+ Bugzilla->request_cache->{'bmo_fields_page'} = 1;
+ }
+ elsif ($page eq 'query_database.html') {
+ query_database($vars);
+ }
+ elsif ($page eq 'attachment_bounty_form.html') {
+ bounty_attachment($vars);
+ }
+ elsif ($page eq 'triage_request.html') {
+ triage_request($vars);
+ }
}
sub bounty_attachment {
- my ($vars) = @_;
-
- my $user = Bugzilla->user;
- $user->in_group('bounty-team')
- || ThrowUserError("auth_failure", { group => "bounty-team",
- action => "add",
- object => "bounty_attachments" });
-
- my $input = Bugzilla->input_params;
- my $dbh = Bugzilla->dbh;
- my $bug = Bugzilla::Bug->check({ id => $input->{bug_id}, cache => 1 });
- my $attachment = first { $_ && _attachment_is_bounty_attachment($_) } @{$bug->attachments};
- $vars->{bug} = $bug;
-
- if ($input->{submit}) {
- ThrowUserError('bounty_attachment_missing_reporter')
- unless $input->{reporter_email};
-
- check_hash_token($input->{token}, ['bounty', $bug->id]);
-
- my @fields = qw( reporter_email amount_paid reported_date fixed_date awarded_date publish );
- my %form = map { $_ => $input->{$_} } @fields;
- $form{credit} = [ grep { defined } map { $input->{"credit_$_"} } 1..3 ];
-
- $dbh->bz_start_transaction();
- if ($attachment) {
- $attachment->set(
- description => format_bounty_attachment_description(\%form)
- );
- $attachment->update;
- }
- else {
- my $attachment = Bugzilla::Attachment->create({
- bug => $bug,
- isprivate => 1,
- mimetype => 'text/plain',
- data => 'bounty',
- filename => 'bugbounty.data',
- description => format_bounty_attachment_description(\%form),
- });
- }
- $dbh->bz_commit_transaction();
+ my ($vars) = @_;
- Bugzilla::BugMail::Send($bug->id, { changer => $user });
+ my $user = Bugzilla->user;
+ $user->in_group('bounty-team')
+ || ThrowUserError("auth_failure",
+ {group => "bounty-team", action => "add", object => "bounty_attachments"});
- print Bugzilla->cgi->redirect('show_bug.cgi?id=' . $bug->id);
- exit;
- }
+ my $input = Bugzilla->input_params;
+ my $dbh = Bugzilla->dbh;
+ my $bug = Bugzilla::Bug->check({id => $input->{bug_id}, cache => 1});
+ my $attachment
+ = first { $_ && _attachment_is_bounty_attachment($_) } @{$bug->attachments};
+ $vars->{bug} = $bug;
+
+ if ($input->{submit}) {
+ ThrowUserError('bounty_attachment_missing_reporter')
+ unless $input->{reporter_email};
+
+ check_hash_token($input->{token}, ['bounty', $bug->id]);
+
+ my @fields
+ = qw( reporter_email amount_paid reported_date fixed_date awarded_date publish );
+ my %form = map { $_ => $input->{$_} } @fields;
+ $form{credit} = [grep {defined} map { $input->{"credit_$_"} } 1 .. 3];
+ $dbh->bz_start_transaction();
if ($attachment) {
- $vars->{form} = $attachment->bounty_details;
+ $attachment->set(description => format_bounty_attachment_description(\%form));
+ $attachment->update;
}
else {
- my $now = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
- $vars->{form} = {
- reporter_email => $bug->reporter->email,
- reported_date => format_time($bug->creation_ts, "%Y-%m-%d"),
- awarded_date => format_time($now, "%Y-%m-%d"),
- publish => 1
- };
- if ($bug->cf_last_resolved) {
- $vars->{form}{fixed_date} = format_time($bug->cf_last_resolved, "%Y-%m-%d"),
- }
+ my $attachment = Bugzilla::Attachment->create({
+ bug => $bug,
+ isprivate => 1,
+ mimetype => 'text/plain',
+ data => 'bounty',
+ filename => 'bugbounty.data',
+ description => format_bounty_attachment_description(\%form),
+ });
+ }
+ $dbh->bz_commit_transaction();
+
+ Bugzilla::BugMail::Send($bug->id, {changer => $user});
+
+ print Bugzilla->cgi->redirect('show_bug.cgi?id=' . $bug->id);
+ exit;
+ }
+
+ if ($attachment) {
+ $vars->{form} = $attachment->bounty_details;
+ }
+ else {
+ my $now = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
+ $vars->{form} = {
+ reporter_email => $bug->reporter->email,
+ reported_date => format_time($bug->creation_ts, "%Y-%m-%d"),
+ awarded_date => format_time($now, "%Y-%m-%d"),
+ publish => 1
+ };
+ if ($bug->cf_last_resolved) {
+ $vars->{form}{fixed_date} = format_time($bug->cf_last_resolved, "%Y-%m-%d"),;
}
- $vars->{form}{token} = issue_hash_token(['bounty', $bug->id]);
+ }
+ $vars->{form}{token} = issue_hash_token(['bounty', $bug->id]);
}
sub _attachment_is_bounty_attachment {
- my ($attachment) = @_;
+ my ($attachment) = @_;
- return 0 unless $attachment->filename eq 'bugbounty.data';
- return 0 unless $attachment->contenttype eq 'text/plain';
- return 0 unless $attachment->isprivate;
- return 0 unless $attachment->attacher->in_group('bounty-team');
+ return 0 unless $attachment->filename eq 'bugbounty.data';
+ return 0 unless $attachment->contenttype eq 'text/plain';
+ return 0 unless $attachment->isprivate;
+ return 0 unless $attachment->attacher->in_group('bounty-team');
- return $attachment->description =~ /^(?:[^,]*,)+[^,]*$/;
+ return $attachment->description =~ /^(?:[^,]*,)+[^,]*$/;
}
sub _attachment_bounty_details {
- my ($attachment) = @_;
- if (!exists $attachment->{bounty_details}) {
- if ($attachment->is_bounty_attachment) {
- $attachment->{bounty_details} = parse_bounty_attachment_description($attachment->description);
- }
- else {
- $attachment->{bounty_details} = undef;
- }
+ my ($attachment) = @_;
+ if (!exists $attachment->{bounty_details}) {
+ if ($attachment->is_bounty_attachment) {
+ $attachment->{bounty_details}
+ = parse_bounty_attachment_description($attachment->description);
+ }
+ else {
+ $attachment->{bounty_details} = undef;
}
- return $attachment->{bounty_details};
+ }
+ return $attachment->{bounty_details};
}
sub format_bounty_attachment_description {
- my ($form) = @_;
- my @fields = (
- @$form{qw( reporter_email amount_paid reported_date fixed_date awarded_date )},
- $form->{publish} ? 'true' : 'false',
- @{ $form->{credit} // [] }
- );
+ my ($form) = @_;
+ my @fields = (
+ @$form{qw( reporter_email amount_paid reported_date fixed_date awarded_date )},
+ $form->{publish} ? 'true' : 'false',
+ @{$form->{credit} // []}
+ );
- return join(',', map { $_ // '' } @fields);
+ return join(',', map { $_ // '' } @fields);
}
sub parse_bounty_attachment_description {
- my ($desc) = @_;
+ my ($desc) = @_;
- my %map = ( true => 1, false => 0 );
- my $date = qr/\d{4}-\d{2}-\d{2}/;
- $desc =~ m!
+ my %map = (true => 1, false => 0);
+ my $date = qr/\d{4}-\d{2}-\d{2}/;
+ $desc =~ m!
^
(?<reporter_email> [^,]+) \s*,\s*
(?<amount_paid> [0-9]+[-+?]?) ? \s*,\s*
@@ -373,1799 +381,1830 @@ sub parse_bounty_attachment_description {
$
!x;
- return {
- reporter_email => $+{reporter_email} // '',
- amount_paid => $+{amount_paid} // '',
- reported_date => $+{reported_date} // '',
- fixed_date => $+{fixed_date} // '',
- awarded_date => $+{awarded_date} // '',
- publish => $map{ $+{publish} // 'false' },
- credit => [grep { $_ } split(/\s*,\s*/, $+{credits}) ]
- };
+ return {
+ reporter_email => $+{reporter_email} // '',
+ amount_paid => $+{amount_paid} // '',
+ reported_date => $+{reported_date} // '',
+ fixed_date => $+{fixed_date} // '',
+ awarded_date => $+{awarded_date} // '',
+ publish => $map{$+{publish} // 'false'},
+ credit => [grep {$_} split(/\s*,\s*/, $+{credits})]
+ };
}
sub triage_request {
- my ($vars) = @_;
- my $user = Bugzilla->login(LOGIN_REQUIRED);
- if (Bugzilla->input_params->{update}) {
- Bugzilla->set_user(Bugzilla::User->super_user);
- $user->set_groups({ add => [ 'canconfirm' ] });
- Bugzilla->set_user($user);
- $user->update();
- $vars->{updated} = 1;
- }
+ my ($vars) = @_;
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
+ if (Bugzilla->input_params->{update}) {
+ Bugzilla->set_user(Bugzilla::User->super_user);
+ $user->set_groups({add => ['canconfirm']});
+ Bugzilla->set_user($user);
+ $user->update();
+ $vars->{updated} = 1;
+ }
}
sub _get_field_values_sort_key {
- my ($field) = @_;
- my $dbh = Bugzilla->dbh;
- my $fields = $dbh->selectall_arrayref(
- "SELECT value, sortkey FROM $field
- ORDER BY sortkey, value");
-
- my %field_values;
- foreach my $field (@$fields) {
- my ($value, $sortkey) = @$field;
- $field_values{$value} = $sortkey;
- }
- return \%field_values;
+ my ($field) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $fields = $dbh->selectall_arrayref(
+ "SELECT value, sortkey FROM $field
+ ORDER BY sortkey, value"
+ );
+
+ my %field_values;
+ foreach my $field (@$fields) {
+ my ($value, $sortkey) = @$field;
+ $field_values{$value} = $sortkey;
+ }
+ return \%field_values;
}
sub active_custom_fields {
- my ($self, $args) = @_;
- my $fields = $args->{'fields'};
- my $params = $args->{'params'};
- my $product = $params->{'product'};
- my $component = $params->{'component'};
+ my ($self, $args) = @_;
+ my $fields = $args->{'fields'};
+ my $params = $args->{'params'};
+ my $product = $params->{'product'};
+ my $component = $params->{'component'};
- return if !$product;
+ return if !$product;
- my $product_name = blessed $product ? $product->name : $product;
- my $component_name = blessed $component ? $component->name : $component;
+ my $product_name = blessed $product ? $product->name : $product;
+ my $component_name = blessed $component ? $component->name : $component;
- my @tmp_fields;
- foreach my $field (@$$fields) {
- next if cf_hidden_in_product($field->name, $product_name, $component_name);
- push(@tmp_fields, $field);
- }
- $$fields = \@tmp_fields;
+ my @tmp_fields;
+ foreach my $field (@$$fields) {
+ next if cf_hidden_in_product($field->name, $product_name, $component_name);
+ push(@tmp_fields, $field);
+ }
+ $$fields = \@tmp_fields;
}
sub cf_hidden_in_product {
- my ($field_name, $product_name, $component_name, $bug) = @_;
-
- # check bugzilla's built-in visibility controls first
- if ($bug) {
- my $field = Bugzilla::Field->new({ name => $field_name, cache => 1 });
- return 1 if $field && !$field->is_visible_on_bug($bug);
- }
-
- # If used in buglist.cgi, we pass in one_product which is a Bugzilla::Product
- # elsewhere, we just pass the name of the product.
- $product_name = blessed($product_name)
- ? $product_name->name
- : $product_name;
-
- # Also in buglist.cgi, we pass in a list of components instead
- # of a single component name everywhere else.
- my $component_list = [];
- if ($component_name) {
- $component_list = ref $component_name
- ? $component_name
- : [ $component_name ];
- }
-
- foreach my $field_re (keys %$cf_visible_in_products) {
- if ($field_name =~ $field_re) {
- # If no product given, for example more than one product
- # in buglist.cgi, then hide field by default
- return 1 if !$product_name;
-
- my $products = $cf_visible_in_products->{$field_re};
- foreach my $product (keys %$products) {
- my $components = $products->{$product};
-
- my $found_component = 0;
- if (@$components) {
- foreach my $component (@$components) {
- if (ref($component) eq 'Regexp') {
- if (grep($_ =~ $component, @$component_list)) {
- $found_component = 1;
- last;
- }
- } else {
- if (grep($_ eq $component, @$component_list)) {
- $found_component = 1;
- last;
- }
- }
- }
- }
-
- # If product matches and at at least one component matches
- # from component_list (if a matching component was required),
- # we allow the field to be seen
- if ($product eq $product_name && (!@$components || $found_component)) {
- return 0;
- }
+ my ($field_name, $product_name, $component_name, $bug) = @_;
+
+ # check bugzilla's built-in visibility controls first
+ if ($bug) {
+ my $field = Bugzilla::Field->new({name => $field_name, cache => 1});
+ return 1 if $field && !$field->is_visible_on_bug($bug);
+ }
+
+ # If used in buglist.cgi, we pass in one_product which is a Bugzilla::Product
+ # elsewhere, we just pass the name of the product.
+ $product_name = blessed($product_name) ? $product_name->name : $product_name;
+
+ # Also in buglist.cgi, we pass in a list of components instead
+ # of a single component name everywhere else.
+ my $component_list = [];
+ if ($component_name) {
+ $component_list = ref $component_name ? $component_name : [$component_name];
+ }
+
+ foreach my $field_re (keys %$cf_visible_in_products) {
+ if ($field_name =~ $field_re) {
+
+ # If no product given, for example more than one product
+ # in buglist.cgi, then hide field by default
+ return 1 if !$product_name;
+
+ my $products = $cf_visible_in_products->{$field_re};
+ foreach my $product (keys %$products) {
+ my $components = $products->{$product};
+
+ my $found_component = 0;
+ if (@$components) {
+ foreach my $component (@$components) {
+ if (ref($component) eq 'Regexp') {
+ if (grep($_ =~ $component, @$component_list)) {
+ $found_component = 1;
+ last;
+ }
}
- return 1;
+ else {
+ if (grep($_ eq $component, @$component_list)) {
+ $found_component = 1;
+ last;
+ }
+ }
+ }
}
+
+ # If product matches and at at least one component matches
+ # from component_list (if a matching component was required),
+ # we allow the field to be seen
+ if ($product eq $product_name && (!@$components || $found_component)) {
+ return 0;
+ }
+ }
+ return 1;
}
+ }
- return 0;
+ return 0;
}
# Purpose: CC certain email addresses on bugmail when a bug is added or
# removed from a particular group.
sub bugmail_recipients {
- my ($self, $args) = @_;
- my $bug = $args->{'bug'};
- my $recipients = $args->{'recipients'};
- my $diffs = $args->{'diffs'};
-
- if (@$diffs) {
- # Changed bug
- foreach my $ref (@$diffs) {
- my $old = $ref->{old};
- my $new = $ref->{new};
- my $fieldname = $ref->{field_name};
-
- if ($fieldname eq "bug_group") {
- _cc_if_special_group($old, $recipients);
- _cc_if_special_group($new, $recipients);
- }
- }
- } else {
- # Determine if it's a new bug, or a comment without a field change
- my $comment_count = scalar @{$bug->comments};
- if ($comment_count == 1) {
- # New bug
- foreach my $group (@{ $bug->groups_in }) {
- _cc_if_special_group($group->{'name'}, $recipients);
- }
- }
+ my ($self, $args) = @_;
+ my $bug = $args->{'bug'};
+ my $recipients = $args->{'recipients'};
+ my $diffs = $args->{'diffs'};
+
+ if (@$diffs) {
+
+ # Changed bug
+ foreach my $ref (@$diffs) {
+ my $old = $ref->{old};
+ my $new = $ref->{new};
+ my $fieldname = $ref->{field_name};
+
+ if ($fieldname eq "bug_group") {
+ _cc_if_special_group($old, $recipients);
+ _cc_if_special_group($new, $recipients);
+ }
+ }
+ }
+ else {
+ # Determine if it's a new bug, or a comment without a field change
+ my $comment_count = scalar @{$bug->comments};
+ if ($comment_count == 1) {
+
+ # New bug
+ foreach my $group (@{$bug->groups_in}) {
+ _cc_if_special_group($group->{'name'}, $recipients);
+ }
}
+ }
}
sub _cc_if_special_group {
- my ($group, $recipients) = @_;
+ my ($group, $recipients) = @_;
- return if !$group;
+ return if !$group;
- if (exists $group_change_notification{$group}) {
- foreach my $login (@{ $group_change_notification{$group} }) {
- my $id = login_to_id($login);
- $recipients->{$id}->{+REL_CC} = Bugzilla::BugMail::BIT_DIRECT();
- }
+ if (exists $group_change_notification{$group}) {
+ foreach my $login (@{$group_change_notification{$group}}) {
+ my $id = login_to_id($login);
+ $recipients->{$id}->{+REL_CC} = Bugzilla::BugMail::BIT_DIRECT();
}
+ }
}
sub _check_trusted {
- my ($field, $trusted, $priv_results) = @_;
+ my ($field, $trusted, $priv_results) = @_;
- my $needed_group = $trusted->{'_default'} || "";
- foreach my $dfield (keys %$trusted) {
- if ($field =~ $dfield) {
- $needed_group = $trusted->{$dfield};
- }
- }
- if ($needed_group && !Bugzilla->user->in_group($needed_group)) {
- push (@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
+ my $needed_group = $trusted->{'_default'} || "";
+ foreach my $dfield (keys %$trusted) {
+ if ($field =~ $dfield) {
+ $needed_group = $trusted->{$dfield};
}
+ }
+ if ($needed_group && !Bugzilla->user->in_group($needed_group)) {
+ push(@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
+ }
}
sub _is_field_set {
- my $value = shift;
- return $value ne '---' && $value !~ /\?$/;
+ my $value = shift;
+ return $value ne '---' && $value !~ /\?$/;
}
sub bug_check_can_change_field {
- my ($self, $args) = @_;
- my $bug = $args->{'bug'};
- my $field = $args->{'field'};
- my $new_value = $args->{'new_value'};
- my $old_value = $args->{'old_value'};
- my $priv_results = $args->{'priv_results'};
- my $user = Bugzilla->user;
-
- if ($field =~ /^cf/ && !@$priv_results && $new_value ne '---') {
- # Cannot use the standard %cf_setter mapping as we want anyone
- # to be able to set ?, just not the other values.
- if ($field eq 'cf_cab_review') {
- if ($new_value ne '1'
- && $new_value ne '?'
- && !$user->in_group('infra', $bug->product_id))
- {
- push (@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
- }
- }
- # "other" custom field setters restrictions
- elsif (exists $cf_setters->{$field}) {
- my $in_group = 0;
- foreach my $group (@{$cf_setters->{$field}}) {
- if ($user->in_group($group, $bug->product_id)) {
- $in_group = 1;
- last;
- }
- }
- if (!$in_group) {
- push (@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
- }
+ my ($self, $args) = @_;
+ my $bug = $args->{'bug'};
+ my $field = $args->{'field'};
+ my $new_value = $args->{'new_value'};
+ my $old_value = $args->{'old_value'};
+ my $priv_results = $args->{'priv_results'};
+ my $user = Bugzilla->user;
+
+ if ($field =~ /^cf/ && !@$priv_results && $new_value ne '---') {
+
+ # Cannot use the standard %cf_setter mapping as we want anyone
+ # to be able to set ?, just not the other values.
+ if ($field eq 'cf_cab_review') {
+ if ( $new_value ne '1'
+ && $new_value ne '?'
+ && !$user->in_group('infra', $bug->product_id))
+ {
+ push(@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
+ }
+ }
+
+ # "other" custom field setters restrictions
+ elsif (exists $cf_setters->{$field}) {
+ my $in_group = 0;
+ foreach my $group (@{$cf_setters->{$field}}) {
+ if ($user->in_group($group, $bug->product_id)) {
+ $in_group = 1;
+ last;
}
+ }
+ if (!$in_group) {
+ push(@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
+ }
}
- elsif ($field eq 'resolution' && $new_value eq 'EXPIRED') {
- # The EXPIRED resolution should only be settable by gerv.
- if ($user->login ne 'gerv@mozilla.org') {
- push (@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
- }
+ }
+ elsif ($field eq 'resolution' && $new_value eq 'EXPIRED') {
- } elsif ($field eq 'resolution' && $new_value eq 'FIXED') {
- # You need at least canconfirm to mark a bug as FIXED
- if (!$user->in_group('canconfirm', $bug->{'product_id'})) {
- push (@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
- }
+ # The EXPIRED resolution should only be settable by gerv.
+ if ($user->login ne 'gerv@mozilla.org') {
+ push(@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
+ }
- } elsif (
- ($field eq 'bug_status' && $old_value eq 'VERIFIED')
- || ($field eq 'dup_id' && $bug->status->name eq 'VERIFIED')
- || ($field eq 'resolution' && $bug->status->name eq 'VERIFIED')
- ) {
- # You need at least editbugs to reopen a resolved/verified bug
- if (!$user->in_group('editbugs', $bug->{'product_id'})) {
- push (@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
- }
+ }
+ elsif ($field eq 'resolution' && $new_value eq 'FIXED') {
- } elsif ($user->in_group('canconfirm', $bug->{'product_id'})) {
- # Canconfirm is really "cantriage"; users with canconfirm can also mark
- # bugs as DUPLICATE, WORKSFORME, and INCOMPLETE.
- if ($field eq 'bug_status'
- && is_open_state($old_value)
- && !is_open_state($new_value))
- {
- push (@$priv_results, PRIVILEGES_REQUIRED_NONE);
- }
- elsif ($field eq 'resolution' &&
- ($new_value eq 'DUPLICATE' ||
- $new_value eq 'WORKSFORME' ||
- $new_value eq 'INCOMPLETE' ||
- ($old_value eq '' && $new_value eq '1')))
- {
- push (@$priv_results, PRIVILEGES_REQUIRED_NONE);
- }
- elsif ($field eq 'dup_id') {
- push (@$priv_results, PRIVILEGES_REQUIRED_NONE);
- }
+ # You need at least canconfirm to mark a bug as FIXED
+ if (!$user->in_group('canconfirm', $bug->{'product_id'})) {
+ push(@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
+ }
- } elsif ($field eq 'bug_status') {
- # Disallow reopening of bugs which have been resolved for > 1 year
- if (is_open_state($new_value)
- && !is_open_state($old_value)
- && $bug->resolution eq 'FIXED')
- {
- my $days_ago = DateTime->now(time_zone => Bugzilla->local_timezone);
- $days_ago->subtract(days => 365);
- my $last_closed = datetime_from($bug->last_closed_date);
- if ($last_closed lt $days_ago) {
- push (@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
- }
- }
+ }
+ elsif (($field eq 'bug_status' && $old_value eq 'VERIFIED')
+ || ($field eq 'dup_id' && $bug->status->name eq 'VERIFIED')
+ || ($field eq 'resolution' && $bug->status->name eq 'VERIFIED'))
+ {
+ # You need at least editbugs to reopen a resolved/verified bug
+ if (!$user->in_group('editbugs', $bug->{'product_id'})) {
+ push(@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
}
+
+ }
+ elsif ($user->in_group('canconfirm', $bug->{'product_id'})) {
+
+ # Canconfirm is really "cantriage"; users with canconfirm can also mark
+ # bugs as DUPLICATE, WORKSFORME, and INCOMPLETE.
+ if ( $field eq 'bug_status'
+ && is_open_state($old_value)
+ && !is_open_state($new_value))
+ {
+ push(@$priv_results, PRIVILEGES_REQUIRED_NONE);
+ }
+ elsif (
+ $field eq 'resolution'
+ && ( $new_value eq 'DUPLICATE'
+ || $new_value eq 'WORKSFORME'
+ || $new_value eq 'INCOMPLETE'
+ || ($old_value eq '' && $new_value eq '1'))
+ )
+ {
+ push(@$priv_results, PRIVILEGES_REQUIRED_NONE);
+ }
+ elsif ($field eq 'dup_id') {
+ push(@$priv_results, PRIVILEGES_REQUIRED_NONE);
+ }
+
+ }
+ elsif ($field eq 'bug_status') {
+
+ # Disallow reopening of bugs which have been resolved for > 1 year
+ if ( is_open_state($new_value)
+ && !is_open_state($old_value)
+ && $bug->resolution eq 'FIXED')
+ {
+ my $days_ago = DateTime->now(time_zone => Bugzilla->local_timezone);
+ $days_ago->subtract(days => 365);
+ my $last_closed = datetime_from($bug->last_closed_date);
+ if ($last_closed lt $days_ago) {
+ push(@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
+ }
+ }
+ }
}
# link up various Mozilla-specific strings
sub bug_format_comment {
- my ($self, $args) = @_;
- my $regexes = $args->{'regexes'};
+ my ($self, $args) = @_;
+ my $regexes = $args->{'regexes'};
- # link to crash-stats
- # Only match if not already in an URL using the negative lookbehind (?<!\/)
- push (@$regexes, {
- match => qr/(?<!\/)\bbp-([a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-
+ # link to crash-stats
+ # Only match if not already in an URL using the negative lookbehind (?<!\/)
+ push(
+ @$regexes,
+ {
+ match => qr/(?<!\/)\bbp-([a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-
[a-f0-9]{4}\-[a-f0-9]{12})\b/x,
- replace => sub {
- my $args = shift;
- my $match = html_quote($args->{matches}->[0]);
- return qq{<a href="https://crash-stats.mozilla.com/report/index/$match">bp-$match</a>};
- }
- });
-
- # link to CVE/CAN security releases
- push (@$regexes, {
- match => qr/(?<!\/|=)\b((?:CVE|CAN)-\d{4}-(?:\d{4}|[1-9]\d{4,})(?!\d))\b/,
- replace => sub {
- my $args = shift;
- my $match = html_quote($args->{matches}->[0]);
- return qq{<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=$match">$match</a>};
- }
- });
-
- # link to svn.m.o
- push (@$regexes, {
- match => qr/(^|\s)r(\d{4,})\b/,
- replace => sub {
- my $args = shift;
- my $match = html_quote($args->{matches}->[1]);
- return
- $args->{matches}->[0] .
- qq{<a href="https://viewvc.svn.mozilla.org/vc?view=rev&amp;revision=$match">r$match</a>};
- }
- });
-
- # link old git.mozilla.org commit messages to github
- push (@$regexes, {
- match => qr#^(To\s(?:ssh://)?(?:[^\@]+\@)?git\.mozilla\.org[:/](.+?\.git)\n
+ replace => sub {
+ my $args = shift;
+ my $match = html_quote($args->{matches}->[0]);
+ return
+ qq{<a href="https://crash-stats.mozilla.com/report/index/$match">bp-$match</a>};
+ }
+ }
+ );
+
+ # link to CVE/CAN security releases
+ push(
+ @$regexes,
+ {
+ match => qr/(?<!\/|=)\b((?:CVE|CAN)-\d{4}-(?:\d{4}|[1-9]\d{4,})(?!\d))\b/,
+ replace => sub {
+ my $args = shift;
+ my $match = html_quote($args->{matches}->[0]);
+ return
+ qq{<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=$match">$match</a>};
+ }
+ }
+ );
+
+ # link to svn.m.o
+ push(
+ @$regexes,
+ {
+ match => qr/(^|\s)r(\d{4,})\b/,
+ replace => sub {
+ my $args = shift;
+ my $match = html_quote($args->{matches}->[1]);
+ return $args->{matches}->[0]
+ . qq{<a href="https://viewvc.svn.mozilla.org/vc?view=rev&amp;revision=$match">r$match</a>};
+ }
+ }
+ );
+
+ # link old git.mozilla.org commit messages to github
+ push(
+ @$regexes,
+ {
+ match => qr#^(To\s(?:ssh://)?(?:[^\@]+\@)?git\.mozilla\.org[:/](.+?\.git)\n
\s+)([0-9a-z]+\.\.([0-9a-z]+)\s+\S+\s->\s\S+)#mx,
- replace => sub {
- my $args = shift;
- my $preamble = html_quote($args->{matches}->[0]);
- my $repo = html_quote($args->{matches}->[1]);
- my $text = html_quote($args->{matches}->[2]);
- my $revision = html_quote($args->{matches}->[3]);
- $repo = 'mozilla/webtools-bmo-bugzilla' if $repo =~ /^webtools\/bmo\/bugzilla/;
- $repo = 'bugzilla/bugzilla' if $repo =~ /^bugzilla\/bugzilla\.git/;
- $repo = 'bugzilla/bugzilla.org' if $repo =~ /^www\/bugzilla\.org/;
- return qq#$preamble<a href="https://github.com/$repo/commit/$revision">$text</a>#;
- }
- });
-
- # link github commit messages
- push (@$regexes, {
- match => qr#^(To\s(?:https://|git@)?github\.com[:/](.+?)\.git\n
+ replace => sub {
+ my $args = shift;
+ my $preamble = html_quote($args->{matches}->[0]);
+ my $repo = html_quote($args->{matches}->[1]);
+ my $text = html_quote($args->{matches}->[2]);
+ my $revision = html_quote($args->{matches}->[3]);
+ $repo = 'mozilla/webtools-bmo-bugzilla' if $repo =~ /^webtools\/bmo\/bugzilla/;
+ $repo = 'bugzilla/bugzilla' if $repo =~ /^bugzilla\/bugzilla\.git/;
+ $repo = 'bugzilla/bugzilla.org' if $repo =~ /^www\/bugzilla\.org/;
+ return
+ qq#$preamble<a href="https://github.com/$repo/commit/$revision">$text</a>#;
+ }
+ }
+ );
+
+ # link github commit messages
+ push(
+ @$regexes,
+ {
+ match => qr#^(To\s(?:https://|git@)?github\.com[:/](.+?)\.git\n
\s+)([0-9a-z]+\.\.([0-9a-z]+)\s+\S+\s->\s\S+)#mx,
- replace => sub {
- my $args = shift;
- my $preamble = html_quote($args->{matches}->[0]);
- my $repo = html_quote($args->{matches}->[1]);
- my $text = html_quote($args->{matches}->[2]);
- my $revision = html_quote($args->{matches}->[3]);
- return qq#$preamble<a href="https://github.com/$repo/commit/$revision">$text</a>#;
- }
- });
-
- # link github pull requests and issues
- push (@$regexes, {
- match => qr/(\s)([A-Za-z0-9_\.-]+)\/([A-Za-z0-9_\.-]+)\#([0-9]+)\b/,
- replace => sub {
- my $args = shift;
- my $owner = html_quote($args->{matches}->[1]);
- my $repo = html_quote($args->{matches}->[2]);
- my $number = html_quote($args->{matches}->[3]);
- return qq# <a href="https://github.com/$owner/$repo/issues/$number">$owner/$repo\#$number</a>#;
+ replace => sub {
+ my $args = shift;
+ my $preamble = html_quote($args->{matches}->[0]);
+ my $repo = html_quote($args->{matches}->[1]);
+ my $text = html_quote($args->{matches}->[2]);
+ my $revision = html_quote($args->{matches}->[3]);
+ return
+ qq#$preamble<a href="https://github.com/$repo/commit/$revision">$text</a>#;
+ }
+ }
+ );
+
+ # link github pull requests and issues
+ push(
+ @$regexes,
+ {
+ match => qr/(\s)([A-Za-z0-9_\.-]+)\/([A-Za-z0-9_\.-]+)\#([0-9]+)\b/,
+ replace => sub {
+ my $args = shift;
+ my $owner = html_quote($args->{matches}->[1]);
+ my $repo = html_quote($args->{matches}->[2]);
+ my $number = html_quote($args->{matches}->[3]);
+ return
+ qq# <a href="https://github.com/$owner/$repo/issues/$number">$owner/$repo\#$number</a>#;
+ }
+ }
+ );
+
+# Update certain links to git.mozilla.org to go to github.com instead
+# https://git.mozilla.org/?p=webtools/bmo/bugzilla.git;a=blob;f=Bugzilla/WebService/Bug.pm;h=d7a1d8f9bb5fdee524f2bb342a4573a63d890f2e;hb=HEAD#l657
+ push(
+ @$regexes,
+ {
+ match => qr#\b(https?://git\.mozilla\.org\S+)\b#mx,
+ replace => sub {
+ my $args = shift;
+ my $match = $args->{matches}->[0];
+ my $uri = URI->new($match);
+ my $text = html_quote($match);
+
+ # Only work on BMO and Bugzilla repos
+ my $repo = html_quote($uri->query_param_delete("p")) || '';
+ if ($repo !~ /(webtools\/bmo|bugzilla)\//) {
+ return qq#<a href="$text">$text</a>#;
}
- });
-
- # Update certain links to git.mozilla.org to go to github.com instead
- # https://git.mozilla.org/?p=webtools/bmo/bugzilla.git;a=blob;f=Bugzilla/WebService/Bug.pm;h=d7a1d8f9bb5fdee524f2bb342a4573a63d890f2e;hb=HEAD#l657
- push(@$regexes, {
- match => qr#\b(https?://git\.mozilla\.org\S+)\b#mx,
- replace => sub {
- my $args = shift;
- my $match = $args->{matches}->[0];
- my $uri = URI->new($match);
- my $text = html_quote($match);
-
- # Only work on BMO and Bugzilla repos
- my $repo = html_quote($uri->query_param_delete("p")) || '';
- if ($repo !~ /(webtools\/bmo|bugzilla)\//) {
- return qq#<a href="$text">$text</a>#;
- }
- my $action = html_quote($uri->query_param_delete("a")) || '';
- my $file = html_quote($uri->query_param_delete("f")) || '';
- my $frag = html_quote($uri->fragment) || '';
- my $from_rev = html_quote($uri->query_param_delete("h")) || '';
- my $to_rev = html_quote($uri->query_param_delete("hb")) || '';
+ my $action = html_quote($uri->query_param_delete("a")) || '';
+ my $file = html_quote($uri->query_param_delete("f")) || '';
+ my $frag = html_quote($uri->fragment) || '';
+ my $from_rev = html_quote($uri->query_param_delete("h")) || '';
+ my $to_rev = html_quote($uri->query_param_delete("hb")) || '';
- if ($frag) {
- $frag =~ tr/l/L/;
- $frag = "#$frag";
- }
+ if ($frag) {
+ $frag =~ tr/l/L/;
+ $frag = "#$frag";
+ }
- $to_rev = $from_rev if !$to_rev;
- $to_rev = 'master' if $to_rev eq 'HEAD';
- $to_rev =~ s#refs/heads/(.*)$#$1#;
+ $to_rev = $from_rev if !$to_rev;
+ $to_rev = 'master' if $to_rev eq 'HEAD';
+ $to_rev =~ s#refs/heads/(.*)$#$1#;
- $repo = 'mozilla-bteam/bmo' if $repo =~ /^webtools\/bmo\/bugzilla\.git$/;
- $repo = 'bugzilla/bugzilla' if $repo =~ /^bugzilla\/bugzilla\.git$/;
- $repo = 'bugzilla/bugzilla.org' if $repo =~ /^www\/bugzilla\.org\.git$/;
+ $repo = 'mozilla-bteam/bmo' if $repo =~ /^webtools\/bmo\/bugzilla\.git$/;
+ $repo = 'bugzilla/bugzilla' if $repo =~ /^bugzilla\/bugzilla\.git$/;
+ $repo = 'bugzilla/bugzilla.org' if $repo =~ /^www\/bugzilla\.org\.git$/;
- if ($action eq 'tree') {
- return $to_rev eq 'HEAD'
- ? qq#<a href="https://github.com/$repo">$text [github]</a>#
- : qq#<a href="https://github.com/$repo/tree/$to_rev">$text [github]</a>#;
- }
- if ($action eq 'blob') {
- return qq#<a href="https://github.com/$repo/blob/$to_rev/$file$frag">$text [github]</a>#;
- }
- if ($action eq 'shortlog' || $action eq 'log') {
- return qq#<a href="https://github.com/$repo/commits/$to_rev">$text [github]</a>#;
- }
- if ($action eq 'commit' || $action eq 'commitdiff') {
- return qq#<a href="https://github.com/$repo/commit/$to_rev">$text [github]</a>#;
- }
- return qq#<a href="$text">$text</a>#;
+ if ($action eq 'tree') {
+ return $to_rev eq 'HEAD'
+ ? qq#<a href="https://github.com/$repo">$text [github]</a>#
+ : qq#<a href="https://github.com/$repo/tree/$to_rev">$text [github]</a>#;
}
- });
-
- # link to hg.m.o
- # Note: for grouping in this regexp, always use non-capturing parentheses.
- my $hgrepos = join('|', qw!(?:releases/)?comm-[\w.]+
- (?:releases/)?mozilla-[\w.]+
- (?:releases/)?mobile-[\w.]+
- tracemonkey
- tamarin-[\w.]+
- camino!);
-
- push (@$regexes, {
- match => qr/\b(($hgrepos)\s+changeset:?\s+(?:\d+:)?([0-9a-fA-F]{12}))\b/,
- replace => sub {
- my $args = shift;
- my $text = html_quote($args->{matches}->[0]);
- my $repo = html_quote($args->{matches}->[1]);
- my $id = html_quote($args->{matches}->[2]);
- $repo = 'integration/mozilla-inbound' if $repo eq 'mozilla-inbound';
- return qq{<a href="https://hg.mozilla.org/$repo/rev/$id">$text</a>};
+ if ($action eq 'blob') {
+ return
+ qq#<a href="https://github.com/$repo/blob/$to_rev/$file$frag">$text [github]</a>#;
}
- });
+ if ($action eq 'shortlog' || $action eq 'log') {
+ return
+ qq#<a href="https://github.com/$repo/commits/$to_rev">$text [github]</a>#;
+ }
+ if ($action eq 'commit' || $action eq 'commitdiff') {
+ return qq#<a href="https://github.com/$repo/commit/$to_rev">$text [github]</a>#;
+ }
+ return qq#<a href="$text">$text</a>#;
+ }
+ }
+ );
+
+ # link to hg.m.o
+ # Note: for grouping in this regexp, always use non-capturing parentheses.
+ my $hgrepos = join(
+ '|', qw!(?:releases/)?comm-[\w.]+
+ (?:releases/)?mozilla-[\w.]+
+ (?:releases/)?mobile-[\w.]+
+ tracemonkey
+ tamarin-[\w.]+
+ camino!
+ );
+
+ push(
+ @$regexes,
+ {
+ match => qr/\b(($hgrepos)\s+changeset:?\s+(?:\d+:)?([0-9a-fA-F]{12}))\b/,
+ replace => sub {
+ my $args = shift;
+ my $text = html_quote($args->{matches}->[0]);
+ my $repo = html_quote($args->{matches}->[1]);
+ my $id = html_quote($args->{matches}->[2]);
+ $repo = 'integration/mozilla-inbound' if $repo eq 'mozilla-inbound';
+ return qq{<a href="https://hg.mozilla.org/$repo/rev/$id">$text</a>};
+ }
+ }
+ );
}
sub quicksearch_map {
- my ($self, $args) = @_;
- my $map = $args->{'map'};
+ my ($self, $args) = @_;
+ my $map = $args->{'map'};
- foreach my $name (keys %$map) {
- if ($name =~ /cf_crash_signature$/) {
- $map->{'sig'} = $name;
- }
+ foreach my $name (keys %$map) {
+ if ($name =~ /cf_crash_signature$/) {
+ $map->{'sig'} = $name;
}
+ }
}
sub object_columns {
- my ($self, $args) = @_;
- return unless $args->{class}->isa('Bugzilla::Product');
- push @{ $args->{columns} }, qw(
- default_platform_id
- default_op_sys_id
- security_group_id
- );
+ my ($self, $args) = @_;
+ return unless $args->{class}->isa('Bugzilla::Product');
+ push @{$args->{columns}}, qw(
+ default_platform_id
+ default_op_sys_id
+ security_group_id
+ );
}
sub object_update_columns {
- my ($self, $args) = @_;
- return unless $args->{object}->isa('Bugzilla::Product');
- push @{ $args->{columns} }, qw(
- default_platform_id
- default_op_sys_id
- security_group_id
- );
+ my ($self, $args) = @_;
+ return unless $args->{object}->isa('Bugzilla::Product');
+ push @{$args->{columns}}, qw(
+ default_platform_id
+ default_op_sys_id
+ security_group_id
+ );
}
sub object_before_create {
- my ($self, $args) = @_;
- return unless $args->{class}->isa('Bugzilla::Product');
+ my ($self, $args) = @_;
+ return unless $args->{class}->isa('Bugzilla::Product');
- my $cgi = Bugzilla->cgi;
- my $params = $args->{params};
- foreach my $field (qw( default_platform_id default_op_sys_id security_group_id )) {
- $params->{$field} = $cgi->param($field);
- }
+ my $cgi = Bugzilla->cgi;
+ my $params = $args->{params};
+ foreach
+ my $field (qw( default_platform_id default_op_sys_id security_group_id ))
+ {
+ $params->{$field} = $cgi->param($field);
+ }
}
sub object_end_of_set_all {
- my ($self, $args) = @_;
- my $object = $args->{object};
- return unless $object->isa('Bugzilla::Product');
-
- my $cgi = Bugzilla->cgi;
- my $params = $args->{params};
- foreach my $field (qw( default_platform_id default_op_sys_id security_group_id )) {
- my $value = $cgi->param($field);
- detaint_natural($value);
- $object->set($field, $value);
- }
+ my ($self, $args) = @_;
+ my $object = $args->{object};
+ return unless $object->isa('Bugzilla::Product');
+
+ my $cgi = Bugzilla->cgi;
+ my $params = $args->{params};
+ foreach
+ my $field (qw( default_platform_id default_op_sys_id security_group_id ))
+ {
+ my $value = $cgi->param($field);
+ detaint_natural($value);
+ $object->set($field, $value);
+ }
}
sub object_end_of_create {
- my ($self, $args) = @_;
- my $class = $args->{class};
-
- if ($class eq 'Bugzilla::User') {
- my $user = $args->{object};
-
- # Log real IP addresses for auditing
- Bugzilla->audit(sprintf('<%s> created user %s', remote_ip(), $user->login));
-
- # Add default searches to new user's footer
- my $dbh = Bugzilla->dbh;
-
- my $sharer = Bugzilla::User->new({ name => Bugzilla->params->{'nobody_user'} })
- or return;
- my $group = Bugzilla::Group->new({ name => 'everyone' })
- or return;
-
- foreach my $definition (@default_named_queries) {
- my ($namedquery_id) = _get_named_query($sharer->id, $group->id, $definition);
- $dbh->do(
- "INSERT INTO namedqueries_link_in_footer(namedquery_id,user_id) VALUES (?,?)",
- undef,
- $namedquery_id, $user->id
- );
- }
+ my ($self, $args) = @_;
+ my $class = $args->{class};
+
+ if ($class eq 'Bugzilla::User') {
+ my $user = $args->{object};
+
+ # Log real IP addresses for auditing
+ Bugzilla->audit(sprintf('<%s> created user %s', remote_ip(), $user->login));
+
+ # Add default searches to new user's footer
+ my $dbh = Bugzilla->dbh;
- } elsif ($class eq 'Bugzilla::Bug') {
- # Log real IP addresses for auditing
- Bugzilla->audit(sprintf('%s <%s> created bug %s', Bugzilla->user->login, remote_ip(), $args->{object}->id));
+ my $sharer = Bugzilla::User->new({name => Bugzilla->params->{'nobody_user'}})
+ or return;
+ my $group = Bugzilla::Group->new({name => 'everyone'}) or return;
+
+ foreach my $definition (@default_named_queries) {
+ my ($namedquery_id) = _get_named_query($sharer->id, $group->id, $definition);
+ $dbh->do(
+ "INSERT INTO namedqueries_link_in_footer(namedquery_id,user_id) VALUES (?,?)",
+ undef, $namedquery_id, $user->id);
}
+
+ }
+ elsif ($class eq 'Bugzilla::Bug') {
+
+ # Log real IP addresses for auditing
+ Bugzilla->audit(sprintf(
+ '%s <%s> created bug %s',
+ Bugzilla->user->login, remote_ip(), $args->{object}->id
+ ));
+ }
}
sub _bug_reporters_hw_os {
- my ($self) = @_;
- return $self->{ua_hw_os} if exists $self->{ua_hw_os};
- my $memcached = Bugzilla->memcached;
- my $hw_os = $memcached->get({ key => 'bug.ua.' . $self->id });
- if (!$hw_os) {
- (my $ua) = Bugzilla->dbh->selectrow_array(
- "SELECT user_agent FROM bug_user_agent WHERE bug_id = ?",
- undef,
- $self->id);
- $hw_os = $ua
- ? [ detect_platform($ua), detect_op_sys($ua) ]
- : [];
- $memcached->set({ key => 'bug.ua.' . $self->id, value => $hw_os });
- }
- return $self->{ua_hw_os} = $hw_os;
+ my ($self) = @_;
+ return $self->{ua_hw_os} if exists $self->{ua_hw_os};
+ my $memcached = Bugzilla->memcached;
+ my $hw_os = $memcached->get({key => 'bug.ua.' . $self->id});
+ if (!$hw_os) {
+ (my $ua)
+ = Bugzilla->dbh->selectrow_array(
+ "SELECT user_agent FROM bug_user_agent WHERE bug_id = ?",
+ undef, $self->id);
+ $hw_os = $ua ? [detect_platform($ua), detect_op_sys($ua)] : [];
+ $memcached->set({key => 'bug.ua.' . $self->id, value => $hw_os});
+ }
+ return $self->{ua_hw_os} = $hw_os;
}
sub _bug_is_unassigned {
- my ($self) = @_;
- my $assignee = $self->assigned_to->login;
- return $assignee eq Bugzilla->params->{'nobody_user'} || $assignee =~ /\.bugs$/;
+ my ($self) = @_;
+ my $assignee = $self->assigned_to->login;
+ return $assignee eq Bugzilla->params->{'nobody_user'} || $assignee =~ /\.bugs$/;
}
sub _bug_has_current_patch {
- my ($self) = @_;
- foreach my $attachment (@{ $self->attachments }) {
- next if $attachment->isobsolete;
- return 1 if $attachment->can_review;
- }
- return 0;
+ my ($self) = @_;
+ foreach my $attachment (@{$self->attachments}) {
+ next if $attachment->isobsolete;
+ return 1 if $attachment->can_review;
+ }
+ return 0;
}
sub _bug_missing_sec_approval {
- my ($self) = @_;
- # see https://wiki.mozilla.org/Security/Bug_Approval_Process for the rules
+ my ($self) = @_;
- # no need to alert once a bug is closed
- return 0 if $self->resolution;
+ # see https://wiki.mozilla.org/Security/Bug_Approval_Process for the rules
- # only bugs with sec-high or sec-critical keywords need sec-approval
- return 0 unless $self->has_keyword('sec-high') || $self->has_keyword('sec-critical');
+ # no need to alert once a bug is closed
+ return 0 if $self->resolution;
- # look for patches with sec-approval set to any value
- foreach my $attachment (@{ $self->attachments }) {
- next if $attachment->isobsolete || !$attachment->ispatch;
- foreach my $flag (@{ $attachment->flags }) {
- # only one patch needs sec-approval
- return 0 if $flag->name eq 'sec-approval';
- }
- }
+ # only bugs with sec-high or sec-critical keywords need sec-approval
+ return 0
+ unless $self->has_keyword('sec-high') || $self->has_keyword('sec-critical');
- # tracking flags
- require Bugzilla::Extension::TrackingFlags::Flag;
- my $flags = Bugzilla::Extension::TrackingFlags::Flag->match({
- product => $self->product,
- component => $self->component,
- bug_id => $self->id,
- is_active => 1,
- WHERE => {
- 'name like ?' => 'cf_status_firefox%',
- },
- });
- # set flags are added after the sql query, filter those out
- $flags = [ grep { $_->name =~ /^cf_status_firefox/ } @$flags ];
- return 0 unless @$flags;
-
- my $nightly = last_value { $_->name !~ /_esr\d+$/ } @$flags;
- my $set = 0;
- foreach my $flag (@$flags) {
- my $value = $flag->bug_flag($self->id)->value;
- next if $value eq '---';
- $set++;
- # sec-approval is required if any of the current status-firefox
- # tracking flags that aren't the latest are set to 'affected'
- return 1 if $flag->name ne $nightly->name && $value eq 'affected';
+ # look for patches with sec-approval set to any value
+ foreach my $attachment (@{$self->attachments}) {
+ next if $attachment->isobsolete || !$attachment->ispatch;
+ foreach my $flag (@{$attachment->flags}) {
+
+ # only one patch needs sec-approval
+ return 0 if $flag->name eq 'sec-approval';
}
- # sec-approval is required if no tracking flags are set
- return $set == 0;
+ }
+
+ # tracking flags
+ require Bugzilla::Extension::TrackingFlags::Flag;
+ my $flags = Bugzilla::Extension::TrackingFlags::Flag->match({
+ product => $self->product,
+ component => $self->component,
+ bug_id => $self->id,
+ is_active => 1,
+ WHERE => {'name like ?' => 'cf_status_firefox%',},
+ });
+
+ # set flags are added after the sql query, filter those out
+ $flags = [grep { $_->name =~ /^cf_status_firefox/ } @$flags];
+ return 0 unless @$flags;
+
+ my $nightly = last_value { $_->name !~ /_esr\d+$/ } @$flags;
+ my $set = 0;
+ foreach my $flag (@$flags) {
+ my $value = $flag->bug_flag($self->id)->value;
+ next if $value eq '---';
+ $set++;
+
+ # sec-approval is required if any of the current status-firefox
+ # tracking flags that aren't the latest are set to 'affected'
+ return 1 if $flag->name ne $nightly->name && $value eq 'affected';
+ }
+
+ # sec-approval is required if no tracking flags are set
+ return $set == 0;
}
sub _product_default_platform_id { $_[0]->{default_platform_id} }
-sub _product_default_op_sys_id { $_[0]->{default_op_sys_id} }
+sub _product_default_op_sys_id { $_[0]->{default_op_sys_id} }
sub _product_default_platform {
- my ($self) = @_;
- if (!exists $self->{default_platform}) {
- $self->{default_platform} = $self->default_platform_id
- ? Bugzilla::Field::Choice
- ->type('rep_platform')
- ->new($_[0]->{default_platform_id})
- ->name
- : undef;
- }
- return $self->{default_platform};
+ my ($self) = @_;
+ if (!exists $self->{default_platform}) {
+ $self->{default_platform}
+ = $self->default_platform_id
+ ? Bugzilla::Field::Choice->type('rep_platform')
+ ->new($_[0]->{default_platform_id})->name
+ : undef;
+ }
+ return $self->{default_platform};
}
+
sub _product_default_op_sys {
- my ($self) = @_;
- if (!exists $self->{default_op_sys}) {
- $self->{default_op_sys} = $self->default_op_sys_id
- ? Bugzilla::Field::Choice
- ->type('op_sys')
- ->new($_[0]->{default_op_sys_id})
- ->name
- : undef;
- }
- return $self->{default_op_sys};
+ my ($self) = @_;
+ if (!exists $self->{default_op_sys}) {
+ $self->{default_op_sys}
+ = $self->default_op_sys_id
+ ? Bugzilla::Field::Choice->type('op_sys')->new($_[0]->{default_op_sys_id})
+ ->name
+ : undef;
+ }
+ return $self->{default_op_sys};
}
sub _get_named_query {
- my ($sharer_id, $group_id, $definition) = @_;
- my $dbh = Bugzilla->dbh;
- # find existing namedquery
- my ($namedquery_id) = $dbh->selectrow_array(
- "SELECT id FROM namedqueries WHERE userid=? AND name=?",
- undef,
- $sharer_id, $definition->{name}
- );
- return $namedquery_id if $namedquery_id;
- # create namedquery
- $dbh->do(
- "INSERT INTO namedqueries(userid,name,query) VALUES (?,?,?)",
- undef,
- $sharer_id, $definition->{name}, $definition->{query}
- );
- $namedquery_id = $dbh->bz_last_key();
- # and share it
- $dbh->do(
- "INSERT INTO namedquery_group_map(namedquery_id,group_id) VALUES (?,?)",
- undef,
- $namedquery_id, $group_id,
- );
- return $namedquery_id;
+ my ($sharer_id, $group_id, $definition) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ # find existing namedquery
+ my ($namedquery_id)
+ = $dbh->selectrow_array(
+ "SELECT id FROM namedqueries WHERE userid=? AND name=?",
+ undef, $sharer_id, $definition->{name});
+ return $namedquery_id if $namedquery_id;
+
+ # create namedquery
+ $dbh->do("INSERT INTO namedqueries(userid,name,query) VALUES (?,?,?)",
+ undef, $sharer_id, $definition->{name}, $definition->{query});
+ $namedquery_id = $dbh->bz_last_key();
+
+ # and share it
+ $dbh->do(
+ "INSERT INTO namedquery_group_map(namedquery_id,group_id) VALUES (?,?)",
+ undef, $namedquery_id, $group_id,);
+ return $namedquery_id;
}
sub bug_end_of_create {
- my ($self, $args) = @_;
- my $bug = $args->{'bug'};
-
- # automatically CC users to bugs based on group & product
- foreach my $group_name (keys %group_auto_cc) {
- my $group_obj = Bugzilla::Group->new({ name => $group_name });
- if ($group_obj && $bug->in_group($group_obj)) {
- my $ra_logins = exists $group_auto_cc{$group_name}->{$bug->product}
- ? $group_auto_cc{$group_name}->{$bug->product}
- : $group_auto_cc{$group_name}->{'_default'};
- foreach my $login (@$ra_logins) {
- $bug->add_cc($login);
- }
- }
- }
-
- # store user-agent
- if (my $ua = Bugzilla->cgi->user_agent) {
- trick_taint($ua);
- Bugzilla->dbh->do(
- "INSERT INTO bug_user_agent (bug_id, user_agent) VALUES (?, ?)",
- undef,
- $bug->id, $ua
- );
- }
+ my ($self, $args) = @_;
+ my $bug = $args->{'bug'};
+
+ # automatically CC users to bugs based on group & product
+ foreach my $group_name (keys %group_auto_cc) {
+ my $group_obj = Bugzilla::Group->new({name => $group_name});
+ if ($group_obj && $bug->in_group($group_obj)) {
+ my $ra_logins
+ = exists $group_auto_cc{$group_name}->{$bug->product}
+ ? $group_auto_cc{$group_name}->{$bug->product}
+ : $group_auto_cc{$group_name}->{'_default'};
+ foreach my $login (@$ra_logins) {
+ $bug->add_cc($login);
+ }
+ }
+ }
+
+ # store user-agent
+ if (my $ua = Bugzilla->cgi->user_agent) {
+ trick_taint($ua);
+ Bugzilla->dbh->do(
+ "INSERT INTO bug_user_agent (bug_id, user_agent) VALUES (?, ?)",
+ undef, $bug->id, $ua);
+ }
}
sub sanitycheck_check {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my $dbh = Bugzilla->dbh;
- my $status = $args->{'status'};
- $status->('bmo_check_cf_visible_in_products');
-
- my $products = $dbh->selectcol_arrayref('SELECT name FROM products');
- my %product = map { $_ => 1 } @$products;
- my @cf_products = map { keys %$_ } values %$cf_visible_in_products;
- foreach my $cf_product (@cf_products) {
- $status->('bmo_check_cf_visible_in_products_missing',
- { cf_product => $cf_product }, 'alert') unless $product{$cf_product};
- }
+ my $dbh = Bugzilla->dbh;
+ my $status = $args->{'status'};
+ $status->('bmo_check_cf_visible_in_products');
+
+ my $products = $dbh->selectcol_arrayref('SELECT name FROM products');
+ my %product = map { $_ => 1 } @$products;
+ my @cf_products = map { keys %$_ } values %$cf_visible_in_products;
+ foreach my $cf_product (@cf_products) {
+ $status->(
+ 'bmo_check_cf_visible_in_products_missing',
+ {cf_product => $cf_product}, 'alert'
+ ) unless $product{$cf_product};
+ }
}
sub db_sanitize {
- print "deleting reporter's user-agents...\n";
- Bugzilla->dbh->do("TRUNCATE TABLE bug_user_agent");
+ print "deleting reporter's user-agents...\n";
+ Bugzilla->dbh->do("TRUNCATE TABLE bug_user_agent");
}
# bugs in an ASSIGNED state must be assigned to a real person
# reset bugs to NEW if the assignee is nobody/.bugs$
sub object_start_of_update {
- my ($self, $args) = @_;
- my ($new_bug, $old_bug) = @$args{qw( object old_object )};
- return unless $new_bug->isa('Bugzilla::Bug');
-
- # if either the assignee or status has changed
- return unless
- $old_bug->assigned_to->id != $new_bug->assigned_to->id
- || $old_bug->bug_status ne $new_bug->bug_status;
-
- # and the bug is now ASSIGNED
- return unless
- $new_bug->bug_status eq 'ASSIGNED';
-
- # and the assignee isn't a real person
- return unless
- $new_bug->assigned_to->login eq Bugzilla->params->{'nobody_user'}
- || $new_bug->assigned_to->login =~ /\.bugs$/;
-
- # and the user can set the status to NEW
- return unless
- $old_bug->check_can_change_field('bug_status', $old_bug->bug_status, 'NEW');
-
- # if the user is changing the assignee, silently change the bug's status to new
- if ($old_bug->assigned_to->id != $new_bug->assigned_to->id) {
- $new_bug->set_bug_status('NEW');
- }
+ my ($self, $args) = @_;
+ my ($new_bug, $old_bug) = @$args{qw( object old_object )};
+ return unless $new_bug->isa('Bugzilla::Bug');
- # otherwise the user is trying to set the bug's status to ASSIGNED without
- # assigning a real person. throw an error.
- else {
- ThrowUserError('bug_status_unassigned');
- }
+ # if either the assignee or status has changed
+ return
+ unless $old_bug->assigned_to->id != $new_bug->assigned_to->id
+ || $old_bug->bug_status ne $new_bug->bug_status;
+
+ # and the bug is now ASSIGNED
+ return unless $new_bug->bug_status eq 'ASSIGNED';
+
+ # and the assignee isn't a real person
+ return
+ unless $new_bug->assigned_to->login eq Bugzilla->params->{'nobody_user'}
+ || $new_bug->assigned_to->login =~ /\.bugs$/;
+
+ # and the user can set the status to NEW
+ return
+ unless $old_bug->check_can_change_field('bug_status', $old_bug->bug_status,
+ 'NEW');
+
+ # if the user is changing the assignee, silently change the bug's status to new
+ if ($old_bug->assigned_to->id != $new_bug->assigned_to->id) {
+ $new_bug->set_bug_status('NEW');
+ }
+
+ # otherwise the user is trying to set the bug's status to ASSIGNED without
+ # assigning a real person. throw an error.
+ else {
+ ThrowUserError('bug_status_unassigned');
+ }
}
# detect github pull requests and reviewboard reviews, set the content-type
sub attachment_process_data {
- my ($self, $args) = @_;
- my $attributes = $args->{attributes};
-
- # must be a text attachment
- return unless $attributes->{mimetype} eq 'text/plain';
-
- # check the attachment size, and get attachment content if it isn't too large
- my $data = $attributes->{data};
- my $url;
- if (blessed($data) && blessed($data) eq 'Fh') {
- # filehandle
- my $size = -s $data;
- return if $size > 256;
- sysread($data, $url, $size);
- seek($data, 0, 0);
- } else {
- # string
- $url = $data;
- }
-
- if (my $detected = _detect_attached_url($url)) {
- $attributes->{mimetype} = $detected->{content_type};
- $attributes->{ispatch} = 0;
- }
+ my ($self, $args) = @_;
+ my $attributes = $args->{attributes};
+
+ # must be a text attachment
+ return unless $attributes->{mimetype} eq 'text/plain';
+
+ # check the attachment size, and get attachment content if it isn't too large
+ my $data = $attributes->{data};
+ my $url;
+ if (blessed($data) && blessed($data) eq 'Fh') {
+
+ # filehandle
+ my $size = -s $data;
+ return if $size > 256;
+ sysread($data, $url, $size);
+ seek($data, 0, 0);
+ }
+ else {
+ # string
+ $url = $data;
+ }
+
+ if (my $detected = _detect_attached_url($url)) {
+ $attributes->{mimetype} = $detected->{content_type};
+ $attributes->{ispatch} = 0;
+ }
}
sub _detect_attached_url {
- my ($url) = @_;
-
- # trim and check for the pull request url
- return unless defined $url;
- return if length($url) > 256;
- $url = trim($url);
- # ignore urls that contain unescaped characters outside of the range mentioned in RFC 3986 section 2
- return if $url =~ m<[^A-Za-z0-9._~:/?#\[\]@!\$&'()*+,;=`.%-]>;
-
- foreach my $key (keys %autodetect_attach_urls) {
- my $regex = $autodetect_attach_urls{$key}->{regex};
- if (ref($regex) eq 'CODE') {
- $regex = $regex->();
- }
- if ($url =~ $regex) {
- return $autodetect_attach_urls{$key};
- }
+ my ($url) = @_;
+
+ # trim and check for the pull request url
+ return unless defined $url;
+ return if length($url) > 256;
+ $url = trim($url);
+
+# ignore urls that contain unescaped characters outside of the range mentioned in RFC 3986 section 2
+ return if $url =~ m<[^A-Za-z0-9._~:/?#\[\]@!\$&'()*+,;=`.%-]>;
+
+ foreach my $key (keys %autodetect_attach_urls) {
+ my $regex = $autodetect_attach_urls{$key}->{regex};
+ if (ref($regex) eq 'CODE') {
+ $regex = $regex->();
+ }
+ if ($url =~ $regex) {
+ return $autodetect_attach_urls{$key};
}
+ }
- return undef;
+ return undef;
}
sub _attachment_external_redirect {
- my ($self) = @_;
+ my ($self) = @_;
- # must be our supported content-type
- return undef unless
- any { $self->contenttype eq $autodetect_attach_urls{$_}->{content_type} }
- keys %autodetect_attach_urls;
+ # must be our supported content-type
+ return undef
+ unless
+ any { $self->contenttype eq $autodetect_attach_urls{$_}->{content_type} }
+ keys %autodetect_attach_urls;
- # must still be a valid url
- return _detect_attached_url($self->data)
+ # must still be a valid url
+ return _detect_attached_url($self->data);
}
sub _attachment_can_review {
- my ($self) = @_;
+ my ($self) = @_;
- return 1 if $self->ispatch;
- my $external = $self->external_redirect // return;
- return $external->{can_review};
+ return 1 if $self->ispatch;
+ my $external = $self->external_redirect // return;
+ return $external->{can_review};
}
sub _attachment_fetch_github_pr_diff {
- my ($self) = @_;
+ my ($self) = @_;
- # must be our supported content-type
- return undef unless
- any { $self->contenttype eq $autodetect_attach_urls{$_}->{content_type} }
- keys %autodetect_attach_urls;
+ # must be our supported content-type
+ return undef
+ unless
+ any { $self->contenttype eq $autodetect_attach_urls{$_}->{content_type} }
+ keys %autodetect_attach_urls;
- # must still be a valid url
- return undef unless _detect_attached_url($self->data);
+ # must still be a valid url
+ return undef unless _detect_attached_url($self->data);
- my $ua = LWP::UserAgent->new( timeout => 10 );
- if (Bugzilla->params->{proxy_url}) {
- $ua->proxy('https', Bugzilla->params->{proxy_url});
- }
+ my $ua = LWP::UserAgent->new(timeout => 10);
+ if (Bugzilla->params->{proxy_url}) {
+ $ua->proxy('https', Bugzilla->params->{proxy_url});
+ }
- my $pr_diff = $self->data . ".diff";
- my $response = $ua->get($pr_diff);
- if ($response->is_error) {
- warn "Github fetch error: $pr_diff, " . $response->status_line;
- return "Error retrieving Github pull request diff for " . $self->data;
- }
- return $response->decoded_content;
+ my $pr_diff = $self->data . ".diff";
+ my $response = $ua->get($pr_diff);
+ if ($response->is_error) {
+ warn "Github fetch error: $pr_diff, " . $response->status_line;
+ return "Error retrieving Github pull request diff for " . $self->data;
+ }
+ return $response->decoded_content;
}
# redirect automatically to github urls
sub attachment_view {
- my ($self, $args) = @_;
- my $attachment = $args->{attachment};
- my $cgi = Bugzilla->cgi;
+ my ($self, $args) = @_;
+ my $attachment = $args->{attachment};
+ my $cgi = Bugzilla->cgi;
- # don't redirect if the content-type is specified explicitly
- return if defined $cgi->param('content_type');
+ # don't redirect if the content-type is specified explicitly
+ return if defined $cgi->param('content_type');
- # must be a valid redirection url
- return unless defined $attachment->external_redirect;
+ # must be a valid redirection url
+ return unless defined $attachment->external_redirect;
- # redirect
- print $cgi->redirect(trim($attachment->data));
- exit;
+ # redirect
+ print $cgi->redirect(trim($attachment->data));
+ exit;
}
sub install_before_final_checks {
- my ($self, $args) = @_;
-
- # Add product chooser setting
- add_setting({
- name => 'product_chooser',
- options => ['pretty_product_chooser', 'full_product_chooser'],
- default => 'pretty_product_chooser',
- category => 'User Interface'
- });
-
- # Add option to inject x-bugzilla headers into the message body to work
- # around gmail filtering limitations
- add_setting({
- name => 'headers_in_body',
- options => ['on', 'off'],
- default => 'off',
- category => 'Email Notifications'
- });
-
- # Migrate from 'gmail_threading' setting to 'bugmail_new_prefix'
- my $dbh = Bugzilla->dbh;
- if ($dbh->selectrow_array("SELECT 1 FROM setting WHERE name='gmail_threading'")) {
- $dbh->bz_start_transaction();
- $dbh->do("UPDATE profile_setting
+ my ($self, $args) = @_;
+
+ # Add product chooser setting
+ add_setting({
+ name => 'product_chooser',
+ options => ['pretty_product_chooser', 'full_product_chooser'],
+ default => 'pretty_product_chooser',
+ category => 'User Interface'
+ });
+
+ # Add option to inject x-bugzilla headers into the message body to work
+ # around gmail filtering limitations
+ add_setting({
+ name => 'headers_in_body',
+ options => ['on', 'off'],
+ default => 'off',
+ category => 'Email Notifications'
+ });
+
+ # Migrate from 'gmail_threading' setting to 'bugmail_new_prefix'
+ my $dbh = Bugzilla->dbh;
+ if ($dbh->selectrow_array("SELECT 1 FROM setting WHERE name='gmail_threading'"))
+ {
+ $dbh->bz_start_transaction();
+ $dbh->do(
+ "UPDATE profile_setting
SET setting_value='on-temp'
- WHERE setting_name='gmail_threading' AND setting_value='Off'");
- $dbh->do("UPDATE profile_setting
+ WHERE setting_name='gmail_threading' AND setting_value='Off'"
+ );
+ $dbh->do(
+ "UPDATE profile_setting
SET setting_value='off'
- WHERE setting_name='gmail_threading' AND setting_value='On'");
- $dbh->do("UPDATE profile_setting
+ WHERE setting_name='gmail_threading' AND setting_value='On'"
+ );
+ $dbh->do(
+ "UPDATE profile_setting
SET setting_value='on'
- WHERE setting_name='gmail_threading' AND setting_value='on-temp'");
- $dbh->do("UPDATE profile_setting
+ WHERE setting_name='gmail_threading' AND setting_value='on-temp'"
+ );
+ $dbh->do(
+ "UPDATE profile_setting
SET setting_name='bugmail_new_prefix'
- WHERE setting_name='gmail_threading'");
- $dbh->do("DELETE FROM setting WHERE name='gmail_threading'");
- $dbh->bz_commit_transaction();
- }
+ WHERE setting_name='gmail_threading'"
+ );
+ $dbh->do("DELETE FROM setting WHERE name='gmail_threading'");
+ $dbh->bz_commit_transaction();
+ }
}
sub db_schema_abstract_schema {
- my ($self, $args) = @_;
- $args->{schema}->{bug_user_agent} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- bug_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'bugs',
- COLUMN => 'bug_id',
- DELETE => 'CASCADE',
- },
- },
- user_agent => {
- TYPE => 'MEDIUMTEXT',
- NOTNULL => 1,
- },
- ],
- INDEXES => [
- bug_user_agent_idx => {
- FIELDS => [ 'bug_id' ],
- TYPE => 'UNIQUE',
- },
- ],
- };
- $args->{schema}->{job_last_run} = {
- FIELDS => [
- id => {
- TYPE => 'INTSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- name => {
- TYPE => 'VARCHAR(100)',
- NOTNULL => 1,
- },
- last_run => {
- TYPE => 'DATETIME',
- NOTNULL => 1,
- },
- ],
- INDEXES => [
- job_last_run_name_idx => {
- FIELDS => [ 'name' ],
- TYPE => 'UNIQUE',
- },
- ],
- };
- $args->{schema}->{secbugs_BugHistory} = {
- FIELDS => [
- bugid => { TYPE => 'BIGINT', NOTNULL => 1 },
- changetime => { TYPE => 'NATIVE_DATETIME' },
- fieldname => { TYPE => 'VARCHAR(32)', NOTNULL => 1 },
- new => { TYPE => 'VARCHAR(255)' },
- old => { TYPE => 'VARCHAR(255)' },
- ],
- };
-
- $args->{schema}->{secbugs_Bugs} = {
- FIELDS => [
- bugid => { TYPE => 'BIGINT', NOTNULL => 1, PRIMARYKEY => 1 },
- opendate => { TYPE => 'NATIVE_DATETIME' },
- closedate => { TYPE => 'NATIVE_DATETIME', NOTNULL => 1 },
- severity => { TYPE => 'VARCHAR(16)' },
- summary => { TYPE => 'VARCHAR(255)' },
- updated => { TYPE => 'NATIVE_DATETIME' },
- ],
- };
-
- $args->{schema}->{secbugs_Details} = {
- FIELDS => [
- did => {
- TYPE => 'INTSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- sid => {
- TYPE => 'INT4',
- },
- product => {
- TYPE => 'VARCHAR(255)',
- },
- component => {
- TYPE => 'VARCHAR(255)',
- },
- count => { TYPE => 'INT4' },
- bug_list => { TYPE => 'TEXT' },
- date => { TYPE => 'NATIVE_DATETIME' },
- avg_age_days => { TYPE => 'INT4' },
- med_age_days => { TYPE => 'INT4' },
- ]
- };
-
- $args->{schema}->{secbugs_Stats} = {
- FIELDS => [
- sid => { TYPE => 'INTSERIAL', NOTNULL => 1, PRIMARYKEY => 1 },
- category => { TYPE => 'VARCHAR(32)' },
- count => { TYPE => 'INT4' },
- date => { TYPE => 'NATIVE_DATETIME' },
- ]
- };
+ my ($self, $args) = @_;
+ $args->{schema}->{bug_user_agent} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ bug_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'bugs', COLUMN => 'bug_id', DELETE => 'CASCADE',},
+ },
+ user_agent => {TYPE => 'MEDIUMTEXT', NOTNULL => 1,},
+ ],
+ INDEXES => [bug_user_agent_idx => {FIELDS => ['bug_id'], TYPE => 'UNIQUE',},],
+ };
+ $args->{schema}->{job_last_run} = {
+ FIELDS => [
+ id => {TYPE => 'INTSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ name => {TYPE => 'VARCHAR(100)', NOTNULL => 1,},
+ last_run => {TYPE => 'DATETIME', NOTNULL => 1,},
+ ],
+ INDEXES => [job_last_run_name_idx => {FIELDS => ['name'], TYPE => 'UNIQUE',},],
+ };
+ $args->{schema}->{secbugs_BugHistory} = {
+ FIELDS => [
+ bugid => {TYPE => 'BIGINT', NOTNULL => 1},
+ changetime => {TYPE => 'NATIVE_DATETIME'},
+ fieldname => {TYPE => 'VARCHAR(32)', NOTNULL => 1},
+ new => {TYPE => 'VARCHAR(255)'},
+ old => {TYPE => 'VARCHAR(255)'},
+ ],
+ };
+
+ $args->{schema}->{secbugs_Bugs} = {
+ FIELDS => [
+ bugid => {TYPE => 'BIGINT', NOTNULL => 1, PRIMARYKEY => 1},
+ opendate => {TYPE => 'NATIVE_DATETIME'},
+ closedate => {TYPE => 'NATIVE_DATETIME', NOTNULL => 1},
+ severity => {TYPE => 'VARCHAR(16)'},
+ summary => {TYPE => 'VARCHAR(255)'},
+ updated => {TYPE => 'NATIVE_DATETIME'},
+ ],
+ };
+
+ $args->{schema}->{secbugs_Details} = {
+ FIELDS => [
+ did => {TYPE => 'INTSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ sid => {TYPE => 'INT4',},
+ product => {TYPE => 'VARCHAR(255)',},
+ component => {TYPE => 'VARCHAR(255)',},
+ count => {TYPE => 'INT4'},
+ bug_list => {TYPE => 'TEXT'},
+ date => {TYPE => 'NATIVE_DATETIME'},
+ avg_age_days => {TYPE => 'INT4'},
+ med_age_days => {TYPE => 'INT4'},
+ ]
+ };
+
+ $args->{schema}->{secbugs_Stats} = {
+ FIELDS => [
+ sid => {TYPE => 'INTSERIAL', NOTNULL => 1, PRIMARYKEY => 1},
+ category => {TYPE => 'VARCHAR(32)'},
+ count => {TYPE => 'INT4'},
+ date => {TYPE => 'NATIVE_DATETIME'},
+ ]
+ };
}
sub install_update_db {
- my $dbh = Bugzilla->dbh;
+ my $dbh = Bugzilla->dbh;
+
+ # per-product hw/os defaults
+ my $op_sys_default = _field_value('op_sys', 'Unspecified', 50);
+ $dbh->bz_add_column(
+ 'products',
+ 'default_op_sys_id' => {
+ TYPE => 'INT2',
+ DEFAULT => $op_sys_default->id,
+ REFERENCES => {TABLE => 'op_sys', COLUMN => 'id', DELETE => 'SET NULL',},
+ }
+ );
+ my $platform_default = _field_value('rep_platform', 'Unspecified', 50);
+ $dbh->bz_add_column(
+ 'products',
+ 'default_platform_id' => {
+ TYPE => 'INT2',
+ DEFAULT => $platform_default->id,
+ REFERENCES => {TABLE => 'rep_platform', COLUMN => 'id', DELETE => 'SET NULL',},
+ }
+ );
+
+ # Migrate old is_active stuff to new patch (is in core in 4.2), The old
+ # column name was 'is_active', the new one is 'isactive' (no underscore).
+ if ($dbh->bz_column_info('milestones', 'is_active')) {
+ $dbh->do("UPDATE milestones SET isactive = 0 WHERE is_active = 0;");
+ $dbh->bz_drop_column('milestones', 'is_active');
+ $dbh->bz_drop_column('milestones', 'is_searchable');
+ }
+
+ # remove tables from the old TryAutoLand extension
+ $dbh->bz_drop_table('autoland_branches');
+ $dbh->bz_drop_table('autoland_attachments');
+
+ unless (Bugzilla::Field->new({name => 'cf_rank'})) {
+ Bugzilla::Field->create({
+ name => 'cf_rank',
+ description => 'Rank',
+ type => FIELD_TYPE_INTEGER,
+ mailhead => 0,
+ enter_bug => 0,
+ obsolete => 0,
+ custom => 1,
+ buglist => 1,
+ });
+ }
+ unless (Bugzilla::Field->new({name => 'cf_crash_signature'})) {
+ Bugzilla::Field->create({
+ name => 'cf_crash_signature',
+ description => 'Crash Signature',
+ type => FIELD_TYPE_TEXTAREA,
+ mailhead => 0,
+ enter_bug => 1,
+ obsolete => 0,
+ custom => 1,
+ buglist => 0,
+ });
+ }
- # per-product hw/os defaults
- my $op_sys_default = _field_value('op_sys', 'Unspecified', 50);
- $dbh->bz_add_column(
- 'products',
- 'default_op_sys_id' => {
- TYPE => 'INT2',
- DEFAULT => $op_sys_default->id,
- REFERENCES => {
- TABLE => 'op_sys',
- COLUMN => 'id',
- DELETE => 'SET NULL',
- },
- }
- );
- my $platform_default = _field_value('rep_platform', 'Unspecified', 50);
+ # Add default security group id column
+ if (!$dbh->bz_column_info('products', 'security_group_id')) {
$dbh->bz_add_column(
- 'products',
- 'default_platform_id' => {
- TYPE => 'INT2',
- DEFAULT => $platform_default->id,
- REFERENCES => {
- TABLE => 'rep_platform',
- COLUMN => 'id',
- DELETE => 'SET NULL',
- },
- }
+ 'products',
+ 'security_group_id' => {
+ TYPE => 'INT3',
+ REFERENCES => {TABLE => 'groups', COLUMN => 'id', DELETE => 'SET NULL',},
+ }
);
- # Migrate old is_active stuff to new patch (is in core in 4.2), The old
- # column name was 'is_active', the new one is 'isactive' (no underscore).
- if ($dbh->bz_column_info('milestones', 'is_active')) {
- $dbh->do("UPDATE milestones SET isactive = 0 WHERE is_active = 0;");
- $dbh->bz_drop_column('milestones', 'is_active');
- $dbh->bz_drop_column('milestones', 'is_searchable');
- }
-
- # remove tables from the old TryAutoLand extension
- $dbh->bz_drop_table('autoland_branches');
- $dbh->bz_drop_table('autoland_attachments');
-
- unless (Bugzilla::Field->new({ name => 'cf_rank' })) {
- Bugzilla::Field->create({
- name => 'cf_rank',
- description => 'Rank',
- type => FIELD_TYPE_INTEGER,
- mailhead => 0,
- enter_bug => 0,
- obsolete => 0,
- custom => 1,
- buglist => 1,
- });
- }
- unless (Bugzilla::Field->new({ name => 'cf_crash_signature' })) {
- Bugzilla::Field->create({
- name => 'cf_crash_signature',
- description => 'Crash Signature',
- type => FIELD_TYPE_TEXTAREA,
- mailhead => 0,
- enter_bug => 1,
- obsolete => 0,
- custom => 1,
- buglist => 0,
- });
- }
-
- # Add default security group id column
- if (!$dbh->bz_column_info('products', 'security_group_id')) {
- $dbh->bz_add_column(
- 'products',
- 'security_group_id' => {
- TYPE => 'INT3',
- REFERENCES => {
- TABLE => 'groups',
- COLUMN => 'id',
- DELETE => 'SET NULL',
- },
- }
- );
-
- # if there are no groups, then we're creating a database from scratch
- # and there's nothing to migrate
- my ($group_count) = $dbh->selectrow_array("SELECT COUNT(*) FROM groups");
- if ($group_count) {
- # Migrate old product_sec_group mappings from the time this change was made
- my %product_sec_groups = (
- "addons.mozilla.org" => 'client-services-security',
- "Air Mozilla" => 'mozilla-employee-confidential',
- "Android Background Services" => 'cloud-services-security',
- "Audio/Visual Infrastructure" => 'mozilla-employee-confidential',
- "AUS" => 'client-services-security',
- "Bugzilla" => 'bugzilla-security',
- "bugzilla.mozilla.org" => 'bugzilla-security',
- "Cloud Services" => 'cloud-services-security',
- "Community Tools" => 'websites-security',
- "Data & BI Services Team" => 'metrics-private',
- "Developer Documentation" => 'websites-security',
- "Developer Ecosystem" => 'client-services-security',
- "Finance" => 'finance',
- "Firefox Friends" => 'mozilla-employee-confidential',
- "Firefox Health Report" => 'cloud-services-security',
- "Infrastructure & Operations" => 'mozilla-employee-confidential',
- "Input" => 'websites-security',
- "Intellego" => 'intellego-team',
- "Internet Public Policy" => 'mozilla-employee-confidential',
- "L20n" => 'l20n-security',
- "Legal" => 'legal',
- "Marketing" => 'marketing-private',
- "Marketplace" => 'client-services-security',
- "Mozilla Communities" => 'mozilla-communities-security',
- "Mozilla Corporation" => 'mozilla-employee-confidential',
- "Mozilla Developer Network" => 'websites-security',
- "Mozilla Foundation" => 'mozilla-employee-confidential',
- "Mozilla Foundation Operations" => 'mozilla-foundation-operations',
- "Mozilla Grants" => 'grants',
- "mozillaignite" => 'websites-security',
- "Mozilla Messaging" => 'mozilla-messaging-confidential',
- "Mozilla Metrics" => 'metrics-private',
- "mozilla.org" => 'mozilla-employee-confidential',
- "Mozilla PR" => 'pr-private',
- "Mozilla QA" => 'mozilla-employee-confidential',
- "Mozilla Reps" => 'mozilla-reps',
- "Popcorn" => 'websites-security',
- "Privacy" => 'privacy',
- "quality.mozilla.org" => 'websites-security',
- "Recruiting" => 'hr',
- "Release Engineering" => 'mozilla-employee-confidential',
- "Snippets" => 'websites-security',
- "Socorro" => 'client-services-security',
- "support.mozillamessaging.com" => 'websites-security',
- "support.mozilla.org" => 'websites-security',
- "Talkback" => 'talkback-private',
- "Tamarin" => 'tamarin-security',
- "Taskcluster" => 'taskcluster-security',
- "Testopia" => 'bugzilla-security',
- "Tree Management" => 'mozilla-employee-confidential',
- "Web Apps" => 'client-services-security',
- "Webmaker" => 'websites-security',
- "Websites" => 'websites-security',
- "Webtools" => 'webtools-security',
- "www.mozilla.org" => 'websites-security',
- );
- # 1. Set all to core-security by default
- my $core_sec_group = Bugzilla::Group->new({ name => Bugzilla->params->{insidergroup} });
- $dbh->do("UPDATE products SET security_group_id = ?", undef, $core_sec_group->id);
- # 2. Update the ones that have explicit security groups
- foreach my $prod_name (keys %product_sec_groups) {
- my $group_name = $product_sec_groups{$prod_name};
- next if $group_name eq Bugzilla->params->{insidergroup}; # already done
- my $group = Bugzilla::Group->new({ name => $group_name, cache => 1 });
- if (!$group) {
- warn "Security group $group_name not found. Using insider group instead.\n";
- next;
- }
- $dbh->do("UPDATE products SET security_group_id = ? WHERE name = ?", undef, $group->id, $prod_name);
- }
+ # if there are no groups, then we're creating a database from scratch
+ # and there's nothing to migrate
+ my ($group_count) = $dbh->selectrow_array("SELECT COUNT(*) FROM groups");
+ if ($group_count) {
+
+ # Migrate old product_sec_group mappings from the time this change was made
+ my %product_sec_groups = (
+ "addons.mozilla.org" => 'client-services-security',
+ "Air Mozilla" => 'mozilla-employee-confidential',
+ "Android Background Services" => 'cloud-services-security',
+ "Audio/Visual Infrastructure" => 'mozilla-employee-confidential',
+ "AUS" => 'client-services-security',
+ "Bugzilla" => 'bugzilla-security',
+ "bugzilla.mozilla.org" => 'bugzilla-security',
+ "Cloud Services" => 'cloud-services-security',
+ "Community Tools" => 'websites-security',
+ "Data & BI Services Team" => 'metrics-private',
+ "Developer Documentation" => 'websites-security',
+ "Developer Ecosystem" => 'client-services-security',
+ "Finance" => 'finance',
+ "Firefox Friends" => 'mozilla-employee-confidential',
+ "Firefox Health Report" => 'cloud-services-security',
+ "Infrastructure & Operations" => 'mozilla-employee-confidential',
+ "Input" => 'websites-security',
+ "Intellego" => 'intellego-team',
+ "Internet Public Policy" => 'mozilla-employee-confidential',
+ "L20n" => 'l20n-security',
+ "Legal" => 'legal',
+ "Marketing" => 'marketing-private',
+ "Marketplace" => 'client-services-security',
+ "Mozilla Communities" => 'mozilla-communities-security',
+ "Mozilla Corporation" => 'mozilla-employee-confidential',
+ "Mozilla Developer Network" => 'websites-security',
+ "Mozilla Foundation" => 'mozilla-employee-confidential',
+ "Mozilla Foundation Operations" => 'mozilla-foundation-operations',
+ "Mozilla Grants" => 'grants',
+ "mozillaignite" => 'websites-security',
+ "Mozilla Messaging" => 'mozilla-messaging-confidential',
+ "Mozilla Metrics" => 'metrics-private',
+ "mozilla.org" => 'mozilla-employee-confidential',
+ "Mozilla PR" => 'pr-private',
+ "Mozilla QA" => 'mozilla-employee-confidential',
+ "Mozilla Reps" => 'mozilla-reps',
+ "Popcorn" => 'websites-security',
+ "Privacy" => 'privacy',
+ "quality.mozilla.org" => 'websites-security',
+ "Recruiting" => 'hr',
+ "Release Engineering" => 'mozilla-employee-confidential',
+ "Snippets" => 'websites-security',
+ "Socorro" => 'client-services-security',
+ "support.mozillamessaging.com" => 'websites-security',
+ "support.mozilla.org" => 'websites-security',
+ "Talkback" => 'talkback-private',
+ "Tamarin" => 'tamarin-security',
+ "Taskcluster" => 'taskcluster-security',
+ "Testopia" => 'bugzilla-security',
+ "Tree Management" => 'mozilla-employee-confidential',
+ "Web Apps" => 'client-services-security',
+ "Webmaker" => 'websites-security',
+ "Websites" => 'websites-security',
+ "Webtools" => 'webtools-security',
+ "www.mozilla.org" => 'websites-security',
+ );
+
+ # 1. Set all to core-security by default
+ my $core_sec_group
+ = Bugzilla::Group->new({name => Bugzilla->params->{insidergroup}});
+ $dbh->do("UPDATE products SET security_group_id = ?",
+ undef, $core_sec_group->id);
+
+ # 2. Update the ones that have explicit security groups
+ foreach my $prod_name (keys %product_sec_groups) {
+ my $group_name = $product_sec_groups{$prod_name};
+ next if $group_name eq Bugzilla->params->{insidergroup}; # already done
+ my $group = Bugzilla::Group->new({name => $group_name, cache => 1});
+ if (!$group) {
+ warn "Security group $group_name not found. Using insider group instead.\n";
+ next;
}
+ $dbh->do("UPDATE products SET security_group_id = ? WHERE name = ?",
+ undef, $group->id, $prod_name);
+ }
}
+ }
}
# return the Bugzilla::Field::Choice object for the specified field and value.
# if the value doesn't exist it will be created.
sub _field_value {
- my ($field_name, $value_name, $sort_key) = @_;
- my $field = Bugzilla::Field->check({ name => $field_name });
- my $existing = Bugzilla::Field::Choice->type($field)->match({ value => $value_name });
- return $existing->[0] if $existing && @$existing;
- return Bugzilla::Field::Choice->type($field)->create({
- value => $value_name,
- sortkey => $sort_key,
- isactive => 1,
- });
+ my ($field_name, $value_name, $sort_key) = @_;
+ my $field = Bugzilla::Field->check({name => $field_name});
+ my $existing
+ = Bugzilla::Field::Choice->type($field)->match({value => $value_name});
+ return $existing->[0] if $existing && @$existing;
+ return Bugzilla::Field::Choice->type($field)
+ ->create({value => $value_name, sortkey => $sort_key, isactive => 1,});
}
sub _last_closed_date {
- my ($self) = @_;
- my $dbh = Bugzilla->dbh;
+ my ($self) = @_;
+ my $dbh = Bugzilla->dbh;
- return $self->{'last_closed_date'} if defined $self->{'last_closed_date'};
+ return $self->{'last_closed_date'} if defined $self->{'last_closed_date'};
- my $closed_statuses = "'" . join("','", map { $_->name } closed_bug_statuses()) . "'";
- my $status_field_id = get_field_id('bug_status');
+ my $closed_statuses
+ = "'" . join("','", map { $_->name } closed_bug_statuses()) . "'";
+ my $status_field_id = get_field_id('bug_status');
- $self->{'last_closed_date'} = $dbh->selectrow_array("
+ $self->{'last_closed_date'} = $dbh->selectrow_array("
SELECT bugs_activity.bug_when
FROM bugs_activity
WHERE bugs_activity.fieldid = ?
AND bugs_activity.added IN ($closed_statuses)
AND bugs_activity.bug_id = ?
- ORDER BY bugs_activity.bug_when DESC " . $dbh->sql_limit(1),
- undef, $status_field_id, $self->id
- );
+ ORDER BY bugs_activity.bug_when DESC " . $dbh->sql_limit(1), undef,
+ $status_field_id, $self->id);
- return $self->{'last_closed_date'};
+ return $self->{'last_closed_date'};
}
sub field_end_of_create {
- my ($self, $args) = @_;
- my $field = $args->{'field'};
-
- # Create an IT bug so Mozilla's DBAs so they can update the grants for metrics
-
- if (Bugzilla->localconfig->{'urlbase'} ne 'https://bugzilla.mozilla.org/'
- && Bugzilla->localconfig->{'urlbase'} ne 'https://bugzilla.allizom.org/')
- {
- return;
- }
-
- my $name = $field->name;
-
- if (Bugzilla->usage_mode == USAGE_MODE_CMDLINE) {
- Bugzilla->set_user(Bugzilla::User->check({ name => Bugzilla->params->{'nobody_user'} }));
- print "Creating IT permission grant bug for new field '$name'...";
- }
-
- my $bug_data = {
- short_desc => "Custom field '$name' added to bugzilla.mozilla.org",
- product => 'Data & BI Services Team',
- component => 'Database Operations',
- bug_severity => 'normal',
- op_sys => 'All',
- rep_platform => 'All',
- version => 'other',
- };
-
- my $comment = <<COMMENT;
+ my ($self, $args) = @_;
+ my $field = $args->{'field'};
+
+ # Create an IT bug so Mozilla's DBAs so they can update the grants for metrics
+
+ if ( Bugzilla->localconfig->{'urlbase'} ne 'https://bugzilla.mozilla.org/'
+ && Bugzilla->localconfig->{'urlbase'} ne 'https://bugzilla.allizom.org/')
+ {
+ return;
+ }
+
+ my $name = $field->name;
+
+ if (Bugzilla->usage_mode == USAGE_MODE_CMDLINE) {
+ Bugzilla->set_user(Bugzilla::User->check(
+ {name => Bugzilla->params->{'nobody_user'}}));
+ print "Creating IT permission grant bug for new field '$name'...";
+ }
+
+ my $bug_data = {
+ short_desc => "Custom field '$name' added to bugzilla.mozilla.org",
+ product => 'Data & BI Services Team',
+ component => 'Database Operations',
+ bug_severity => 'normal',
+ op_sys => 'All',
+ rep_platform => 'All',
+ version => 'other',
+ };
+
+ my $comment = <<COMMENT;
The custom field '$name' has been added to the BMO database.
Please run the following on bugzilla1.db.scl3.mozilla.com:
COMMENT
- if ($field->type == FIELD_TYPE_SINGLE_SELECT
- || $field->type == FIELD_TYPE_MULTI_SELECT) {
- $comment .= <<COMMENT;
+ if ( $field->type == FIELD_TYPE_SINGLE_SELECT
+ || $field->type == FIELD_TYPE_MULTI_SELECT)
+ {
+ $comment .= <<COMMENT;
GRANT SELECT ON `bugs`.`$name` TO 'metrics'\@'10.22.70.20_';
GRANT SELECT ON `bugs`.`$name` TO 'metrics'\@'10.22.70.21_';
COMMENT
- }
- if ($field->type == FIELD_TYPE_MULTI_SELECT) {
- $comment .= <<COMMENT;
+ }
+ if ($field->type == FIELD_TYPE_MULTI_SELECT) {
+ $comment .= <<COMMENT;
GRANT SELECT ON `bugs`.`bug_$name` TO 'metrics'\@'10.22.70.20_';
GRANT SELECT ON `bugs`.`bug_$name` TO 'metrics'\@'10.22.70.21_';
COMMENT
- }
- if ($field->type != FIELD_TYPE_MULTI_SELECT) {
- $comment .= <<COMMENT;
+ }
+ if ($field->type != FIELD_TYPE_MULTI_SELECT) {
+ $comment .= <<COMMENT;
GRANT SELECT ($name) ON `bugs`.`bugs` TO 'metrics'\@'10.22.70.20_';
GRANT SELECT ($name) ON `bugs`.`bugs` TO 'metrics'\@'10.22.70.21_';
COMMENT
- }
+ }
- $bug_data->{'comment'} = $comment;
+ $bug_data->{'comment'} = $comment;
- my $old_error_mode = Bugzilla->error_mode;
- Bugzilla->error_mode(ERROR_MODE_DIE);
+ my $old_error_mode = Bugzilla->error_mode;
+ Bugzilla->error_mode(ERROR_MODE_DIE);
- my $new_bug = eval { Bugzilla::Bug->create($bug_data) };
+ my $new_bug = eval { Bugzilla::Bug->create($bug_data) };
- my $error = $@;
- undef $@;
- Bugzilla->error_mode($old_error_mode);
+ my $error = $@;
+ undef $@;
+ Bugzilla->error_mode($old_error_mode);
- if ($error || !($new_bug && $new_bug->{'bug_id'})) {
- warn "Error creating IT bug for new field $name: $error";
- if (Bugzilla->usage_mode == USAGE_MODE_CMDLINE) {
- print "\nError: $error\n";
- }
+ if ($error || !($new_bug && $new_bug->{'bug_id'})) {
+ warn "Error creating IT bug for new field $name: $error";
+ if (Bugzilla->usage_mode == USAGE_MODE_CMDLINE) {
+ print "\nError: $error\n";
}
- else {
- Bugzilla::BugMail::Send($new_bug->id, { changer => Bugzilla->user });
- if (Bugzilla->usage_mode == USAGE_MODE_CMDLINE) {
- print "bug " . $new_bug->id . " created.\n";
- }
+ }
+ else {
+ Bugzilla::BugMail::Send($new_bug->id, {changer => Bugzilla->user});
+ if (Bugzilla->usage_mode == USAGE_MODE_CMDLINE) {
+ print "bug " . $new_bug->id . " created.\n";
}
+ }
}
sub webservice {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my $dispatch = $args->{dispatch};
- $dispatch->{BMO} = "Bugzilla::Extension::BMO::WebService";
+ my $dispatch = $args->{dispatch};
+ $dispatch->{BMO} = "Bugzilla::Extension::BMO::WebService";
}
sub psgi_builder {
- my ($self, $args) = @_;
- my $mount = $args->{mount};
-
- my $ses_index = Plack::Builder::builder(sub {
- my $auth_user = Bugzilla->localconfig->{ses_username};
- my $auth_pass = Bugzilla->localconfig->{ses_password};
- Plack::Builder::enable("Auth::Basic", authenticator => sub {
- my ($username, $password, $env) = @_;
- return ( $auth_user
- && $auth_pass
- && $username
- && $password
- && $username eq $auth_user
- && $password eq $auth_pass );
- });
- compile_cgi("ses/index.cgi");
- });
+ my ($self, $args) = @_;
+ my $mount = $args->{mount};
+
+ my $ses_index = Plack::Builder::builder(sub {
+ my $auth_user = Bugzilla->localconfig->{ses_username};
+ my $auth_pass = Bugzilla->localconfig->{ses_password};
+ Plack::Builder::enable(
+ "Auth::Basic",
+ authenticator => sub {
+ my ($username, $password, $env) = @_;
+ return ($auth_user
+ && $auth_pass
+ && $username
+ && $password
+ && $username eq $auth_user
+ && $password eq $auth_pass);
+ }
+ );
+ compile_cgi("ses/index.cgi");
+ });
- $mount->{'ses/index.cgi'} = $ses_index;
+ $mount->{'ses/index.cgi'} = $ses_index;
}
our $search_content_matches;
+
BEGIN {
- $search_content_matches = \&Bugzilla::Search::_content_matches;
+ $search_content_matches = \&Bugzilla::Search::_content_matches;
}
sub search_operator_field_override {
- my ($self, $args) = @_;
- my $search = $args->{'search'};
- my $operators = $args->{'operators'};
-
- my $cgi = Bugzilla->cgi;
- my @comments = $cgi->param('comments');
- my $exclude_comments = scalar(@comments) && !grep { $_ eq '1' } @comments;
-
- if ($cgi->param('query_format')
- && $cgi->param('query_format') eq 'specific'
- && $exclude_comments
- ) {
- # use the non-comment operator
- $operators->{'content'}->{matches} = \&_short_desc_matches;
- $operators->{'content'}->{notmatches} = \&_short_desc_matches;
-
- } else {
- # restore default content operator
- $operators->{'content'}->{matches} = $search_content_matches;
- $operators->{'content'}->{notmatches} = $search_content_matches;
- }
+ my ($self, $args) = @_;
+ my $search = $args->{'search'};
+ my $operators = $args->{'operators'};
+
+ my $cgi = Bugzilla->cgi;
+ my @comments = $cgi->param('comments');
+ my $exclude_comments = scalar(@comments) && !grep { $_ eq '1' } @comments;
+
+ if ( $cgi->param('query_format')
+ && $cgi->param('query_format') eq 'specific'
+ && $exclude_comments)
+ {
+ # use the non-comment operator
+ $operators->{'content'}->{matches} = \&_short_desc_matches;
+ $operators->{'content'}->{notmatches} = \&_short_desc_matches;
+
+ }
+ else {
+ # restore default content operator
+ $operators->{'content'}->{matches} = $search_content_matches;
+ $operators->{'content'}->{notmatches} = $search_content_matches;
+ }
}
sub _short_desc_matches {
- # copy of Bugzilla::Search::_content_matches with comment searching removed
- my ($self, $args) = @_;
- my ($chart_id, $joins, $fields, $operator, $value) =
- @$args{qw(chart_id joins fields operator value)};
- my $dbh = Bugzilla->dbh;
+ # copy of Bugzilla::Search::_content_matches with comment searching removed
- # Add the fulltext table to the query so we can search on it.
- my $table = "bugs_fulltext_$chart_id";
- push(@$joins, { table => 'bugs_fulltext', as => $table });
+ my ($self, $args) = @_;
+ my ($chart_id, $joins, $fields, $operator, $value)
+ = @$args{qw(chart_id joins fields operator value)};
+ my $dbh = Bugzilla->dbh;
- # Create search terms to add to the SELECT and WHERE clauses.
- my ($term, $rterm) =
- $dbh->sql_fulltext_search("$table.short_desc", $value, 2);
- $rterm = $term if !$rterm;
+ # Add the fulltext table to the query so we can search on it.
+ my $table = "bugs_fulltext_$chart_id";
+ push(@$joins, {table => 'bugs_fulltext', as => $table});
- # The term to use in the WHERE clause.
- if ($operator =~ /not/i) {
- $term = "NOT($term)";
- }
- $args->{term} = $term;
+ # Create search terms to add to the SELECT and WHERE clauses.
+ my ($term, $rterm) = $dbh->sql_fulltext_search("$table.short_desc", $value, 2);
+ $rterm = $term if !$rterm;
+
+ # The term to use in the WHERE clause.
+ if ($operator =~ /not/i) {
+ $term = "NOT($term)";
+ }
+ $args->{term} = $term;
+
+ my $current = $self->COLUMNS->{'relevance'}->{name};
+ $current = $current ? "$current + " : '';
- my $current = $self->COLUMNS->{'relevance'}->{name};
- $current = $current ? "$current + " : '';
- # For NOT searches, we just add 0 to the relevance.
- my $select_term = $operator =~ /not/ ? 0 : "($current$rterm)";
- $self->COLUMNS->{'relevance'}->{name} = $select_term;
+ # For NOT searches, we just add 0 to the relevance.
+ my $select_term = $operator =~ /not/ ? 0 : "($current$rterm)";
+ $self->COLUMNS->{'relevance'}->{name} = $select_term;
}
sub mailer_before_send {
- my ($self, $args) = @_;
- my $email = $args->{email};
+ my ($self, $args) = @_;
+ my $email = $args->{email};
- _log_sent_email($email);
+ _log_sent_email($email);
- # $bug->mentors is added by the Review extension
- if (Bugzilla::Bug->can('mentors')) {
- _add_mentors_header($email);
- }
+ # $bug->mentors is added by the Review extension
+ if (Bugzilla::Bug->can('mentors')) {
+ _add_mentors_header($email);
+ }
- # insert x-bugzilla headers into the body
- _inject_headers_into_body($email);
+ # insert x-bugzilla headers into the body
+ _inject_headers_into_body($email);
}
# Log a summary of bugmail sent to the syslog, for auditing and monitoring
sub _log_sent_email {
- my $email = shift;
+ my $email = shift;
- my $recipient = $email->header('to');
- return unless $recipient;
+ my $recipient = $email->header('to');
+ return unless $recipient;
- my $subject = $email->header('Subject');
+ my $subject = $email->header('Subject');
- my $bug_id = $email->header('X-Bugzilla-ID');
- if (!$bug_id && $subject =~ /[\[\(]Bug (\d+)/i) {
- $bug_id = $1;
- }
- $bug_id = $bug_id ? "bug-$bug_id" : '-';
-
- my $message_type;
- my $type = $email->header('X-Bugzilla-Type');
- my $reason = $email->header('X-Bugzilla-Reason');
- if ($type eq 'whine' || $type eq 'request' || $type eq 'admin') {
- $message_type = $type;
- } elsif ($reason && $reason ne 'None') {
- $message_type = $reason;
- } else {
- $message_type = $email->header('X-Bugzilla-Watch-Reason');
- }
- $message_type ||= $type || '?';
+ my $bug_id = $email->header('X-Bugzilla-ID');
+ if (!$bug_id && $subject =~ /[\[\(]Bug (\d+)/i) {
+ $bug_id = $1;
+ }
+ $bug_id = $bug_id ? "bug-$bug_id" : '-';
- $subject =~ s/[\[\(]Bug \d+[\]\)]\s*//;
+ my $message_type;
+ my $type = $email->header('X-Bugzilla-Type');
+ my $reason = $email->header('X-Bugzilla-Reason');
+ if ($type eq 'whine' || $type eq 'request' || $type eq 'admin') {
+ $message_type = $type;
+ }
+ elsif ($reason && $reason ne 'None') {
+ $message_type = $reason;
+ }
+ else {
+ $message_type = $email->header('X-Bugzilla-Watch-Reason');
+ }
+ $message_type ||= $type || '?';
- _syslog("[bugmail] $recipient ($message_type) $bug_id $subject");
+ $subject =~ s/[\[\(]Bug \d+[\]\)]\s*//;
+
+ _syslog("[bugmail] $recipient ($message_type) $bug_id $subject");
}
# Add X-Bugzilla-Mentors field to bugmail
sub _add_mentors_header {
- my $email = shift;
- return unless my $bug_id = $email->header('X-Bugzilla-ID');
- return unless my $bug = Bugzilla::Bug->new({ id => $bug_id, cache => 1 });
- return unless my $mentors = $bug->mentors;
- return unless @$mentors;
- $email->header_set('X-Bugzilla-Mentors', join(', ', map { $_->login } @$mentors));
+ my $email = shift;
+ return unless my $bug_id = $email->header('X-Bugzilla-ID');
+ return unless my $bug = Bugzilla::Bug->new({id => $bug_id, cache => 1});
+ return unless my $mentors = $bug->mentors;
+ return unless @$mentors;
+ $email->header_set('X-Bugzilla-Mentors',
+ join(', ', map { $_->login } @$mentors));
}
sub _inject_headers_into_body {
- my $email = shift;
- my $replacement = '';
-
- my $recipient = Bugzilla::User->new({ name => $email->header('To'), cache => 1 });
- if ($recipient
- && $recipient->settings->{headers_in_body}->{value} eq 'on')
- {
- my @headers;
- my $it = natatime(2, $email->header_pairs);
- while (my ($name, $value) = $it->()) {
- next unless $name =~ /^X-Bugzilla-(.+)/;
- if ($name eq 'X-Bugzilla-Flags' || $name eq 'X-Bugzilla-Changed-Field-Names') {
- # these are multi-value fields, split on space
- foreach my $v (split(/\s+/, $value)) {
- push @headers, "$name: $v";
- }
- }
- elsif ($name eq 'X-Bugzilla-Changed-Fields') {
- # cannot split on space for this field, because field names contain
- # spaces. instead work from a list of field names.
- my @fields =
- map { $_->description }
- @{ Bugzilla->fields };
- # these aren't real fields, but exist in the headers
- push @fields, ('Comment Created', 'Attachment Created');
- @fields =
- sort { length($b) <=> length($a) }
- @fields;
- while ($value ne '') {
- foreach my $field (@fields) {
- if ($value eq $field) {
- push @headers, "$name: $field";
- $value = '';
- last;
- }
- if (substr($value, 0, length($field) + 1) eq $field . ' ') {
- push @headers, "$name: $field";
- $value = substr($value, length($field) + 1);
- last;
- }
- }
- }
+ my $email = shift;
+ my $replacement = '';
+
+ my $recipient = Bugzilla::User->new({name => $email->header('To'), cache => 1});
+ if ($recipient && $recipient->settings->{headers_in_body}->{value} eq 'on') {
+ my @headers;
+ my $it = natatime(2, $email->header_pairs);
+ while (my ($name, $value) = $it->()) {
+ next unless $name =~ /^X-Bugzilla-(.+)/;
+ if ($name eq 'X-Bugzilla-Flags' || $name eq 'X-Bugzilla-Changed-Field-Names') {
+
+ # these are multi-value fields, split on space
+ foreach my $v (split(/\s+/, $value)) {
+ push @headers, "$name: $v";
+ }
+ }
+ elsif ($name eq 'X-Bugzilla-Changed-Fields') {
+
+ # cannot split on space for this field, because field names contain
+ # spaces. instead work from a list of field names.
+ my @fields = map { $_->description } @{Bugzilla->fields};
+
+ # these aren't real fields, but exist in the headers
+ push @fields, ('Comment Created', 'Attachment Created');
+ @fields = sort { length($b) <=> length($a) } @fields;
+ while ($value ne '') {
+ foreach my $field (@fields) {
+ if ($value eq $field) {
+ push @headers, "$name: $field";
+ $value = '';
+ last;
}
- else {
- push @headers, "$name: $value";
+ if (substr($value, 0, length($field) + 1) eq $field . ' ') {
+ push @headers, "$name: $field";
+ $value = substr($value, length($field) + 1);
+ last;
}
+ }
}
- $replacement = join("\n", @headers);
- }
-
- # update the message body
- if (scalar($email->parts) > 1) {
- $email->walk_parts(sub {
- my ($part) = @_;
-
- # skip top-level
- return if $part->parts > 1;
-
- # do not filter attachments such as patches, etc.
- return if
- $part->header('Content-Disposition')
- && $part->header('Content-Disposition') =~ /attachment/;
-
- # text/plain|html only
- return unless $part->content_type =~ /^text\/(?:html|plain)/;
-
- # hide in html content
- if ($replacement && $part->content_type =~ /^text\/html/) {
- $replacement = '<pre style="font-size: 0pt; color: #fff">' . $replacement . '</pre>';
- }
-
- # and inject
- _replace_placeholder_in_part($part, $replacement);
- });
+ }
+ else {
+ push @headers, "$name: $value";
+ }
+ }
+ $replacement = join("\n", @headers);
+ }
+
+ # update the message body
+ if (scalar($email->parts) > 1) {
+ $email->walk_parts(sub {
+ my ($part) = @_;
+
+ # skip top-level
+ return if $part->parts > 1;
+
+ # do not filter attachments such as patches, etc.
+ return
+ if $part->header('Content-Disposition')
+ && $part->header('Content-Disposition') =~ /attachment/;
+
+ # text/plain|html only
+ return unless $part->content_type =~ /^text\/(?:html|plain)/;
+
+ # hide in html content
+ if ($replacement && $part->content_type =~ /^text\/html/) {
+ $replacement
+ = '<pre style="font-size: 0pt; color: #fff">' . $replacement . '</pre>';
+ }
+
+ # and inject
+ _replace_placeholder_in_part($part, $replacement);
+ });
- # force Email::MIME to re-create all the parts. without this
- # as_string() doesn't return the updated body for multi-part sub-parts.
- $email->parts_set([ $email->subparts ]);
- }
- elsif (!$email->content_type
- || $email->content_type =~ /^text\/(?:html|plain)/)
- {
- # text-only email
- _replace_placeholder_in_part($email, $replacement);
- }
+ # force Email::MIME to re-create all the parts. without this
+ # as_string() doesn't return the updated body for multi-part sub-parts.
+ $email->parts_set([$email->subparts]);
+ }
+ elsif (!$email->content_type || $email->content_type =~ /^text\/(?:html|plain)/)
+ {
+ # text-only email
+ _replace_placeholder_in_part($email, $replacement);
+ }
}
sub _replace_placeholder_in_part {
- my ($part, $replacement) = @_;
+ my ($part, $replacement) = @_;
- _fix_encoding($part);
+ _fix_encoding($part);
- # replace
- my $placeholder = quotemeta('@@body-headers@@');
- my $body = $part->body_str;
- $body =~ s/$placeholder/$replacement/;
- $part->body_str_set($body);
+ # replace
+ my $placeholder = quotemeta('@@body-headers@@');
+ my $body = $part->body_str;
+ $body =~ s/$placeholder/$replacement/;
+ $part->body_str_set($body);
}
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 _syslog {
- my $message = shift;
- openlog('apache', 'cons,pid', 'local4');
- syslog('notice', encode_utf8($message));
- closelog();
+ my $message = shift;
+ openlog('apache', 'cons,pid', 'local4');
+ syslog('notice', encode_utf8($message));
+ closelog();
}
sub post_bug_after_creation {
- my ($self, $args) = @_;
- return unless my $format = Bugzilla->input_params->{format};
- my $bug = $args->{vars}->{bug};
-
- if ($format eq 'employee-incident'
- && $bug->component eq 'Server Operations: Desktop Issues')
- {
- $self->_post_employee_incident_bug($args);
- }
- elsif ($format eq 'swag') {
- $self->_post_gear_bug($args);
- }
- elsif ($format eq 'mozpr') {
- $self->_post_mozpr_bug($args);
- }
- elsif ($format eq 'dev-engagement-event') {
- $self->_post_dev_engagement($args);
- }
- elsif ($format eq 'shield-studies') {
- $self->_post_shield_studies($args);
- }
+ my ($self, $args) = @_;
+ return unless my $format = Bugzilla->input_params->{format};
+ my $bug = $args->{vars}->{bug};
+
+ if ( $format eq 'employee-incident'
+ && $bug->component eq 'Server Operations: Desktop Issues')
+ {
+ $self->_post_employee_incident_bug($args);
+ }
+ elsif ($format eq 'swag') {
+ $self->_post_gear_bug($args);
+ }
+ elsif ($format eq 'mozpr') {
+ $self->_post_mozpr_bug($args);
+ }
+ elsif ($format eq 'dev-engagement-event') {
+ $self->_post_dev_engagement($args);
+ }
+ elsif ($format eq 'shield-studies') {
+ $self->_post_shield_studies($args);
+ }
}
sub _post_employee_incident_bug {
- my ($self, $args) = @_;
- my $vars = $args->{vars};
- my $bug = $vars->{bug};
-
- my $error_mode_cache = Bugzilla->error_mode;
- Bugzilla->error_mode(ERROR_MODE_DIE);
-
- my $template = Bugzilla->template;
- my $cgi = Bugzilla->cgi;
+ my ($self, $args) = @_;
+ my $vars = $args->{vars};
+ my $bug = $vars->{bug};
+
+ my $error_mode_cache = Bugzilla->error_mode;
+ Bugzilla->error_mode(ERROR_MODE_DIE);
+
+ my $template = Bugzilla->template;
+ my $cgi = Bugzilla->cgi;
+
+ my ($investigate_bug, $ssh_key_bug);
+ my $old_user = Bugzilla->user;
+ eval {
+ Bugzilla->set_user(Bugzilla::User->new(
+ {name => Bugzilla->params->{'nobody_user'}}));
+ my $new_user = Bugzilla->user;
+
+ # HACK: User needs to be in the editbugs and primary bug's group to allow
+ # setting of dependencies.
+ $new_user->{'groups'} = [
+ Bugzilla::Group->new({name => 'editbugs'}),
+ Bugzilla::Group->new({name => 'infra'}),
+ Bugzilla::Group->new({name => 'infrasec'})
+ ];
- my ($investigate_bug, $ssh_key_bug);
- my $old_user = Bugzilla->user;
- eval {
- Bugzilla->set_user(Bugzilla::User->new({ name => Bugzilla->params->{'nobody_user'} }));
- my $new_user = Bugzilla->user;
-
- # HACK: User needs to be in the editbugs and primary bug's group to allow
- # setting of dependencies.
- $new_user->{'groups'} = [ Bugzilla::Group->new({ name => 'editbugs' }),
- Bugzilla::Group->new({ name => 'infra' }),
- Bugzilla::Group->new({ name => 'infrasec' }) ];
-
- my $recipients = { changer => $new_user };
- $vars->{original_reporter} = $old_user;
-
- my $comment;
- $cgi->param('display_action', '');
- $template->process('bug/create/comment-employee-incident.txt.tmpl', $vars, \$comment)
- || ThrowTemplateError($template->error());
-
- $investigate_bug = Bugzilla::Bug->create({
- short_desc => 'Investigate Lost Device',
- product => 'mozilla.org',
- component => 'Security Assurance: Incident',
- status_whiteboard => '[infrasec:incident]',
- bug_severity => 'critical',
- cc => [ 'jstevensen@mozilla.com' ],
- groups => [ 'infrasec' ],
- comment => $comment,
- op_sys => 'All',
- rep_platform => 'All',
- version => 'other',
- dependson => $bug->bug_id,
- });
- $bug->set_all({ blocked => { add => [ $investigate_bug->bug_id ] }});
- Bugzilla::BugMail::Send($investigate_bug->id, $recipients);
-
- Bugzilla->set_user($old_user);
- $vars->{original_reporter} = '';
- $comment = '';
- $cgi->param('display_action', 'ssh');
- $template->process('bug/create/comment-employee-incident.txt.tmpl', $vars, \$comment)
- || ThrowTemplateError($template->error());
-
- $ssh_key_bug = Bugzilla::Bug->create({
- short_desc => 'Disable/Regenerate SSH Key',
- product => $bug->product,
- component => $bug->component,
- bug_severity => 'critical',
- cc => $bug->cc,
- groups => [ map { $_->{name} } @{ $bug->groups } ],
- comment => $comment,
- op_sys => 'All',
- rep_platform => 'All',
- version => 'other',
- dependson => $bug->bug_id,
- });
- $bug->set_all({ blocked => { add => [ $ssh_key_bug->bug_id ] }});
- Bugzilla::BugMail::Send($ssh_key_bug->id, $recipients);
- };
- my $error = $@;
+ my $recipients = {changer => $new_user};
+ $vars->{original_reporter} = $old_user;
+
+ my $comment;
+ $cgi->param('display_action', '');
+ $template->process('bug/create/comment-employee-incident.txt.tmpl',
+ $vars, \$comment)
+ || ThrowTemplateError($template->error());
+
+ $investigate_bug = Bugzilla::Bug->create({
+ short_desc => 'Investigate Lost Device',
+ product => 'mozilla.org',
+ component => 'Security Assurance: Incident',
+ status_whiteboard => '[infrasec:incident]',
+ bug_severity => 'critical',
+ cc => ['jstevensen@mozilla.com'],
+ groups => ['infrasec'],
+ comment => $comment,
+ op_sys => 'All',
+ rep_platform => 'All',
+ version => 'other',
+ dependson => $bug->bug_id,
+ });
+ $bug->set_all({blocked => {add => [$investigate_bug->bug_id]}});
+ Bugzilla::BugMail::Send($investigate_bug->id, $recipients);
Bugzilla->set_user($old_user);
- Bugzilla->error_mode($error_mode_cache);
+ $vars->{original_reporter} = '';
+ $comment = '';
+ $cgi->param('display_action', 'ssh');
+ $template->process('bug/create/comment-employee-incident.txt.tmpl',
+ $vars, \$comment)
+ || ThrowTemplateError($template->error());
+
+ $ssh_key_bug = Bugzilla::Bug->create({
+ short_desc => 'Disable/Regenerate SSH Key',
+ product => $bug->product,
+ component => $bug->component,
+ bug_severity => 'critical',
+ cc => $bug->cc,
+ groups => [map { $_->{name} } @{$bug->groups}],
+ comment => $comment,
+ op_sys => 'All',
+ rep_platform => 'All',
+ version => 'other',
+ dependson => $bug->bug_id,
+ });
+ $bug->set_all({blocked => {add => [$ssh_key_bug->bug_id]}});
+ Bugzilla::BugMail::Send($ssh_key_bug->id, $recipients);
+ };
+ my $error = $@;
- if ($error || !$investigate_bug || !$ssh_key_bug) {
- warn "Failed to create additional employee-incident bug: $error" if $error;
- $vars->{'message'} = 'employee_incident_creation_failed';
- }
+ Bugzilla->set_user($old_user);
+ Bugzilla->error_mode($error_mode_cache);
+
+ if ($error || !$investigate_bug || !$ssh_key_bug) {
+ warn "Failed to create additional employee-incident bug: $error" if $error;
+ $vars->{'message'} = 'employee_incident_creation_failed';
+ }
}
sub _post_gear_bug {
- my ($self, $args) = @_;
- my $vars = $args->{vars};
- my $bug = $vars->{bug};
- my $input = Bugzilla->input_params;
-
- my ($team, $code) = $input->{teamcode} =~ /^(.+?) \((\d+)\)$/;
- my @request = (
- "Date Required: $input->{date_required}",
- "$input->{firstname} $input->{lastname}",
- $input->{email},
- $input->{mozspace},
- $team,
- $code,
- $input->{purpose},
- );
- my @recipient = (
- "$input->{shiptofirstname} $input->{shiptolastname}",
- $input->{shiptoemail},
- $input->{shiptoaddress1},
- $input->{shiptoaddress2},
- $input->{shiptocity},
- $input->{shiptostate},
- $input->{shiptopostcode},
- $input->{shiptocountry},
- "Phone: $input->{shiptophone}",
- $input->{shiptoidrut},
- );
-
- # the csv has 14 item fields
- my @items = map { trim($_) } split(/\n/, $input->{items});
- my @csv;
- while (@items) {
- my @batch;
- if (scalar(@items) > 14) {
- @batch = splice(@items, 0, 14);
- }
- else {
- @batch = @items;
- push @batch, '' for scalar(@items)..13;
- @items = ();
- }
- push @csv, [ @request, @batch, @recipient ];
+ my ($self, $args) = @_;
+ my $vars = $args->{vars};
+ my $bug = $vars->{bug};
+ my $input = Bugzilla->input_params;
+
+ my ($team, $code) = $input->{teamcode} =~ /^(.+?) \((\d+)\)$/;
+ my @request = (
+ "Date Required: $input->{date_required}",
+ "$input->{firstname} $input->{lastname}",
+ $input->{email}, $input->{mozspace}, $team, $code, $input->{purpose},
+ );
+ my @recipient = (
+ "$input->{shiptofirstname} $input->{shiptolastname}",
+ $input->{shiptoemail},
+ $input->{shiptoaddress1},
+ $input->{shiptoaddress2},
+ $input->{shiptocity},
+ $input->{shiptostate},
+ $input->{shiptopostcode},
+ $input->{shiptocountry},
+ "Phone: $input->{shiptophone}",
+ $input->{shiptoidrut},
+ );
+
+ # the csv has 14 item fields
+ my @items = map { trim($_) } split(/\n/, $input->{items});
+ my @csv;
+ while (@items) {
+ my @batch;
+ if (scalar(@items) > 14) {
+ @batch = splice(@items, 0, 14);
+ }
+ else {
+ @batch = @items;
+ push @batch, '' for scalar(@items) .. 13;
+ @items = ();
}
+ push @csv, [@request, @batch, @recipient];
+ }
- # csv quoting and concat
- foreach my $line (@csv) {
- foreach my $field (@$line) {
- if ($field =~ s/"/""/g || $field =~ /,/) {
- $field = qq#"$field"#;
- }
- }
- $line = join(',', @$line);
+ # csv quoting and concat
+ foreach my $line (@csv) {
+ foreach my $field (@$line) {
+ if ($field =~ s/"/""/g || $field =~ /,/) {
+ $field = qq#"$field"#;
+ }
}
+ $line = join(',', @$line);
+ }
- $self->_add_attachment($args, {
- data => join("\n", @csv),
- description => "Items (CSV)",
- filename => "gear_" . $bug->id . ".csv",
- mimetype => "text/csv",
- });
- $bug->update($bug->creation_ts);
+ $self->_add_attachment(
+ $args,
+ {
+ data => join("\n", @csv),
+ description => "Items (CSV)",
+ filename => "gear_" . $bug->id . ".csv",
+ mimetype => "text/csv",
+ }
+ );
+ $bug->update($bug->creation_ts);
}
sub _post_mozpr_bug {
- my ($self, $args) = @_;
- my $vars = $args->{vars};
- my $bug = $vars->{bug};
- my $input = Bugzilla->input_params;
-
- if ($input->{proj_mat_file}) {
- $self->_add_attachment($args, {
- data => $input->{proj_mat_file_attach},
- description => $input->{proj_mat_file_desc},
- filename => scalar $input->{proj_mat_file_attach},
- });
- }
- if ($input->{pr_mat_file}) {
- $self->_add_attachment($args, {
- data => $input->{pr_mat_file_attach},
- description => $input->{pr_mat_file_desc},
- filename => scalar $input->{pr_mat_file_attach},
- });
- }
- $bug->update($bug->creation_ts);
+ my ($self, $args) = @_;
+ my $vars = $args->{vars};
+ my $bug = $vars->{bug};
+ my $input = Bugzilla->input_params;
+
+ if ($input->{proj_mat_file}) {
+ $self->_add_attachment(
+ $args,
+ {
+ data => $input->{proj_mat_file_attach},
+ description => $input->{proj_mat_file_desc},
+ filename => scalar $input->{proj_mat_file_attach},
+ }
+ );
+ }
+ if ($input->{pr_mat_file}) {
+ $self->_add_attachment(
+ $args,
+ {
+ data => $input->{pr_mat_file_attach},
+ description => $input->{pr_mat_file_desc},
+ filename => scalar $input->{pr_mat_file_attach},
+ }
+ );
+ }
+ $bug->update($bug->creation_ts);
}
sub _post_dev_engagement {
- my ($self, $args) = @_;
- my $vars = $args->{vars};
- my $parent_bug = $vars->{bug};
- my $template = Bugzilla->template;
- my $cgi = Bugzilla->cgi;
- my $params = Bugzilla->input_params;
- my $old_user = Bugzilla->user;
-
- my $error_mode_cache = Bugzilla->error_mode;
- Bugzilla->error_mode(ERROR_MODE_DIE);
-
- eval {
- # Add attachment containing tab delimited field values for
- # spreadsheet import.
- my @columns = qw(event start_date end_date location attendees
- audience desc mozilla_attending_list);
- my @attach_values;
- foreach my $column(@columns) {
- my $value = $params->{$column} || "";
- $value =~ s/"/""/g;
- push(@attach_values, qq{"$value"});
- }
-
- my @requested;
- foreach my $param (grep(/^request_/, keys %$params)) {
- next if !$params->{$param} || $param eq 'request_other_text';
- $param =~ s/^request_//;
- push(@requested, ucfirst($param));
- }
- push(@attach_values, '"' . join(",", @requested) . '"');
-
- # we wrap the data inside a textarea to allow for the delimited data to
- # be pasted directly into google docs.
-
- my $values = html_quote(join("\t", @attach_values));
- my $data = <<EOF;
+ my ($self, $args) = @_;
+ my $vars = $args->{vars};
+ my $parent_bug = $vars->{bug};
+ my $template = Bugzilla->template;
+ my $cgi = Bugzilla->cgi;
+ my $params = Bugzilla->input_params;
+ my $old_user = Bugzilla->user;
+
+ my $error_mode_cache = Bugzilla->error_mode;
+ Bugzilla->error_mode(ERROR_MODE_DIE);
+
+ eval {
+ # Add attachment containing tab delimited field values for
+ # spreadsheet import.
+ my @columns = qw(event start_date end_date location attendees
+ audience desc mozilla_attending_list);
+ my @attach_values;
+ foreach my $column (@columns) {
+ my $value = $params->{$column} || "";
+ $value =~ s/"/""/g;
+ push(@attach_values, qq{"$value"});
+ }
+
+ my @requested;
+ foreach my $param (grep(/^request_/, keys %$params)) {
+ next if !$params->{$param} || $param eq 'request_other_text';
+ $param =~ s/^request_//;
+ push(@requested, ucfirst($param));
+ }
+ push(@attach_values, '"' . join(",", @requested) . '"');
+
+ # we wrap the data inside a textarea to allow for the delimited data to
+ # be pasted directly into google docs.
+
+ my $values = html_quote(join("\t", @attach_values));
+ my $data = <<EOF;
<!doctype html>
<html>
<head>
@@ -2196,651 +2235,728 @@ sub _post_dev_engagement {
</html>
EOF
- $self->_add_attachment($args, {
- data => $data,
- description => 'Spreadsheet Data',
- filename => 'dev_engagement_submission.html',
- mimetype => 'text/html',
- });
- };
+ $self->_add_attachment(
+ $args,
+ {
+ data => $data,
+ description => 'Spreadsheet Data',
+ filename => 'dev_engagement_submission.html',
+ mimetype => 'text/html',
+ }
+ );
+ };
- $parent_bug->update($parent_bug->creation_ts);
+ $parent_bug->update($parent_bug->creation_ts);
}
sub _post_shield_studies {
- my ($self, $args) = @_;
- my $vars = $args->{vars};
- my $parent_bug = $vars->{bug};
- my $params = Bugzilla->input_params;
- my (@dep_comment, @dep_errors, @send_mail);
-
- # Common parameters always passed to _file_child_bug
- # bug_data and template_suffix will be different for each bug
- my $child_params = {
- parent_bug => $parent_bug,
- template_vars => $vars,
- dep_comment => \@dep_comment,
- dep_errors => \@dep_errors,
- send_mail => \@send_mail,
- };
-
- # Study Validation Review
- $child_params->{'bug_data'} = {
- short_desc => '[SHIELD] Study Validation Review for ' . $params->{hypothesis},
- product => 'Shield',
- component => 'Shield Study',
- bug_severity => 'normal',
- op_sys => 'All',
- rep_platform => 'All',
- version => 'unspecified',
- blocked => $parent_bug->bug_id,
- };
- $child_params->{'template_suffix'} = 'validation-review';
- _file_child_bug($child_params);
-
- # Shipping Status
- $child_params->{'bug_data'} = {
- short_desc => '[SHIELD] Shipping Status for ' . $params->{hypothesis},
- product => 'Shield',
- component => 'Shield Study',
- bug_severity => 'normal',
- op_sys => 'All',
- rep_platform => 'All',
- version => 'unspecified',
- blocked => $parent_bug->bug_id,
- };
- $child_params->{'template_suffix'} = 'shipping-status';
-
- # Data Review
- _file_child_bug($child_params);
- $child_params->{'bug_data'} = {
- short_desc => '[SHIELD] Data Review for ' . $params->{hypothesis},
- product => 'Shield',
- component => 'Shield Study',
- bug_severity => 'normal',
- op_sys => 'All',
- rep_platform => 'All',
- version => 'unspecified',
- blocked => $parent_bug->bug_id,
- };
- $child_params->{'template_suffix'} = 'data-review';
- _file_child_bug($child_params);
-
- # Legal Review
- $child_params->{'bug_data'} = {
- short_desc => '[SHIELD] Legal Review for ' . $params->{hypothesis},
- product => 'Legal',
- component => 'Firefox',
- bug_severity => 'normal',
- op_sys => 'All',
- rep_platform => 'All',
- groups => [ 'mozilla-employee-confidential' ],
- version => 'unspecified',
- blocked => $parent_bug->bug_id,
- };
- $child_params->{'template_suffix'} = 'legal';
- _file_child_bug($child_params);
-
+ my ($self, $args) = @_;
+ my $vars = $args->{vars};
+ my $parent_bug = $vars->{bug};
+ my $params = Bugzilla->input_params;
+ my (@dep_comment, @dep_errors, @send_mail);
+
+ # Common parameters always passed to _file_child_bug
+ # bug_data and template_suffix will be different for each bug
+ my $child_params = {
+ parent_bug => $parent_bug,
+ template_vars => $vars,
+ dep_comment => \@dep_comment,
+ dep_errors => \@dep_errors,
+ send_mail => \@send_mail,
+ };
+
+ # Study Validation Review
+ $child_params->{'bug_data'} = {
+ short_desc => '[SHIELD] Study Validation Review for ' . $params->{hypothesis},
+ product => 'Shield',
+ component => 'Shield Study',
+ bug_severity => 'normal',
+ op_sys => 'All',
+ rep_platform => 'All',
+ version => 'unspecified',
+ blocked => $parent_bug->bug_id,
+ };
+ $child_params->{'template_suffix'} = 'validation-review';
+ _file_child_bug($child_params);
+
+ # Shipping Status
+ $child_params->{'bug_data'} = {
+ short_desc => '[SHIELD] Shipping Status for ' . $params->{hypothesis},
+ product => 'Shield',
+ component => 'Shield Study',
+ bug_severity => 'normal',
+ op_sys => 'All',
+ rep_platform => 'All',
+ version => 'unspecified',
+ blocked => $parent_bug->bug_id,
+ };
+ $child_params->{'template_suffix'} = 'shipping-status';
+
+ # Data Review
+ _file_child_bug($child_params);
+ $child_params->{'bug_data'} = {
+ short_desc => '[SHIELD] Data Review for ' . $params->{hypothesis},
+ product => 'Shield',
+ component => 'Shield Study',
+ bug_severity => 'normal',
+ op_sys => 'All',
+ rep_platform => 'All',
+ version => 'unspecified',
+ blocked => $parent_bug->bug_id,
+ };
+ $child_params->{'template_suffix'} = 'data-review';
+ _file_child_bug($child_params);
+
+ # Legal Review
+ $child_params->{'bug_data'} = {
+ short_desc => '[SHIELD] Legal Review for ' . $params->{hypothesis},
+ product => 'Legal',
+ component => 'Firefox',
+ bug_severity => 'normal',
+ op_sys => 'All',
+ rep_platform => 'All',
+ groups => ['mozilla-employee-confidential'],
+ version => 'unspecified',
+ blocked => $parent_bug->bug_id,
+ };
+ $child_params->{'template_suffix'} = 'legal';
+ _file_child_bug($child_params);
+
+ if (scalar @dep_errors) {
+ warn "[Bug "
+ . $parent_bug->id
+ . "] Failed to create additional moz-project-review bugs:\n"
+ . join("\n", @dep_errors);
+ $vars->{'message'} = 'moz_project_review_creation_failed';
+ }
+
+ if (scalar @dep_comment) {
+ my $comment = join("\n", @dep_comment);
if (scalar @dep_errors) {
- warn "[Bug " . $parent_bug->id . "] Failed to create additional moz-project-review bugs:\n" .
- join("\n", @dep_errors);
- $vars->{'message'} = 'moz_project_review_creation_failed';
- }
-
- if (scalar @dep_comment) {
- my $comment = join("\n", @dep_comment);
- if (scalar @dep_errors) {
- $comment .= "\n\nSome errors occurred creating dependent bugs and have been recorded";
- }
- $parent_bug->add_comment($comment);
- $parent_bug->update($parent_bug->creation_ts);
+ $comment
+ .= "\n\nSome errors occurred creating dependent bugs and have been recorded";
}
+ $parent_bug->add_comment($comment);
+ $parent_bug->update($parent_bug->creation_ts);
+ }
- foreach my $bug_id (@send_mail) {
- Bugzilla::BugMail::Send($bug_id, { changer => Bugzilla->user });
- }
+ foreach my $bug_id (@send_mail) {
+ Bugzilla::BugMail::Send($bug_id, {changer => Bugzilla->user});
+ }
}
sub _file_child_bug {
- my ($params) = @_;
- my ($parent_bug, $template_vars, $template_suffix, $bug_data, $dep_comment, $dep_errors, $send_mail)
- = @$params{qw(parent_bug template_vars template_suffix bug_data dep_comment dep_errors send_mail)};
- my $old_error_mode = Bugzilla->error_mode;
- Bugzilla->error_mode(ERROR_MODE_DIE);
-
- my $new_bug;
- eval {
- my $comment;
- my $full_template = "bug/create/comment-shield-studies-$template_suffix.txt.tmpl";
- Bugzilla->template->process($full_template, $template_vars, \$comment)
- || ThrowTemplateError(Bugzilla->template->error());
- $bug_data->{'comment'} = $comment;
- if ($new_bug = Bugzilla::Bug->create($bug_data)) {
- my $set_all = {
- dependson => { add => [ $new_bug->bug_id ] }
- };
- $parent_bug->set_all($set_all);
- $parent_bug->update($parent_bug->creation_ts);
- }
+ my ($params) = @_;
+ my ($parent_bug, $template_vars, $template_suffix, $bug_data, $dep_comment,
+ $dep_errors, $send_mail)
+ = @$params{
+ qw(parent_bug template_vars template_suffix bug_data dep_comment dep_errors send_mail)
};
-
- if ($@ || !($new_bug && $new_bug->{'bug_id'})) {
- push(@$dep_comment, "Error creating $template_suffix review bug");
- push(@$dep_errors, "$template_suffix : $@") if $@;
- # Since we performed Bugzilla::Bug::create in an eval block, we
- # need to manually rollback the commit as this is not done
- # in Bugzilla::Error automatically for eval'ed code.
- Bugzilla->dbh->bz_rollback_transaction();
- }
- else {
- push(@$send_mail, $new_bug->id);
- push(@$dep_comment, "Bug " . $new_bug->id . " - " . $new_bug->short_desc);
+ my $old_error_mode = Bugzilla->error_mode;
+ Bugzilla->error_mode(ERROR_MODE_DIE);
+
+ my $new_bug;
+ eval {
+ my $comment;
+ my $full_template
+ = "bug/create/comment-shield-studies-$template_suffix.txt.tmpl";
+ Bugzilla->template->process($full_template, $template_vars, \$comment)
+ || ThrowTemplateError(Bugzilla->template->error());
+ $bug_data->{'comment'} = $comment;
+ if ($new_bug = Bugzilla::Bug->create($bug_data)) {
+ my $set_all = {dependson => {add => [$new_bug->bug_id]}};
+ $parent_bug->set_all($set_all);
+ $parent_bug->update($parent_bug->creation_ts);
}
+ };
+
+ if ($@ || !($new_bug && $new_bug->{'bug_id'})) {
+ push(@$dep_comment, "Error creating $template_suffix review bug");
+ push(@$dep_errors, "$template_suffix : $@") if $@;
+
+ # Since we performed Bugzilla::Bug::create in an eval block, we
+ # need to manually rollback the commit as this is not done
+ # in Bugzilla::Error automatically for eval'ed code.
+ Bugzilla->dbh->bz_rollback_transaction();
+ }
+ else {
+ push(@$send_mail, $new_bug->id);
+ push(@$dep_comment, "Bug " . $new_bug->id . " - " . $new_bug->short_desc);
+ }
- undef $@;
- Bugzilla->error_mode($old_error_mode);
+ undef $@;
+ Bugzilla->error_mode($old_error_mode);
}
sub _pre_fxos_feature {
- my ($self, $args) = @_;
- my $cgi = Bugzilla->cgi;
- my $user = Bugzilla->user;
- my $params = $args->{params};
+ my ($self, $args) = @_;
+ my $cgi = Bugzilla->cgi;
+ my $user = Bugzilla->user;
+ my $params = $args->{params};
- $params->{keywords} = 'foxfood';
- $params->{keywords} .= ',feature' if ($cgi->param('feature_type') // '') eq 'new';
- $params->{bug_status} = $user->in_group('canconfirm') ? 'NEW' : 'UNCONFIRMED';
+ $params->{keywords} = 'foxfood';
+ $params->{keywords} .= ',feature'
+ if ($cgi->param('feature_type') // '') eq 'new';
+ $params->{bug_status} = $user->in_group('canconfirm') ? 'NEW' : 'UNCONFIRMED';
}
sub _add_attachment {
- my ($self, $args, $attachment_args) = @_;
-
- my $bug = $args->{vars}->{bug};
- $attachment_args->{bug} = $bug;
- $attachment_args->{creation_ts} = $bug->creation_ts;
- $attachment_args->{ispatch} = 0 unless exists $attachment_args->{ispatch};
- $attachment_args->{isprivate} = 0 unless exists $attachment_args->{isprivate};
- $attachment_args->{mimetype} ||= $self->_detect_content_type($attachment_args->{data});
-
- # If the attachment cannot be successfully added to the bug,
- # we notify the user, but we don't interrupt the bug creation process.
- my $old_error_mode = Bugzilla->error_mode;
- Bugzilla->error_mode(ERROR_MODE_DIE);
- my $attachment;
- eval {
- $attachment = Bugzilla::Attachment->create($attachment_args);
- };
- warn "$@" if $@;
- Bugzilla->error_mode($old_error_mode);
-
- if ($attachment) {
- # Insert comment for attachment
- $bug->add_comment('', { isprivate => 0,
- type => CMT_ATTACHMENT_CREATED,
- extra_data => $attachment->id });
- delete $bug->{attachments};
- }
- else {
- $args->{vars}->{'message'} = 'attachment_creation_failed';
- }
+ my ($self, $args, $attachment_args) = @_;
+
+ my $bug = $args->{vars}->{bug};
+ $attachment_args->{bug} = $bug;
+ $attachment_args->{creation_ts} = $bug->creation_ts;
+ $attachment_args->{ispatch} = 0 unless exists $attachment_args->{ispatch};
+ $attachment_args->{isprivate} = 0 unless exists $attachment_args->{isprivate};
+ $attachment_args->{mimetype}
+ ||= $self->_detect_content_type($attachment_args->{data});
+
+ # If the attachment cannot be successfully added to the bug,
+ # we notify the user, but we don't interrupt the bug creation process.
+ my $old_error_mode = Bugzilla->error_mode;
+ Bugzilla->error_mode(ERROR_MODE_DIE);
+ my $attachment;
+ eval { $attachment = Bugzilla::Attachment->create($attachment_args); };
+ warn "$@" if $@;
+ Bugzilla->error_mode($old_error_mode);
+
+ if ($attachment) {
+
+ # Insert comment for attachment
+ $bug->add_comment('',
+ {isprivate => 0, type => CMT_ATTACHMENT_CREATED, extra_data => $attachment->id}
+ );
+ delete $bug->{attachments};
+ }
+ else {
+ $args->{vars}->{'message'} = 'attachment_creation_failed';
+ }
- # Note: you must call $bug->update($bug->creation_ts) after adding all attachments
+# Note: you must call $bug->update($bug->creation_ts) after adding all attachments
}
# bugzilla's content_type detection makes assumptions about form fields, which
# means we can't use it here. this code is lifted from
# Bugzilla::Attachment::get_content_type and the TypeSniffer extension.
sub _detect_content_type {
- my ($self, $data) = @_;
- my $cgi = Bugzilla->cgi;
-
- # browser provided content-type
- my $content_type = $cgi->uploadInfo($data)->{'Content-Type'};
- $content_type = 'image/png' if $content_type eq 'image/x-png';
-
- if ($content_type eq 'application/octet-stream') {
- # detect from filename
- my $filename = scalar($data);
- if (my $from_filename = mimetype($filename)) {
- return $from_filename;
- }
+ my ($self, $data) = @_;
+ my $cgi = Bugzilla->cgi;
+
+ # browser provided content-type
+ my $content_type = $cgi->uploadInfo($data)->{'Content-Type'};
+ $content_type = 'image/png' if $content_type eq 'image/x-png';
+
+ if ($content_type eq 'application/octet-stream') {
+
+ # detect from filename
+ my $filename = scalar($data);
+ if (my $from_filename = mimetype($filename)) {
+ return $from_filename;
}
+ }
- return $content_type || 'application/octet-stream';
+ return $content_type || 'application/octet-stream';
}
sub buglist_columns {
- my ($self, $args) = @_;
- my $columns = $args->{columns};
- $columns->{'cc_count'} = {
- name => '(SELECT COUNT(*) FROM cc WHERE cc.bug_id = bugs.bug_id)',
- title => 'CC Count',
- };
- $columns->{'dupe_count'} = {
- name => '(SELECT COUNT(*) FROM duplicates WHERE duplicates.dupe_of = bugs.bug_id)',
- title => 'Duplicate Count',
- };
+ my ($self, $args) = @_;
+ my $columns = $args->{columns};
+ $columns->{'cc_count'} = {
+ name => '(SELECT COUNT(*) FROM cc WHERE cc.bug_id = bugs.bug_id)',
+ title => 'CC Count',
+ };
+ $columns->{'dupe_count'} = {
+ name =>
+ '(SELECT COUNT(*) FROM duplicates WHERE duplicates.dupe_of = bugs.bug_id)',
+ title => 'Duplicate Count',
+ };
}
sub enter_bug_start {
- my ($self, $args) = @_;
- # if configured with create_bug_formats, force users into a custom bug
- # format (can be overridden with a __standard__ format)
- my $cgi = Bugzilla->cgi;
- if ($cgi->param('format')) {
- if ($cgi->param('format') eq '__standard__') {
- $cgi->delete('format');
- $cgi->param('format_forced', 1);
- }
- } elsif (my $format = forced_format($cgi->param('product'))) {
- $cgi->param('format', $format);
- }
-
- # If product eq 'mozilla.org' and format eq 'itrequest', then
- # switch to the new 'Infrastructure & Operations' product.
- if ($cgi->param('product') && $cgi->param('product') eq 'mozilla.org'
- && $cgi->param('format') && $cgi->param('format') eq 'itrequest')
- {
- $cgi->param('product', 'Infrastructure & Operations');
- }
-
- # map renamed groups
- $cgi->param('groups', _map_groups($cgi->param('groups')));
+ my ($self, $args) = @_;
+
+ # if configured with create_bug_formats, force users into a custom bug
+ # format (can be overridden with a __standard__ format)
+ my $cgi = Bugzilla->cgi;
+ if ($cgi->param('format')) {
+ if ($cgi->param('format') eq '__standard__') {
+ $cgi->delete('format');
+ $cgi->param('format_forced', 1);
+ }
+ }
+ elsif (my $format = forced_format($cgi->param('product'))) {
+ $cgi->param('format', $format);
+ }
+
+ # If product eq 'mozilla.org' and format eq 'itrequest', then
+ # switch to the new 'Infrastructure & Operations' product.
+ if ( $cgi->param('product')
+ && $cgi->param('product') eq 'mozilla.org'
+ && $cgi->param('format')
+ && $cgi->param('format') eq 'itrequest')
+ {
+ $cgi->param('product', 'Infrastructure & Operations');
+ }
+
+ # map renamed groups
+ $cgi->param('groups', _map_groups($cgi->param('groups')));
}
sub bug_before_create {
- my ($self, $args) = @_;
- my $params = $args->{params};
- if (exists $params->{groups}) {
- # map renamed groups
- $params->{groups} = [ _map_groups($params->{groups}) ];
- }
- if ((Bugzilla->cgi->param('format') // '') eq 'fxos-feature') {
- $self->_pre_fxos_feature($args);
- }
+ my ($self, $args) = @_;
+ my $params = $args->{params};
+ if (exists $params->{groups}) {
+
+ # map renamed groups
+ $params->{groups} = [_map_groups($params->{groups})];
+ }
+ if ((Bugzilla->cgi->param('format') // '') eq 'fxos-feature') {
+ $self->_pre_fxos_feature($args);
+ }
}
sub _map_groups {
- my (@groups) = @_;
- return unless @groups;
- @groups = @{ $groups[0] } if ref($groups[0]);
- return map {
- # map mozilla-corporation-confidential => mozilla-employee-confidential
- $_ eq 'mozilla-corporation-confidential'
- ? 'mozilla-employee-confidential'
- : $_
- } @groups;
+ my (@groups) = @_;
+ return unless @groups;
+ @groups = @{$groups[0]} if ref($groups[0]);
+ return map {
+
+ # map mozilla-corporation-confidential => mozilla-employee-confidential
+ $_ eq 'mozilla-corporation-confidential' ? 'mozilla-employee-confidential' : $_
+ } @groups;
}
sub forced_format {
- # note: this is also called from the guided bug entry extension
- my ($product) = @_;
- return undef unless defined $product;
-
- # always work on the correct product name
- $product = Bugzilla::Product->new({ name => $product, cache => 1 })
- unless blessed($product);
- return undef unless $product;
-
- # check for a forced-format entry
- my $forced = $create_bug_formats{$product->name}
- || return;
-
- # should this user be included?
- my $user = Bugzilla->user;
- my $include = ref($forced->{include}) ? $forced->{include} : [ $forced->{include} ];
- foreach my $inc (@$include) {
- return $forced->{format} if $user->in_group($inc);
- }
- return undef;
+ # note: this is also called from the guided bug entry extension
+ my ($product) = @_;
+ return undef unless defined $product;
+
+ # always work on the correct product name
+ $product = Bugzilla::Product->new({name => $product, cache => 1})
+ unless blessed($product);
+ return undef unless $product;
+
+ # check for a forced-format entry
+ my $forced = $create_bug_formats{$product->name} || return;
+
+ # should this user be included?
+ my $user = Bugzilla->user;
+ my $include
+ = ref($forced->{include}) ? $forced->{include} : [$forced->{include}];
+ foreach my $inc (@$include) {
+ return $forced->{format} if $user->in_group($inc);
+ }
+
+ return undef;
}
sub query_database {
- my ($vars) = @_;
- my $cgi = Bugzilla->cgi;
- my $user = Bugzilla->user;
- my $template = Bugzilla->template;
-
- # validate group membership
- $user->in_group('query_database')
- || ThrowUserError('auth_failure', { group => 'query_database',
- action => 'access',
- object => 'query_database' });
-
- # read query
- my $input = Bugzilla->input_params;
- my $query = $input->{query};
- $vars->{query} = $query;
-
- if ($query) {
- # Only allow POST requests
- if ($cgi->request_method ne 'POST') {
- ThrowCodeError('illegal_request_method',
- { method => $cgi->request_method, accepted => ['POST'] });
- }
+ my ($vars) = @_;
+ my $cgi = Bugzilla->cgi;
+ my $user = Bugzilla->user;
+ my $template = Bugzilla->template;
- check_hash_token($input->{token}, ['query_database']);
- trick_taint($query);
- $vars->{executed} = 1;
+ # validate group membership
+ $user->in_group('query_database')
+ || ThrowUserError('auth_failure',
+ {group => 'query_database', action => 'access', object => 'query_database'});
- # add limit if missing
- if ($query !~ /\sLIMIT\s+\d+\s*$/si) {
- $query .= ' LIMIT 1000';
- $vars->{query} = $query;
- }
+ # read query
+ my $input = Bugzilla->input_params;
+ my $query = $input->{query};
+ $vars->{query} = $query;
- # log query
- _syslog(sprintf("[db_query] %s %s", $user->login, $query));
-
- # connect to database and execute
- # switching to the shadow db gives us a read-only connection
- my $dbh = Bugzilla->switch_to_shadow_db();
- my $sth;
- eval {
- $sth = $dbh->prepare($query);
- $sth->execute();
- };
- if ($@) {
- $vars->{sql_error} = $@;
- return;
- }
+ if ($query) {
- # build result
- my $columns = $sth->{NAME};
- my $rows;
- while (my @row = $sth->fetchrow_array) {
- push @$rows, \@row;
- }
+ # Only allow POST requests
+ if ($cgi->request_method ne 'POST') {
+ ThrowCodeError('illegal_request_method',
+ {method => $cgi->request_method, accepted => ['POST']});
+ }
- # return results
- $vars->{columns} = $columns;
- $vars->{rows} = $rows;
+ check_hash_token($input->{token}, ['query_database']);
+ trick_taint($query);
+ $vars->{executed} = 1;
- if ($input->{csv}) {
- print $cgi->header(-type=> 'text/csv',
- -content_disposition=> "attachment; filename=\"query_database.csv\"");
- $template->process("pages/query_database.csv.tmpl", $vars)
- || ThrowTemplateError($template->error());
- exit;
- }
+ # add limit if missing
+ if ($query !~ /\sLIMIT\s+\d+\s*$/si) {
+ $query .= ' LIMIT 1000';
+ $vars->{query} = $query;
}
+
+ # log query
+ _syslog(sprintf("[db_query] %s %s", $user->login, $query));
+
+ # connect to database and execute
+ # switching to the shadow db gives us a read-only connection
+ my $dbh = Bugzilla->switch_to_shadow_db();
+ my $sth;
+ eval {
+ $sth = $dbh->prepare($query);
+ $sth->execute();
+ };
+ if ($@) {
+ $vars->{sql_error} = $@;
+ return;
+ }
+
+ # build result
+ my $columns = $sth->{NAME};
+ my $rows;
+ while (my @row = $sth->fetchrow_array) {
+ push @$rows, \@row;
+ }
+
+ # return results
+ $vars->{columns} = $columns;
+ $vars->{rows} = $rows;
+
+ if ($input->{csv}) {
+ print $cgi->header(
+ -type => 'text/csv',
+ -content_disposition => "attachment; filename=\"query_database.csv\""
+ );
+ $template->process("pages/query_database.csv.tmpl", $vars)
+ || ThrowTemplateError($template->error());
+ exit;
+ }
+ }
}
# you can always file bugs into a product's default security group, as well as
# into any of the groups in @always_fileable_groups
sub _group_always_settable {
- my ($self, $group) = @_;
- return
- $group->name eq $self->default_security_group
- || ((grep { $_ eq $group->name } @always_fileable_groups) ? 1 : 0);
+ my ($self, $group) = @_;
+ return $group->name eq $self->default_security_group
+ || ((grep { $_ eq $group->name } @always_fileable_groups) ? 1 : 0);
}
sub _default_security_group {
- return $_[0]->default_security_group_obj->name;
+ return $_[0]->default_security_group_obj->name;
}
sub _default_security_group_obj {
- my $group_id = $_[0]->{security_group_id};
- if (!$group_id) {
- return Bugzilla::Group->new({ name => Bugzilla->params->{insidergroup}, cache => 1 });
- }
- return Bugzilla::Group->new({ id => $group_id, cache => 1 });
+ my $group_id = $_[0]->{security_group_id};
+ if (!$group_id) {
+ return Bugzilla::Group->new(
+ {name => Bugzilla->params->{insidergroup}, cache => 1});
+ }
+ return Bugzilla::Group->new({id => $group_id, cache => 1});
}
# called from the verify version, component, and group page.
# if we're making a group invalid, stuff the default group into the cgi param
# to make it checked by default.
sub _check_default_product_security_group {
- my ($self, $product, $invalid_groups, $optional_group_controls) = @_;
- return unless my $group = $product->default_security_group_obj;
- if (@$invalid_groups) {
- my $cgi = Bugzilla->cgi;
- my @groups = $cgi->param('groups');
- push @groups, $group->name unless grep { $_ eq $group->name } @groups;
- $cgi->param('groups', @groups);
- }
+ my ($self, $product, $invalid_groups, $optional_group_controls) = @_;
+ return unless my $group = $product->default_security_group_obj;
+ if (@$invalid_groups) {
+ my $cgi = Bugzilla->cgi;
+ my @groups = $cgi->param('groups');
+ push @groups, $group->name unless grep { $_ eq $group->name } @groups;
+ $cgi->param('groups', @groups);
+ }
}
sub install_filesystem {
- my ($self, $args) = @_;
- my $files = $args->{files};
- my $create_files = $args->{create_files};
- my $extensions_dir = bz_locations()->{extensionsdir};
- $create_files->{__lbheartbeat__} = {
- perms => Bugzilla::Install::Filesystem::WS_SERVE,
- overwrite => 1, # the original value for this was wrong, overwrite it
- contents => 'httpd OK',
- };
-
-
- # version.json needs to have a source attribute pointing to
- # our repository. We already have this information in the (static)
- # contribute.json file, so parse that in
- my $json = JSON::XS->new->pretty->utf8->canonical();
- my $contribute = eval {
- $json->decode(scalar read_file(bz_locations()->{cgi_path} . "/contribute.json"));
- };
-
- if (!$contribute) {
- die "Missing or invalid contribute.json file";
- }
-
- my $version_obj = {
- source => $contribute->{repository}{url},
- version => BUGZILLA_VERSION,
- commit => $ENV{CIRCLE_SHA1} // 'unknown',
- build => $ENV{CIRCLE_BUILD_URL} // 'unknown',
- };
-
- $create_files->{'version.json'} = {
- overwrite => 1,
- perms => Bugzilla::Install::Filesystem::WS_SERVE,
- contents => $json->encode($version_obj),
- };
-
- $files->{"$extensions_dir/BMO/bin/migrate-github-pull-requests.pl"} = {
- perms => Bugzilla::Install::Filesystem::OWNER_EXECUTE
- };
+ my ($self, $args) = @_;
+ my $files = $args->{files};
+ my $create_files = $args->{create_files};
+ my $extensions_dir = bz_locations()->{extensionsdir};
+ $create_files->{__lbheartbeat__} = {
+ perms => Bugzilla::Install::Filesystem::WS_SERVE,
+ overwrite => 1, # the original value for this was wrong, overwrite it
+ contents => 'httpd OK',
+ };
+
+
+ # version.json needs to have a source attribute pointing to
+ # our repository. We already have this information in the (static)
+ # contribute.json file, so parse that in
+ my $json = JSON::XS->new->pretty->utf8->canonical();
+ my $contribute = eval {
+ $json->decode(
+ scalar read_file(bz_locations()->{cgi_path} . "/contribute.json"));
+ };
+
+ if (!$contribute) {
+ die "Missing or invalid contribute.json file";
+ }
+
+ my $version_obj = {
+ source => $contribute->{repository}{url},
+ version => BUGZILLA_VERSION,
+ commit => $ENV{CIRCLE_SHA1} // 'unknown',
+ build => $ENV{CIRCLE_BUILD_URL} // 'unknown',
+ };
+
+ $create_files->{'version.json'} = {
+ overwrite => 1,
+ perms => Bugzilla::Install::Filesystem::WS_SERVE,
+ contents => $json->encode($version_obj),
+ };
+
+ $files->{"$extensions_dir/BMO/bin/migrate-github-pull-requests.pl"}
+ = {perms => Bugzilla::Install::Filesystem::OWNER_EXECUTE};
}
# "deleted" comment tag
sub config_modify_panels {
- my ($self, $args) = @_;
- push @{ $args->{panels}->{groupsecurity}->{params} }, {
- name => 'delete_comments_group',
- type => 's',
- choices => \&get_all_group_names,
- default => 'admin',
- checker => \&check_group
+ my ($self, $args) = @_;
+ push @{$args->{panels}->{groupsecurity}->{params}},
+ {
+ name => 'delete_comments_group',
+ type => 's',
+ choices => \&get_all_group_names,
+ default => 'admin',
+ checker => \&check_group
};
}
sub comment_after_add_tag {
- my ($self, $args) = @_;
- my $tag = $args->{tag};
- return unless lc($tag) eq 'deleted';
-
- my $group_name = Bugzilla->params->{delete_comments_group};
- if (!$group_name || !Bugzilla->user->in_group($group_name)) {
- ThrowUserError('auth_failure', { group => $group_name,
- action => 'delete',
- object => 'comments' });
- }
+ my ($self, $args) = @_;
+ my $tag = $args->{tag};
+ return unless lc($tag) eq 'deleted';
+
+ my $group_name = Bugzilla->params->{delete_comments_group};
+ if (!$group_name || !Bugzilla->user->in_group($group_name)) {
+ ThrowUserError('auth_failure',
+ {group => $group_name, action => 'delete', object => 'comments'});
+ }
}
sub comment_after_remove_tag {
- my ($self, $args) = @_;
- my $tag = $args->{tag};
- return unless lc($tag) eq 'deleted';
-
- my $group_name = Bugzilla->params->{delete_comments_group};
- if (!$group_name || !Bugzilla->user->in_group($group_name)) {
- ThrowUserError('auth_failure', { group => $group_name,
- action => 'delete',
- object => 'comments' });
- }
+ my ($self, $args) = @_;
+ my $tag = $args->{tag};
+ return unless lc($tag) eq 'deleted';
+
+ my $group_name = Bugzilla->params->{delete_comments_group};
+ if (!$group_name || !Bugzilla->user->in_group($group_name)) {
+ ThrowUserError('auth_failure',
+ {group => $group_name, action => 'delete', object => 'comments'});
+ }
}
BEGIN {
- *Bugzilla::Comment::has_tag = \&_comment_has_tag;
+ *Bugzilla::Comment::has_tag = \&_comment_has_tag;
}
sub _comment_has_tag {
- my ($self, $test_tag) = @_;
- $test_tag = lc($test_tag);
- foreach my $tag (@{ $self->tags }) {
- return 1 if lc($tag) eq $test_tag;
- }
- return 0;
+ my ($self, $test_tag) = @_;
+ $test_tag = lc($test_tag);
+ foreach my $tag (@{$self->tags}) {
+ return 1 if lc($tag) eq $test_tag;
+ }
+ return 0;
}
sub bug_comments {
- my ($self, $args) = @_;
- my $can_delete = Bugzilla->user->in_group(Bugzilla->params->{delete_comments_group});
- my $comments = $args->{comments};
- my @deleted = grep { $_->has_tag('deleted') } @$comments;
- while (my $comment = pop @deleted) {
- for (my $i = scalar(@$comments) - 1; $i >= 0; $i--) {
- if ($comment == $comments->[$i]) {
- if ($can_delete) {
- # don't remove comment from users who can "delete" them
- # just collapse it instead
- $comment->{collapsed} = 1;
- }
- else {
- # otherwise, remove it from the array
- splice(@$comments, $i, 1);
- }
- last;
- }
+ my ($self, $args) = @_;
+ my $can_delete
+ = Bugzilla->user->in_group(Bugzilla->params->{delete_comments_group});
+ my $comments = $args->{comments};
+ my @deleted = grep { $_->has_tag('deleted') } @$comments;
+ while (my $comment = pop @deleted) {
+ for (my $i = scalar(@$comments) - 1; $i >= 0; $i--) {
+ if ($comment == $comments->[$i]) {
+ if ($can_delete) {
+
+ # don't remove comment from users who can "delete" them
+ # just collapse it instead
+ $comment->{collapsed} = 1;
}
+ else {
+ # otherwise, remove it from the array
+ splice(@$comments, $i, 1);
+ }
+ last;
+ }
}
+ }
}
sub _split_crash_signature {
- my ($self, $vars) = @_;
- my $bug = $vars->{bug} // return;
- my $crash_signature = $bug->cf_crash_signature // return;
- return [
- grep { /\S/ }
- extract_multiple($crash_signature, [ sub { extract_bracketed($_[0], '[]') } ])
- ];
+ my ($self, $vars) = @_;
+ my $bug = $vars->{bug} // return;
+ my $crash_signature = $bug->cf_crash_signature // return;
+ return [grep {/\S/}
+ extract_multiple($crash_signature, [sub { extract_bracketed($_[0], '[]') }])];
}
sub enter_bug_entrydefaultvars {
- my ($self, $args) = @_;
- my $vars = $args->{vars};
- my $cgi = Bugzilla->cgi;
- return unless my $format = $cgi->param('format');
-
- if ($format eq 'fxos-feature') {
- $vars->{feature_type} = $cgi->param('feature_type');
- $vars->{description} = $cgi->param('description');
- $vars->{discussion} = $cgi->param('discussion');
- }
+ my ($self, $args) = @_;
+ my $vars = $args->{vars};
+ my $cgi = Bugzilla->cgi;
+ return unless my $format = $cgi->param('format');
+
+ if ($format eq 'fxos-feature') {
+ $vars->{feature_type} = $cgi->param('feature_type');
+ $vars->{description} = $cgi->param('description');
+ $vars->{discussion} = $cgi->param('discussion');
+ }
}
sub app_startup {
- my ($self, $args) = @_;
- my $app = $args->{app};
- my $r = $app->routes;
-
- $r->get(
- '/favicon.ico' => sub {
- my $c = shift;
- $c->reply->file(
- $c->app->home->child('extensions/BMO/web/images/favicon.ico')
- );
- }
+ my ($self, $args) = @_;
+ my $app = $args->{app};
+ my $r = $app->routes;
+
+ $r->get(
+ '/favicon.ico' => sub {
+ my $c = shift;
+ $c->reply->file($c->app->home->child('extensions/BMO/web/images/favicon.ico'));
+ }
+ );
+
+ $r->any('/:REWRITE_itrequest' => [REWRITE_itrequest => qr{form[\.:]itrequest}])
+ ->to('CGI#enter_bug_cgi' =>
+ {'product' => 'Infrastructure & Operations', 'format' => 'itrequest'});
+ $r->any('/:REWRITE_mozlist' => [REWRITE_mozlist => qr{form[\.:]mozlist}])
+ ->to(
+ 'CGI#enter_bug_cgi' => {'product' => 'mozilla.org', 'format' => 'mozlist'});
+ $r->any('/:REWRITE_poweredby' => [REWRITE_poweredby => qr{form[\.:]poweredby}])
+ ->to(
+ 'CGI#enter_bug_cgi' => {'product' => 'mozilla.org', 'format' => 'poweredby'});
+ $r->any(
+ '/:REWRITE_presentation' => [REWRITE_presentation => qr{form[\.:]presentation}])
+ ->to(
+ 'cgi#enter_bug_cgi' => {'product' => 'mozilla.org', 'format' => 'presentation'}
);
-
- $r->any( '/:REWRITE_itrequest' => [ REWRITE_itrequest => qr{form[\.:]itrequest} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Infrastructure & Operations', 'format' => 'itrequest' } );
- $r->any( '/:REWRITE_mozlist' => [ REWRITE_mozlist => qr{form[\.:]mozlist} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'mozilla.org', 'format' => 'mozlist' } );
- $r->any( '/:REWRITE_poweredby' => [ REWRITE_poweredby => qr{form[\.:]poweredby} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'mozilla.org', 'format' => 'poweredby' } );
- $r->any( '/:REWRITE_presentation' => [ REWRITE_presentation => qr{form[\.:]presentation} ] )
- ->to( 'cgi#enter_bug_cgi' => { 'product' => 'mozilla.org', 'format' => 'presentation' } );
- $r->any( '/:REWRITE_trademark' => [ REWRITE_trademark => qr{form[\.:]trademark} ] )
- ->to( 'cgi#enter_bug_cgi' => { 'product' => 'mozilla.org', 'format' => 'trademark' } );
- $r->any( '/:REWRITE_recoverykey' => [ REWRITE_recoverykey => qr{form[\.:]recoverykey} ] )
- ->to( 'cgi#enter_bug_cgi' => { 'product' => 'mozilla.org', 'format' => 'recoverykey' } );
- $r->any( '/:REWRITE_legal' => [ REWRITE_legal => qr{form[\.:]legal} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Legal', 'format' => 'legal' }, );
- $r->any( '/:REWRITE_recruiting' => [ REWRITE_recruiting => qr{form[\.:]recruiting} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Recruiting', 'format' => 'recruiting' } );
- $r->any( '/:REWRITE_intern' => [ REWRITE_intern => qr{form[\.:]intern} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Recruiting', 'format' => 'intern' } );
- $r->any( '/:REWRITE_mozpr' => [ REWRITE_mozpr => qr{form[\.:]mozpr} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Mozilla PR', 'format' => 'mozpr' }, );
- $r->any( '/:REWRITE_reps_mentorship' => [ REWRITE_reps_mentorship => qr{form[\.:]reps[\.:]mentorship} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Mozilla Reps', 'format' => 'mozreps' }, );
- $r->any( '/:REWRITE_reps_budget' => [ REWRITE_reps_budget => qr{form[\.:]reps[\.:]budget} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Mozilla Reps', 'format' => 'remo-budget' } );
- $r->any( '/:REWRITE_reps_swag' => [ REWRITE_reps_swag => qr{form[\.:]reps[\.:]swag} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Mozilla Reps', 'format' => 'remo-swag' } );
- $r->any( '/:REWRITE_reps_it' => [ REWRITE_reps_it => qr{form[\.:]reps[\.:]it} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Mozilla Reps', 'format' => 'remo-it' } );
- $r->any( '/:REWRITE_reps_payment' => [ REWRITE_reps_payment => qr{form[\.:]reps[\.:]payment} ] )
- ->to( 'CGI#page_cgi' => { 'id' => 'remo-form-payment.html' } );
- $r->any( '/:REWRITE_csa_discourse' => [ REWRITE_csa_discourse => qr{form[\.:]csa[\.:]discourse} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Infrastructure & Operations', 'format' => 'csa-discourse' } );
- $r->any( '/:REWRITE_employee_incident' => [ REWRITE_employee_incident => qr{form[\.:]employee[\.\-:]incident} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'mozilla.org', 'format' => 'employee-incident' } );
- $r->any( '/:REWRITE_brownbag' => [ REWRITE_brownbag => qr{form[\.:]brownbag} ] )
- ->to( 'CGI#https_air_mozilla_org_requests' => {} );
- $r->any( '/:REWRITE_finance' => [ REWRITE_finance => qr{form[\.:]finance} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Finance', 'format' => 'finance' } );
- $r->any(
- '/:REWRITE_moz_project_review' => [ REWRITE_moz_project_review => qr{form[\.:]moz[\.\-:]project[\.\-:]review} ]
- )->to( 'CGI#enter_bug_cgi' => { 'product' => 'mozilla.org', 'format' => 'moz-project-review' } );
- $r->any( '/:REWRITE_docs' => [ REWRITE_docs => qr{form[\.:]docs?} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Developer Documentation', 'format' => 'doc' } );
- $r->any( '/:REWRITE_mdn' => [ REWRITE_mdn => qr{form[\.:]mdn?} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'format' => 'mdn', 'product' => 'developer.mozilla.org' } );
- $r->any( '/:REWRITE_swag_gear' => [ REWRITE_swag_gear => qr{form[\.:](swag|gear)} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'format' => 'swag', 'product' => 'Marketing' } );
- $r->any( '/:REWRITE_costume' => [ REWRITE_costume => qr{form[\.:]costume} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Marketing', 'format' => 'costume' } );
- $r->any( '/:REWRITE_ipp' => [ REWRITE_ipp => qr{form[\.:]ipp} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Internet Public Policy', 'format' => 'ipp' } );
- $r->any( '/:REWRITE_creative' => [ REWRITE_creative => qr{form[\.:]creative} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'format' => 'creative', 'product' => 'Marketing' } );
- $r->any( '/:REWRITE_user_engagement' => [ REWRITE_user_engagement => qr{form[\.:]user[\.\-:]engagement} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'format' => 'user-engagement', 'product' => 'Marketing' } );
- $r->any( '/:REWRITE_dev_engagement_event' =>
- [ REWRITE_dev_engagement_event => qr{form[\.:]dev[\.\-:]engagement[\.\-\:]event} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Developer Engagement', 'format' => 'dev-engagement-event' } );
- $r->any( '/:REWRITE_mobile_compat' => [ REWRITE_mobile_compat => qr{form[\.:]mobile[\.\-:]compat} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Tech Evangelism', 'format' => 'mobile-compat' } );
- $r->any( '/:REWRITE_web_bounty' => [ REWRITE_web_bounty => qr{form[\.:]web[\.:]bounty} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'format' => 'web-bounty', 'product' => 'mozilla.org' } );
- $r->any( '/:REWRITE_automative' => [ REWRITE_automative => qr{form[\.:]automative} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Testing', 'format' => 'automative' } );
- $r->any( '/:REWRITE_comm_newsletter' => [ REWRITE_comm_newsletter => qr{form[\.:]comm[\.:]newsletter} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'format' => 'comm-newsletter', 'product' => 'Marketing' } );
- $r->any( '/:REWRITE_screen_share_whitelist' =>
- [ REWRITE_screen_share_whitelist => qr{form[\.:]screen[\.:]share[\.:]whitelist} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'format' => 'screen-share-whitelist', 'product' => 'Firefox' } );
- $r->any( '/:REWRITE_data_compliance' => [ REWRITE_data_compliance => qr{form[\.:]data[\.\-:]compliance} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Data Compliance', 'format' => 'data-compliance' } );
- $r->any( '/:REWRITE_fsa_budget' => [ REWRITE_fsa_budget => qr{form[\.:]fsa[\.:]budget} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'FSA', 'format' => 'fsa-budget' } );
- $r->any( '/:REWRITE_triage_request' => [ REWRITE_triage_request => qr{form[\.:]triage[\.\-]request} ] )
- ->to( 'CGI#page_cgi' => { 'id' => 'triage_request.html' } );
- $r->any( '/:REWRITE_crm_CRM' => [ REWRITE_crm_CRM => qr{form[\.:](crm|CRM)} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'format' => 'crm', 'product' => 'Marketing' } );
- $r->any( '/:REWRITE_nda' => [ REWRITE_nda => qr{form[\.:]nda} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Legal', 'format' => 'nda' } );
- $r->any( '/:REWRITE_name_clearance' => [ REWRITE_name_clearance => qr{form[\.:]name[\.:]clearance} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'format' => 'name-clearance', 'product' => 'Legal' } );
- $r->any( '/:REWRITE_shield_studies' => [ REWRITE_shield_studies => qr{form[\.:]shield[\.:]studies} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Shield', 'format' => 'shield-studies' } );
- $r->any( '/:REWRITE_client_bounty' => [ REWRITE_client_bounty => qr{form[\.:]client[\.:]bounty} ] )
- ->to( 'CGI#enter_bug_cgi' => { 'product' => 'Firefox', 'format' => 'client-bounty' } );
+ $r->any('/:REWRITE_trademark' => [REWRITE_trademark => qr{form[\.:]trademark}])
+ ->to(
+ 'cgi#enter_bug_cgi' => {'product' => 'mozilla.org', 'format' => 'trademark'});
+ $r->any(
+ '/:REWRITE_recoverykey' => [REWRITE_recoverykey => qr{form[\.:]recoverykey}])
+ ->to(
+ 'cgi#enter_bug_cgi' => {'product' => 'mozilla.org', 'format' => 'recoverykey'});
+ $r->any('/:REWRITE_legal' => [REWRITE_legal => qr{form[\.:]legal}])
+ ->to('CGI#enter_bug_cgi' => {'product' => 'Legal', 'format' => 'legal'},);
+ $r->any(
+ '/:REWRITE_recruiting' => [REWRITE_recruiting => qr{form[\.:]recruiting}])
+ ->to(
+ 'CGI#enter_bug_cgi' => {'product' => 'Recruiting', 'format' => 'recruiting'});
+ $r->any('/:REWRITE_intern' => [REWRITE_intern => qr{form[\.:]intern}])
+ ->to(
+ 'CGI#enter_bug_cgi' => {'product' => 'Recruiting', 'format' => 'intern'});
+ $r->any('/:REWRITE_mozpr' => [REWRITE_mozpr => qr{form[\.:]mozpr}])
+ ->to('CGI#enter_bug_cgi' => {'product' => 'Mozilla PR', 'format' => 'mozpr'},
+ );
+ $r->any('/:REWRITE_reps_mentorship' =>
+ [REWRITE_reps_mentorship => qr{form[\.:]reps[\.:]mentorship}])
+ ->to(
+ 'CGI#enter_bug_cgi' => {'product' => 'Mozilla Reps', 'format' => 'mozreps'},);
+ $r->any('/:REWRITE_reps_budget' =>
+ [REWRITE_reps_budget => qr{form[\.:]reps[\.:]budget}])
+ ->to(
+ 'CGI#enter_bug_cgi' => {'product' => 'Mozilla Reps', 'format' => 'remo-budget'}
+ );
+ $r->any(
+ '/:REWRITE_reps_swag' => [REWRITE_reps_swag => qr{form[\.:]reps[\.:]swag}])
+ ->to(
+ 'CGI#enter_bug_cgi' => {'product' => 'Mozilla Reps', 'format' => 'remo-swag'});
+ $r->any('/:REWRITE_reps_it' => [REWRITE_reps_it => qr{form[\.:]reps[\.:]it}])
+ ->to(
+ 'CGI#enter_bug_cgi' => {'product' => 'Mozilla Reps', 'format' => 'remo-it'});
+ $r->any('/:REWRITE_reps_payment' =>
+ [REWRITE_reps_payment => qr{form[\.:]reps[\.:]payment}])
+ ->to('CGI#page_cgi' => {'id' => 'remo-form-payment.html'});
+ $r->any('/:REWRITE_csa_discourse' =>
+ [REWRITE_csa_discourse => qr{form[\.:]csa[\.:]discourse}])
+ ->to('CGI#enter_bug_cgi' =>
+ {'product' => 'Infrastructure & Operations', 'format' => 'csa-discourse'});
+ $r->any('/:REWRITE_employee_incident' =>
+ [REWRITE_employee_incident => qr{form[\.:]employee[\.\-:]incident}])
+ ->to('CGI#enter_bug_cgi' =>
+ {'product' => 'mozilla.org', 'format' => 'employee-incident'});
+ $r->any('/:REWRITE_brownbag' => [REWRITE_brownbag => qr{form[\.:]brownbag}])
+ ->to('CGI#https_air_mozilla_org_requests' => {});
+ $r->any('/:REWRITE_finance' => [REWRITE_finance => qr{form[\.:]finance}])
+ ->to('CGI#enter_bug_cgi' => {'product' => 'Finance', 'format' => 'finance'});
+ $r->any('/:REWRITE_moz_project_review' =>
+ [REWRITE_moz_project_review => qr{form[\.:]moz[\.\-:]project[\.\-:]review}])
+ ->to('CGI#enter_bug_cgi' =>
+ {'product' => 'mozilla.org', 'format' => 'moz-project-review'});
+ $r->any('/:REWRITE_docs' => [REWRITE_docs => qr{form[\.:]docs?}])
+ ->to('CGI#enter_bug_cgi' =>
+ {'product' => 'Developer Documentation', 'format' => 'doc'});
+ $r->any('/:REWRITE_mdn' => [REWRITE_mdn => qr{form[\.:]mdn?}])
+ ->to('CGI#enter_bug_cgi' =>
+ {'format' => 'mdn', 'product' => 'developer.mozilla.org'});
+ $r->any(
+ '/:REWRITE_swag_gear' => [REWRITE_swag_gear => qr{form[\.:](swag|gear)}])
+ ->to('CGI#enter_bug_cgi' => {'format' => 'swag', 'product' => 'Marketing'});
+ $r->any('/:REWRITE_costume' => [REWRITE_costume => qr{form[\.:]costume}])
+ ->to(
+ 'CGI#enter_bug_cgi' => {'product' => 'Marketing', 'format' => 'costume'});
+ $r->any('/:REWRITE_ipp' => [REWRITE_ipp => qr{form[\.:]ipp}])
+ ->to('CGI#enter_bug_cgi' =>
+ {'product' => 'Internet Public Policy', 'format' => 'ipp'});
+ $r->any('/:REWRITE_creative' => [REWRITE_creative => qr{form[\.:]creative}])
+ ->to(
+ 'CGI#enter_bug_cgi' => {'format' => 'creative', 'product' => 'Marketing'});
+ $r->any('/:REWRITE_user_engagement' =>
+ [REWRITE_user_engagement => qr{form[\.:]user[\.\-:]engagement}])
+ ->to('CGI#enter_bug_cgi' =>
+ {'format' => 'user-engagement', 'product' => 'Marketing'});
+ $r->any(
+ '/:REWRITE_dev_engagement_event' => [
+ REWRITE_dev_engagement_event => qr{form[\.:]dev[\.\-:]engagement[\.\-\:]event}
+ ]
+ )
+ ->to('CGI#enter_bug_cgi' =>
+ {'product' => 'Developer Engagement', 'format' => 'dev-engagement-event'});
+ $r->any('/:REWRITE_mobile_compat' =>
+ [REWRITE_mobile_compat => qr{form[\.:]mobile[\.\-:]compat}])
+ ->to('CGI#enter_bug_cgi' =>
+ {'product' => 'Tech Evangelism', 'format' => 'mobile-compat'});
+ $r->any(
+ '/:REWRITE_web_bounty' => [REWRITE_web_bounty => qr{form[\.:]web[\.:]bounty}])
+ ->to(
+ 'CGI#enter_bug_cgi' => {'format' => 'web-bounty', 'product' => 'mozilla.org'});
+ $r->any(
+ '/:REWRITE_automative' => [REWRITE_automative => qr{form[\.:]automative}])
+ ->to(
+ 'CGI#enter_bug_cgi' => {'product' => 'Testing', 'format' => 'automative'});
+ $r->any('/:REWRITE_comm_newsletter' =>
+ [REWRITE_comm_newsletter => qr{form[\.:]comm[\.:]newsletter}])
+ ->to('CGI#enter_bug_cgi' =>
+ {'format' => 'comm-newsletter', 'product' => 'Marketing'});
+ $r->any(
+ '/:REWRITE_screen_share_whitelist' => [
+ REWRITE_screen_share_whitelist => qr{form[\.:]screen[\.:]share[\.:]whitelist}
+ ]
+ )
+ ->to('CGI#enter_bug_cgi' =>
+ {'format' => 'screen-share-whitelist', 'product' => 'Firefox'});
+ $r->any('/:REWRITE_data_compliance' =>
+ [REWRITE_data_compliance => qr{form[\.:]data[\.\-:]compliance}])
+ ->to('CGI#enter_bug_cgi' =>
+ {'product' => 'Data Compliance', 'format' => 'data-compliance'});
+ $r->any(
+ '/:REWRITE_fsa_budget' => [REWRITE_fsa_budget => qr{form[\.:]fsa[\.:]budget}])
+ ->to('CGI#enter_bug_cgi' => {'product' => 'FSA', 'format' => 'fsa-budget'});
+ $r->any('/:REWRITE_triage_request' =>
+ [REWRITE_triage_request => qr{form[\.:]triage[\.\-]request}])
+ ->to('CGI#page_cgi' => {'id' => 'triage_request.html'});
+ $r->any('/:REWRITE_crm_CRM' => [REWRITE_crm_CRM => qr{form[\.:](crm|CRM)}])
+ ->to('CGI#enter_bug_cgi' => {'format' => 'crm', 'product' => 'Marketing'});
+ $r->any('/:REWRITE_nda' => [REWRITE_nda => qr{form[\.:]nda}])
+ ->to('CGI#enter_bug_cgi' => {'product' => 'Legal', 'format' => 'nda'});
+ $r->any('/:REWRITE_name_clearance' =>
+ [REWRITE_name_clearance => qr{form[\.:]name[\.:]clearance}])
+ ->to(
+ 'CGI#enter_bug_cgi' => {'format' => 'name-clearance', 'product' => 'Legal'});
+ $r->any('/:REWRITE_shield_studies' =>
+ [REWRITE_shield_studies => qr{form[\.:]shield[\.:]studies}])
+ ->to(
+ 'CGI#enter_bug_cgi' => {'product' => 'Shield', 'format' => 'shield-studies'});
+ $r->any('/:REWRITE_client_bounty' =>
+ [REWRITE_client_bounty => qr{form[\.:]client[\.:]bounty}])
+ ->to(
+ 'CGI#enter_bug_cgi' => {'product' => 'Firefox', 'format' => 'client-bounty'});
}
__PACKAGE__->NAME;
diff --git a/extensions/BMO/bin/bug_1022707.pl b/extensions/BMO/bin/bug_1022707.pl
index 4d48db01d..31afa7d05 100755
--- a/extensions/BMO/bin/bug_1022707.pl
+++ b/extensions/BMO/bin/bug_1022707.pl
@@ -37,8 +37,10 @@ print "About to fix $total flags\n";
print "Press <enter> to start, or ^C to cancel...\n";
readline;
-my $update_fsa_sql= "UPDATE flag_state_activity SET type_id = 4 WHERE " . $dbh->sql_in('flag_id', $flag_ids);
-my $update_flags_sql = "UPDATE flags SET type_id = 4 WHERE " . $dbh->sql_in('id', $flag_ids);
+my $update_fsa_sql = "UPDATE flag_state_activity SET type_id = 4 WHERE "
+ . $dbh->sql_in('flag_id', $flag_ids);
+my $update_flags_sql
+ = "UPDATE flags SET type_id = 4 WHERE " . $dbh->sql_in('id', $flag_ids);
$dbh->bz_start_transaction();
$dbh->do($update_fsa_sql);
diff --git a/extensions/BMO/bin/bug_1093952.pl b/extensions/BMO/bin/bug_1093952.pl
index fd891f4ae..f52427284 100755
--- a/extensions/BMO/bin/bug_1093952.pl
+++ b/extensions/BMO/bin/bug_1093952.pl
@@ -23,14 +23,17 @@ Bugzilla->usage_mode(USAGE_MODE_CMDLINE);
my $dbh = Bugzilla->dbh;
-my $infra = Bugzilla::Product->check({ name => 'Infrastructure & Operations' });
-my $relops_id = Bugzilla::Component->check({ product => $infra, name => 'RelOps' })->id;
-my $puppet_id = Bugzilla::Component->check({ product => $infra, name => 'RelOps: Puppet' })->id;
-my $infra_id = $infra->id;
-my $components = $dbh->sql_in('component_id', [ $relops_id, $puppet_id ]);
+my $infra = Bugzilla::Product->check({name => 'Infrastructure & Operations'});
+my $relops_id
+ = Bugzilla::Component->check({product => $infra, name => 'RelOps'})->id;
+my $puppet_id
+ = Bugzilla::Component->check({product => $infra, name => 'RelOps: Puppet'})
+ ->id;
+my $infra_id = $infra->id;
+my $components = $dbh->sql_in('component_id', [$relops_id, $puppet_id]);
print "Searching for bugs..\n";
-my $bugs = $dbh->selectall_arrayref(<<EOF, { Slice => {} });
+my $bugs = $dbh->selectall_arrayref(<<EOF, {Slice => {}});
SELECT
bug_id,
product_id,
@@ -52,9 +55,9 @@ printf "About to fix %s bugs\n", scalar(@$bugs);
print "Press <Ctrl-C> to stop or <Enter> to continue...\n";
getc();
-my $nobody = Bugzilla::User->check({ name => Bugzilla->params->{'nobody_user'} });
-my $field = Bugzilla::Field->check({ name => 'status_whiteboard' });
-my $when = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
+my $nobody = Bugzilla::User->check({name => Bugzilla->params->{'nobody_user'}});
+my $field = Bugzilla::Field->check({name => 'status_whiteboard'});
+my $when = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
my $sth_bugs = $dbh->prepare("
UPDATE bugs
@@ -70,22 +73,25 @@ my $sth_activity = $dbh->prepare("
$dbh->bz_start_transaction();
foreach my $bug (@$bugs) {
- my $bug_id = $bug->{bug_id};
- my $whiteboard = $bug->{status_whiteboard};
- print "bug $bug_id\n $whiteboard\n";
+ my $bug_id = $bug->{bug_id};
+ my $whiteboard = $bug->{status_whiteboard};
+ print "bug $bug_id\n $whiteboard\n";
- my $updated = $whiteboard;
- $updated =~ s#\[kanban:engops:https://kanbanize\.com/ctrl_board/6/[^\]]*\]\s*##g;
- if ($bug->{product_id} == $infra->id
- && $bug->{component_id} != $relops_id
- && $bug->{component_id} != $puppet_id
- ) {
- $updated =~ s#\[kanban:engops:https://mozilla\.kanbanize\.com/ctrl_board/6/[^\]]*\]\s*##g;
- }
- print " $updated\n";
+ my $updated = $whiteboard;
+ $updated
+ =~ s#\[kanban:engops:https://kanbanize\.com/ctrl_board/6/[^\]]*\]\s*##g;
+ if ( $bug->{product_id} == $infra->id
+ && $bug->{component_id} != $relops_id
+ && $bug->{component_id} != $puppet_id)
+ {
+ $updated
+ =~ s#\[kanban:engops:https://mozilla\.kanbanize\.com/ctrl_board/6/[^\]]*\]\s*##g;
+ }
+ print " $updated\n";
- $sth_bugs->execute($updated, $when, $when, $bug_id);
- $sth_activity->execute($bug_id, $nobody->id, $when, $field->id, $whiteboard, $updated);
+ $sth_bugs->execute($updated, $when, $when, $bug_id);
+ $sth_activity->execute($bug_id, $nobody->id, $when, $field->id, $whiteboard,
+ $updated);
}
$dbh->bz_commit_transaction();
diff --git a/extensions/BMO/bin/bug_1141452.pl b/extensions/BMO/bin/bug_1141452.pl
index 155c4704c..56b63db91 100755
--- a/extensions/BMO/bin/bug_1141452.pl
+++ b/extensions/BMO/bin/bug_1141452.pl
@@ -13,8 +13,8 @@ use 5.10.1;
use lib qw(. lib local/lib/perl5);
BEGIN {
- use Bugzilla;
- Bugzilla->extensions;
+ use Bugzilla;
+ Bugzilla->extensions;
}
use Bugzilla::Constants qw( USAGE_MODE_CMDLINE );
@@ -24,14 +24,17 @@ use Bugzilla::User;
Bugzilla->usage_mode(USAGE_MODE_CMDLINE);
my $dbh = Bugzilla->dbh;
-my $blocking_b2g = Bugzilla::Extension::TrackingFlags::Flag->check({ name => 'cf_blocking_b2g' });
-my $tracking_b2g = Bugzilla::Extension::TrackingFlags::Flag->check({ name => 'cf_tracking_b2g' });
+my $blocking_b2g = Bugzilla::Extension::TrackingFlags::Flag->check(
+ {name => 'cf_blocking_b2g'});
+my $tracking_b2g = Bugzilla::Extension::TrackingFlags::Flag->check(
+ {name => 'cf_tracking_b2g'});
die "tracking-b2g does not have a 'backlog' value\n"
- unless grep { $_->value eq 'backlog' } @{ $tracking_b2g->values };
+ unless grep { $_->value eq 'backlog' } @{$tracking_b2g->values};
print "Searching for bugs..\n";
-my $flags = $dbh->selectall_arrayref(<<EOF, { Slice => {} }, $blocking_b2g->flag_id, $tracking_b2g->flag_id);
+my $flags = $dbh->selectall_arrayref(
+ <<EOF, {Slice => {}}, $blocking_b2g->flag_id, $tracking_b2g->flag_id);
SELECT
bugs.bug_id,
blocking_b2g.id id,
@@ -50,54 +53,59 @@ printf "About to fix %s bugs\n", scalar(@$flags);
print "Press <Ctrl-C> to stop or <Enter> to continue...\n";
getc();
-my $nobody = Bugzilla::User->check({ name => Bugzilla->params->{'nobody_user'} });
-my $when = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
+my $nobody = Bugzilla::User->check({name => Bugzilla->params->{'nobody_user'}});
+my $when = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
$dbh->bz_start_transaction();
foreach my $flag (@$flags) {
- if (!$flag->{value}) {
- print $flag->{bug_id}, ": changing blocking_b2g:backlog -> tracking_b2g:backlog\n";
- # no tracking_b2g value, change blocking_b2g:backlog -> tracking_b2g:backlog
- $dbh->do(
- "UPDATE tracking_flags_bugs SET tracking_flag_id = ? WHERE id = ?",
- undef,
- $tracking_b2g->flag_id, $flag->{id},
- );
- $dbh->do(
- "UPDATE bugs SET delta_ts = ?, lastdiffed = ? WHERE bug_id = ?",
- undef,
- $when, $when, $flag->{bug_id},
- );
- $dbh->do(
- "INSERT INTO bugs_activity(bug_id, who, bug_when, fieldid, removed, added) VALUES (?, ?, ?, ?, ?, ?)",
- undef,
- $flag->{bug_id}, $nobody->id, $when, $blocking_b2g->id, 'backlog', '---',
- );
- $dbh->do(
- "INSERT INTO bugs_activity(bug_id, who, bug_when, fieldid, removed, added) VALUES (?, ?, ?, ?, ?, ?)",
- undef,
- $flag->{bug_id}, $nobody->id, $when, $tracking_b2g->id, '---', 'backlog',
- );
- }
- elsif ($flag->{value}) {
- print $flag->{bug_id}, ": deleting blocking_b2g:backlog\n";
- # tracking_b2g already has a value, just delete blocking_b2g:backlog
- $dbh->do(
- "DELETE FROM tracking_flags_bugs WHERE id = ?",
- undef,
- $flag->{id},
- );
- $dbh->do(
- "UPDATE bugs SET delta_ts = ?, lastdiffed = ? WHERE bug_id = ?",
- undef,
- $when, $when, $flag->{bug_id},
- );
- $dbh->do(
- "INSERT INTO bugs_activity(bug_id, who, bug_when, fieldid, removed, added) VALUES (?, ?, ?, ?, ?, ?)",
- undef,
- $flag->{bug_id}, $nobody->id, $when, $blocking_b2g->id, 'backlog', '---',
- );
- }
+ if (!$flag->{value}) {
+ print $flag->{bug_id},
+ ": changing blocking_b2g:backlog -> tracking_b2g:backlog\n";
+
+ # no tracking_b2g value, change blocking_b2g:backlog -> tracking_b2g:backlog
+ $dbh->do("UPDATE tracking_flags_bugs SET tracking_flag_id = ? WHERE id = ?",
+ undef, $tracking_b2g->flag_id, $flag->{id},);
+ $dbh->do("UPDATE bugs SET delta_ts = ?, lastdiffed = ? WHERE bug_id = ?",
+ undef, $when, $when, $flag->{bug_id},);
+ $dbh->do(
+ "INSERT INTO bugs_activity(bug_id, who, bug_when, fieldid, removed, added) VALUES (?, ?, ?, ?, ?, ?)",
+ undef,
+ $flag->{bug_id},
+ $nobody->id,
+ $when,
+ $blocking_b2g->id,
+ 'backlog',
+ '---',
+ );
+ $dbh->do(
+ "INSERT INTO bugs_activity(bug_id, who, bug_when, fieldid, removed, added) VALUES (?, ?, ?, ?, ?, ?)",
+ undef,
+ $flag->{bug_id},
+ $nobody->id,
+ $when,
+ $tracking_b2g->id,
+ '---',
+ 'backlog',
+ );
+ }
+ elsif ($flag->{value}) {
+ print $flag->{bug_id}, ": deleting blocking_b2g:backlog\n";
+
+ # tracking_b2g already has a value, just delete blocking_b2g:backlog
+ $dbh->do("DELETE FROM tracking_flags_bugs WHERE id = ?", undef, $flag->{id},);
+ $dbh->do("UPDATE bugs SET delta_ts = ?, lastdiffed = ? WHERE bug_id = ?",
+ undef, $when, $when, $flag->{bug_id},);
+ $dbh->do(
+ "INSERT INTO bugs_activity(bug_id, who, bug_when, fieldid, removed, added) VALUES (?, ?, ?, ?, ?, ?)",
+ undef,
+ $flag->{bug_id},
+ $nobody->id,
+ $when,
+ $blocking_b2g->id,
+ 'backlog',
+ '---',
+ );
+ }
}
$dbh->bz_commit_transaction();
diff --git a/extensions/BMO/bin/migrate-github-pull-requests.pl b/extensions/BMO/bin/migrate-github-pull-requests.pl
index c39778a4a..c8afdedfb 100755
--- a/extensions/BMO/bin/migrate-github-pull-requests.pl
+++ b/extensions/BMO/bin/migrate-github-pull-requests.pl
@@ -22,9 +22,9 @@ use Bugzilla::Install::Util qw(indicate_progress);
use Bugzilla::User;
use Bugzilla::Util qw(trim);
-my $dbh = Bugzilla->dbh;
-my $nobody = Bugzilla::User->check({ name => Bugzilla->params->{'nobody_user'} });
-my $field = Bugzilla::Field->check({ name => 'attachments.mimetype' });
+my $dbh = Bugzilla->dbh;
+my $nobody = Bugzilla::User->check({name => Bugzilla->params->{'nobody_user'}});
+my $field = Bugzilla::Field->check({name => 'attachments.mimetype'});
# grab list of suitable attachments
@@ -42,7 +42,7 @@ SELECT attachments.attach_id,
AND LENGTH(thedata) <= 256
EOF
print "Searching for suitable attachments..\n";
-my $attachments = $dbh->selectall_arrayref($sql, { Slice => {} });
+my $attachments = $dbh->selectall_arrayref($sql, {Slice => {}});
my ($current, $total, $updated) = (1, scalar(@$attachments), 0);
die "No suitable attachments found\n" unless $total;
@@ -52,39 +52,32 @@ print "Press <enter> to start, or ^C to cancel...\n";
<>;
foreach my $attachment (@$attachments) {
- indicate_progress({ current => $current++, total => $total, every => 25 });
-
- # check payload
- my $url = trim($attachment->{thedata});
- next if $url =~ /\s/;
- next unless $url =~ m#^https://github\.com/[^/]+/[^/]+/pull/\d+\/?$#i;
-
- $dbh->bz_start_transaction;
-
- # set content-type
- $dbh->do(
- "UPDATE attachments SET mimetype = ? WHERE attach_id = ?",
- undef,
- 'text/x-github-pull-request', $attachment->{attach_id}
- );
-
- # insert into bugs_activity
- my $timestamp = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
- $dbh->do(
- "INSERT INTO bugs_activity(bug_id, who, bug_when, fieldid, removed, added)
- VALUES (?, ?, ?, ?, ?, ?)",
- undef,
- $attachment->{bug_id}, $nobody->id, $timestamp, $field->id,
- $attachment->{mimetype}, 'text/x-github-pull-request'
- );
- $dbh->do(
- "UPDATE bugs SET delta_ts = ?, lastdiffed = ? WHERE bug_id = ?",
- undef,
- $timestamp, $timestamp, $attachment->{bug_id}
- );
-
- $dbh->bz_commit_transaction;
- $updated++;
+ indicate_progress({current => $current++, total => $total, every => 25});
+
+ # check payload
+ my $url = trim($attachment->{thedata});
+ next if $url =~ /\s/;
+ next unless $url =~ m#^https://github\.com/[^/]+/[^/]+/pull/\d+\/?$#i;
+
+ $dbh->bz_start_transaction;
+
+ # set content-type
+ $dbh->do("UPDATE attachments SET mimetype = ? WHERE attach_id = ?",
+ undef, 'text/x-github-pull-request', $attachment->{attach_id});
+
+ # insert into bugs_activity
+ my $timestamp = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
+ $dbh->do(
+ "INSERT INTO bugs_activity(bug_id, who, bug_when, fieldid, removed, added)
+ VALUES (?, ?, ?, ?, ?, ?)", undef, $attachment->{bug_id},
+ $nobody->id, $timestamp, $field->id, $attachment->{mimetype},
+ 'text/x-github-pull-request'
+ );
+ $dbh->do("UPDATE bugs SET delta_ts = ?, lastdiffed = ? WHERE bug_id = ?",
+ undef, $timestamp, $timestamp, $attachment->{bug_id});
+
+ $dbh->bz_commit_transaction;
+ $updated++;
}
print "Attachments updated: $updated\n";
diff --git a/extensions/BMO/lib/Constants.pm b/extensions/BMO/lib/Constants.pm
index 8227208c8..7ec92befb 100644
--- a/extensions/BMO/lib/Constants.pm
+++ b/extensions/BMO/lib/Constants.pm
@@ -13,8 +13,8 @@ use warnings;
use base qw(Exporter);
our @EXPORT = qw(
- REQUEST_MAX_ATTACH_LINES
- DEV_ENGAGE_DISCUSS_NEEDINFO
+ REQUEST_MAX_ATTACH_LINES
+ DEV_ENGAGE_DISCUSS_NEEDINFO
);
# Maximum attachment size in lines that will be sent with a
@@ -24,7 +24,7 @@ use constant REQUEST_MAX_ATTACH_LINES => 1000;
# Requestees who need a needinfo flag set for the dev engagement
# discussion bug
use constant DEV_ENGAGE_DISCUSS_NEEDINFO => qw(
- spersing@mozilla.com
+ spersing@mozilla.com
);
1;
diff --git a/extensions/BMO/lib/Data.pm b/extensions/BMO/lib/Data.pm
index 349f88093..a1e010346 100644
--- a/extensions/BMO/lib/Data.pm
+++ b/extensions/BMO/lib/Data.pm
@@ -15,13 +15,13 @@ use base qw(Exporter);
use Tie::IxHash;
our @EXPORT = qw( $cf_visible_in_products
- %group_change_notification
- $cf_setters
- @always_fileable_groups
- %group_auto_cc
- %create_bug_formats
- @default_named_queries
- %autodetect_attach_urls );
+ %group_change_notification
+ $cf_setters
+ @always_fileable_groups
+ %group_auto_cc
+ %create_bug_formats
+ @default_named_queries
+ %autodetect_attach_urls );
# Creating an attachment whose contents is a URL matching one of these regexes
# will result in the user being redirected to that URL when viewing the
@@ -43,35 +43,37 @@ my $mozreview_url_re = qr{
}ix;
sub phabricator_url_re {
- my $phab_uri = Bugzilla->params->{phabricator_base_uri} || 'https://example.com';
- return qr/^\Q${phab_uri}\ED\d+$/i;
+ my $phab_uri
+ = Bugzilla->params->{phabricator_base_uri} || 'https://example.com';
+ return qr/^\Q${phab_uri}\ED\d+$/i;
}
our %autodetect_attach_urls = (
- github_pr => {
- title => 'GitHub Pull Request',
- regex => qr#^https://github\.com/[^/]+/[^/]+/pull/\d+/?$#i,
- content_type => 'text/x-github-pull-request',
- can_review => 1,
- },
- reviewboard => {
- title => 'MozReview',
- regex => $mozreview_url_re,
- content_type => 'text/x-review-board-request',
- can_review => 0,
- },
- Phabricator => {
- title => 'Phabricator',
- regex => \&phabricator_url_re,
- content_type => 'text/x-phabricator-request',
- can_review => 1,
- },
- google_docs => {
- title => 'Google Doc',
- regex => qr#^https://docs\.google\.com/(?:document|spreadsheets|presentation)/d/#i,
- content_type => 'text/x-google-doc',
- can_review => 0,
- },
+ github_pr => {
+ title => 'GitHub Pull Request',
+ regex => qr#^https://github\.com/[^/]+/[^/]+/pull/\d+/?$#i,
+ content_type => 'text/x-github-pull-request',
+ can_review => 1,
+ },
+ reviewboard => {
+ title => 'MozReview',
+ regex => $mozreview_url_re,
+ content_type => 'text/x-review-board-request',
+ can_review => 0,
+ },
+ Phabricator => {
+ title => 'Phabricator',
+ regex => \&phabricator_url_re,
+ content_type => 'text/x-phabricator-request',
+ can_review => 1,
+ },
+ google_docs => {
+ title => 'Google Doc',
+ regex =>
+ qr#^https://docs\.google\.com/(?:document|spreadsheets|presentation)/d/#i,
+ content_type => 'text/x-google-doc',
+ can_review => 0,
+ },
);
# Which custom fields are visible in which products and components.
@@ -83,209 +85,184 @@ our %autodetect_attach_urls = (
#
# IxHash keeps them in insertion order, and so we get regexp priorities right.
our $cf_visible_in_products;
-tie(%$cf_visible_in_products, "Tie::IxHash",
- qr/^cf_colo_site$/ => {
- "mozilla.org" => [
- "Server Operations",
- "Server Operations: DCOps",
- "Server Operations: Projects",
- "Server Operations: RelEng",
- "Server Operations: Security",
- ],
- "Infrastructure & Operations" => [
- "RelOps",
- "RelOps: Puppet",
- "DCOps",
- ],
- },
- qr/^cf_office$/ => {
- "mozilla.org" => ["Server Operations: Desktop Issues"],
- },
- qr/^cf_crash_signature$/ => {
- "Add-on SDK" => [],
- "addons.mozilla.org" => [],
- "Android Background Services" => [],
- "B2GDroid" => [],
- "Calendar" => [],
- "Composer" => [],
- "Core" => [],
- "DevTools" => [],
- "Directory" => [],
- "External Software Affecting Firefox" => [],
- "Firefox" => [],
- "Firefox for Android" => [],
- "GeckoView" => [],
- "JSS" => [],
- "MailNews Core" => [],
- "Mozilla Labs" => [],
- "Mozilla Localizations" => [],
- "mozilla.org" => [],
- "Cloud Services" => [],
- "NSPR" => [],
- "NSS" => [],
- "Other Applications" => [],
- "Penelope" => [],
- "Release Engineering" => [],
- "Rhino" => [],
- "SeaMonkey" => [],
- "Tamarin" => [],
- "Tech Evangelism" => [],
- "Testing" => [],
- "Thunderbird" => [],
- "Toolkit" => [],
- "WebExtensions" => [],
- },
- qr/^cf_due_date$/ => {
- "bugzilla.mozilla.org" => [],
- "Community Building" => [],
- "Data & BI Services Team" => [],
- "Data Compliance" => [],
- "Developer Engagement" => [],
- "Firefox" => ["Security: Review Requests"],
- "Infrastructure & Operations" => [],
- "Marketing" => [],
- "mozilla.org" => ["Security Assurance: Review Request"],
- "Mozilla Metrics" => [],
- "Mozilla PR" => [],
- "Mozilla Reps" => [],
- },
- qr/^cf_locale$/ => {
- "Mozilla Localizations" => ['Other'],
- "www.mozilla.org" => [],
- },
- qr/^cf_mozilla_project$/ => {
- "Data & BI Services Team" => [],
- },
- qr/^cf_machine_state$/ => {
- "Release Engineering" => ["Buildduty"],
- },
- qr/^cf_rank$/ => {
- "Core" => [],
- "Firefox for Android" => [],
- "Firefox for iOS" => [],
- "Firefox" => [],
- "GeckoView" => [],
- "Hello (Loop)" => [],
- "Cloud Services" => [],
- "Tech Evangelism" => [],
- "Toolkit" => [],
- },
- qr/^cf_has_regression_range$/ => {
- "Core" => [],
- "Firefox for Android" => [],
- "Firefox for iOS" => [],
- "Firefox" => [],
- "GeckoView" => [],
- "Toolkit" => [],
- },
- qr/^cf_has_str$/ => {
- "Core" => [],
- "Firefox for Android" => [],
- "Firefox for iOS" => [],
- "Firefox" => [],
- "GeckoView" => [],
- "Toolkit" => [],
- },
- qr/^cf_cab_review$/ => {
- "Infrastructure & Operations Graveyard" => [],
- "Infrastructure & Operations" => [],
- "Data & BI Services Team" => [],
- }
+tie(
+ %$cf_visible_in_products,
+ "Tie::IxHash",
+ qr/^cf_colo_site$/ => {
+ "mozilla.org" => [
+ "Server Operations",
+ "Server Operations: DCOps",
+ "Server Operations: Projects",
+ "Server Operations: RelEng",
+ "Server Operations: Security",
+ ],
+ "Infrastructure & Operations" => ["RelOps", "RelOps: Puppet", "DCOps",],
+ },
+ qr/^cf_office$/ => {"mozilla.org" => ["Server Operations: Desktop Issues"],},
+ qr/^cf_crash_signature$/ => {
+ "Add-on SDK" => [],
+ "addons.mozilla.org" => [],
+ "Android Background Services" => [],
+ "B2GDroid" => [],
+ "Calendar" => [],
+ "Composer" => [],
+ "Core" => [],
+ "DevTools" => [],
+ "Directory" => [],
+ "External Software Affecting Firefox" => [],
+ "Firefox" => [],
+ "Firefox for Android" => [],
+ "GeckoView" => [],
+ "JSS" => [],
+ "MailNews Core" => [],
+ "Mozilla Labs" => [],
+ "Mozilla Localizations" => [],
+ "mozilla.org" => [],
+ "Cloud Services" => [],
+ "NSPR" => [],
+ "NSS" => [],
+ "Other Applications" => [],
+ "Penelope" => [],
+ "Release Engineering" => [],
+ "Rhino" => [],
+ "SeaMonkey" => [],
+ "Tamarin" => [],
+ "Tech Evangelism" => [],
+ "Testing" => [],
+ "Thunderbird" => [],
+ "Toolkit" => [],
+ "WebExtensions" => [],
+ },
+ qr/^cf_due_date$/ => {
+ "bugzilla.mozilla.org" => [],
+ "Community Building" => [],
+ "Data & BI Services Team" => [],
+ "Data Compliance" => [],
+ "Developer Engagement" => [],
+ "Firefox" => ["Security: Review Requests"],
+ "Infrastructure & Operations" => [],
+ "Marketing" => [],
+ "mozilla.org" => ["Security Assurance: Review Request"],
+ "Mozilla Metrics" => [],
+ "Mozilla PR" => [],
+ "Mozilla Reps" => [],
+ },
+ qr/^cf_locale$/ =>
+ {"Mozilla Localizations" => ['Other'], "www.mozilla.org" => [],},
+ qr/^cf_mozilla_project$/ => {"Data & BI Services Team" => [],},
+ qr/^cf_machine_state$/ => {"Release Engineering" => ["Buildduty"],},
+ qr/^cf_rank$/ => {
+ "Core" => [],
+ "Firefox for Android" => [],
+ "Firefox for iOS" => [],
+ "Firefox" => [],
+ "GeckoView" => [],
+ "Hello (Loop)" => [],
+ "Cloud Services" => [],
+ "Tech Evangelism" => [],
+ "Toolkit" => [],
+ },
+ qr/^cf_has_regression_range$/ => {
+ "Core" => [],
+ "Firefox for Android" => [],
+ "Firefox for iOS" => [],
+ "Firefox" => [],
+ "GeckoView" => [],
+ "Toolkit" => [],
+ },
+ qr/^cf_has_str$/ => {
+ "Core" => [],
+ "Firefox for Android" => [],
+ "Firefox for iOS" => [],
+ "Firefox" => [],
+ "GeckoView" => [],
+ "Toolkit" => [],
+ },
+ qr/^cf_cab_review$/ => {
+ "Infrastructure & Operations Graveyard" => [],
+ "Infrastructure & Operations" => [],
+ "Data & BI Services Team" => [],
+ }
);
# Who to CC on particular bugmails when certain groups are added or removed.
our %group_change_notification = (
- 'addons-security' => ['amo-editors@mozilla.org'],
- 'b2g-core-security' => ['security@mozilla.org'],
- 'bugzilla-security' => ['security@bugzilla.org'],
- 'client-services-security' => ['amo-admins@mozilla.org', 'web-security@mozilla.org'],
- 'cloud-services-security' => ['web-security@mozilla.org'],
- 'core-security' => ['security@mozilla.org'],
- 'crypto-core-security' => ['security@mozilla.org'],
- 'dom-core-security' => ['security@mozilla.org'],
- 'firefox-core-security' => ['security@mozilla.org'],
- 'gfx-core-security' => ['security@mozilla.org'],
- 'javascript-core-security' => ['security@mozilla.org'],
- 'layout-core-security' => ['security@mozilla.org'],
- 'mail-core-security' => ['security@mozilla.org'],
- 'media-core-security' => ['security@mozilla.org'],
- 'network-core-security' => ['security@mozilla.org'],
- 'core-security-release' => ['security@mozilla.org'],
- 'tamarin-security' => ['tamarinsecurity@adobe.com'],
- 'toolkit-core-security' => ['security@mozilla.org'],
- 'websites-security' => ['web-security@mozilla.org'],
- 'webtools-security' => ['web-security@mozilla.org'],
+ 'addons-security' => ['amo-editors@mozilla.org'],
+ 'b2g-core-security' => ['security@mozilla.org'],
+ 'bugzilla-security' => ['security@bugzilla.org'],
+ 'client-services-security' =>
+ ['amo-admins@mozilla.org', 'web-security@mozilla.org'],
+ 'cloud-services-security' => ['web-security@mozilla.org'],
+ 'core-security' => ['security@mozilla.org'],
+ 'crypto-core-security' => ['security@mozilla.org'],
+ 'dom-core-security' => ['security@mozilla.org'],
+ 'firefox-core-security' => ['security@mozilla.org'],
+ 'gfx-core-security' => ['security@mozilla.org'],
+ 'javascript-core-security' => ['security@mozilla.org'],
+ 'layout-core-security' => ['security@mozilla.org'],
+ 'mail-core-security' => ['security@mozilla.org'],
+ 'media-core-security' => ['security@mozilla.org'],
+ 'network-core-security' => ['security@mozilla.org'],
+ 'core-security-release' => ['security@mozilla.org'],
+ 'tamarin-security' => ['tamarinsecurity@adobe.com'],
+ 'toolkit-core-security' => ['security@mozilla.org'],
+ 'websites-security' => ['web-security@mozilla.org'],
+ 'webtools-security' => ['web-security@mozilla.org'],
);
# Who can set custom flags (use full field names only, not regex's)
-our $cf_setters = {
- 'cf_colo_site' => [ 'infra', 'build' ],
- 'cf_rank' => [ 'rank-setters' ],
-};
+our $cf_setters
+ = {'cf_colo_site' => ['infra', 'build'], 'cf_rank' => ['rank-setters'],};
# Groups in which you can always file a bug, regardless of product or user.
our @always_fileable_groups = qw(
- addons-security
- bugzilla-security
- client-services-security
- consulting
- core-security
- finance
- infra
- infrasec
- l20n-security
- marketing-private
- mozilla-confidential
- mozilla-employee-confidential
- mozilla-foundation-confidential
- mozilla-engagement
- mozilla-messaging-confidential
- partner-confidential
- payments-confidential
- tamarin-security
- websites-security
- webtools-security
+ addons-security
+ bugzilla-security
+ client-services-security
+ consulting
+ core-security
+ finance
+ infra
+ infrasec
+ l20n-security
+ marketing-private
+ mozilla-confidential
+ mozilla-employee-confidential
+ mozilla-foundation-confidential
+ mozilla-engagement
+ mozilla-messaging-confidential
+ partner-confidential
+ payments-confidential
+ tamarin-security
+ websites-security
+ webtools-security
);
# Automatically CC users to bugs filed into configured groups and products
our %group_auto_cc = (
- 'partner-confidential' => {
- 'Marketing' => ['jbalaco@mozilla.com'],
- '_default' => ['mbest@mozilla.com'],
- },
+ 'partner-confidential' => {
+ 'Marketing' => ['jbalaco@mozilla.com'],
+ '_default' => ['mbest@mozilla.com'],
+ },
);
# Force create-bug template by product
# Users in 'include' group will be forced into using the form.
our %create_bug_formats = (
- 'Data Compliance' => {
- 'format' => 'data-compliance',
- 'include' => 'everyone',
- },
- 'developer.mozilla.org' => {
- 'format' => 'mdn',
- 'include' => 'everyone',
- },
- 'Legal' => {
- 'format' => 'legal',
- 'include' => 'everyone',
- },
- 'Recruiting' => {
- 'format' => 'recruiting',
- 'include' => 'everyone',
- },
- 'Internet Public Policy' => {
- 'format' => 'ipp',
- 'include' => 'everyone',
- },
+ 'Data Compliance' => {'format' => 'data-compliance', 'include' => 'everyone',},
+ 'developer.mozilla.org' => {'format' => 'mdn', 'include' => 'everyone',},
+ 'Legal' => {'format' => 'legal', 'include' => 'everyone',},
+ 'Recruiting' => {'format' => 'recruiting', 'include' => 'everyone',},
+ 'Internet Public Policy' => {'format' => 'ipp', 'include' => 'everyone',},
);
# List of named queries which will be added to new users' footer
our @default_named_queries = (
- {
- name => 'Bugs Filed Today',
- query => 'query_format=advanced&chfieldto=Now&chfield=[Bug creation]&chfieldfrom=-24h&order=bug_id',
- },
+ {
+ name => 'Bugs Filed Today',
+ query =>
+ 'query_format=advanced&chfieldto=Now&chfield=[Bug creation]&chfieldfrom=-24h&order=bug_id',
+ },
);
1;
diff --git a/extensions/BMO/lib/FakeBug.pm b/extensions/BMO/lib/FakeBug.pm
index f84835ddd..5b5395619 100644
--- a/extensions/BMO/lib/FakeBug.pm
+++ b/extensions/BMO/lib/FakeBug.pm
@@ -12,32 +12,32 @@ use Bugzilla::Bug;
our $AUTOLOAD;
sub new {
- my $class = shift;
- my $self = shift;
- bless $self, $class;
- return $self;
+ my $class = shift;
+ my $self = shift;
+ bless $self, $class;
+ return $self;
}
sub AUTOLOAD {
- my $self = shift;
- my $name = $AUTOLOAD;
- $name =~ s/.*://;
- return exists $self->{$name} ? $self->{$name} : undef;
+ my $self = shift;
+ my $name = $AUTOLOAD;
+ $name =~ s/.*://;
+ return exists $self->{$name} ? $self->{$name} : undef;
}
sub check_can_change_field {
- my $self = shift;
- return Bugzilla::Bug::check_can_change_field($self, @_)
+ my $self = shift;
+ return Bugzilla::Bug::check_can_change_field($self, @_);
}
sub _changes_everconfirmed {
- my $self = shift;
- return Bugzilla::Bug::_changes_everconfirmed($self, @_)
+ my $self = shift;
+ return Bugzilla::Bug::_changes_everconfirmed($self, @_);
}
sub everconfirmed {
- my $self = shift;
- return ($self->{'status'} == 'UNCONFIRMED') ? 0 : 1;
+ my $self = shift;
+ return ($self->{'status'} == 'UNCONFIRMED') ? 0 : 1;
}
1;
diff --git a/extensions/BMO/lib/Reports/Groups.pm b/extensions/BMO/lib/Reports/Groups.pm
index ce7df767c..7b395aca9 100644
--- a/extensions/BMO/lib/Reports/Groups.pm
+++ b/extensions/BMO/lib/Reports/Groups.pm
@@ -19,25 +19,24 @@ use Bugzilla::Util qw(trim datetime_from);
use JSON qw(encode_json);
sub admins_report {
- my ($vars) = @_;
- my $dbh = Bugzilla->dbh;
- my $user = Bugzilla->user;
+ my ($vars) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->user;
- ($user->in_group('editbugs'))
- || ThrowUserError('auth_failure', { group => 'editbugs',
- action => 'run',
- object => 'group_admins' });
+ ($user->in_group('editbugs'))
+ || ThrowUserError('auth_failure',
+ {group => 'editbugs', action => 'run', object => 'group_admins'});
- my @grouplist =
- ($user->in_group('editusers') || $user->in_group('infrasec'))
- ? map { lc($_->name) } Bugzilla::Group->get_all
- : _get_permitted_membership_groups();
+ my @grouplist
+ = ($user->in_group('editusers') || $user->in_group('infrasec'))
+ ? map { lc($_->name) } Bugzilla::Group->get_all
+ : _get_permitted_membership_groups();
- my $groups = join(',', map { $dbh->quote($_) } @grouplist);
+ my $groups = join(',', map { $dbh->quote($_) } @grouplist);
- my $query = "
- SELECT groups.id, " .
- $dbh->sql_group_concat('profiles.userid', "','", 1) . "
+ my $query = "
+ SELECT groups.id, "
+ . $dbh->sql_group_concat('profiles.userid', "','", 1) . "
FROM groups
LEFT JOIN user_group_map
ON user_group_map.group_id = groups.id
@@ -49,271 +48,275 @@ sub admins_report {
AND groups.name IN ($groups)
GROUP BY groups.name";
- my @groups;
- foreach my $row (@{ $dbh->selectall_arrayref($query) }) {
- my $group = Bugzilla::Group->new({ id => shift @$row, cache => 1});
- my @admins;
- if (my $admin_ids = shift @$row) {
- foreach my $uid (split(/,/, $admin_ids)) {
- push(@admins, Bugzilla::User->new({ id => $uid, cache => 1 }));
- }
- }
- push(@groups, { name => $group->name,
- description => $group->description,
- owner => $group->owner,
- admins => \@admins });
+ my @groups;
+ foreach my $row (@{$dbh->selectall_arrayref($query)}) {
+ my $group = Bugzilla::Group->new({id => shift @$row, cache => 1});
+ my @admins;
+ if (my $admin_ids = shift @$row) {
+ foreach my $uid (split(/,/, $admin_ids)) {
+ push(@admins, Bugzilla::User->new({id => $uid, cache => 1}));
+ }
}
+ push(
+ @groups,
+ {
+ name => $group->name,
+ description => $group->description,
+ owner => $group->owner,
+ admins => \@admins
+ }
+ );
+ }
- $vars->{'groups'} = \@groups;
+ $vars->{'groups'} = \@groups;
}
sub membership_report {
- my ($page, $vars) = @_;
- my $dbh = Bugzilla->dbh;
- my $user = Bugzilla->user;
- my $cgi = Bugzilla->cgi;
-
- ($user->in_group('editusers') || $user->in_group('infrasec'))
- || ThrowUserError('auth_failure', { group => 'editusers',
- action => 'run',
- object => 'group_admins' });
-
- my $who = $cgi->param('who');
- if (!defined($who) || $who eq '') {
- if ($page eq 'group_membership.txt') {
- print $cgi->redirect("page.cgi?id=group_membership.html&output=txt");
- exit;
- }
- $vars->{'output'} = $cgi->param('output');
- return;
+ my ($page, $vars) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->user;
+ my $cgi = Bugzilla->cgi;
+
+ ($user->in_group('editusers') || $user->in_group('infrasec'))
+ || ThrowUserError('auth_failure',
+ {group => 'editusers', action => 'run', object => 'group_admins'});
+
+ my $who = $cgi->param('who');
+ if (!defined($who) || $who eq '') {
+ if ($page eq 'group_membership.txt') {
+ print $cgi->redirect("page.cgi?id=group_membership.html&output=txt");
+ exit;
}
+ $vars->{'output'} = $cgi->param('output');
+ return;
+ }
- Bugzilla::User::match_field({ 'who' => {'type' => 'multi'} });
- $who = Bugzilla->input_params->{'who'};
- $who = ref($who) ? $who : [ $who ];
+ Bugzilla::User::match_field({'who' => {'type' => 'multi'}});
+ $who = Bugzilla->input_params->{'who'};
+ $who = ref($who) ? $who : [$who];
- my @users;
- foreach my $login (@$who) {
- my $u = Bugzilla::User->new(login_to_id($login, 1));
+ my @users;
+ foreach my $login (@$who) {
+ my $u = Bugzilla::User->new(login_to_id($login, 1));
- # this is lifted from $user->groups()
- # we need to show which groups are direct and which are inherited
+ # this is lifted from $user->groups()
+ # we need to show which groups are direct and which are inherited
- my $groups_to_check = $dbh->selectcol_arrayref(
- q{SELECT DISTINCT group_id
+ my $groups_to_check = $dbh->selectcol_arrayref(
+ q{SELECT DISTINCT group_id
FROM user_group_map
- WHERE user_id = ? AND isbless = 0}, undef, $u->id);
+ WHERE user_id = ? AND isbless = 0}, undef, $u->id
+ );
- my $rows = $dbh->selectall_arrayref(
- "SELECT DISTINCT grantor_id, member_id
+ my $rows = $dbh->selectall_arrayref(
+ "SELECT DISTINCT grantor_id, member_id
FROM group_group_map
- WHERE grant_type = " . GROUP_MEMBERSHIP);
+ WHERE grant_type = " . GROUP_MEMBERSHIP
+ );
- my %group_membership;
- foreach my $row (@$rows) {
- my ($grantor_id, $member_id) = @$row;
- push (@{ $group_membership{$member_id} }, $grantor_id);
- }
+ my %group_membership;
+ foreach my $row (@$rows) {
+ my ($grantor_id, $member_id) = @$row;
+ push(@{$group_membership{$member_id}}, $grantor_id);
+ }
- my %checked_groups;
- my %direct_groups;
- my %indirect_groups;
- my %groups;
+ my %checked_groups;
+ my %direct_groups;
+ my %indirect_groups;
+ my %groups;
- foreach my $member_id (@$groups_to_check) {
- $direct_groups{$member_id} = 1;
- }
+ foreach my $member_id (@$groups_to_check) {
+ $direct_groups{$member_id} = 1;
+ }
- while (scalar(@$groups_to_check) > 0) {
- my $member_id = shift @$groups_to_check;
- if (!$checked_groups{$member_id}) {
- $checked_groups{$member_id} = 1;
- my $members = $group_membership{$member_id};
- my @new_to_check = grep(!$checked_groups{$_}, @$members);
- push(@$groups_to_check, @new_to_check);
- foreach my $id (@new_to_check) {
- $indirect_groups{$id} = $member_id;
- }
- $groups{$member_id} = 1;
- }
+ while (scalar(@$groups_to_check) > 0) {
+ my $member_id = shift @$groups_to_check;
+ if (!$checked_groups{$member_id}) {
+ $checked_groups{$member_id} = 1;
+ my $members = $group_membership{$member_id};
+ my @new_to_check = grep(!$checked_groups{$_}, @$members);
+ push(@$groups_to_check, @new_to_check);
+ foreach my $id (@new_to_check) {
+ $indirect_groups{$id} = $member_id;
}
+ $groups{$member_id} = 1;
+ }
+ }
- my @groups;
- my $ra_groups = Bugzilla::Group->new_from_list([keys %groups]);
- foreach my $group (@$ra_groups) {
- my $via;
- if ($direct_groups{$group->id}) {
- $via = '';
- } else {
- foreach my $g (@$ra_groups) {
- if ($g->id == $indirect_groups{$group->id}) {
- $via = $g->name;
- last;
- }
- }
- }
- push @groups, {
- name => $group->name,
- desc => $group->description,
- via => $via,
- };
+ my @groups;
+ my $ra_groups = Bugzilla::Group->new_from_list([keys %groups]);
+ foreach my $group (@$ra_groups) {
+ my $via;
+ if ($direct_groups{$group->id}) {
+ $via = '';
+ }
+ else {
+ foreach my $g (@$ra_groups) {
+ if ($g->id == $indirect_groups{$group->id}) {
+ $via = $g->name;
+ last;
+ }
}
-
- push @users, {
- user => $u,
- groups => \@groups,
- };
+ }
+ push @groups, {name => $group->name, desc => $group->description, via => $via,};
}
- $vars->{'who'} = $who;
- $vars->{'users'} = \@users;
+ push @users, {user => $u, groups => \@groups,};
+ }
+
+ $vars->{'who'} = $who;
+ $vars->{'users'} = \@users;
}
sub members_report {
- my ($page, $vars) = @_;
- my $dbh = Bugzilla->dbh;
- my $user = Bugzilla->user;
- my $cgi = Bugzilla->cgi;
-
- ($user->in_group('editbugs'))
- || ThrowUserError('auth_failure', { group => 'editbugs',
- action => 'run',
- object => 'group_admins' });
-
- my $privileged = $user->in_group('editusers') || $user->in_group('infrasec');
- $vars->{privileged} = $privileged;
-
- my @grouplist = $privileged
- ? map { lc($_->name) } Bugzilla::Group->get_all
- : _get_permitted_membership_groups();
-
- my $include_disabled = $cgi->param('include_disabled') ? 1 : 0;
- $vars->{'include_disabled'} = $include_disabled;
-
- # don't allow all groups, to avoid putting pain on the servers
- my @group_names =
- sort
- grep { !/^(?:bz_.+|canconfirm|editbugs|editbugs-team|everyone)$/ }
- @grouplist;
- unshift(@group_names, '');
- $vars->{'groups'} = \@group_names;
-
- # load selected group
- my $group = lc(trim($cgi->param('group') || ''));
- $group = '' unless grep { $_ eq $group } @group_names;
- return if $group eq '';
- my $group_obj = Bugzilla::Group->new({ name => $group });
- $vars->{'group'} = $group_obj;
-
- $vars->{'privileged'} = 1 if ($group_obj->owner && $group_obj->owner->id == $user->id);
-
- my @types;
- my $members = $group_obj->members_complete();
- foreach my $name (sort keys %$members) {
- push @types, {
- name => ($name eq '_direct' ? 'direct' : $name),
- members => _filter_userlist($members->{$name}),
+ my ($page, $vars) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->user;
+ my $cgi = Bugzilla->cgi;
+
+ ($user->in_group('editbugs'))
+ || ThrowUserError('auth_failure',
+ {group => 'editbugs', action => 'run', object => 'group_admins'});
+
+ my $privileged = $user->in_group('editusers') || $user->in_group('infrasec');
+ $vars->{privileged} = $privileged;
+
+ my @grouplist
+ = $privileged
+ ? map { lc($_->name) } Bugzilla::Group->get_all
+ : _get_permitted_membership_groups();
+
+ my $include_disabled = $cgi->param('include_disabled') ? 1 : 0;
+ $vars->{'include_disabled'} = $include_disabled;
+
+ # don't allow all groups, to avoid putting pain on the servers
+ my @group_names
+ = sort grep { !/^(?:bz_.+|canconfirm|editbugs|editbugs-team|everyone)$/ }
+ @grouplist;
+ unshift(@group_names, '');
+ $vars->{'groups'} = \@group_names;
+
+ # load selected group
+ my $group = lc(trim($cgi->param('group') || ''));
+ $group = '' unless grep { $_ eq $group } @group_names;
+ return if $group eq '';
+ my $group_obj = Bugzilla::Group->new({name => $group});
+ $vars->{'group'} = $group_obj;
+
+ $vars->{'privileged'} = 1
+ if ($group_obj->owner && $group_obj->owner->id == $user->id);
+
+ my @types;
+ my $members = $group_obj->members_complete();
+ foreach my $name (sort keys %$members) {
+ push @types,
+ {
+ name => ($name eq '_direct' ? 'direct' : $name),
+ members => _filter_userlist($members->{$name}),
+ };
+ }
+
+ # make it easy for the template to detect an empty group
+ my $has_members = 0;
+ foreach my $type (@types) {
+ $has_members += scalar(@{$type->{members}});
+ last if $has_members;
+ }
+ @types = () unless $has_members;
+
+ if ($page eq 'group_members.json') {
+ my %users;
+ foreach my $rh (@types) {
+ foreach my $member (@{$rh->{members}}) {
+ my $login = $member->login;
+ if (exists $users{$login}) {
+ push @{$users{$login}->{groups}}, $rh->{name} if $privileged;
}
- }
-
- # make it easy for the template to detect an empty group
- my $has_members = 0;
- foreach my $type (@types) {
- $has_members += scalar(@{ $type->{members} });
- last if $has_members;
- }
- @types = () unless $has_members;
-
- if ($page eq 'group_members.json') {
- my %users;
- foreach my $rh (@types) {
- foreach my $member (@{ $rh->{members} }) {
- my $login = $member->login;
- if (exists $users{$login}) {
- push @{ $users{$login}->{groups} }, $rh->{name} if $privileged;
- }
- else {
- my $rh_user = {
- login => $login,
- membership => $rh->{name} eq 'direct' ? 'direct' : 'indirect',
- rh_name => $rh->{name},
- };
- if ($privileged) {
- $rh_user->{group} = $rh->{name};
- $rh_user->{groups} = [ $rh->{name} ];
- $rh_user->{lastseeon} = $member->last_seen_date;
- $rh_user->{mfa} = $member->mfa;
- $rh_user->{api_key_only} = $member->settings->{api_key_only}->{value} eq 'on'
- ? JSON::true : JSON::false;
- }
- $users{$login} = $rh_user;
- }
- }
+ else {
+ my $rh_user = {
+ login => $login,
+ membership => $rh->{name} eq 'direct' ? 'direct' : 'indirect',
+ rh_name => $rh->{name},
+ };
+ if ($privileged) {
+ $rh_user->{group} = $rh->{name};
+ $rh_user->{groups} = [$rh->{name}];
+ $rh_user->{lastseeon} = $member->last_seen_date;
+ $rh_user->{mfa} = $member->mfa;
+ $rh_user->{api_key_only}
+ = $member->settings->{api_key_only}->{value} eq 'on'
+ ? JSON::true
+ : JSON::false;
+ }
+ $users{$login} = $rh_user;
}
- $vars->{types_json} = JSON->new->pretty->canonical->utf8->encode([ values %users ]);
+ }
}
- else {
- my %users;
- foreach my $rh (@types) {
- foreach my $member (@{ $rh->{members} }) {
- $users{$member->login} = 1 unless exists $users{$member->login};
- }
- }
- $vars->{types} = \@types;
- $vars->{count} = scalar(keys %users);
+ $vars->{types_json}
+ = JSON->new->pretty->canonical->utf8->encode([values %users]);
+ }
+ else {
+ my %users;
+ foreach my $rh (@types) {
+ foreach my $member (@{$rh->{members}}) {
+ $users{$member->login} = 1 unless exists $users{$member->login};
+ }
}
+ $vars->{types} = \@types;
+ $vars->{count} = scalar(keys %users);
+ }
}
sub _filter_userlist {
- my ($list, $include_disabled) = @_;
- $list = [ grep { $_->is_enabled } @$list ] unless $include_disabled;
- my $now = DateTime->now();
- my $never = DateTime->from_epoch( epoch => 0 );
- foreach my $user (@$list) {
- my $last_seen = $user->last_seen_date ? datetime_from($user->last_seen_date) : $never;
- $user->{last_seen_days} = sprintf(
- '%.0f',
- $now->subtract_datetime_absolute($last_seen)->delta_seconds / (28 * 60 * 60));
- }
- return [ sort { lc($a->identity) cmp lc($b->identity) } @$list ];
+ my ($list, $include_disabled) = @_;
+ $list = [grep { $_->is_enabled } @$list] unless $include_disabled;
+ my $now = DateTime->now();
+ my $never = DateTime->from_epoch(epoch => 0);
+ foreach my $user (@$list) {
+ my $last_seen
+ = $user->last_seen_date ? datetime_from($user->last_seen_date) : $never;
+ $user->{last_seen_days} = sprintf('%.0f',
+ $now->subtract_datetime_absolute($last_seen)->delta_seconds / (28 * 60 * 60));
+ }
+ return [sort { lc($a->identity) cmp lc($b->identity) } @$list];
}
# Groups that any user with editbugs can see the membership or admin lists for.
# Transparency FTW.
sub _get_permitted_membership_groups {
- my $user = Bugzilla->user;
-
- # Default publicly viewable groups
- my %default_public_groups = map { $_ => 1 } qw(
- bugzilla-approvers
- bugzilla-reviewers
- can_restrict_comments
- community-it-team
- mozilla-employee-confidential
- mozilla-foundation-confidential
- mozilla-reps
- qa-approvers
- );
-
- # We add the group to the permitted list if:
- # 1. it is a drivers group - this gives us a little
- # future-proofing
- # 2. it is a one of the default public groups
- # 3. the user is the group's owner
- # 4. or the user can bless others into the group
- my @permitted_groups;
- foreach my $group (Bugzilla::Group->get_all) {
- my $name = $group->name;
- if ($name =~ /-drivers$/
- || exists $default_public_groups{$name}
- || ($group->owner && $group->owner->id == $user->id)
- || $user->can_bless($group->id))
- {
- push(@permitted_groups, $name);
- }
+ my $user = Bugzilla->user;
+
+ # Default publicly viewable groups
+ my %default_public_groups = map { $_ => 1 } qw(
+ bugzilla-approvers
+ bugzilla-reviewers
+ can_restrict_comments
+ community-it-team
+ mozilla-employee-confidential
+ mozilla-foundation-confidential
+ mozilla-reps
+ qa-approvers
+ );
+
+ # We add the group to the permitted list if:
+ # 1. it is a drivers group - this gives us a little
+ # future-proofing
+ # 2. it is a one of the default public groups
+ # 3. the user is the group's owner
+ # 4. or the user can bless others into the group
+ my @permitted_groups;
+ foreach my $group (Bugzilla::Group->get_all) {
+ my $name = $group->name;
+ if ( $name =~ /-drivers$/
+ || exists $default_public_groups{$name}
+ || ($group->owner && $group->owner->id == $user->id)
+ || $user->can_bless($group->id))
+ {
+ push(@permitted_groups, $name);
}
+ }
- return @permitted_groups;
+ return @permitted_groups;
}
1;
diff --git a/extensions/BMO/lib/Reports/Internship.pm b/extensions/BMO/lib/Reports/Internship.pm
index 2dfa583a6..f9ad1a578 100644
--- a/extensions/BMO/lib/Reports/Internship.pm
+++ b/extensions/BMO/lib/Reports/Internship.pm
@@ -17,33 +17,30 @@ use Bugzilla::Product;
use Bugzilla::Component;
sub report {
- my ($vars) = @_;
- my $user = Bugzilla->user;
-
- $user->in_group('hr')
- || ThrowUserError('auth_failure', { group => 'hr',
- action => 'run',
- object => 'internship_dashboard' });
-
- my $product = Bugzilla::Product->check({ name => 'Recruiting', cache => 1 });
- my $component = Bugzilla::Component->new({ product => $product, name => 'Intern', cache => 1 });
-
- # find all open internship bugs
- my $bugs = Bugzilla::Bug->match({
- product_id => $product->id,
- component_id => $component->id,
- resolution => '',
- });
-
- # filter bugs based on visibility and re-bless
- $user->visible_bugs($bugs);
- $bugs = [
- map { bless($_, 'InternshipBug') }
- grep { $user->can_see_bug($_->id) }
- @$bugs
- ];
-
- $vars->{bugs} = $bugs;
+ my ($vars) = @_;
+ my $user = Bugzilla->user;
+
+ $user->in_group('hr')
+ || ThrowUserError('auth_failure',
+ {group => 'hr', action => 'run', object => 'internship_dashboard'});
+
+ my $product = Bugzilla::Product->check({name => 'Recruiting', cache => 1});
+ my $component = Bugzilla::Component->new(
+ {product => $product, name => 'Intern', cache => 1});
+
+ # find all open internship bugs
+ my $bugs = Bugzilla::Bug->match({
+ product_id => $product->id,
+ component_id => $component->id,
+ resolution => '',
+ });
+
+ # filter bugs based on visibility and re-bless
+ $user->visible_bugs($bugs);
+ $bugs = [map { bless($_, 'InternshipBug') }
+ grep { $user->can_see_bug($_->id) } @$bugs];
+
+ $vars->{bugs} = $bugs;
}
1;
@@ -58,64 +55,62 @@ use Bugzilla::Comment;
use Bugzilla::Util qw(trim);
sub _extract {
- my ($self) = @_;
- return if exists $self->{internship_data};
- $self->{internship_data} = {};
-
- # we only need the first comment
- my $comment = Bugzilla::Comment->match({
- bug_id => $self->id,
- LIMIT => 1,
- })->[0]->body;
-
- # extract just what we need
- # changing the comment will break this
-
- if ($comment =~ /Hiring Manager:\s+(.+)\nTeam:\n/s) {
- $self->{internship_data}->{hiring_manager} = trim($1);
- }
- if ($comment =~ /\nVP Authority:\s+(.+)\nProduct Line:\n/s) {
- $self->{internship_data}->{scvp} = trim($1);
- }
- if ($comment =~ /\nProduct Line:\s+(.+)\nLevel 1/s) {
- $self->{internship_data}->{product_line} = trim($1);
- }
- if ($comment =~ /\nBusiness Need:\s+(.+)\nPotential Project:\n/s) {
- $self->{internship_data}->{business_need} = trim($1);
- }
- if ($comment =~ /\nName:\s+(.+)$/s) {
- $self->{internship_data}->{intern_name} = trim($1);
- }
+ my ($self) = @_;
+ return if exists $self->{internship_data};
+ $self->{internship_data} = {};
+
+ # we only need the first comment
+ my $comment
+ = Bugzilla::Comment->match({bug_id => $self->id, LIMIT => 1,})->[0]->body;
+
+ # extract just what we need
+ # changing the comment will break this
+
+ if ($comment =~ /Hiring Manager:\s+(.+)\nTeam:\n/s) {
+ $self->{internship_data}->{hiring_manager} = trim($1);
+ }
+ if ($comment =~ /\nVP Authority:\s+(.+)\nProduct Line:\n/s) {
+ $self->{internship_data}->{scvp} = trim($1);
+ }
+ if ($comment =~ /\nProduct Line:\s+(.+)\nLevel 1/s) {
+ $self->{internship_data}->{product_line} = trim($1);
+ }
+ if ($comment =~ /\nBusiness Need:\s+(.+)\nPotential Project:\n/s) {
+ $self->{internship_data}->{business_need} = trim($1);
+ }
+ if ($comment =~ /\nName:\s+(.+)$/s) {
+ $self->{internship_data}->{intern_name} = trim($1);
+ }
}
sub hiring_manager {
- my ($self) = @_;
- $self->_extract();
- return $self->{internship_data}->{hiring_manager};
+ my ($self) = @_;
+ $self->_extract();
+ return $self->{internship_data}->{hiring_manager};
}
sub scvp {
- my ($self) = @_;
- $self->_extract();
- return $self->{internship_data}->{scvp};
+ my ($self) = @_;
+ $self->_extract();
+ return $self->{internship_data}->{scvp};
}
sub business_need {
- my ($self) = @_;
- $self->_extract();
- return $self->{internship_data}->{business_need};
+ my ($self) = @_;
+ $self->_extract();
+ return $self->{internship_data}->{business_need};
}
sub product_line {
- my ($self) = @_;
- $self->_extract();
- return $self->{internship_data}->{product_line};
+ my ($self) = @_;
+ $self->_extract();
+ return $self->{internship_data}->{product_line};
}
sub intern_name {
- my ($self) = @_;
- $self->_extract();
- return $self->{internship_data}->{intern_name};
+ my ($self) = @_;
+ $self->_extract();
+ return $self->{internship_data}->{intern_name};
}
1;
diff --git a/extensions/BMO/lib/Reports/ProductSecurity.pm b/extensions/BMO/lib/Reports/ProductSecurity.pm
index e7ccda171..fb773cd93 100644
--- a/extensions/BMO/lib/Reports/ProductSecurity.pm
+++ b/extensions/BMO/lib/Reports/ProductSecurity.pm
@@ -16,54 +16,54 @@ use Bugzilla::Error;
use Bugzilla::Product;
sub report {
- my ($vars) = @_;
- my $user = Bugzilla->user;
+ my ($vars) = @_;
+ my $user = Bugzilla->user;
- ($user->in_group('admin') || $user->in_group('infrasec'))
- || ThrowUserError('auth_failure', { group => 'admin',
- action => 'run',
- object => 'product_security' });
+ ($user->in_group('admin') || $user->in_group('infrasec'))
+ || ThrowUserError('auth_failure',
+ {group => 'admin', action => 'run', object => 'product_security'});
- my $moco = Bugzilla::Group->new({ name => 'mozilla-employee-confidential' })
- or return;
+ my $moco = Bugzilla::Group->new({name => 'mozilla-employee-confidential'})
+ or return;
- my $products = [];
- foreach my $product (@{ Bugzilla::Product->match({}) }) {
- my $default_group = $product->default_security_group_obj;
- my $group_controls = $product->group_controls();
+ my $products = [];
+ foreach my $product (@{Bugzilla::Product->match({})}) {
+ my $default_group = $product->default_security_group_obj;
+ my $group_controls = $product->group_controls();
- my $item = {
- name => $product->name,
- default_security_group => $product->default_security_group,
- group_visibility => 'None/None',
- moco => exists $group_controls->{$moco->id},
- };
+ my $item = {
+ name => $product->name,
+ default_security_group => $product->default_security_group,
+ group_visibility => 'None/None',
+ moco => exists $group_controls->{$moco->id},
+ };
- if ($default_group) {
- if (my $control = $group_controls->{$default_group->id}) {
- $item->{group_visibility} = control_to_string($control->{membercontrol}) .
- '/' . control_to_string($control->{othercontrol});
- }
- }
+ if ($default_group) {
+ if (my $control = $group_controls->{$default_group->id}) {
+ $item->{group_visibility} = control_to_string($control->{membercontrol}) . '/'
+ . control_to_string($control->{othercontrol});
+ }
+ }
- $item->{group_problem} = $default_group ? '' : "Invalid group " . $product->default_security_group;
- $item->{visibility_problem} = 'Default security group should be Shown/Shown'
- if ($item->{group_visibility} ne 'Shown/Shown')
- && ($item->{group_visibility} ne 'Mandatory/Mandatory')
- && ($item->{group_visibility} ne 'Default/Default');
+ $item->{group_problem}
+ = $default_group ? '' : "Invalid group " . $product->default_security_group;
+ $item->{visibility_problem} = 'Default security group should be Shown/Shown'
+ if ($item->{group_visibility} ne 'Shown/Shown')
+ && ($item->{group_visibility} ne 'Mandatory/Mandatory')
+ && ($item->{group_visibility} ne 'Default/Default');
- push @$products, $item;
- }
- $vars->{products} = $products;
+ push @$products, $item;
+ }
+ $vars->{products} = $products;
}
sub control_to_string {
- my ($control) = @_;
- return 'NA' if $control == CONTROLMAPNA;
- return 'Shown' if $control == CONTROLMAPSHOWN;
- return 'Default' if $control == CONTROLMAPDEFAULT;
- return 'Mandatory' if $control == CONTROLMAPMANDATORY;
- return '';
+ my ($control) = @_;
+ return 'NA' if $control == CONTROLMAPNA;
+ return 'Shown' if $control == CONTROLMAPSHOWN;
+ return 'Default' if $control == CONTROLMAPDEFAULT;
+ return 'Mandatory' if $control == CONTROLMAPMANDATORY;
+ return '';
}
1;
diff --git a/extensions/BMO/lib/Reports/Recruiting.pm b/extensions/BMO/lib/Reports/Recruiting.pm
index 39eb8327d..c35b0cbff 100644
--- a/extensions/BMO/lib/Reports/Recruiting.pm
+++ b/extensions/BMO/lib/Reports/Recruiting.pm
@@ -17,33 +17,30 @@ use Bugzilla::Product;
use Bugzilla::Component;
sub report {
- my ($vars) = @_;
- my $user = Bugzilla->user;
-
- $user->in_group('hr')
- || ThrowUserError('auth_failure', { group => 'hr',
- action => 'run',
- object => 'recruiting_dashboard' });
-
- my $product = Bugzilla::Product->check({ name => 'Recruiting', cache => 1 });
- my $component = Bugzilla::Component->new({ product => $product, name => 'General', cache => 1 });
-
- # find all open recruiting bugs
- my $bugs = Bugzilla::Bug->match({
- product_id => $product->id,
- component_id => $component->id,
- resolution => '',
- });
-
- # filter bugs based on visibility and re-bless
- $user->visible_bugs($bugs);
- $bugs = [
- map { bless($_, 'RecruitingBug') }
- grep { $user->can_see_bug($_->id) }
- @$bugs
- ];
-
- $vars->{bugs} = $bugs;
+ my ($vars) = @_;
+ my $user = Bugzilla->user;
+
+ $user->in_group('hr')
+ || ThrowUserError('auth_failure',
+ {group => 'hr', action => 'run', object => 'recruiting_dashboard'});
+
+ my $product = Bugzilla::Product->check({name => 'Recruiting', cache => 1});
+ my $component = Bugzilla::Component->new(
+ {product => $product, name => 'General', cache => 1});
+
+ # find all open recruiting bugs
+ my $bugs = Bugzilla::Bug->match({
+ product_id => $product->id,
+ component_id => $component->id,
+ resolution => '',
+ });
+
+ # filter bugs based on visibility and re-bless
+ $user->visible_bugs($bugs);
+ $bugs = [map { bless($_, 'RecruitingBug') }
+ grep { $user->can_see_bug($_->id) } @$bugs];
+
+ $vars->{bugs} = $bugs;
}
1;
@@ -58,55 +55,56 @@ use Bugzilla::Comment;
use Bugzilla::Util qw(trim);
sub _extract {
- my ($self) = @_;
- return if exists $self->{recruitment_data};
- $self->{recruitment_data} = {};
-
- # we only need the first comment
- my $comment = Bugzilla::Comment->match({
- bug_id => $self->id,
- LIMIT => 1,
- })->[0]->body;
-
- # extract just what we need
- # changing the comment will break this
-
- if ($comment =~ /\nHiring Manager:\s+(.+)VP Authority:\n/s) {
- $self->{recruitment_data}->{hiring_manager} = trim($1);
- }
- if ($comment =~ /\nVP Authority:\s+(.+)HRBP:\n/s) {
- $self->{recruitment_data}->{scvp} = trim($1);
- }
- if ($comment =~ /\nWhat part of your strategic plan does this role impact\?\s+(.+)Why is this critical for success\?\n/s) {
- $self->{recruitment_data}->{strategic_plan} = trim($1);
- }
- if ($comment =~ /\nWhy is this critical for success\?\s+(.+)$/s) {
- $self->{recruitment_data}->{why_critical} = trim($1);
- }
+ my ($self) = @_;
+ return if exists $self->{recruitment_data};
+ $self->{recruitment_data} = {};
+
+ # we only need the first comment
+ my $comment
+ = Bugzilla::Comment->match({bug_id => $self->id, LIMIT => 1,})->[0]->body;
+
+ # extract just what we need
+ # changing the comment will break this
+
+ if ($comment =~ /\nHiring Manager:\s+(.+)VP Authority:\n/s) {
+ $self->{recruitment_data}->{hiring_manager} = trim($1);
+ }
+ if ($comment =~ /\nVP Authority:\s+(.+)HRBP:\n/s) {
+ $self->{recruitment_data}->{scvp} = trim($1);
+ }
+ if ($comment
+ =~ /\nWhat part of your strategic plan does this role impact\?\s+(.+)Why is this critical for success\?\n/s
+ )
+ {
+ $self->{recruitment_data}->{strategic_plan} = trim($1);
+ }
+ if ($comment =~ /\nWhy is this critical for success\?\s+(.+)$/s) {
+ $self->{recruitment_data}->{why_critical} = trim($1);
+ }
}
sub hiring_manager {
- my ($self) = @_;
- $self->_extract();
- return $self->{recruitment_data}->{hiring_manager};
+ my ($self) = @_;
+ $self->_extract();
+ return $self->{recruitment_data}->{hiring_manager};
}
sub scvp {
- my ($self) = @_;
- $self->_extract();
- return $self->{recruitment_data}->{scvp};
+ my ($self) = @_;
+ $self->_extract();
+ return $self->{recruitment_data}->{scvp};
}
sub strategic_plan {
- my ($self) = @_;
- $self->_extract();
- return $self->{recruitment_data}->{strategic_plan};
+ my ($self) = @_;
+ $self->_extract();
+ return $self->{recruitment_data}->{strategic_plan};
}
sub why_critical {
- my ($self) = @_;
- $self->_extract();
- return $self->{recruitment_data}->{why_critical};
+ my ($self) = @_;
+ $self->_extract();
+ return $self->{recruitment_data}->{why_critical};
}
1;
diff --git a/extensions/BMO/lib/Reports/ReleaseTracking.pm b/extensions/BMO/lib/Reports/ReleaseTracking.pm
index 9fba1e14b..38a07aee7 100644
--- a/extensions/BMO/lib/Reports/ReleaseTracking.pm
+++ b/extensions/BMO/lib/Reports/ReleaseTracking.pm
@@ -21,496 +21,381 @@ use JSON qw(-convert_blessed_universally);
use List::MoreUtils qw(uniq);
use constant DATE_RANGES => [
- {
- value => '20160126-20160307',
- label => '2016-01-26 and 2016-03-07'
- },
- {
- value => '20151215-20160125',
- label => '2015-12-15 and 2016-01-25'
- },
- {
- value => '20151103-20151214',
- label => '2015-11-03 and 2015-12-14'
- },
- {
- value => '20150922-20151102',
- label => '2015-09-22 and 2015-11-02'
- },
- {
- value => '20150811-20150921',
- label => '2015-08-11 and 2015-09-21'
- },
- {
- value => '20150630-20150810',
- label => '2015-06-30 and 2015-08-10'
- },
- {
- value => '20150512-20150629',
- label => '2015-05-12 and 2015-06-29'
- },
- {
- value => '20150331-20150511',
- label => '2015-03-31 and 2015-05-11'
- },
- {
- value => '20150224-20150330',
- label => '2015-02-24 and 2015-03-30'
- },
- {
- value => '20150113-20150223',
- label => '2015-01-13 and 2015-02-23'
- },
- {
- value => '20141111-20141222',
- label => '2014-11-11 and 2014-12-22'
- },
- {
- value => '20140930-20141110',
- label => '2014-09-30 and 2014-11-10'
- },
- {
- value => '20140819-20140929',
- label => '2014-08-19 and 2014-09-29'
- },
- {
- value => '20140708-20140818',
- label => '2014-07-08 and 2014-08-18'
- },
- {
- value => '20140527-20140707',
- label => '2014-05-27 and 2014-07-07'
- },
- {
- value => '20140415-20140526',
- label => '2014-04-15 and 2014-05-26'
- },
- {
- value => '20140304-20140414',
- label => '2014-03-04 and 2014-04-14'
- },
- {
- value => '20140121-20140303',
- label => '2014-01-21 and 2014-03-03'
- },
- {
- value => '20131210-20140120',
- label => '2013-12-10 and 2014-01-20'
- },
- {
- value => '20131029-20131209',
- label => '2013-10-29 and 2013-12-09'
- },
- {
- value => '20130917-20131028',
- label => '2013-09-17 and 2013-10-28'
- },
- {
- value => '20130806-20130916',
- label => '2013-08-06 and 2013-09-16'
- },
- {
- value => '20130625-20130805',
- label => '2013-06-25 and 2013-08-05'
- },
- {
- value => '20130514-20130624',
- label => '2013-05-14 and 2013-06-24'
- },
- {
- value => '20130402-20130513',
- label => '2013-04-02 and 2013-05-13'
- },
- {
- value => '20130219-20130401',
- label => '2013-02-19 and 2013-04-01'
- },
- {
- value => '20130108-20130218',
- label => '2013-01-08 and 2013-02-18'
- },
- {
- value => '20121120-20130107',
- label => '2012-11-20 and 2013-01-07'
- },
- {
- value => '20121009-20121119',
- label => '2012-10-09 and 2012-11-19'
- },
- {
- value => '20120828-20121008',
- label => '2012-08-28 and 2012-10-08'
- },
- {
- value => '20120717-20120827',
- label => '2012-07-17 and 2012-08-27'
- },
- {
- value => '20120605-20120716',
- label => '2012-06-05 and 2012-07-16'
- },
- {
- value => '20120424-20120604',
- label => '2012-04-24 and 2012-06-04'
- },
- {
- value => '20120313-20120423',
- label => '2012-03-13 and 2012-04-23'
- },
- {
- value => '20120131-20120312',
- label => '2012-01-31 and 2012-03-12'
- },
- {
- value => '20111220-20120130',
- label => '2011-12-20 and 2012-01-30'
- },
- {
- value => '20111108-20111219',
- label => '2011-11-08 and 2011-12-19'
- },
- {
- value => '20110927-20111107',
- label => '2011-09-27 and 2011-11-07'
- },
- {
- value => '20110816-20110926',
- label => '2011-08-16 and 2011-09-26'
- },
- {
- value => '*',
- label => 'Anytime'
- }
+ {value => '20160126-20160307', label => '2016-01-26 and 2016-03-07'},
+ {value => '20151215-20160125', label => '2015-12-15 and 2016-01-25'},
+ {value => '20151103-20151214', label => '2015-11-03 and 2015-12-14'},
+ {value => '20150922-20151102', label => '2015-09-22 and 2015-11-02'},
+ {value => '20150811-20150921', label => '2015-08-11 and 2015-09-21'},
+ {value => '20150630-20150810', label => '2015-06-30 and 2015-08-10'},
+ {value => '20150512-20150629', label => '2015-05-12 and 2015-06-29'},
+ {value => '20150331-20150511', label => '2015-03-31 and 2015-05-11'},
+ {value => '20150224-20150330', label => '2015-02-24 and 2015-03-30'},
+ {value => '20150113-20150223', label => '2015-01-13 and 2015-02-23'},
+ {value => '20141111-20141222', label => '2014-11-11 and 2014-12-22'},
+ {value => '20140930-20141110', label => '2014-09-30 and 2014-11-10'},
+ {value => '20140819-20140929', label => '2014-08-19 and 2014-09-29'},
+ {value => '20140708-20140818', label => '2014-07-08 and 2014-08-18'},
+ {value => '20140527-20140707', label => '2014-05-27 and 2014-07-07'},
+ {value => '20140415-20140526', label => '2014-04-15 and 2014-05-26'},
+ {value => '20140304-20140414', label => '2014-03-04 and 2014-04-14'},
+ {value => '20140121-20140303', label => '2014-01-21 and 2014-03-03'},
+ {value => '20131210-20140120', label => '2013-12-10 and 2014-01-20'},
+ {value => '20131029-20131209', label => '2013-10-29 and 2013-12-09'},
+ {value => '20130917-20131028', label => '2013-09-17 and 2013-10-28'},
+ {value => '20130806-20130916', label => '2013-08-06 and 2013-09-16'},
+ {value => '20130625-20130805', label => '2013-06-25 and 2013-08-05'},
+ {value => '20130514-20130624', label => '2013-05-14 and 2013-06-24'},
+ {value => '20130402-20130513', label => '2013-04-02 and 2013-05-13'},
+ {value => '20130219-20130401', label => '2013-02-19 and 2013-04-01'},
+ {value => '20130108-20130218', label => '2013-01-08 and 2013-02-18'},
+ {value => '20121120-20130107', label => '2012-11-20 and 2013-01-07'},
+ {value => '20121009-20121119', label => '2012-10-09 and 2012-11-19'},
+ {value => '20120828-20121008', label => '2012-08-28 and 2012-10-08'},
+ {value => '20120717-20120827', label => '2012-07-17 and 2012-08-27'},
+ {value => '20120605-20120716', label => '2012-06-05 and 2012-07-16'},
+ {value => '20120424-20120604', label => '2012-04-24 and 2012-06-04'},
+ {value => '20120313-20120423', label => '2012-03-13 and 2012-04-23'},
+ {value => '20120131-20120312', label => '2012-01-31 and 2012-03-12'},
+ {value => '20111220-20120130', label => '2011-12-20 and 2012-01-30'},
+ {value => '20111108-20111219', label => '2011-11-08 and 2011-12-19'},
+ {value => '20110927-20111107', label => '2011-09-27 and 2011-11-07'},
+ {value => '20110816-20110926', label => '2011-08-16 and 2011-09-26'},
+ {value => '*', label => 'Anytime'}
];
sub report {
- my ($vars) = @_;
- my $dbh = Bugzilla->dbh;
- my $input = Bugzilla->input_params;
- my $user = Bugzilla->user;
-
- my @flag_names = qw(
- approval-mozilla-release
- approval-mozilla-beta
- approval-mozilla-aurora
- approval-mozilla-central
- approval-comm-release
- approval-comm-beta
- approval-comm-aurora
- approval-calendar-release
- approval-calendar-beta
- approval-calendar-aurora
- approval-mozilla-esr10
- );
-
- my @flags_json;
- my @fields_json;
- my @products_json;
-
- #
- # tracking flags
- #
-
- my $all_products = $user->get_selectable_products;
- my @usable_products;
-
- # build list of flags and their matching products
-
- my @invalid_flag_names;
- foreach my $flag_name (@flag_names) {
- # grab all matching flag_types
- my @flag_types = @{Bugzilla::FlagType::match({ name => $flag_name, is_active => 1 })};
-
- # remove invalid flags
- if (!@flag_types) {
- push @invalid_flag_names, $flag_name;
- next;
- }
+ my ($vars) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $input = Bugzilla->input_params;
+ my $user = Bugzilla->user;
+
+ my @flag_names = qw(
+ approval-mozilla-release
+ approval-mozilla-beta
+ approval-mozilla-aurora
+ approval-mozilla-central
+ approval-comm-release
+ approval-comm-beta
+ approval-comm-aurora
+ approval-calendar-release
+ approval-calendar-beta
+ approval-calendar-aurora
+ approval-mozilla-esr10
+ );
+
+ my @flags_json;
+ my @fields_json;
+ my @products_json;
+
+ #
+ # tracking flags
+ #
+
+ my $all_products = $user->get_selectable_products;
+ my @usable_products;
+
+ # build list of flags and their matching products
+
+ my @invalid_flag_names;
+ foreach my $flag_name (@flag_names) {
+
+ # grab all matching flag_types
+ my @flag_types
+ = @{Bugzilla::FlagType::match({name => $flag_name, is_active => 1})};
+
+ # remove invalid flags
+ if (!@flag_types) {
+ push @invalid_flag_names, $flag_name;
+ next;
+ }
- # we need a list of products, based on inclusions/exclusions
- my @products;
- my %flag_types;
- foreach my $flag_type (@flag_types) {
- $flag_types{$flag_type->name} = $flag_type->id;
- my $has_all = 0;
- my @exclusion_ids;
- my @inclusion_ids;
- foreach my $flag_type (@flag_types) {
- if (scalar keys %{$flag_type->inclusions}) {
- my $inclusions = $flag_type->inclusions;
- foreach my $key (keys %$inclusions) {
- push @inclusion_ids, ($inclusions->{$key} =~ /^(\d+)/);
- }
- } elsif (scalar keys %{$flag_type->exclusions}) {
- my $exclusions = $flag_type->exclusions;
- foreach my $key (keys %$exclusions) {
- push @exclusion_ids, ($exclusions->{$key} =~ /^(\d+)/);
- }
- } else {
- $has_all = 1;
- last;
- }
- }
-
- if ($has_all) {
- push @products, @$all_products;
- } elsif (scalar @exclusion_ids) {
- push @products, @$all_products;
- foreach my $exclude_id (uniq @exclusion_ids) {
- @products = grep { $_->id != $exclude_id } @products;
- }
- } else {
- foreach my $include_id (uniq @inclusion_ids) {
- push @products, grep { $_->id == $include_id } @$all_products;
- }
- }
+ # we need a list of products, based on inclusions/exclusions
+ my @products;
+ my %flag_types;
+ foreach my $flag_type (@flag_types) {
+ $flag_types{$flag_type->name} = $flag_type->id;
+ my $has_all = 0;
+ my @exclusion_ids;
+ my @inclusion_ids;
+ foreach my $flag_type (@flag_types) {
+ if (scalar keys %{$flag_type->inclusions}) {
+ my $inclusions = $flag_type->inclusions;
+ foreach my $key (keys %$inclusions) {
+ push @inclusion_ids, ($inclusions->{$key} =~ /^(\d+)/);
+ }
}
- @products = uniq @products;
- push @usable_products, @products;
- my @product_ids = map { $_->id } sort { lc($a->name) cmp lc($b->name) } @products;
-
- push @flags_json, {
- name => $flag_name,
- id => $flag_types{$flag_name} || 0,
- products => \@product_ids,
- fields => [],
- };
- }
- foreach my $flag_name (@invalid_flag_names) {
- @flag_names = grep { $_ ne $flag_name } @flag_names;
- }
- @usable_products = uniq @usable_products;
-
- # build a list of tracking flags for each product
- # also build the list of all fields
-
- my @unlink_products;
- foreach my $product (@usable_products) {
- my @fields =
- sort { $a->sortkey <=> $b->sortkey }
- grep { is_active_status_field($_) }
- Bugzilla->active_custom_fields({ product => $product });
- my @field_ids = map { $_->id } @fields;
- if (!scalar @fields) {
- push @unlink_products, $product;
- next;
+ elsif (scalar keys %{$flag_type->exclusions}) {
+ my $exclusions = $flag_type->exclusions;
+ foreach my $key (keys %$exclusions) {
+ push @exclusion_ids, ($exclusions->{$key} =~ /^(\d+)/);
+ }
}
-
- # product
- push @products_json, {
- name => $product->name,
- id => $product->id,
- fields => \@field_ids,
- };
-
- # add fields to flags
- foreach my $rh (@flags_json) {
- if (grep { $_ eq $product->id } @{$rh->{products}}) {
- push @{$rh->{fields}}, @field_ids;
- }
+ else {
+ $has_all = 1;
+ last;
}
-
- # add fields to fields_json
- foreach my $field (@fields) {
- my $existing = 0;
- foreach my $rh (@fields_json) {
- if ($rh->{id} == $field->id) {
- $existing = 1;
- last;
- }
- }
- if (!$existing) {
- push @fields_json, {
- name => $field->name,
- desc => $field->description,
- id => $field->id,
- };
- }
+ }
+
+ if ($has_all) {
+ push @products, @$all_products;
+ }
+ elsif (scalar @exclusion_ids) {
+ push @products, @$all_products;
+ foreach my $exclude_id (uniq @exclusion_ids) {
+ @products = grep { $_->id != $exclude_id } @products;
}
+ }
+ else {
+ foreach my $include_id (uniq @inclusion_ids) {
+ push @products, grep { $_->id == $include_id } @$all_products;
+ }
+ }
}
- foreach my $rh (@flags_json) {
- my @fields = uniq @{$rh->{fields}};
- $rh->{fields} = \@fields;
+ @products = uniq @products;
+ push @usable_products, @products;
+ my @product_ids
+ = map { $_->id } sort { lc($a->name) cmp lc($b->name) } @products;
+
+ push @flags_json,
+ {
+ name => $flag_name,
+ id => $flag_types{$flag_name} || 0,
+ products => \@product_ids,
+ fields => [],
+ };
+ }
+ foreach my $flag_name (@invalid_flag_names) {
+ @flag_names = grep { $_ ne $flag_name } @flag_names;
+ }
+ @usable_products = uniq @usable_products;
+
+ # build a list of tracking flags for each product
+ # also build the list of all fields
+
+ my @unlink_products;
+ foreach my $product (@usable_products) {
+ my @fields
+ = sort { $a->sortkey <=> $b->sortkey }
+ grep { is_active_status_field($_) }
+ Bugzilla->active_custom_fields({product => $product});
+ my @field_ids = map { $_->id } @fields;
+ if (!scalar @fields) {
+ push @unlink_products, $product;
+ next;
}
- # remove products which aren't linked with status fields
+ # product
+ push @products_json,
+ {name => $product->name, id => $product->id, fields => \@field_ids,};
+ # add fields to flags
foreach my $rh (@flags_json) {
- my @product_ids;
- foreach my $id (@{$rh->{products}}) {
- unless (grep { $_->id == $id } @unlink_products) {
- push @product_ids, $id;
- }
- $rh->{products} = \@product_ids;
+ if (grep { $_ eq $product->id } @{$rh->{products}}) {
+ push @{$rh->{fields}}, @field_ids;
+ }
+ }
+
+ # add fields to fields_json
+ foreach my $field (@fields) {
+ my $existing = 0;
+ foreach my $rh (@fields_json) {
+ if ($rh->{id} == $field->id) {
+ $existing = 1;
+ last;
}
+ }
+ if (!$existing) {
+ push @fields_json,
+ {name => $field->name, desc => $field->description, id => $field->id,};
+ }
}
+ }
+ foreach my $rh (@flags_json) {
+ my @fields = uniq @{$rh->{fields}};
+ $rh->{fields} = \@fields;
+ }
+
+ # remove products which aren't linked with status fields
+
+ foreach my $rh (@flags_json) {
+ my @product_ids;
+ foreach my $id (@{$rh->{products}}) {
+ unless (grep { $_->id == $id } @unlink_products) {
+ push @product_ids, $id;
+ }
+ $rh->{products} = \@product_ids;
+ }
+ }
- #
- # run report
- #
+ #
+ # run report
+ #
- if ($input->{q} && !$input->{edit}) {
- my $q = _parse_query($input->{q});
+ if ($input->{q} && !$input->{edit}) {
+ my $q = _parse_query($input->{q});
- my @where;
- my @params;
- my $query = "
+ my @where;
+ my @params;
+ my $query = "
SELECT DISTINCT b.bug_id
FROM bugs b
INNER JOIN flags f ON f.bug_id = b.bug_id\n";
- if ($q->{start_date}) {
- $query .= "INNER JOIN bugs_activity a ON a.bug_id = b.bug_id\n";
- }
+ if ($q->{start_date}) {
+ $query .= "INNER JOIN bugs_activity a ON a.bug_id = b.bug_id\n";
+ }
- $query .= "WHERE ";
+ $query .= "WHERE ";
- if ($q->{start_date}) {
- push @where, "(a.fieldid = ?)";
- push @params, $q->{field_id};
+ if ($q->{start_date}) {
+ push @where, "(a.fieldid = ?)";
+ push @params, $q->{field_id};
- push @where, "(CONVERT_TZ(a.bug_when, 'UTC', 'America/Los_Angeles') >= ?)";
- push @params, $q->{start_date} . ' 00:00:00';
- push @where, "(CONVERT_TZ(a.bug_when, 'UTC', 'America/Los_Angeles') <= ?)";
- push @params, $q->{end_date} . ' 23:59:59';
+ push @where, "(CONVERT_TZ(a.bug_when, 'UTC', 'America/Los_Angeles') >= ?)";
+ push @params, $q->{start_date} . ' 00:00:00';
+ push @where, "(CONVERT_TZ(a.bug_when, 'UTC', 'America/Los_Angeles') <= ?)";
+ push @params, $q->{end_date} . ' 23:59:59';
- push @where, "(a.added LIKE ?)";
- push @params, '%' . $q->{flag_name} . $q->{flag_status} . '%';
- }
+ push @where, "(a.added LIKE ?)";
+ push @params, '%' . $q->{flag_name} . $q->{flag_status} . '%';
+ }
- my ($type_id) = $dbh->selectrow_array(
- "SELECT id FROM flagtypes WHERE name = ?",
- undef,
- $q->{flag_name}
- );
- push @where, "(f.type_id = ?)";
- push @params, $type_id;
+ my ($type_id) = $dbh->selectrow_array("SELECT id FROM flagtypes WHERE name = ?",
+ undef, $q->{flag_name});
+ push @where, "(f.type_id = ?)";
+ push @params, $type_id;
- push @where, "(f.status = ?)";
- push @params, $q->{flag_status};
+ push @where, "(f.status = ?)";
+ push @params, $q->{flag_status};
- if ($q->{product_id}) {
- push @where, "(b.product_id = ?)";
- push @params, $q->{product_id};
- }
+ if ($q->{product_id}) {
+ push @where, "(b.product_id = ?)";
+ push @params, $q->{product_id};
+ }
- if (scalar @{$q->{fields}}) {
- my @fields;
- foreach my $field (@{$q->{fields}}) {
- my $field_sql = "(";
- if ($field->{type} == FIELD_TYPE_EXTENSION) {
- $field_sql .= "
+ if (scalar @{$q->{fields}}) {
+ my @fields;
+ foreach my $field (@{$q->{fields}}) {
+ my $field_sql = "(";
+ if ($field->{type} == FIELD_TYPE_EXTENSION) {
+ $field_sql .= "
COALESCE(
(SELECT tracking_flags_bugs.value
FROM tracking_flags_bugs
LEFT JOIN tracking_flags
ON tracking_flags.id = tracking_flags_bugs.tracking_flag_id
WHERE tracking_flags_bugs.bug_id = b.bug_id
- AND tracking_flags.name = " . $dbh->quote($field->{name}) . ")
+ AND tracking_flags.name = "
+ . $dbh->quote($field->{name}) . ")
, '') ";
- }
- else {
- $field_sql .= "b." . $field->{name};
- }
- $field_sql .= " " . ($field->{value} eq '+' ? '' : 'NOT ') . "IN ('fixed','verified'))";
- push(@fields, $field_sql);
- }
- my $join = uc $q->{join};
- push @where, '(' . join(" $join ", @fields) . ')';
}
-
- $query .= join("\nAND ", @where);
-
- my $bugs = $dbh->selectcol_arrayref($query, undef, @params);
- push @$bugs, 0 unless @$bugs;
-
- my $urlbase = Bugzilla->localconfig->{urlbase};
- my $cgi = Bugzilla->cgi;
- print $cgi->redirect(
- -url => "${urlbase}buglist.cgi?bug_id=" . join(',', @$bugs)
- );
- exit;
+ else {
+ $field_sql .= "b." . $field->{name};
+ }
+ $field_sql
+ .= " " . ($field->{value} eq '+' ? '' : 'NOT ') . "IN ('fixed','verified'))";
+ push(@fields, $field_sql);
+ }
+ my $join = uc $q->{join};
+ push @where, '(' . join(" $join ", @fields) . ')';
}
- #
- # set template vars
- #
-
- my $json = JSON->new()->shrink(1);
- $vars->{flags_json} = $json->encode(\@flags_json);
- $vars->{products_json} = $json->encode(\@products_json);
- $vars->{fields_json} = $json->encode(\@fields_json);
- $vars->{flag_names} = \@flag_names;
- $vars->{ranges} = DATE_RANGES;
- $vars->{default_query} = $input->{q};
- $vars->{is_custom} = $input->{is_custom};
- foreach my $field (qw(product flags range)) {
- $vars->{$field} = $input->{$field};
- }
+ $query .= join("\nAND ", @where);
+
+ my $bugs = $dbh->selectcol_arrayref($query, undef, @params);
+ push @$bugs, 0 unless @$bugs;
+
+ my $urlbase = Bugzilla->localconfig->{urlbase};
+ my $cgi = Bugzilla->cgi;
+ print $cgi->redirect(
+ -url => "${urlbase}buglist.cgi?bug_id=" . join(',', @$bugs));
+ exit;
+ }
+
+ #
+ # set template vars
+ #
+
+ my $json = JSON->new()->shrink(1);
+ $vars->{flags_json} = $json->encode(\@flags_json);
+ $vars->{products_json} = $json->encode(\@products_json);
+ $vars->{fields_json} = $json->encode(\@fields_json);
+ $vars->{flag_names} = \@flag_names;
+ $vars->{ranges} = DATE_RANGES;
+ $vars->{default_query} = $input->{q};
+ $vars->{is_custom} = $input->{is_custom};
+ foreach my $field (qw(product flags range)) {
+ $vars->{$field} = $input->{$field};
+ }
}
sub _parse_query {
- my $q = shift;
- my @query = split(/:/, $q);
- my $query;
-
- # field_id for flag changes
- $query->{field_id} = get_field_id('flagtypes.name');
-
- # flag_name
- my $flag_name = shift @query;
- @{Bugzilla::FlagType::match({ name => $flag_name, is_active => 1 })}
- or ThrowUserError('report_invalid_parameter', { name => 'flag_name' });
- trick_taint($flag_name);
- $query->{flag_name} = $flag_name;
-
- # flag_status
- my $flag_status = shift @query;
- $flag_status =~ /^([\?\-\+])$/
- or ThrowUserError('report_invalid_parameter', { name => 'flag_status' });
- $query->{flag_status} = $1;
-
- # date_range -> from_ymd to_ymd
- my $date_range = shift @query;
- if ($date_range ne '*') {
- $date_range =~ /^(\d\d\d\d)(\d\d)(\d\d)-(\d\d\d\d)(\d\d)(\d\d)$/
- or ThrowUserError('report_invalid_parameter', { name => 'date_range' });
- $query->{start_date} = "$1-$2-$3";
- $query->{end_date} = "$4-$5-$6";
- validate_date($query->{start_date})
- || ThrowUserError('illegal_date', { date => $query->{start_date},
- format => 'YYYY-MM-DD' });
- validate_date($query->{end_date})
- || ThrowUserError('illegal_date', { date => $query->{end_date},
- format => 'YYYY-MM-DD' });
- }
-
- # product_id
- my $product_id = shift @query;
- $product_id =~ /^(\d+)$/
- or ThrowUserError('report_invalid_parameter', { name => 'product_id' });
- $query->{product_id} = $1;
-
- # join
- my $join = shift @query;
- $join =~ /^(and|or)$/
- or ThrowUserError('report_invalid_parameter', { name => 'join' });
- $query->{join} = $1;
-
- # fields
- my @fields;
- foreach my $field (@query) {
- $field =~ /^(\d+)([\-\+])$/
- or ThrowUserError('report_invalid_parameter', { name => 'fields' });
- my ($id, $value) = ($1, $2);
- my $field_obj = Bugzilla::Field->new($id)
- or ThrowUserError('report_invalid_parameter', { name => 'field_id' });
- push @fields, { id => $id, value => $value,
- name => $field_obj->name, type => $field_obj->type };
- }
- $query->{fields} = \@fields;
-
- return $query;
+ my $q = shift;
+ my @query = split(/:/, $q);
+ my $query;
+
+ # field_id for flag changes
+ $query->{field_id} = get_field_id('flagtypes.name');
+
+ # flag_name
+ my $flag_name = shift @query;
+ @{Bugzilla::FlagType::match({name => $flag_name, is_active => 1})}
+ or ThrowUserError('report_invalid_parameter', {name => 'flag_name'});
+ trick_taint($flag_name);
+ $query->{flag_name} = $flag_name;
+
+ # flag_status
+ my $flag_status = shift @query;
+ $flag_status =~ /^([\?\-\+])$/
+ or ThrowUserError('report_invalid_parameter', {name => 'flag_status'});
+ $query->{flag_status} = $1;
+
+ # date_range -> from_ymd to_ymd
+ my $date_range = shift @query;
+ if ($date_range ne '*') {
+ $date_range =~ /^(\d\d\d\d)(\d\d)(\d\d)-(\d\d\d\d)(\d\d)(\d\d)$/
+ or ThrowUserError('report_invalid_parameter', {name => 'date_range'});
+ $query->{start_date} = "$1-$2-$3";
+ $query->{end_date} = "$4-$5-$6";
+ validate_date($query->{start_date})
+ || ThrowUserError('illegal_date',
+ {date => $query->{start_date}, format => 'YYYY-MM-DD'});
+ validate_date($query->{end_date})
+ || ThrowUserError('illegal_date',
+ {date => $query->{end_date}, format => 'YYYY-MM-DD'});
+ }
+
+ # product_id
+ my $product_id = shift @query;
+ $product_id =~ /^(\d+)$/
+ or ThrowUserError('report_invalid_parameter', {name => 'product_id'});
+ $query->{product_id} = $1;
+
+ # join
+ my $join = shift @query;
+ $join =~ /^(and|or)$/
+ or ThrowUserError('report_invalid_parameter', {name => 'join'});
+ $query->{join} = $1;
+
+ # fields
+ my @fields;
+ foreach my $field (@query) {
+ $field =~ /^(\d+)([\-\+])$/
+ or ThrowUserError('report_invalid_parameter', {name => 'fields'});
+ my ($id, $value) = ($1, $2);
+ my $field_obj = Bugzilla::Field->new($id)
+ or ThrowUserError('report_invalid_parameter', {name => 'field_id'});
+ push @fields,
+ {
+ id => $id,
+ value => $value,
+ name => $field_obj->name,
+ type => $field_obj->type
+ };
+ }
+ $query->{fields} = \@fields;
+
+ return $query;
}
1;
diff --git a/extensions/BMO/lib/Reports/Triage.pm b/extensions/BMO/lib/Reports/Triage.pm
index 55eeb17eb..0ccbbee6e 100644
--- a/extensions/BMO/lib/Reports/Triage.pm
+++ b/extensions/BMO/lib/Reports/Triage.pm
@@ -25,261 +25,279 @@ use List::MoreUtils qw(any);
# set an upper limit on the *unfiltered* number of bugs to process
use constant MAX_NUMBER_BUGS => 4000;
-use constant DEFAULT_OWNER_PRODUCTS => (
- 'Core',
- 'Firefox',
- 'Firefox for Android',
- 'Firefox for iOS',
- 'Toolkit',
-);
+use constant DEFAULT_OWNER_PRODUCTS =>
+ ('Core', 'Firefox', 'Firefox for Android', 'Firefox for iOS', 'Toolkit',);
sub unconfirmed {
- my ($vars, $filter) = @_;
- my $dbh = Bugzilla->dbh;
- my $input = Bugzilla->input_params;
- my $user = Bugzilla->user;
-
- if (exists $input->{'action'} && $input->{'action'} eq 'run' && $input->{'product'}) {
-
- # load product and components from input
-
- my $product = Bugzilla::Product->new({ name => $input->{'product'} })
- || ThrowUserError('invalid_object', { object => 'Product', value => $input->{'product'} });
-
- my @component_ids;
- if ($input->{'component'} ne '') {
- my $ra_components = ref($input->{'component'})
- ? $input->{'component'} : [ $input->{'component'} ];
- foreach my $component_name (@$ra_components) {
- my $component = Bugzilla::Component->new({ name => $component_name, product => $product })
- || ThrowUserError('invalid_object', { object => 'Component', value => $component_name });
- push @component_ids, $component->id;
- }
- }
+ my ($vars, $filter) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $input = Bugzilla->input_params;
+ my $user = Bugzilla->user;
- # determine which comment filters to run
+ if ( exists $input->{'action'}
+ && $input->{'action'} eq 'run'
+ && $input->{'product'})
+ {
- my $filter_commenter = $input->{'filter_commenter'};
- my $filter_commenter_on = $input->{'commenter'};
- my $filter_last = $input->{'filter_last'};
- my $filter_last_period = $input->{'last'};
+ # load product and components from input
- if (!$filter_commenter || $filter_last) {
- $filter_commenter = '1';
- $filter_commenter_on = 'reporter';
- }
+ my $product
+ = Bugzilla::Product->new({name => $input->{'product'}})
+ || ThrowUserError('invalid_object',
+ {object => 'Product', value => $input->{'product'}});
- my $filter_commenter_id;
- if ($filter_commenter && $filter_commenter_on eq 'is') {
- Bugzilla::User::match_field({ 'commenter_is' => {'type' => 'single'} });
- my $user = Bugzilla::User->new({ name => $input->{'commenter_is'} })
- || ThrowUserError('invalid_object', { object => 'User', value => $input->{'commenter_is'} });
- $filter_commenter_id = $user ? $user->id : 0;
- }
+ my @component_ids;
+ if ($input->{'component'} ne '') {
+ my $ra_components
+ = ref($input->{'component'})
+ ? $input->{'component'}
+ : [$input->{'component'}];
+ foreach my $component_name (@$ra_components) {
+ my $component
+ = Bugzilla::Component->new({name => $component_name, product => $product})
+ || ThrowUserError('invalid_object',
+ {object => 'Component', value => $component_name});
+ push @component_ids, $component->id;
+ }
+ }
- my $filter_last_time;
- if ($filter_last) {
- if ($filter_last_period eq 'is') {
- $filter_last_period = -1;
- $filter_last_time = str2time($input->{'last_is'} . " 00:00:00") || 0;
- } else {
- detaint_natural($filter_last_period);
- $filter_last_period = 14 if $filter_last_period < 14;
- }
- }
+ # determine which comment filters to run
+
+ my $filter_commenter = $input->{'filter_commenter'};
+ my $filter_commenter_on = $input->{'commenter'};
+ my $filter_last = $input->{'filter_last'};
+ my $filter_last_period = $input->{'last'};
+
+ if (!$filter_commenter || $filter_last) {
+ $filter_commenter = '1';
+ $filter_commenter_on = 'reporter';
+ }
+
+ my $filter_commenter_id;
+ if ($filter_commenter && $filter_commenter_on eq 'is') {
+ Bugzilla::User::match_field({'commenter_is' => {'type' => 'single'}});
+ my $user
+ = Bugzilla::User->new({name => $input->{'commenter_is'}})
+ || ThrowUserError('invalid_object',
+ {object => 'User', value => $input->{'commenter_is'}});
+ $filter_commenter_id = $user ? $user->id : 0;
+ }
- # form sql queries
+ my $filter_last_time;
+ if ($filter_last) {
+ if ($filter_last_period eq 'is') {
+ $filter_last_period = -1;
+ $filter_last_time = str2time($input->{'last_is'} . " 00:00:00") || 0;
+ }
+ else {
+ detaint_natural($filter_last_period);
+ $filter_last_period = 14 if $filter_last_period < 14;
+ }
+ }
+
+ # form sql queries
- my $now = (time);
- my $bugs_sql = "
+ my $now = (time);
+ my $bugs_sql = "
SELECT bug_id, short_desc, reporter, creation_ts
FROM bugs
WHERE product_id = ?
AND bug_status = 'UNCONFIRMED'";
- if (@component_ids) {
- $bugs_sql .= " AND component_id IN (" . join(',', @component_ids) . ")";
- }
- $bugs_sql .= "
+ if (@component_ids) {
+ $bugs_sql .= " AND component_id IN (" . join(',', @component_ids) . ")";
+ }
+ $bugs_sql .= "
ORDER BY creation_ts
";
- my $comment_count_sql = "
+ my $comment_count_sql = "
SELECT COUNT(*)
FROM longdescs
WHERE bug_id = ?
";
- my $comment_sql = "
+ my $comment_sql = "
SELECT who, bug_when, type, thetext, extra_data
FROM longdescs
WHERE bug_id = ?
";
- if (!Bugzilla->user->is_insider) {
- $comment_sql .= " AND isprivate = 0 ";
- }
- $comment_sql .= "
+ if (!Bugzilla->user->is_insider) {
+ $comment_sql .= " AND isprivate = 0 ";
+ }
+ $comment_sql .= "
ORDER BY bug_when DESC
LIMIT 1
";
- my $attach_sql = "
+ my $attach_sql = "
SELECT description, isprivate
FROM attachments
WHERE attach_id = ?
";
- # work on an initial list of bugs
+ # work on an initial list of bugs
- my $list = $dbh->selectall_arrayref($bugs_sql, undef, $product->id);
- my @bugs;
+ my $list = $dbh->selectall_arrayref($bugs_sql, undef, $product->id);
+ my @bugs;
- # this can be slow to process, resulting in 'service unavailable' errors from zeus
- # so if too many bugs are returned, throw an error
+ # this can be slow to process, resulting in 'service unavailable' errors from zeus
+ # so if too many bugs are returned, throw an error
- if (scalar(@$list) > MAX_NUMBER_BUGS) {
- ThrowUserError('report_too_many_bugs');
- }
+ if (scalar(@$list) > MAX_NUMBER_BUGS) {
+ ThrowUserError('report_too_many_bugs');
+ }
- foreach my $entry (@$list) {
- my ($bug_id, $summary, $reporter_id, $creation_ts) = @$entry;
-
- next unless $user->can_see_bug($bug_id);
-
- # get last comment information
-
- my ($comment_count) = $dbh->selectrow_array($comment_count_sql, undef, $bug_id);
- my ($commenter_id, $comment_ts, $type, $comment, $extra)
- = $dbh->selectrow_array($comment_sql, undef, $bug_id);
- my $commenter = 0;
-
- # apply selected filters
-
- if ($filter_commenter) {
- next if $comment_count <= 1;
-
- if ($filter_commenter_on eq 'reporter') {
- next if $commenter_id != $reporter_id;
-
- } elsif ($filter_commenter_on eq 'noconfirm') {
- $commenter = Bugzilla::User->new({ id => $commenter_id, cache => 1 });
- next if $commenter_id != $reporter_id
- || $commenter->in_group('canconfirm');
-
- } elsif ($filter_commenter_on eq 'is') {
- next if $commenter_id != $filter_commenter_id;
- }
- } else {
- $input->{'commenter'} = '';
- $input->{'commenter_is'} = '';
- }
-
- if ($filter_last) {
- my $comment_time = str2time($comment_ts)
- or next;
- if ($filter_last_period == -1) {
- next if $comment_time >= $filter_last_time;
- } else {
- next if $now - $comment_time <= 60 * 60 * 24 * $filter_last_period;
- }
- } else {
- $input->{'last'} = '';
- $input->{'last_is'} = '';
- }
-
- # get data for attachment comments
-
- if ($comment eq '' && $type == CMT_ATTACHMENT_CREATED) {
- my ($description, $is_private) = $dbh->selectrow_array($attach_sql, undef, $extra);
- next if $is_private && !Bugzilla->user->is_insider;
- $comment = "(Attachment) " . $description;
- }
-
- # truncate long comments
-
- if (length($comment) > 80) {
- $comment = substr($comment, 0, 80) . '...';
- }
-
- # build bug hash for template
-
- my $bug = {};
- $bug->{id} = $bug_id;
- $bug->{summary} = $summary;
- $bug->{reporter} = Bugzilla::User->new({ id => $reporter_id, cache => 1 });
- $bug->{creation_ts} = $creation_ts;
- $bug->{commenter} = $commenter || Bugzilla::User->new({ id => $commenter_id, cache => 1 });
- $bug->{comment_ts} = $comment_ts;
- $bug->{comment} = $comment;
- $bug->{comment_count} = $comment_count;
- push @bugs, $bug;
- }
+ foreach my $entry (@$list) {
+ my ($bug_id, $summary, $reporter_id, $creation_ts) = @$entry;
- @bugs = sort { $b->{comment_ts} cmp $a->{comment_ts} } @bugs;
+ next unless $user->can_see_bug($bug_id);
- $vars->{bugs} = \@bugs;
- } else {
- $input->{action} = '';
- }
+ # get last comment information
- if (!$input->{filter_commenter} && !$input->{filter_last}) {
- $input->{filter_commenter} = 1;
- }
+ my ($comment_count) = $dbh->selectrow_array($comment_count_sql, undef, $bug_id);
+ my ($commenter_id, $comment_ts, $type, $comment, $extra)
+ = $dbh->selectrow_array($comment_sql, undef, $bug_id);
+ my $commenter = 0;
- $vars->{'input'} = $input;
-}
+ # apply selected filters
-sub owners {
- my ($vars, $filter) = @_;
- my $dbh = Bugzilla->dbh;
- my $input = Bugzilla->input_params;
- my $user = Bugzilla->user;
+ if ($filter_commenter) {
+ next if $comment_count <= 1;
- Bugzilla::User::match_field({ 'owner' => {'type' => 'multi'} });
+ if ($filter_commenter_on eq 'reporter') {
+ next if $commenter_id != $reporter_id;
- my @products;
- if (!$input->{product} && $input->{owner}) {
- @products = @{ $user->get_selectable_products };
- }
- else {
- my @product_names = $input->{product} ? ($input->{product}) : DEFAULT_OWNER_PRODUCTS;
- foreach my $name (@product_names) {
- push(@products, Bugzilla::Product->check({ name => $name }));
}
- }
+ elsif ($filter_commenter_on eq 'noconfirm') {
+ $commenter = Bugzilla::User->new({id => $commenter_id, cache => 1});
+ next if $commenter_id != $reporter_id || $commenter->in_group('canconfirm');
- my @component_ids;
- if (@products == 1 && $input->{'component'}) {
- my $ra_components = ref($input->{'component'})
- ? $input->{'component'}
- : [ $input->{'component'} ];
- foreach my $component_name (@$ra_components) {
- my $component = Bugzilla::Component->check({ name => $component_name, product => $products[0] });
- push @component_ids, $component->id;
}
+ elsif ($filter_commenter_on eq 'is') {
+ next if $commenter_id != $filter_commenter_id;
+ }
+ }
+ else {
+ $input->{'commenter'} = '';
+ $input->{'commenter_is'} = '';
+ }
+
+ if ($filter_last) {
+ my $comment_time = str2time($comment_ts) or next;
+ if ($filter_last_period == -1) {
+ next if $comment_time >= $filter_last_time;
+ }
+ else {
+ next if $now - $comment_time <= 60 * 60 * 24 * $filter_last_period;
+ }
+ }
+ else {
+ $input->{'last'} = '';
+ $input->{'last_is'} = '';
+ }
+
+ # get data for attachment comments
+
+ if ($comment eq '' && $type == CMT_ATTACHMENT_CREATED) {
+ my ($description, $is_private)
+ = $dbh->selectrow_array($attach_sql, undef, $extra);
+ next if $is_private && !Bugzilla->user->is_insider;
+ $comment = "(Attachment) " . $description;
+ }
+
+ # truncate long comments
+
+ if (length($comment) > 80) {
+ $comment = substr($comment, 0, 80) . '...';
+ }
+
+ # build bug hash for template
+
+ my $bug = {};
+ $bug->{id} = $bug_id;
+ $bug->{summary} = $summary;
+ $bug->{reporter} = Bugzilla::User->new({id => $reporter_id, cache => 1});
+ $bug->{creation_ts} = $creation_ts;
+ $bug->{commenter}
+ = $commenter || Bugzilla::User->new({id => $commenter_id, cache => 1});
+ $bug->{comment_ts} = $comment_ts;
+ $bug->{comment} = $comment;
+ $bug->{comment_count} = $comment_count;
+ push @bugs, $bug;
}
- my @owner_names = split(/[,;]+/, $input->{owner}) if $input->{owner};
- my @owner_ids;
- foreach my $name (@owner_names) {
- $name = trim($name);
- next unless $name;
- push(@owner_ids, login_to_id($name, THROW_ERROR));
- }
+ @bugs = sort { $b->{comment_ts} cmp $a->{comment_ts} } @bugs;
- my $sql = "SELECT products.name, components.name, components.id, components.triage_owner_id
- FROM components JOIN products ON components.product_id = products.id
- WHERE products.id IN (" . join(',', map { $_->id } @products) . ")";
- if (@component_ids) {
- $sql .= " AND components.id IN (" . join(',', @component_ids) . ")";
- }
- if (@owner_ids) {
- $sql .= " AND components.triage_owner_id IN (" . join(',', @owner_ids) . ")";
- }
- $sql .= " ORDER BY products.name, components.name";
+ $vars->{bugs} = \@bugs;
+ }
+ else {
+ $input->{action} = '';
+ }
- my $rows = $dbh->selectall_arrayref($sql);
+ if (!$input->{filter_commenter} && !$input->{filter_last}) {
+ $input->{filter_commenter} = 1;
+ }
- my $bug_count_sth = $dbh->prepare("
+ $vars->{'input'} = $input;
+}
+
+sub owners {
+ my ($vars, $filter) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $input = Bugzilla->input_params;
+ my $user = Bugzilla->user;
+
+ Bugzilla::User::match_field({'owner' => {'type' => 'multi'}});
+
+ my @products;
+ if (!$input->{product} && $input->{owner}) {
+ @products = @{$user->get_selectable_products};
+ }
+ else {
+ my @product_names
+ = $input->{product} ? ($input->{product}) : DEFAULT_OWNER_PRODUCTS;
+ foreach my $name (@product_names) {
+ push(@products, Bugzilla::Product->check({name => $name}));
+ }
+ }
+
+ my @component_ids;
+ if (@products == 1 && $input->{'component'}) {
+ my $ra_components
+ = ref($input->{'component'})
+ ? $input->{'component'}
+ : [$input->{'component'}];
+ foreach my $component_name (@$ra_components) {
+ my $component = Bugzilla::Component->check(
+ {name => $component_name, product => $products[0]});
+ push @component_ids, $component->id;
+ }
+ }
+
+ my @owner_names = split(/[,;]+/, $input->{owner}) if $input->{owner};
+ my @owner_ids;
+ foreach my $name (@owner_names) {
+ $name = trim($name);
+ next unless $name;
+ push(@owner_ids, login_to_id($name, THROW_ERROR));
+ }
+
+ my $sql
+ = "SELECT products.name, components.name, components.id, components.triage_owner_id
+ FROM components JOIN products ON components.product_id = products.id
+ WHERE products.id IN ("
+ . join(',', map { $_->id } @products) . ")";
+ if (@component_ids) {
+ $sql .= " AND components.id IN (" . join(',', @component_ids) . ")";
+ }
+ if (@owner_ids) {
+ $sql .= " AND components.triage_owner_id IN (" . join(',', @owner_ids) . ")";
+ }
+ $sql .= " ORDER BY products.name, components.name";
+
+ my $rows = $dbh->selectall_arrayref($sql);
+
+ my $bug_count_sth = $dbh->prepare("
SELECT COUNT(bugs.bug_id)
FROM bugs INNER JOIN components AS map_component ON bugs.component_id = map_component.id
INNER JOIN bug_status AS map_bug_status ON bugs.bug_status = map_bug_status.value
@@ -296,55 +314,57 @@ sub owners {
WHERE bugs_1.bug_id = bugs.bug_id AND CONCAT(flagtypes_1.name, flags_1.status) = 'needinfo?')))
AND bugs.component_id = ?");
- my @results;
- foreach my $row (@$rows) {
- my ($product_name, $component_name, $component_id, $triage_owner_id) = @$row;
- my $triage_owner = $triage_owner_id
- ? Bugzilla::User->new({ id => $triage_owner_id, cache => 1 })
- : "";
- my $data = {
- product => $product_name,
- component => $component_name,
- owner => $triage_owner,
- };
- $data->{buglist_url} = 'priority=--&resolution=---&f1=creation_ts&o1=greaterthaneq&v1=2016-06-01'.
- '&f2=flagtypes.name&o2=notequals&v2=needinfo%3F';
- if ($triage_owner) {
- $data->{buglist_url} .= '&f3=triage_owner&o3=equals&v3=' . url_quote($triage_owner->login);
- }
- $bug_count_sth->execute($component_id);
- ($data->{bug_count}) = $bug_count_sth->fetchrow_array();
- push @results, $data;
+ my @results;
+ foreach my $row (@$rows) {
+ my ($product_name, $component_name, $component_id, $triage_owner_id) = @$row;
+ my $triage_owner
+ = $triage_owner_id
+ ? Bugzilla::User->new({id => $triage_owner_id, cache => 1})
+ : "";
+ my $data = {
+ product => $product_name,
+ component => $component_name,
+ owner => $triage_owner,
+ };
+ $data->{buglist_url}
+ = 'priority=--&resolution=---&f1=creation_ts&o1=greaterthaneq&v1=2016-06-01'
+ . '&f2=flagtypes.name&o2=notequals&v2=needinfo%3F';
+ if ($triage_owner) {
+ $data->{buglist_url}
+ .= '&f3=triage_owner&o3=equals&v3=' . url_quote($triage_owner->login);
}
- $vars->{results} = \@results;
-
- my $json_data = { products => [] };
- foreach my $product (@{ $user->get_selectable_products }) {
- my $prod_data = {
- name => $product->name,
- components => [],
- };
- foreach my $component (@{ $product->components }) {
- my $selected = 0;
- if ($input->{product}
- && $input->{product} eq $product->name
- && $input->{component})
- {
- $selected = 1 if (ref $input->{component} && any { $_ eq $component->name } @{ $input->{component} });
- $selected = 1 if (!ref $input->{componet} && $input->{component} eq $component->name);
- }
- my $comp_data = {
- name => $component->name,
- selected => $selected
- };
- push(@{ $prod_data->{components} }, $comp_data);
- }
- push(@{ $json_data->{products} }, $prod_data);
+ $bug_count_sth->execute($component_id);
+ ($data->{bug_count}) = $bug_count_sth->fetchrow_array();
+ push @results, $data;
+ }
+ $vars->{results} = \@results;
+
+ my $json_data = {products => []};
+ foreach my $product (@{$user->get_selectable_products}) {
+ my $prod_data = {name => $product->name, components => [],};
+ foreach my $component (@{$product->components}) {
+ my $selected = 0;
+ if ( $input->{product}
+ && $input->{product} eq $product->name
+ && $input->{component})
+ {
+ $selected = 1
+ if (
+ ref $input->{component} && any { $_ eq $component->name }
+ @{$input->{component}}
+ );
+ $selected = 1
+ if (!ref $input->{componet} && $input->{component} eq $component->name);
+ }
+ my $comp_data = {name => $component->name, selected => $selected};
+ push(@{$prod_data->{components}}, $comp_data);
}
+ push(@{$json_data->{products}}, $prod_data);
+ }
- $vars->{product} = $input->{product};
- $vars->{owner} = $input->{owner};
- $vars->{json_data} = encode_json($json_data);
+ $vars->{product} = $input->{product};
+ $vars->{owner} = $input->{owner};
+ $vars->{json_data} = encode_json($json_data);
}
1;
diff --git a/extensions/BMO/lib/Reports/UserActivity.pm b/extensions/BMO/lib/Reports/UserActivity.pm
index 8dfe0c5cd..3be6f74c9 100644
--- a/extensions/BMO/lib/Reports/UserActivity.pm
+++ b/extensions/BMO/lib/Reports/UserActivity.pm
@@ -18,104 +18,104 @@ use Bugzilla::Util qw(trim);
use DateTime;
sub report {
- my ($vars) = @_;
- my $dbh = Bugzilla->dbh;
- my $input = Bugzilla->input_params;
-
- my @who = ();
- my $from = trim($input->{'from'} || '');
- my $to = trim($input->{'to'} || '');
- my $action = $input->{'action'} || '';
-
- # fix non-breaking hyphens
- $from =~ s/\N{U+2011}/-/g;
- $to =~ s/\N{U+2011}/-/g;
-
- if ($from eq '') {
- my $dt = DateTime->now()->subtract('weeks' => 1);
- $from = $dt->ymd('-');
+ my ($vars) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $input = Bugzilla->input_params;
+
+ my @who = ();
+ my $from = trim($input->{'from'} || '');
+ my $to = trim($input->{'to'} || '');
+ my $action = $input->{'action'} || '';
+
+ # fix non-breaking hyphens
+ $from =~ s/\N{U+2011}/-/g;
+ $to =~ s/\N{U+2011}/-/g;
+
+ if ($from eq '') {
+ my $dt = DateTime->now()->subtract('weeks' => 1);
+ $from = $dt->ymd('-');
+ }
+ if ($to eq '') {
+ my $dt = DateTime->now();
+ $to = $dt->ymd('-');
+ }
+
+ if ($action eq 'run') {
+ if (!exists $input->{'who'} || $input->{'who'} eq '') {
+ ThrowUserError('user_activity_missing_username');
}
- if ($to eq '') {
- my $dt = DateTime->now();
- $to = $dt->ymd('-');
- }
-
- if ($action eq 'run') {
- if (!exists $input->{'who'} || $input->{'who'} eq '') {
- ThrowUserError('user_activity_missing_username');
- }
- Bugzilla::User::match_field({ 'who' => {'type' => 'multi'} });
+ Bugzilla::User::match_field({'who' => {'type' => 'multi'}});
- my $from_dt = string_to_datetime($from);
- $from = $from_dt->ymd();
+ my $from_dt = string_to_datetime($from);
+ $from = $from_dt->ymd();
- my $to_dt = string_to_datetime($to);
- $to = $to_dt->ymd();
+ my $to_dt = string_to_datetime($to);
+ $to = $to_dt->ymd();
- my ($activity_joins, $activity_where) = ('', '');
- my ($attachments_joins, $attachments_where) = ('', '');
- my ($tags_activity_joins, $tags_activity_where) = ('', '');
- if (Bugzilla->params->{"insidergroup"}
- && !Bugzilla->user->in_group(Bugzilla->params->{'insidergroup'}))
- {
- $activity_joins = "LEFT JOIN attachments
+ my ($activity_joins, $activity_where) = ('', '');
+ my ($attachments_joins, $attachments_where) = ('', '');
+ my ($tags_activity_joins, $tags_activity_where) = ('', '');
+ if (Bugzilla->params->{"insidergroup"}
+ && !Bugzilla->user->in_group(Bugzilla->params->{'insidergroup'}))
+ {
+ $activity_joins = "LEFT JOIN attachments
ON attachments.attach_id = bugs_activity.attach_id";
- $activity_where = "AND COALESCE(attachments.isprivate, 0) = 0";
- $attachments_where = $activity_where;
+ $activity_where = "AND COALESCE(attachments.isprivate, 0) = 0";
+ $attachments_where = $activity_where;
- $tags_activity_joins = 'LEFT JOIN longdescs
+ $tags_activity_joins = 'LEFT JOIN longdescs
ON longdescs_tags_activity.comment_id = longdescs.comment_id';
- $tags_activity_where = 'AND COALESCE(longdescs.isprivate, 0) = 0';
- }
+ $tags_activity_where = 'AND COALESCE(longdescs.isprivate, 0) = 0';
+ }
- my @who_bits;
- foreach my $who (
- ref $input->{'who'}
- ? @{$input->{'who'}}
- : $input->{'who'}
- ) {
- push @who, $who;
- push @who_bits, '?';
- }
- my $who_bits = join(',', @who_bits);
-
- if (!@who) {
- my $template = Bugzilla->template;
- my $cgi = Bugzilla->cgi;
- my $vars = {};
- $vars->{'script'} = $cgi->url(-relative => 1);
- $vars->{'fields'} = {};
- $vars->{'matches'} = [];
- $vars->{'matchsuccess'} = 0;
- $vars->{'matchmultiple'} = 1;
- print $cgi->header();
- $template->process("global/confirm-user-match.html.tmpl", $vars)
- || ThrowTemplateError($template->error());
- exit;
- }
+ my @who_bits;
+ foreach my $who (ref $input->{'who'} ? @{$input->{'who'}} : $input->{'who'}) {
+ push @who, $who;
+ push @who_bits, '?';
+ }
+ my $who_bits = join(',', @who_bits);
+
+ if (!@who) {
+ my $template = Bugzilla->template;
+ my $cgi = Bugzilla->cgi;
+ my $vars = {};
+ $vars->{'script'} = $cgi->url(-relative => 1);
+ $vars->{'fields'} = {};
+ $vars->{'matches'} = [];
+ $vars->{'matchsuccess'} = 0;
+ $vars->{'matchmultiple'} = 1;
+ print $cgi->header();
+ $template->process("global/confirm-user-match.html.tmpl", $vars)
+ || ThrowTemplateError($template->error());
+ exit;
+ }
- $from_dt = $from_dt->ymd() . ' 00:00:00';
- $to_dt = $to_dt->ymd() . ' 23:59:59';
- my @params;
- for (1..5) {
- push @params, @who;
- push @params, ($from_dt, $to_dt);
- }
+ $from_dt = $from_dt->ymd() . ' 00:00:00';
+ $to_dt = $to_dt->ymd() . ' 23:59:59';
+ my @params;
+ for (1 .. 5) {
+ push @params, @who;
+ push @params, ($from_dt, $to_dt);
+ }
- my $order = ($input->{'group'} && $input->{'group'} eq 'bug')
- ? 'bug_id, bug_when' : 'bug_when';
+ my $order
+ = ($input->{'group'} && $input->{'group'} eq 'bug')
+ ? 'bug_id, bug_when'
+ : 'bug_when';
- my $comment_filter = '';
- if (!Bugzilla->user->is_insider) {
- $comment_filter = 'AND longdescs.isprivate = 0';
- }
+ my $comment_filter = '';
+ if (!Bugzilla->user->is_insider) {
+ $comment_filter = 'AND longdescs.isprivate = 0';
+ }
- my $query = "
+ my $query = "
SELECT
fielddefs.name,
bugs_activity.bug_id,
bugs_activity.attach_id,
- ".$dbh->sql_date_format('bugs_activity.bug_when', '%Y.%m.%d %H:%i:%s')." AS ts,
+ "
+ . $dbh->sql_date_format('bugs_activity.bug_when', '%Y.%m.%d %H:%i:%s')
+ . " AS ts,
bugs_activity.removed,
bugs_activity.added,
profiles.login_name,
@@ -138,8 +138,10 @@ sub report {
'comment_tag' AS name,
longdescs_tags_activity.bug_id,
NULL as attach_id,
- ".$dbh->sql_date_format('longdescs_tags_activity.bug_when',
- '%Y.%m.%d %H:%i:%s') . " AS bug_when,
+ "
+ . $dbh->sql_date_format('longdescs_tags_activity.bug_when',
+ '%Y.%m.%d %H:%i:%s')
+ . " AS bug_when,
longdescs_tags_activity.removed,
longdescs_tags_activity.added,
profiles.login_name,
@@ -160,7 +162,8 @@ sub report {
'bug_id' AS name,
bugs.bug_id,
NULL AS attach_id,
- ".$dbh->sql_date_format('bugs.creation_ts', '%Y.%m.%d %H:%i:%s')." AS ts,
+ "
+ . $dbh->sql_date_format('bugs.creation_ts', '%Y.%m.%d %H:%i:%s') . " AS ts,
'(new bug)' AS removed,
bugs.short_desc AS added,
profiles.login_name,
@@ -199,7 +202,9 @@ sub report {
'attachments.description' AS name,
attachments.bug_id,
attachments.attach_id,
- ".$dbh->sql_date_format('attachments.creation_ts', '%Y.%m.%d %H:%i:%s')." AS ts,
+ "
+ . $dbh->sql_date_format('attachments.creation_ts', '%Y.%m.%d %H:%i:%s')
+ . " AS ts,
'(new attachment)' AS removed,
attachments.description AS added,
profiles.login_name,
@@ -215,119 +220,118 @@ sub report {
ORDER BY $order ";
- my $list = $dbh->selectall_arrayref($query, undef, @params);
+ my $list = $dbh->selectall_arrayref($query, undef, @params);
- if ($input->{debug}) {
- while (my $param = shift @params) {
- $query =~ s/\?/$dbh->quote($param)/e;
- }
- $vars->{debug_sql} = $query;
+ if ($input->{debug}) {
+ while (my $param = shift @params) {
+ $query =~ s/\?/$dbh->quote($param)/e;
+ }
+ $vars->{debug_sql} = $query;
+ }
+
+ my @operations;
+ my $operation = {};
+ my $changes = [];
+ my $incomplete_data = 0;
+ my %bug_ids;
+
+ foreach my $entry (@$list) {
+ my ($fieldname, $bugid, $attachid, $when, $removed, $added, $who, $comment_id)
+ = @$entry;
+ my %change;
+ my $activity_visible = 1;
+
+ next unless Bugzilla->user->can_see_bug($bugid);
+
+ # check if the user should see this field's activity
+ if ( $fieldname eq 'remaining_time'
+ || $fieldname eq 'estimated_time'
+ || $fieldname eq 'work_time'
+ || $fieldname eq 'deadline')
+ {
+ $activity_visible = Bugzilla->user->is_timetracker;
+ }
+ elsif ($fieldname eq 'longdescs.isprivate'
+ && !Bugzilla->user->is_insider
+ && $added)
+ {
+ $activity_visible = 0;
+ }
+ else {
+ $activity_visible = 1;
+ }
+
+ if ($activity_visible) {
+
+ # Check for the results of an old Bugzilla data corruption bug
+ if ( ($added eq '?' && $removed eq '?')
+ || ($added =~ /^\? / || $removed =~ /^\? /))
+ {
+ $incomplete_data = 1;
}
- my @operations;
- my $operation = {};
- my $changes = [];
- my $incomplete_data = 0;
- my %bug_ids;
-
- foreach my $entry (@$list) {
- my ($fieldname, $bugid, $attachid, $when, $removed, $added, $who,
- $comment_id) = @$entry;
- my %change;
- my $activity_visible = 1;
-
- next unless Bugzilla->user->can_see_bug($bugid);
-
- # check if the user should see this field's activity
- if ($fieldname eq 'remaining_time'
- || $fieldname eq 'estimated_time'
- || $fieldname eq 'work_time'
- || $fieldname eq 'deadline')
- {
- $activity_visible = Bugzilla->user->is_timetracker;
- }
- elsif ($fieldname eq 'longdescs.isprivate'
- && !Bugzilla->user->is_insider
- && $added)
- {
- $activity_visible = 0;
- }
- else {
- $activity_visible = 1;
- }
-
- if ($activity_visible) {
- # Check for the results of an old Bugzilla data corruption bug
- if (($added eq '?' && $removed eq '?')
- || ($added =~ /^\? / || $removed =~ /^\? /)) {
- $incomplete_data = 1;
- }
-
- # Start a new changeset if required (depends on the grouping type)
- my $is_new_changeset;
- if ($order eq 'bug_when') {
- $is_new_changeset =
- $operation->{'who'} &&
- (
- $who ne $operation->{'who'}
- || $when ne $operation->{'when'}
- || $bugid != $operation->{'bug'}
- );
- } else {
- $is_new_changeset =
- $operation->{'bug'} &&
- $bugid != $operation->{'bug'};
- }
- if ($is_new_changeset) {
- $operation->{'changes'} = $changes;
- push (@operations, $operation);
- $operation = {};
- $changes = [];
- }
-
- $bug_ids{$bugid} = 1;
-
- $operation->{'bug'} = $bugid;
- $operation->{'who'} = $who;
- $operation->{'when'} = $when;
-
- $change{'fieldname'} = $fieldname;
- $change{'attachid'} = $attachid;
- $change{'removed'} = $removed;
- $change{'added'} = $added;
- $change{'when'} = $when;
-
- if ($comment_id) {
- $change{'comment'} = Bugzilla::Comment->new($comment_id);
- next if $change{'comment'}->count == 0;
- }
-
- if ($attachid) {
- $change{'attach'} = Bugzilla::Attachment->new($attachid);
- }
-
- push (@$changes, \%change);
- }
+ # Start a new changeset if required (depends on the grouping type)
+ my $is_new_changeset;
+ if ($order eq 'bug_when') {
+ $is_new_changeset
+ = $operation->{'who'}
+ && ($who ne $operation->{'who'}
+ || $when ne $operation->{'when'}
+ || $bugid != $operation->{'bug'});
+ }
+ else {
+ $is_new_changeset = $operation->{'bug'} && $bugid != $operation->{'bug'};
}
+ if ($is_new_changeset) {
+ $operation->{'changes'} = $changes;
+ push(@operations, $operation);
+ $operation = {};
+ $changes = [];
+ }
+
+ $bug_ids{$bugid} = 1;
+
+ $operation->{'bug'} = $bugid;
+ $operation->{'who'} = $who;
+ $operation->{'when'} = $when;
- if ($operation->{'who'}) {
- $operation->{'changes'} = $changes;
- push (@operations, $operation);
+ $change{'fieldname'} = $fieldname;
+ $change{'attachid'} = $attachid;
+ $change{'removed'} = $removed;
+ $change{'added'} = $added;
+ $change{'when'} = $when;
+
+ if ($comment_id) {
+ $change{'comment'} = Bugzilla::Comment->new($comment_id);
+ next if $change{'comment'}->count == 0;
+ }
+
+ if ($attachid) {
+ $change{'attach'} = Bugzilla::Attachment->new($attachid);
}
- $vars->{'incomplete_data'} = $incomplete_data;
- $vars->{'operations'} = \@operations;
+ push(@$changes, \%change);
+ }
+ }
- my @bug_ids = sort { $a <=> $b } keys %bug_ids;
- $vars->{'bug_ids'} = \@bug_ids;
+ if ($operation->{'who'}) {
+ $operation->{'changes'} = $changes;
+ push(@operations, $operation);
}
- $vars->{'action'} = $action;
- $vars->{'who'} = join(',', @who);
- $vars->{'who_count'} = scalar @who;
- $vars->{'from'} = $from;
- $vars->{'to'} = $to;
- $vars->{'group'} = $input->{'group'};
+ $vars->{'incomplete_data'} = $incomplete_data;
+ $vars->{'operations'} = \@operations;
+
+ my @bug_ids = sort { $a <=> $b } keys %bug_ids;
+ $vars->{'bug_ids'} = \@bug_ids;
+ }
+
+ $vars->{'action'} = $action;
+ $vars->{'who'} = join(',', @who);
+ $vars->{'who_count'} = scalar @who;
+ $vars->{'from'} = $from;
+ $vars->{'to'} = $to;
+ $vars->{'group'} = $input->{'group'};
}
1;
diff --git a/extensions/BMO/lib/Util.pm b/extensions/BMO/lib/Util.pm
index dc9c904f9..4d376aecb 100644
--- a/extensions/BMO/lib/Util.pm
+++ b/extensions/BMO/lib/Util.pm
@@ -19,74 +19,77 @@ use DateTime;
use base qw(Exporter);
our @EXPORT = qw( string_to_datetime
- time_to_datetime
- parse_date
- is_active_status_field );
+ time_to_datetime
+ parse_date
+ is_active_status_field );
sub string_to_datetime {
- my $input = shift;
- my $time = parse_date($input)
- or ThrowUserError('report_invalid_date', { date => $input });
- return time_to_datetime($time);
+ my $input = shift;
+ my $time = parse_date($input)
+ or ThrowUserError('report_invalid_date', {date => $input});
+ return time_to_datetime($time);
}
sub time_to_datetime {
- my $time = shift;
- return DateTime->from_epoch(epoch => $time)
- ->set_time_zone('local')
- ->truncate(to => 'day');
+ my $time = shift;
+ return DateTime->from_epoch(epoch => $time)->set_time_zone('local')
+ ->truncate(to => 'day');
}
sub parse_date {
- my ($str) = @_;
- if ($str =~ /^(-|\+)?(\d+)([hHdDwWmMyY])$/) {
- # relative date
- my ($sign, $amount, $unit, $date) = ($1, $2, lc $3, time);
- my ($sec, $min, $hour, $mday, $month, $year, $wday) = localtime($date);
- $amount = -$amount if $sign && $sign eq '+';
- if ($unit eq 'w') {
- # convert weeks to days
- $amount = 7 * $amount + $wday;
- $unit = 'd';
- }
- if ($unit eq 'd') {
- $date -= $sec + 60 * $min + 3600 * $hour + 24 * 3600 * $amount;
- return $date;
- }
- elsif ($unit eq 'y') {
- return str2time(sprintf("%4d-01-01 00:00:00", $year + 1900 - $amount));
- }
- elsif ($unit eq 'm') {
- $month -= $amount;
- while ($month < 0) { $year--; $month += 12; }
- return str2time(sprintf("%4d-%02d-01 00:00:00", $year + 1900, $month + 1));
- }
- elsif ($unit eq 'h') {
- # Special case 0h for 'beginning of this hour'
- if ($amount == 0) {
- $date -= $sec + 60 * $min;
- } else {
- $date -= 3600 * $amount;
- }
- return $date;
- }
- return undef;
+ my ($str) = @_;
+ if ($str =~ /^(-|\+)?(\d+)([hHdDwWmMyY])$/) {
+
+ # relative date
+ my ($sign, $amount, $unit, $date) = ($1, $2, lc $3, time);
+ my ($sec, $min, $hour, $mday, $month, $year, $wday) = localtime($date);
+ $amount = -$amount if $sign && $sign eq '+';
+ if ($unit eq 'w') {
+
+ # convert weeks to days
+ $amount = 7 * $amount + $wday;
+ $unit = 'd';
+ }
+ if ($unit eq 'd') {
+ $date -= $sec + 60 * $min + 3600 * $hour + 24 * 3600 * $amount;
+ return $date;
+ }
+ elsif ($unit eq 'y') {
+ return str2time(sprintf("%4d-01-01 00:00:00", $year + 1900 - $amount));
}
- return str2time($str);
+ elsif ($unit eq 'm') {
+ $month -= $amount;
+ while ($month < 0) { $year--; $month += 12; }
+ return str2time(sprintf("%4d-%02d-01 00:00:00", $year + 1900, $month + 1));
+ }
+ elsif ($unit eq 'h') {
+
+ # Special case 0h for 'beginning of this hour'
+ if ($amount == 0) {
+ $date -= $sec + 60 * $min;
+ }
+ else {
+ $date -= 3600 * $amount;
+ }
+ return $date;
+ }
+ return undef;
+ }
+ return str2time($str);
}
sub is_active_status_field {
- my ($field) = @_;
+ my ($field) = @_;
- if ($field->type == FIELD_TYPE_EXTENSION
- && $field->isa('Bugzilla::Extension::TrackingFlags::Flag')
- && $field->flag_type eq 'tracking'
- && $field->name =~ /_status_/
- ) {
- return $field->is_active;
- }
+ if ( $field->type == FIELD_TYPE_EXTENSION
+ && $field->isa('Bugzilla::Extension::TrackingFlags::Flag')
+ && $field->flag_type eq 'tracking'
+ && $field->name =~ /_status_/)
+ {
+ return $field->is_active;
+ }
- return 0;
+ return 0;
}
1;
diff --git a/extensions/BMO/lib/WebService.pm b/extensions/BMO/lib/WebService.pm
index 327c8563f..4c9187254 100644
--- a/extensions/BMO/lib/WebService.pm
+++ b/extensions/BMO/lib/WebService.pm
@@ -32,26 +32,26 @@ use Bugzilla::WebService::Util qw(validate);
use Bugzilla::Field;
use constant PUBLIC_METHODS => qw(
- getBugsConfirmer
- getBugsVerifier
+ getBugsConfirmer
+ getBugsVerifier
);
sub getBugsConfirmer {
- my ($self, $params) = validate(@_, 'names');
- my $dbh = Bugzilla->dbh;
+ my ($self, $params) = validate(@_, 'names');
+ my $dbh = Bugzilla->dbh;
- defined($params->{names})
- || ThrowCodeError('params_required',
- { function => 'BMO.getBugsConfirmer', params => ['names'] });
+ defined($params->{names})
+ || ThrowCodeError('params_required',
+ {function => 'BMO.getBugsConfirmer', params => ['names']});
- my @user_objects = map { Bugzilla::User->check($_) } @{ $params->{names} };
+ my @user_objects = map { Bugzilla::User->check($_) } @{$params->{names}};
- # start filtering to remove duplicate user ids
- @user_objects = values %{{ map { $_->id => $_ } @user_objects }};
+ # start filtering to remove duplicate user ids
+ @user_objects = values %{{map { $_->id => $_ } @user_objects}};
- my $fieldid = get_field_id('bug_status');
+ my $fieldid = get_field_id('bug_status');
- my $query = "SELECT DISTINCT bugs_activity.bug_id
+ my $query = "SELECT DISTINCT bugs_activity.bug_id
FROM bugs_activity
LEFT JOIN bug_group_map
ON bugs_activity.bug_id = bug_group_map.bug_id
@@ -62,31 +62,31 @@ sub getBugsConfirmer {
AND bug_group_map.bug_id IS NULL
ORDER BY bugs_activity.bug_id";
- my %users;
- foreach my $user (@user_objects) {
- my $bugs = $dbh->selectcol_arrayref($query, undef, $fieldid, $user->id);
- $users{$user->login} = $bugs;
- }
+ my %users;
+ foreach my $user (@user_objects) {
+ my $bugs = $dbh->selectcol_arrayref($query, undef, $fieldid, $user->id);
+ $users{$user->login} = $bugs;
+ }
- return \%users;
+ return \%users;
}
sub getBugsVerifier {
- my ($self, $params) = validate(@_, 'names');
- my $dbh = Bugzilla->dbh;
+ my ($self, $params) = validate(@_, 'names');
+ my $dbh = Bugzilla->dbh;
- defined($params->{names})
- || ThrowCodeError('params_required',
- { function => 'BMO.getBugsVerifier', params => ['names'] });
+ defined($params->{names})
+ || ThrowCodeError('params_required',
+ {function => 'BMO.getBugsVerifier', params => ['names']});
- my @user_objects = map { Bugzilla::User->check($_) } @{ $params->{names} };
+ my @user_objects = map { Bugzilla::User->check($_) } @{$params->{names}};
- # start filtering to remove duplicate user ids
- @user_objects = values %{{ map { $_->id => $_ } @user_objects }};
+ # start filtering to remove duplicate user ids
+ @user_objects = values %{{map { $_->id => $_ } @user_objects}};
- my $fieldid = get_field_id('bug_status');
+ my $fieldid = get_field_id('bug_status');
- my $query = "SELECT DISTINCT bugs_activity.bug_id
+ my $query = "SELECT DISTINCT bugs_activity.bug_id
FROM bugs_activity
LEFT JOIN bug_group_map
ON bugs_activity.bug_id = bug_group_map.bug_id
@@ -97,13 +97,13 @@ sub getBugsVerifier {
AND bug_group_map.bug_id IS NULL
ORDER BY bugs_activity.bug_id";
- my %users;
- foreach my $user (@user_objects) {
- my $bugs = $dbh->selectcol_arrayref($query, undef, $fieldid, $user->id);
- $users{$user->login} = $bugs;
- }
+ my %users;
+ foreach my $user (@user_objects) {
+ my $bugs = $dbh->selectcol_arrayref($query, undef, $fieldid, $user->id);
+ $users{$user->login} = $bugs;
+ }
- return \%users;
+ return \%users;
}
1;
diff --git a/extensions/BMO/t/bounty_attachment.t b/extensions/BMO/t/bounty_attachment.t
index 6e596eeba..61552c074 100644
--- a/extensions/BMO/t/bounty_attachment.t
+++ b/extensions/BMO/t/bounty_attachment.t
@@ -13,46 +13,69 @@ use Test::More;
use Bugzilla;
BEGIN { Bugzilla->extensions }
-my $class = 'Bugzilla::Extension::BMO';
+my $class = 'Bugzilla::Extension::BMO';
my $parse = $class->can('parse_bounty_attachment_description');
my $format = $class->can('format_bounty_attachment_description');
ok($parse, "got the function");
my $bughunter = $parse->('bughunter@hacker.org, , 2014-06-25, , ,false');
-is_deeply({ reporter_email => 'bughunter@hacker.org',
- amount_paid => '',
- reported_date => '2014-06-25',
- fixed_date => '',
- awarded_date => '',
- publish => 0,
- credit => []}, $bughunter);
-
-my $hfli = $parse->('hfli@fortinet.com, 1000, 2010-07-16, 2010-08-04, 2011-06-15, true, Fortiguard Labs');
-is_deeply({ reporter_email => 'hfli@fortinet.com',
- amount_paid => '1000',
- reported_date => '2010-07-16',
- fixed_date => '2010-08-04',
- awarded_date => '2011-06-15',
- publish => 1,
- credit => ['Fortiguard Labs']}, $hfli);
-
-is('batman@justiceleague.america,1000,2015-01-01,2015-02-02,2015-03-03,true,JLA,Wayne Industries,Test',
- $format->({ reporter_email => 'batman@justiceleague.america',
- amount_paid => 1000,
- reported_date => '2015-01-01',
- fixed_date => '2015-02-02',
- awarded_date => '2015-03-03',
- publish => 1,
- credit => ['JLA', 'Wayne Industries', 'Test'] }));
-
-my $dylan = $parse->('dylan@hardison.net,2,2014-09-23,2014-09-24,2014-09-25,true,Foo bar,Bork,');
-is_deeply({ reporter_email => 'dylan@hardison.net',
- amount_paid => 2,
- reported_date => '2014-09-23',
- fixed_date => '2014-09-24',
- awarded_date => '2014-09-25',
- publish => 1,
- credit => ['Foo bar', 'Bork']}, $dylan);
+is_deeply(
+ {
+ reporter_email => 'bughunter@hacker.org',
+ amount_paid => '',
+ reported_date => '2014-06-25',
+ fixed_date => '',
+ awarded_date => '',
+ publish => 0,
+ credit => []
+ },
+ $bughunter
+);
+
+my $hfli
+ = $parse->(
+ 'hfli@fortinet.com, 1000, 2010-07-16, 2010-08-04, 2011-06-15, true, Fortiguard Labs'
+ );
+is_deeply(
+ {
+ reporter_email => 'hfli@fortinet.com',
+ amount_paid => '1000',
+ reported_date => '2010-07-16',
+ fixed_date => '2010-08-04',
+ awarded_date => '2011-06-15',
+ publish => 1,
+ credit => ['Fortiguard Labs']
+ },
+ $hfli
+);
+
+is(
+ 'batman@justiceleague.america,1000,2015-01-01,2015-02-02,2015-03-03,true,JLA,Wayne Industries,Test',
+ $format->({
+ reporter_email => 'batman@justiceleague.america',
+ amount_paid => 1000,
+ reported_date => '2015-01-01',
+ fixed_date => '2015-02-02',
+ awarded_date => '2015-03-03',
+ publish => 1,
+ credit => ['JLA', 'Wayne Industries', 'Test']
+ })
+);
+
+my $dylan = $parse->(
+ 'dylan@hardison.net,2,2014-09-23,2014-09-24,2014-09-25,true,Foo bar,Bork,');
+is_deeply(
+ {
+ reporter_email => 'dylan@hardison.net',
+ amount_paid => 2,
+ reported_date => '2014-09-23',
+ fixed_date => '2014-09-24',
+ awarded_date => '2014-09-25',
+ publish => 1,
+ credit => ['Foo bar', 'Bork']
+ },
+ $dylan
+);
done_testing;
diff --git a/extensions/Bitly/Config.pm b/extensions/Bitly/Config.pm
index 7e46a6ad8..ce27508c7 100644
--- a/extensions/Bitly/Config.pm
+++ b/extensions/Bitly/Config.pm
@@ -16,6 +16,6 @@ use constant NAME => 'Bitly';
use constant REQUIRED_MODULES => [];
use constant OPTIONAL_MODULES => [];
-use constant API_VERSION_MAP => { '1_0' => '1_0' };
+use constant API_VERSION_MAP => {'1_0' => '1_0'};
__PACKAGE__->NAME;
diff --git a/extensions/Bitly/Extension.pm b/extensions/Bitly/Extension.pm
index 82f17bc2a..31f4b7438 100644
--- a/extensions/Bitly/Extension.pm
+++ b/extensions/Bitly/Extension.pm
@@ -17,17 +17,14 @@ our $VERSION = '1';
use Bugzilla;
sub webservice {
- my ($self, $args) = @_;
- $args->{dispatch}->{Bitly} = "Bugzilla::Extension::Bitly::WebService";
+ my ($self, $args) = @_;
+ $args->{dispatch}->{Bitly} = "Bugzilla::Extension::Bitly::WebService";
}
sub config_modify_panels {
- my ($self, $args) = @_;
- push @{ $args->{panels}->{advanced}->{params} }, {
- name => 'bitly_token',
- type => 't',
- default => '',
- };
+ my ($self, $args) = @_;
+ push @{$args->{panels}->{advanced}->{params}},
+ {name => 'bitly_token', type => 't', default => '',};
}
__PACKAGE__->NAME;
diff --git a/extensions/Bitly/lib/WebService.pm b/extensions/Bitly/lib/WebService.pm
index 4b44faa0e..b900e17ee 100644
--- a/extensions/Bitly/lib/WebService.pm
+++ b/extensions/Bitly/lib/WebService.pm
@@ -27,121 +27,115 @@ use URI::Escape;
use URI::QueryParam;
use constant PUBLIC_METHODS => qw(
- list
- shorten
+ list
+ shorten
);
sub _validate_uri {
- my ($self, $params) = @_;
-
- # extract url from params
- if (!defined $params->{url}) {
- ThrowCodeError(
- 'param_required',
- { function => 'Bitly.shorten', param => 'url' }
- );
- }
- my $url = ref($params->{url}) ? $params->{url}->[0] : $params->{url};
-
- # only allow buglist queries for this bugzilla install
- my $uri = URI->new($url);
- $uri->query(undef);
- $uri->fragment(undef);
- if ($uri->as_string ne Bugzilla->localconfig->{urlbase} . 'buglist.cgi') {
- ThrowUserError('bitly_unsupported');
- }
-
- return URI->new($url);
+ my ($self, $params) = @_;
+
+ # extract url from params
+ if (!defined $params->{url}) {
+ ThrowCodeError('param_required', {function => 'Bitly.shorten', param => 'url'});
+ }
+ my $url = ref($params->{url}) ? $params->{url}->[0] : $params->{url};
+
+ # only allow buglist queries for this bugzilla install
+ my $uri = URI->new($url);
+ $uri->query(undef);
+ $uri->fragment(undef);
+ if ($uri->as_string ne Bugzilla->localconfig->{urlbase} . 'buglist.cgi') {
+ ThrowUserError('bitly_unsupported');
+ }
+
+ return URI->new($url);
}
sub shorten {
- my ($self) = shift;
- my $uri = $self->_validate_uri(@_);
+ my ($self) = shift;
+ my $uri = $self->_validate_uri(@_);
- # the list_id is user-specific, remove it
- $uri->query_param_delete('list_id');
+ # the list_id is user-specific, remove it
+ $uri->query_param_delete('list_id');
- return $self->_bitly($uri);
+ return $self->_bitly($uri);
}
sub list {
- my ($self) = shift;
- my $uri = $self->_validate_uri(@_);
-
- # map params to cgi vars, converting quicksearch if required
- my $params = $uri->query_param('quicksearch')
- ? Bugzilla::CGI->new(quicksearch($uri->query_param('quicksearch')))->Vars
- : Bugzilla::CGI->new($uri->query)->Vars;
-
- # execute the search
- my $search = Bugzilla::Search->new(
- params => $params,
- fields => ['bug_id'],
- limit => Bugzilla->params->{max_search_results},
- );
- my $data = $search->data;
-
- # form a bug_id only url, sanity check the length
- $uri = URI->new(Bugzilla->localconfig->{urlbase} . 'buglist.cgi?bug_id=' . join(',', map { $_->[0] } @$data));
- if (length($uri->as_string) > CGI_URI_LIMIT) {
- ThrowUserError('bitly_failure', { message => "Too many bugs returned by search" });
- }
-
- # shorten
- return $self->_bitly($uri);
+ my ($self) = shift;
+ my $uri = $self->_validate_uri(@_);
+
+ # map params to cgi vars, converting quicksearch if required
+ my $params
+ = $uri->query_param('quicksearch')
+ ? Bugzilla::CGI->new(quicksearch($uri->query_param('quicksearch')))->Vars
+ : Bugzilla::CGI->new($uri->query)->Vars;
+
+ # execute the search
+ my $search = Bugzilla::Search->new(
+ params => $params,
+ fields => ['bug_id'],
+ limit => Bugzilla->params->{max_search_results},
+ );
+ my $data = $search->data;
+
+ # form a bug_id only url, sanity check the length
+ $uri
+ = URI->new(Bugzilla->localconfig->{urlbase}
+ . 'buglist.cgi?bug_id='
+ . join(',', map { $_->[0] } @$data));
+ if (length($uri->as_string) > CGI_URI_LIMIT) {
+ ThrowUserError('bitly_failure',
+ {message => "Too many bugs returned by search"});
+ }
+
+ # shorten
+ return $self->_bitly($uri);
}
sub _bitly {
- my ($self, $uri) = @_;
-
- # form request url
- # http://dev.bitly.com/links.html#v3_shorten
- my $bitly_url = sprintf(
- 'https://api-ssl.bitly.com/v3/shorten?access_token=%s&longUrl=%s',
- Bugzilla->params->{bitly_token},
- uri_escape($uri->as_string)
- );
-
- # is Mozilla::CA isn't installed, skip certificate verification
- eval { require Mozilla::CA };
- $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = $@ ? 0 : 1;
-
- # request
- my $ua = LWP::UserAgent->new(agent => 'Bugzilla');
- $ua->timeout(10);
- $ua->protocols_allowed(['http', 'https']);
- if (my $proxy_url = Bugzilla->params->{proxy_url}) {
- $ua->proxy(['http', 'https'], $proxy_url);
- }
- else {
- $ua->env_proxy();
- }
- my $response = $ua->get($bitly_url);
- if ($response->is_error) {
- ThrowUserError('bitly_failure', { message => $response->message });
- }
- my $result = decode_json($response->decoded_content);
- if ($result->{status_code} != 200) {
- ThrowUserError('bitly_failure', { message => $result->{status_txt} });
- }
-
- # return just the short url
- return { url => $result->{data}->{url} };
+ my ($self, $uri) = @_;
+
+ # form request url
+ # http://dev.bitly.com/links.html#v3_shorten
+ my $bitly_url = sprintf(
+ 'https://api-ssl.bitly.com/v3/shorten?access_token=%s&longUrl=%s',
+ Bugzilla->params->{bitly_token},
+ uri_escape($uri->as_string)
+ );
+
+ # is Mozilla::CA isn't installed, skip certificate verification
+ eval { require Mozilla::CA };
+ $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = $@ ? 0 : 1;
+
+ # request
+ my $ua = LWP::UserAgent->new(agent => 'Bugzilla');
+ $ua->timeout(10);
+ $ua->protocols_allowed(['http', 'https']);
+ if (my $proxy_url = Bugzilla->params->{proxy_url}) {
+ $ua->proxy(['http', 'https'], $proxy_url);
+ }
+ else {
+ $ua->env_proxy();
+ }
+ my $response = $ua->get($bitly_url);
+ if ($response->is_error) {
+ ThrowUserError('bitly_failure', {message => $response->message});
+ }
+ my $result = decode_json($response->decoded_content);
+ if ($result->{status_code} != 200) {
+ ThrowUserError('bitly_failure', {message => $result->{status_txt}});
+ }
+
+ # return just the short url
+ return {url => $result->{data}->{url}};
}
sub rest_resources {
- return [
- qr{^/bitly/shorten$}, {
- GET => {
- method => 'shorten',
- },
- },
- qr{^/bitly/list$}, {
- GET => {
- method => 'list',
- },
- },
- ]
+ return [
+ qr{^/bitly/shorten$}, {GET => {method => 'shorten',},},
+ qr{^/bitly/list$}, {GET => {method => 'list',},},
+ ];
}
1;
diff --git a/extensions/BugModal/Config.pm b/extensions/BugModal/Config.pm
index 25a864e6e..bd15c075c 100644
--- a/extensions/BugModal/Config.pm
+++ b/extensions/BugModal/Config.pm
@@ -10,8 +10,8 @@ use 5.10.1;
use strict;
use warnings;
-use constant NAME => 'BugModal';
-use constant REQUIRED_MODULES => [ ];
-use constant OPTIONAL_MODULES => [ ];
+use constant NAME => 'BugModal';
+use constant REQUIRED_MODULES => [];
+use constant OPTIONAL_MODULES => [];
__PACKAGE__->NAME;
diff --git a/extensions/BugModal/Extension.pm b/extensions/BugModal/Extension.pm
index ef9c93a37..8b72bb757 100644
--- a/extensions/BugModal/Extension.pm
+++ b/extensions/BugModal/Extension.pm
@@ -26,322 +26,321 @@ use JSON::XS qw(encode_json);
our $VERSION = '1';
use constant READABLE_BUG_STATUS_PRODUCTS => (
- 'Core',
- 'Toolkit',
- 'Firefox',
- 'Firefox for Android',
- 'Firefox for iOS',
- 'Bugzilla',
- 'bugzilla.mozilla.org'
+ 'Core', 'Toolkit',
+ 'Firefox', 'Firefox for Android',
+ 'Firefox for iOS', 'Bugzilla',
+ 'bugzilla.mozilla.org'
);
sub show_bug_format {
- my ($self, $args) = @_;
- $args->{format} = _alternative_show_bug_format();
+ my ($self, $args) = @_;
+ $args->{format} = _alternative_show_bug_format();
}
sub edit_bug_format {
- my ($self, $args) = @_;
- $args->{format} = _alternative_show_bug_format();
+ my ($self, $args) = @_;
+ $args->{format} = _alternative_show_bug_format();
}
sub _alternative_show_bug_format {
- my $cgi = Bugzilla->cgi;
- my $user = Bugzilla->user;
- if (my $ctype = $cgi->param('ctype')) {
- return '' if $ctype ne 'html';
- }
- if (my $format = $cgi->param('format')) {
- return ($format eq '__default__' || $format eq 'default') ? '' : $format;
- }
- return $user->setting('ui_experiments') eq 'on' ? 'modal' : '';
+ my $cgi = Bugzilla->cgi;
+ my $user = Bugzilla->user;
+ if (my $ctype = $cgi->param('ctype')) {
+ return '' if $ctype ne 'html';
+ }
+ if (my $format = $cgi->param('format')) {
+ return ($format eq '__default__' || $format eq 'default') ? '' : $format;
+ }
+ return $user->setting('ui_experiments') eq 'on' ? 'modal' : '';
}
sub template_after_create {
- my ($self, $args) = @_;
- my $context = $args->{template}->context;
-
- # wrapper around time_ago()
- $context->define_filter(
- time_duration => sub {
- my ($context) = @_;
- return sub {
- my ($timestamp) = @_;
- my $datetime = datetime_from($timestamp)
- // return $timestamp;
- return time_ago($datetime);
- };
- }, 1
- );
-
- # morph a string into one which is suitable to use as an element's id
- $context->define_filter(
- id => sub {
- my ($context) = @_;
- return sub {
- my ($id) = @_;
- $id //= '';
- $id = lc($id);
- while ($id ne '' && $id !~ /^[a-z]/) {
- $id = substr($id, 1);
- }
- $id =~ tr/ /-/;
- $id =~ s/[^a-z\d\-_:\.]/_/g;
- return $id;
- };
- }, 1
- );
-
- # parse date string and output epoch
- $context->define_filter(
- epoch => sub {
- my ($context) = @_;
- return sub {
- my ($date_str) = @_;
- return date_str_to_time($date_str);
- };
- }, 1
- );
-
- # flatten a list of hashrefs to a list of values
- # eg. logins = users.pluck("login")
- $context->define_vmethod(
- list => pluck => sub {
- my ($list, $field) = @_;
- return [ map { $_->$field } @$list ];
- }
- );
-
- # returns array where the value in $field does not equal $value
- # opposite of "only"
- # eg. not_byron = users.skip("name", "Byron")
- $context->define_vmethod(
- list => skip => sub {
- my ($list, $field, $value) = @_;
- return [ grep { $_->$field ne $value } @$list ];
- }
- );
-
- # returns array where the value in $field equals $value
- # opposite of "skip"
- # eg. byrons_only = users.only("name", "Byron")
- $context->define_vmethod(
- list => only => sub {
- my ($list, $field, $value) = @_;
- return [ grep { $_->$field eq $value } @$list ];
- }
- );
-
- # returns boolean indicating if the value exists in the list
- # eg. has_byron = user_names.exists("byron")
- $context->define_vmethod(
- list => exists => sub {
- my ($list, $value) = @_;
- return any { $_ eq $value } @$list;
- }
- );
-
- # ucfirst is only available in new template::toolkit versions
- $context->define_vmethod(
- item => ucfirst => sub {
- my ($text) = @_;
- return ucfirst($text);
- }
- );
-}
-
-sub template_before_process {
- my ($self, $args) = @_;
- my $file = $args->{file};
- my $vars = $args->{vars};
-
- if ($file eq 'bug/process/header.html.tmpl'
- || $file eq 'bug/create/created.html.tmpl'
- || $file eq 'attachment/created.html.tmpl'
- || $file eq 'attachment/updated.html.tmpl')
- {
- if (_alternative_show_bug_format() eq 'modal') {
- $vars->{alt_ui_header} = 'bug_modal/header.html.tmpl';
- $vars->{alt_ui_show} = 'bug/show-modal.html.tmpl';
- $vars->{alt_ui_edit} = 'bug_modal/edit.html.tmpl';
+ my ($self, $args) = @_;
+ my $context = $args->{template}->context;
+
+ # wrapper around time_ago()
+ $context->define_filter(
+ time_duration => sub {
+ my ($context) = @_;
+ return sub {
+ my ($timestamp) = @_;
+ my $datetime = datetime_from($timestamp) // return $timestamp;
+ return time_ago($datetime);
+ };
+ },
+ 1
+ );
+
+ # morph a string into one which is suitable to use as an element's id
+ $context->define_filter(
+ id => sub {
+ my ($context) = @_;
+ return sub {
+ my ($id) = @_;
+ $id //= '';
+ $id = lc($id);
+ while ($id ne '' && $id !~ /^[a-z]/) {
+ $id = substr($id, 1);
}
- return;
+ $id =~ tr/ /-/;
+ $id =~ s/[^a-z\d\-_:\.]/_/g;
+ return $id;
+ };
+ },
+ 1
+ );
+
+ # parse date string and output epoch
+ $context->define_filter(
+ epoch => sub {
+ my ($context) = @_;
+ return sub {
+ my ($date_str) = @_;
+ return date_str_to_time($date_str);
+ };
+ },
+ 1
+ );
+
+ # flatten a list of hashrefs to a list of values
+ # eg. logins = users.pluck("login")
+ $context->define_vmethod(
+ list => pluck => sub {
+ my ($list, $field) = @_;
+ return [map { $_->$field } @$list];
}
-
- if ($file =~ m#^bug/show-([^\.]+)\.html\.tmpl$#) {
- my $format = $1;
- return unless _alternative_show_bug_format() eq $format;
+ );
+
+ # returns array where the value in $field does not equal $value
+ # opposite of "only"
+ # eg. not_byron = users.skip("name", "Byron")
+ $context->define_vmethod(
+ list => skip => sub {
+ my ($list, $field, $value) = @_;
+ return [grep { $_->$field ne $value } @$list];
}
- elsif ($file ne 'bug_modal/edit.html.tmpl') {
- return;
+ );
+
+ # returns array where the value in $field equals $value
+ # opposite of "skip"
+ # eg. byrons_only = users.only("name", "Byron")
+ $context->define_vmethod(
+ list => only => sub {
+ my ($list, $field, $value) = @_;
+ return [grep { $_->$field eq $value } @$list];
}
-
- if ($vars->{bug} && !$vars->{bugs}) {
- $vars->{bugs} = [$vars->{bug}];
+ );
+
+ # returns boolean indicating if the value exists in the list
+ # eg. has_byron = user_names.exists("byron")
+ $context->define_vmethod(
+ list => exists => sub {
+ my ($list, $value) = @_;
+ return any { $_ eq $value } @$list;
}
+ );
- return unless
- $vars->{bugs}
- && ref($vars->{bugs}) eq 'ARRAY'
- && scalar(@{ $vars->{bugs} }) == 1;
- my $bug = $vars->{bugs}->[0];
- return if exists $bug->{error};
-
- # trigger loading of tracking flags
- if (Bugzilla->has_extension('TrackingFlags')) {
- Bugzilla::Extension::TrackingFlags->template_before_process({
- file => 'bug/edit.html.tmpl',
- vars => $vars,
- });
+ # ucfirst is only available in new template::toolkit versions
+ $context->define_vmethod(
+ item => ucfirst => sub {
+ my ($text) = @_;
+ return ucfirst($text);
}
+ );
+}
- if (any { $bug->product eq $_ } READABLE_BUG_STATUS_PRODUCTS) {
- my @flags = map { { name => $_->name, status => $_->status } } @{$bug->flags};
- $vars->{readable_bug_status_json} = encode_json({
- dupe_of => $bug->dup_id,
- id => $bug->id,
- keywords => [ map { $_->name } @{$bug->keyword_objects} ],
- priority => $bug->priority,
- resolution => $bug->resolution,
- status => $bug->bug_status,
- flags => \@flags,
- target_milestone => $bug->target_milestone,
- map { $_->name => $_->bug_flag($bug->id)->value } @{$vars->{tracking_flags}},
- });
- # HTML4 attributes cannot be longer than this, so just skip it in this case.
- if (length($vars->{readable_bug_status_json}) > 65536) {
- delete $vars->{readable_bug_status_json};
- }
+sub template_before_process {
+ my ($self, $args) = @_;
+ my $file = $args->{file};
+ my $vars = $args->{vars};
+
+ if ( $file eq 'bug/process/header.html.tmpl'
+ || $file eq 'bug/create/created.html.tmpl'
+ || $file eq 'attachment/created.html.tmpl'
+ || $file eq 'attachment/updated.html.tmpl')
+ {
+ if (_alternative_show_bug_format() eq 'modal') {
+ $vars->{alt_ui_header} = 'bug_modal/header.html.tmpl';
+ $vars->{alt_ui_show} = 'bug/show-modal.html.tmpl';
+ $vars->{alt_ui_edit} = 'bug_modal/edit.html.tmpl';
}
+ return;
+ }
+
+ if ($file =~ m#^bug/show-([^\.]+)\.html\.tmpl$#) {
+ my $format = $1;
+ return unless _alternative_show_bug_format() eq $format;
+ }
+ elsif ($file ne 'bug_modal/edit.html.tmpl') {
+ return;
+ }
+
+ if ($vars->{bug} && !$vars->{bugs}) {
+ $vars->{bugs} = [$vars->{bug}];
+ }
+
+ return
+ unless $vars->{bugs}
+ && ref($vars->{bugs}) eq 'ARRAY'
+ && scalar(@{$vars->{bugs}}) == 1;
+ my $bug = $vars->{bugs}->[0];
+ return if exists $bug->{error};
+
+ # trigger loading of tracking flags
+ if (Bugzilla->has_extension('TrackingFlags')) {
+ Bugzilla::Extension::TrackingFlags->template_before_process({
+ file => 'bug/edit.html.tmpl', vars => $vars,
+ });
+ }
+
+ if (any { $bug->product eq $_ } READABLE_BUG_STATUS_PRODUCTS) {
+ my @flags = map { {name => $_->name, status => $_->status} } @{$bug->flags};
+ $vars->{readable_bug_status_json} = encode_json({
+ dupe_of => $bug->dup_id,
+ id => $bug->id,
+ keywords => [map { $_->name } @{$bug->keyword_objects}],
+ priority => $bug->priority,
+ resolution => $bug->resolution,
+ status => $bug->bug_status,
+ flags => \@flags,
+ target_milestone => $bug->target_milestone,
+ map { $_->name => $_->bug_flag($bug->id)->value } @{$vars->{tracking_flags}},
+ });
- # bug->choices loads a lot of data that we want to lazy-load
- # just load the status and resolutions and perform extra checks here
- # upstream does these checks in the bug/fields template
- my $perms = $bug->user;
- my @resolutions;
- foreach my $r (@{ Bugzilla::Field->new({ name => 'resolution', cache => 1 })->legal_values }) {
- my $resolution = $r->name;
- next unless $resolution;
-
- # always allow the current value
- if ($resolution eq $bug->resolution) {
- push @resolutions, $r;
- next;
- }
-
- # never allow inactive values
- next unless $r->is_active;
+ # HTML4 attributes cannot be longer than this, so just skip it in this case.
+ if (length($vars->{readable_bug_status_json}) > 65536) {
+ delete $vars->{readable_bug_status_json};
+ }
+ }
+
+ # bug->choices loads a lot of data that we want to lazy-load
+ # just load the status and resolutions and perform extra checks here
+ # upstream does these checks in the bug/fields template
+ my $perms = $bug->user;
+ my @resolutions;
+ foreach my $r (
+ @{Bugzilla::Field->new({name => 'resolution', cache => 1})->legal_values})
+ {
+ my $resolution = $r->name;
+ next unless $resolution;
+
+ # always allow the current value
+ if ($resolution eq $bug->resolution) {
+ push @resolutions, $r;
+ next;
+ }
- # ensure the user has basic rights to change this field
- next unless $bug->check_can_change_field('resolution', '---', $resolution);
+ # never allow inactive values
+ next unless $r->is_active;
- # canconfirm users can only set the resolution to WFM, INCOMPLETE or DUPE
- if ($perms->{canconfirm}
- && !($perms->{canedit} || $perms->{isreporter}))
- {
- next if
- $resolution ne 'WORKSFORME'
- && $resolution ne 'INCOMPLETE'
- && $resolution ne 'DUPLICATE';
- }
+ # ensure the user has basic rights to change this field
+ next unless $bug->check_can_change_field('resolution', '---', $resolution);
- # reporters can set it to anything, except INCOMPLETE
- if ($perms->{isreporter}
- && !($perms->{canconfirm} || $perms->{canedit}))
- {
- next if $resolution eq 'INCOMPLETE';
- }
+ # canconfirm users can only set the resolution to WFM, INCOMPLETE or DUPE
+ if ($perms->{canconfirm} && !($perms->{canedit} || $perms->{isreporter})) {
+ next
+ if $resolution ne 'WORKSFORME'
+ && $resolution ne 'INCOMPLETE'
+ && $resolution ne 'DUPLICATE';
+ }
- # expired has, uh, expired
- next if $resolution eq 'EXPIRED';
+ # reporters can set it to anything, except INCOMPLETE
+ if ($perms->{isreporter} && !($perms->{canconfirm} || $perms->{canedit})) {
+ next if $resolution eq 'INCOMPLETE';
+ }
- push @resolutions, $r;
+ # expired has, uh, expired
+ next if $resolution eq 'EXPIRED';
+
+ push @resolutions, $r;
+ }
+ $bug->{choices} = {
+ bug_status => [
+ grep { $_->is_active || $_->name eq $bug->bug_status }
+ @{$bug->statuses_available}
+ ],
+ resolution => \@resolutions,
+ };
+
+ # group tracking flags by version to allow for a better tabular output
+ my @tracking_table;
+ my $tracking_flags = $vars->{tracking_flags};
+ foreach my $flag (@$tracking_flags) {
+ my $flag_type = $flag->flag_type;
+ my $type = 'status';
+ my $name = $flag->description;
+ if ($flag_type eq 'tracking' && $name =~ /^(tracking|status)-(.+)/) {
+ ($type, $name) = ($1, $2);
}
- $bug->{choices} = {
- bug_status => [
- grep { $_->is_active || $_->name eq $bug->bug_status }
- @{ $bug->statuses_available }
- ],
- resolution => \@resolutions,
- };
-
- # group tracking flags by version to allow for a better tabular output
- my @tracking_table;
- my $tracking_flags = $vars->{tracking_flags};
- foreach my $flag (@$tracking_flags) {
- my $flag_type = $flag->flag_type;
- my $type = 'status';
- my $name = $flag->description;
- if ($flag_type eq 'tracking' && $name =~ /^(tracking|status)-(.+)/) {
- ($type, $name) = ($1, $2);
- }
- my ($existing) = grep { $_->{type} eq $flag_type && $_->{name} eq $name } @tracking_table;
- if ($existing) {
- $existing->{$type} = $flag;
- }
- else {
- push @tracking_table, {
- $type => $flag,
- name => $name,
- type => $flag_type,
- };
- }
+ my ($existing)
+ = grep { $_->{type} eq $flag_type && $_->{name} eq $name } @tracking_table;
+ if ($existing) {
+ $existing->{$type} = $flag;
}
- $vars->{tracking_flags_table} = \@tracking_table;
-
- # for the "view -> hide treeherder comments" menu item
- my $treeherder_id = Bugzilla->treeherder_user->id;
- foreach my $change_set (@{ $bug->activity_stream }) {
- if ($change_set->{comment} && $change_set->{comment}->author->id == $treeherder_id) {
- $vars->{treeherder} = Bugzilla->treeherder_user;
- last;
- }
+ else {
+ push @tracking_table, {$type => $flag, name => $name, type => $flag_type,};
+ }
+ }
+ $vars->{tracking_flags_table} = \@tracking_table;
+
+ # for the "view -> hide treeherder comments" menu item
+ my $treeherder_id = Bugzilla->treeherder_user->id;
+ foreach my $change_set (@{$bug->activity_stream}) {
+ if ( $change_set->{comment}
+ && $change_set->{comment}->author->id == $treeherder_id)
+ {
+ $vars->{treeherder} = Bugzilla->treeherder_user;
+ last;
}
+ }
}
sub bug_start_of_set_all {
- my ($self, $args) = @_;
- my $bug = $args->{bug};
- my $params = $args->{params};
-
- # reset to the component defaults if not supplied
- if (exists $params->{assigned_to} && (!defined $params->{assigned_to} || $params->{assigned_to} eq '')) {
- $params->{assigned_to} = $bug->component_obj->default_assignee->login;
- }
- if (exists $params->{qa_contact} && (!defined $params->{qa_contact} || $params->{qa_contact} eq '')
- && $bug->component_obj->default_qa_contact->id)
- {
- $params->{qa_contact} = $bug->component_obj->default_qa_contact->login;
- }
+ my ($self, $args) = @_;
+ my $bug = $args->{bug};
+ my $params = $args->{params};
+
+ # reset to the component defaults if not supplied
+ if (exists $params->{assigned_to}
+ && (!defined $params->{assigned_to} || $params->{assigned_to} eq ''))
+ {
+ $params->{assigned_to} = $bug->component_obj->default_assignee->login;
+ }
+ if ( exists $params->{qa_contact}
+ && (!defined $params->{qa_contact} || $params->{qa_contact} eq '')
+ && $bug->component_obj->default_qa_contact->id)
+ {
+ $params->{qa_contact} = $bug->component_obj->default_qa_contact->login;
+ }
}
sub webservice {
- my ($self, $args) = @_;
- my $dispatch = $args->{dispatch};
- $dispatch->{bug_modal} = 'Bugzilla::Extension::BugModal::WebService';
+ my ($self, $args) = @_;
+ my $dispatch = $args->{dispatch};
+ $dispatch->{bug_modal} = 'Bugzilla::Extension::BugModal::WebService';
}
sub install_before_final_checks {
- my ($self, $args) = @_;
- add_setting({
- name => 'ui_experiments',
- options => ['on', 'off'],
- default => 'on',
- category => 'User Interface'
- });
- add_setting({
- name => 'ui_remember_collapsed',
- options => ['on', 'off'],
- default => 'off',
- category => 'User Interface'
- });
- add_setting({
- name => 'ui_use_absolute_time',
- options => ['on', 'off'],
- default => 'off',
- category => 'User Interface',
- });
+ my ($self, $args) = @_;
+ add_setting({
+ name => 'ui_experiments',
+ options => ['on', 'off'],
+ default => 'on',
+ category => 'User Interface'
+ });
+ add_setting({
+ name => 'ui_remember_collapsed',
+ options => ['on', 'off'],
+ default => 'off',
+ category => 'User Interface'
+ });
+ add_setting({
+ name => 'ui_use_absolute_time',
+ options => ['on', 'off'],
+ default => 'off',
+ category => 'User Interface',
+ });
}
__PACKAGE__->NAME;
diff --git a/extensions/BugModal/lib/ActivityStream.pm b/extensions/BugModal/lib/ActivityStream.pm
index 098c5df33..a7983e85c 100644
--- a/extensions/BugModal/lib/ActivityStream.pm
+++ b/extensions/BugModal/lib/ActivityStream.pm
@@ -49,310 +49,324 @@ use Bugzilla::Constants;
# ]
sub activity_stream {
- my ($self) = @_;
- if (!$self->{activity_stream}) {
- my $stream = [];
- _add_comments_to_stream($self, $stream);
- _add_activities_to_stream($self, $stream);
- _add_duplicates_to_stream($self, $stream);
-
- my $base_time = date_str_to_time($self->creation_ts);
- foreach my $change_set (@$stream) {
- $change_set->{id} = $change_set->{comment}
- ? 'c' . $change_set->{comment}->count
- : 'a' . ($change_set->{time} - $base_time) . '_' . $change_set->{user_id};
- foreach my $activity (@{ $change_set->{activity} }) {
- $activity->{changes} = [
- sort { $a->{fieldname} cmp $b->{fieldname} }
- @{ $activity->{changes} }
- ];
- }
- }
- my $order = Bugzilla->user->setting('comment_sort_order');
- if ($order eq 'oldest_to_newest') {
- $self->{activity_stream} = [ sort { $a->{time} <=> $b->{time} } @$stream ];
- }
- elsif ($order eq 'newest_to_oldest') {
- $self->{activity_stream} = [ sort { $b->{time} <=> $a->{time} } @$stream ];
- }
- elsif ($order eq 'newest_to_oldest_desc_first') {
- my $desc = shift @$stream;
- $self->{activity_stream} = [ $desc, sort { $b->{time} <=> $a->{time} } @$stream ];
- }
+ my ($self) = @_;
+ if (!$self->{activity_stream}) {
+ my $stream = [];
+ _add_comments_to_stream($self, $stream);
+ _add_activities_to_stream($self, $stream);
+ _add_duplicates_to_stream($self, $stream);
+
+ my $base_time = date_str_to_time($self->creation_ts);
+ foreach my $change_set (@$stream) {
+ $change_set->{id}
+ = $change_set->{comment}
+ ? 'c' . $change_set->{comment}->count
+ : 'a' . ($change_set->{time} - $base_time) . '_' . $change_set->{user_id};
+ foreach my $activity (@{$change_set->{activity}}) {
+ $activity->{changes}
+ = [sort { $a->{fieldname} cmp $b->{fieldname} } @{$activity->{changes}}];
+ }
+ }
+ my $order = Bugzilla->user->setting('comment_sort_order');
+ if ($order eq 'oldest_to_newest') {
+ $self->{activity_stream} = [sort { $a->{time} <=> $b->{time} } @$stream];
}
- return $self->{activity_stream};
+ elsif ($order eq 'newest_to_oldest') {
+ $self->{activity_stream} = [sort { $b->{time} <=> $a->{time} } @$stream];
+ }
+ elsif ($order eq 'newest_to_oldest_desc_first') {
+ my $desc = shift @$stream;
+ $self->{activity_stream} = [$desc, sort { $b->{time} <=> $a->{time} } @$stream];
+ }
+ }
+ return $self->{activity_stream};
}
sub find_activity_id_for_attachment {
- my ($self, $attachment) = @_;
- my $attach_id = $attachment->id;
- my $stream = $self->activity_stream;
- foreach my $change_set (@$stream) {
- next unless exists $change_set->{attach_id};
- return $change_set->{id} if $change_set->{attach_id} == $attach_id;
- }
- return undef;
+ my ($self, $attachment) = @_;
+ my $attach_id = $attachment->id;
+ my $stream = $self->activity_stream;
+ foreach my $change_set (@$stream) {
+ next unless exists $change_set->{attach_id};
+ return $change_set->{id} if $change_set->{attach_id} == $attach_id;
+ }
+ return undef;
}
sub find_activity_id_for_flag {
- my ($self, $flag) = @_;
- my $flagtype_name = $flag->type->name;
- my $date = $flag->modification_date;
- my $setter_id = $flag->setter->id;
- my $stream = $self->activity_stream;
-
- # unfortunately bugs_activity treats all flag changes as the same field, so
- # we don't have an object_id to match on
-
- if (!exists $self->{activity_cache}->{flag}->{$flag->id}) {
- foreach my $change_set (reverse @$stream) {
- foreach my $activity (@{ $change_set->{activity} }) {
- # match by user, timestamp, and flag-type name
- next unless
- $activity->{who}->id == $setter_id
- && $activity->{when} eq $date;
- foreach my $change (@{ $activity->{changes} }) {
- next unless
- $change->{fieldname} eq 'flagtypes.name'
- && $change->{flagtype_name} eq $flagtype_name;
- $self->{activity_cache}->{flag}->{$flag->id} = $change_set->{id};
- return $change_set->{id};
- }
- }
+ my ($self, $flag) = @_;
+ my $flagtype_name = $flag->type->name;
+ my $date = $flag->modification_date;
+ my $setter_id = $flag->setter->id;
+ my $stream = $self->activity_stream;
+
+ # unfortunately bugs_activity treats all flag changes as the same field, so
+ # we don't have an object_id to match on
+
+ if (!exists $self->{activity_cache}->{flag}->{$flag->id}) {
+ foreach my $change_set (reverse @$stream) {
+ foreach my $activity (@{$change_set->{activity}}) {
+
+ # match by user, timestamp, and flag-type name
+ next unless $activity->{who}->id == $setter_id && $activity->{when} eq $date;
+ foreach my $change (@{$activity->{changes}}) {
+ next
+ unless $change->{fieldname} eq 'flagtypes.name'
+ && $change->{flagtype_name} eq $flagtype_name;
+ $self->{activity_cache}->{flag}->{$flag->id} = $change_set->{id};
+ return $change_set->{id};
}
- # if we couldn't find the flag in bugs_activity it means it was set
- # during bug creation
- $self->{activity_cache}->{flag}->{$flag->id} = 'c0';
+ }
}
- return $self->{activity_cache}->{flag}->{$flag->id};
+
+ # if we couldn't find the flag in bugs_activity it means it was set
+ # during bug creation
+ $self->{activity_cache}->{flag}->{$flag->id} = 'c0';
+ }
+ return $self->{activity_cache}->{flag}->{$flag->id};
}
# comments are processed first, so there's no need to merge into existing entries
sub _add_comment_to_stream {
- my ($stream, $time, $user_id, $comment) = @_;
- my $rh = {
- time => $time,
- user_id => $user_id,
- comment => $comment,
- activity => [],
- };
- if ($comment->type == CMT_ATTACHMENT_CREATED || $comment->type == CMT_ATTACHMENT_UPDATED) {
- $rh->{attach_id} = $comment->extra_data;
- }
- push @$stream, $rh;
+ my ($stream, $time, $user_id, $comment) = @_;
+ my $rh
+ = {time => $time, user_id => $user_id, comment => $comment, activity => [],};
+ if ( $comment->type == CMT_ATTACHMENT_CREATED
+ || $comment->type == CMT_ATTACHMENT_UPDATED)
+ {
+ $rh->{attach_id} = $comment->extra_data;
+ }
+ push @$stream, $rh;
}
sub _add_activity_to_stream {
- my ($stream, $time, $user_id, $data) = @_;
- foreach my $entry (@$stream) {
- next unless $entry->{time} == $time && $entry->{user_id} == $user_id;
- $entry->{cc_only} = $entry->{cc_only} && $data->{cc_only};
- push @{ $entry->{activity} }, $data;
- return;
- }
- push @$stream, {
- time => $time,
- user_id => $user_id,
- comment => undef,
- cc_only => $data->{cc_only},
- activity => [ $data ],
+ my ($stream, $time, $user_id, $data) = @_;
+ foreach my $entry (@$stream) {
+ next unless $entry->{time} == $time && $entry->{user_id} == $user_id;
+ $entry->{cc_only} = $entry->{cc_only} && $data->{cc_only};
+ push @{$entry->{activity}}, $data;
+ return;
+ }
+ push @$stream,
+ {
+ time => $time,
+ user_id => $user_id,
+ comment => undef,
+ cc_only => $data->{cc_only},
+ activity => [$data],
};
}
sub _add_comments_to_stream {
- my ($bug, $stream) = @_;
- my $user = Bugzilla->user;
- my $treeherder_id = Bugzilla->treeherder_user->id;
-
- my $raw_comments = $bug->comments();
- foreach my $comment (@$raw_comments) {
- next if $comment->type == CMT_HAS_DUPE;
- my $author_id = $comment->author->id;
- next if $comment->is_private && !($user->is_insider || $user->id == $author_id);
- next if $comment->body eq '' && ($comment->work_time - 0) != 0 && $user->is_timetracker;
-
- # treeherder is so spammy we hide its comments by default
- if ($author_id == $treeherder_id) {
- $comment->{collapsed} = 1;
- $comment->{collapsed_reason} = $comment->author->name;
- }
- if ($comment->type != CMT_ATTACHMENT_CREATED && $comment->count == 0 && length($comment->body) == 0) {
- $comment->{collapsed} = 1;
- $comment->{collapsed_reason} = 'empty';
- }
- # If comment type is resolved as duplicate, do not add '...marked as duplicate...' string to comment body
- if ($comment->type == CMT_DUPE_OF) {
- $comment->set_type(0);
- # Skip if user did not supply comment also
- next if $comment->body eq '';
- }
-
- _add_comment_to_stream($stream, date_str_to_time($comment->creation_ts), $comment->author->id, $comment);
+ my ($bug, $stream) = @_;
+ my $user = Bugzilla->user;
+ my $treeherder_id = Bugzilla->treeherder_user->id;
+
+ my $raw_comments = $bug->comments();
+ foreach my $comment (@$raw_comments) {
+ next if $comment->type == CMT_HAS_DUPE;
+ my $author_id = $comment->author->id;
+ next if $comment->is_private && !($user->is_insider || $user->id == $author_id);
+ next
+ if $comment->body eq ''
+ && ($comment->work_time - 0) != 0
+ && $user->is_timetracker;
+
+ # treeherder is so spammy we hide its comments by default
+ if ($author_id == $treeherder_id) {
+ $comment->{collapsed} = 1;
+ $comment->{collapsed_reason} = $comment->author->name;
}
-}
-
-sub _add_activities_to_stream {
- my ($bug, $stream) = @_;
- my $dbh = Bugzilla->dbh;
- my $user = Bugzilla->user;
-
- # build bug activity
- my ($raw_activity) = $bug->can('get_activity')
- ? $bug->get_activity()
- : Bugzilla::Bug::GetBugActivity($bug->id);
-
- # allow other extensions to alter history
- Bugzilla::Hook::process('inline_history_activitiy', { activity => $raw_activity });
-
- my %attachment_cache;
- foreach my $attachment (@{$bug->attachments}) {
- $attachment_cache{$attachment->id} = $attachment;
+ if ( $comment->type != CMT_ATTACHMENT_CREATED
+ && $comment->count == 0
+ && length($comment->body) == 0)
+ {
+ $comment->{collapsed} = 1;
+ $comment->{collapsed_reason} = 'empty';
}
- # build a list of bugs we need to check visibility of, so we can check with a single query
- my %visible_bug_ids;
+# If comment type is resolved as duplicate, do not add '...marked as duplicate...' string to comment body
+ if ($comment->type == CMT_DUPE_OF) {
+ $comment->set_type(0);
- # envelope, augment and tweak
- foreach my $operation (@$raw_activity) {
-
- # make operation.who an object
- $operation->{who} = Bugzilla::User->new({ name => $operation->{who}, cache => 1 });
-
- # we need to track operations which are just cc changes
- $operation->{cc_only} = 1;
-
- 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';
- }
-
- # load field object (only required for custom fields), and set the
- # field type for custom fields
- my $field_obj;
- if ($change->{fieldname} =~ /^cf_/) {
- $field_obj = Bugzilla::Field->new({ name => $change->{fieldname}, cache => 1 });
- $change->{fieldtype} = $field_obj->type;
- }
+ # Skip if user did not supply comment also
+ next if $comment->body eq '';
+ }
- # 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;
- }
- }
- }
- }
+ _add_comment_to_stream($stream, date_str_to_time($comment->creation_ts),
+ $comment->author->id, $comment);
+ }
+}
- # split see-also
- if ($change->{fieldname} eq 'see_also') {
- my $url_base = Bugzilla->localconfig->{urlbase};
- foreach my $f (qw( added removed )) {
- my @values;
- foreach my $value (split(/, /, $change->{$f})) {
- my ($bug_id) = substr($value, 0, length($url_base)) eq $url_base
- ? $value =~ /id=(\d+)$/
- : undef;
- push @values, {
- url => $value,
- bug_id => $bug_id,
- };
- }
- $change->{$f} = \@values;
- }
+sub _add_activities_to_stream {
+ my ($bug, $stream) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->user;
+
+ # build bug activity
+ my ($raw_activity)
+ = $bug->can('get_activity')
+ ? $bug->get_activity()
+ : Bugzilla::Bug::GetBugActivity($bug->id);
+
+ # allow other extensions to alter history
+ Bugzilla::Hook::process('inline_history_activitiy',
+ {activity => $raw_activity});
+
+ 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;
+
+ # envelope, augment and tweak
+ foreach my $operation (@$raw_activity) {
+
+ # make operation.who an object
+ $operation->{who}
+ = Bugzilla::User->new({name => $operation->{who}, cache => 1});
+
+ # we need to track operations which are just cc changes
+ $operation->{cc_only} = 1;
+
+ 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';
+ }
+
+ # load field object (only required for custom fields), and set the
+ # field type for custom fields
+ my $field_obj;
+ if ($change->{fieldname} =~ /^cf_/) {
+ $field_obj = Bugzilla::Field->new({name => $change->{fieldname}, cache => 1});
+ $change->{fieldtype} = $field_obj->type;
+ }
+
+ # 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 see-also
+ if ($change->{fieldname} eq 'see_also') {
+ my $url_base = Bugzilla->localconfig->{urlbase};
+ foreach my $f (qw( added removed )) {
+ my @values;
+ foreach my $value (split(/, /, $change->{$f})) {
+ my ($bug_id)
+ = substr($value, 0, length($url_base)) eq $url_base
+ ? $value =~ /id=(\d+)$/
+ : undef;
+ push @values, {url => $value, bug_id => $bug_id,};
+ }
+ $change->{$f} = \@values;
+ }
+ }
+
+ # track cc-only
+ if ($change->{fieldname} ne 'cc') {
+ $operation->{cc_only} = 0;
+ }
+
+ # split multiple flag changes (must be processed last)
+ # set $change->{flagtype_name} to make searching the activity
+ # stream for flag changes easier and quicker
+ if ($change->{fieldname} eq 'flagtypes.name') {
+ my @added = split(/, /, $change->{added});
+ my @removed = split(/, /, $change->{removed});
+ if (scalar(@added) <= 1 && scalar(@removed) <= 1) {
+ $change->{flagtype_name} = _extract_flagtype($added[0] || $removed[0]);
+ next;
+ }
- # track cc-only
- if ($change->{fieldname} ne 'cc') {
- $operation->{cc_only} = 0;
- }
+ # remove current change
+ splice(@{$operation->{changes}}, $i, 1);
- # split multiple flag changes (must be processed last)
- # set $change->{flagtype_name} to make searching the activity
- # stream for flag changes easier and quicker
- if ($change->{fieldname} eq 'flagtypes.name') {
- my @added = split(/, /, $change->{added});
- my @removed = split(/, /, $change->{removed});
- if (scalar(@added) <= 1 && scalar(@removed) <= 1) {
- $change->{flagtype_name} = _extract_flagtype($added[0] || $removed[0]);
- next;
- }
- # remove current change
- splice(@{$operation->{changes}}, $i, 1);
- # restructure into added/removed for each flag
- my %flags;
- foreach my $flag (@added) {
- $flags{$flag}{added} = $flag;
- $flags{$flag}{removed} = '';
- }
- foreach my $flag (@removed) {
- $flags{$flag}{added} = '';
- $flags{$flag}{removed} = $flag;
- }
- # 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};
- $flag_change->{flagtype_name} = _extract_flagtype($flag);
- splice(@{$operation->{changes}}, $i, 0, $flag_change);
- }
- $i--;
- }
+ # restructure into added/removed for each flag
+ my %flags;
+ foreach my $flag (@added) {
+ $flags{$flag}{added} = $flag;
+ $flags{$flag}{removed} = '';
+ }
+ foreach my $flag (@removed) {
+ $flags{$flag}{added} = '';
+ $flags{$flag}{removed} = $flag;
}
- _add_activity_to_stream($stream, date_str_to_time($operation->{when}), $operation->{who}->id, $operation);
+ # 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};
+ $flag_change->{flagtype_name} = _extract_flagtype($flag);
+ splice(@{$operation->{changes}}, $i, 0, $flag_change);
+ }
+ $i--;
+ }
}
- # prime the visible-bugs cache
- $user->visible_bugs([keys %visible_bug_ids]);
+ _add_activity_to_stream(
+ $stream,
+ date_str_to_time($operation->{when}),
+ $operation->{who}->id, $operation
+ );
+ }
+
+ # prime the visible-bugs cache
+ $user->visible_bugs([keys %visible_bug_ids]);
}
sub _extract_flagtype {
- my ($value) = @_;
- return $value =~ /^(.+)[\?\-\+]/ ? $1 : undef;
+ my ($value) = @_;
+ return $value =~ /^(.+)[\?\-\+]/ ? $1 : undef;
}
# display 'duplicate of this bug' as an activity entry, not a comment
sub _add_duplicates_to_stream {
- my ($bug, $stream) = @_;
- my $dbh = Bugzilla->dbh;
+ my ($bug, $stream) = @_;
+ my $dbh = Bugzilla->dbh;
- my $sth = $dbh->prepare("
+ my $sth = $dbh->prepare("
SELECT longdescs.who,
- UNIX_TIMESTAMP(bug_when), " .
- $dbh->sql_date_format('bug_when') . ",
+ UNIX_TIMESTAMP(bug_when), " . $dbh->sql_date_format('bug_when') . ",
type,
extra_data
FROM longdescs
@@ -360,19 +374,22 @@ sub _add_duplicates_to_stream {
WHERE bug_id = ? AND (type = ? OR type = ?)
ORDER BY bug_when
");
- $sth->execute($bug->id, CMT_HAS_DUPE, CMT_DUPE_OF);
-
- while (my($who, $time, $when, $type, $dupe_id) = $sth->fetchrow_array) {
- _add_activity_to_stream($stream, $time, $who, {
- who => Bugzilla::User->new({ id => $who, cache => 1 }),
- when => $when,
- changes => [{
- fieldname => ($type == CMT_HAS_DUPE ? 'has_dupe' : 'dupe_of'),
- added => $dupe_id,
- buglist => 1,
- }],
- });
- }
+ $sth->execute($bug->id, CMT_HAS_DUPE, CMT_DUPE_OF);
+
+ while (my ($who, $time, $when, $type, $dupe_id) = $sth->fetchrow_array) {
+ _add_activity_to_stream(
+ $stream, $time, $who,
+ {
+ who => Bugzilla::User->new({id => $who, cache => 1}),
+ when => $when,
+ changes => [{
+ fieldname => ($type == CMT_HAS_DUPE ? 'has_dupe' : 'dupe_of'),
+ added => $dupe_id,
+ buglist => 1,
+ }],
+ }
+ );
+ }
}
1;
diff --git a/extensions/BugModal/lib/MonkeyPatches.pm b/extensions/BugModal/lib/MonkeyPatches.pm
index 54bd6e560..042dabc38 100644
--- a/extensions/BugModal/lib/MonkeyPatches.pm
+++ b/extensions/BugModal/lib/MonkeyPatches.pm
@@ -17,10 +17,10 @@ use warnings;
use Bugzilla::User;
sub treeherder_user {
- return Bugzilla->process_cache->{treeherder_user} //=
- Bugzilla::User->new({ name => 'tbplbot@gmail.com', cache => 1 })
- || Bugzilla::User->new({ name => 'orangefactor@bots.tld', cache => 1 })
- || Bugzilla::User->new();
+ return Bugzilla->process_cache->{treeherder_user}
+ //= Bugzilla::User->new({name => 'tbplbot@gmail.com', cache => 1})
+ || Bugzilla::User->new({name => 'orangefactor@bots.tld', cache => 1})
+ || Bugzilla::User->new();
}
package Bugzilla::Bug;
@@ -32,10 +32,11 @@ use warnings;
use Bugzilla::Attachment;
sub active_attachments {
- my ($self) = @_;
- return [] if $self->{error};
- return $self->{active_attachments} //= Bugzilla::Attachment->get_attachments_by_bug(
- $self, { exclude_obsolete => 1, preload => 1 });
+ my ($self) = @_;
+ return [] if $self->{error};
+ return $self->{active_attachments}
+ //= Bugzilla::Attachment->get_attachments_by_bug($self,
+ {exclude_obsolete => 1, preload => 1});
}
1;
@@ -47,8 +48,8 @@ use strict;
use warnings;
sub is_image {
- my ($self) = @_;
- return substr($self->contenttype, 0, 6) eq 'image/';
+ my ($self) = @_;
+ return substr($self->contenttype, 0, 6) eq 'image/';
}
1;
diff --git a/extensions/BugModal/lib/Util.pm b/extensions/BugModal/lib/Util.pm
index 6a453159e..b1d7068d8 100644
--- a/extensions/BugModal/lib/Util.pm
+++ b/extensions/BugModal/lib/Util.pm
@@ -21,19 +21,21 @@ use DateTime::TimeZone;
use Time::Local qw(timelocal);
sub date_str_to_time {
- my ($date) = @_;
- # avoid creating a DateTime object
- if ($date =~ /^(\d{4})[\.\-](\d{2})[\.\-](\d{2}) (\d{2}):(\d{2}):(\d{2})$/) {
- return timelocal($6, $5, $4, $3, $2 - 1, $1 - 1900);
- }
- state $tz //= DateTime::TimeZone->new( name => 'local' );
- my $dt = datetime_from($date, $tz);
- if (!$dt) {
- # this should never happen
- warn("invalid datetime '$date'");
- return undef;
- }
- return $dt->epoch;
+ my ($date) = @_;
+
+ # avoid creating a DateTime object
+ if ($date =~ /^(\d{4})[\.\-](\d{2})[\.\-](\d{2}) (\d{2}):(\d{2}):(\d{2})$/) {
+ return timelocal($6, $5, $4, $3, $2 - 1, $1 - 1900);
+ }
+ state $tz //= DateTime::TimeZone->new(name => 'local');
+ my $dt = datetime_from($date, $tz);
+ if (!$dt) {
+
+ # this should never happen
+ warn("invalid datetime '$date'");
+ return undef;
+ }
+ return $dt->epoch;
}
1;
diff --git a/extensions/BugModal/lib/WebService.pm b/extensions/BugModal/lib/WebService.pm
index b69d609dd..5f3308327 100644
--- a/extensions/BugModal/lib/WebService.pm
+++ b/extensions/BugModal/lib/WebService.pm
@@ -27,353 +27,357 @@ use Taint::Util qw(untaint);
# these methods are much lighter than our public API calls
sub rest_resources {
- return [
- # return all the products accessible by the user.
- # required by new-bug
- qr{^/bug_modal/initial_field_values}, {
- GET => {
- method => 'initial_field_values'
- },
+ return [
+ # return all the products accessible by the user.
+ # required by new-bug
+ qr{^/bug_modal/initial_field_values},
+ {GET => {method => 'initial_field_values'},},
+
+ # return all the components pertaining to the product.
+ # required by new-bug
+ qr{^/bug_modal/product_info},
+ {
+ GET => {
+ method => 'product_info',
+ params => sub {
+ return {product_name => Bugzilla->input_params->{product}};
},
+ },
+ },
- # return all the components pertaining to the product.
- # required by new-bug
- qr{^/bug_modal/product_info}, {
- GET => {
- method => 'product_info',
- params => sub {
- return { product_name => Bugzilla->input_params->{product} }
- },
- },
+ # return all the lazy-loaded data; kept in sync with the UI's
+ # requirements.
+ qr{^/bug_modal/edit/(\d+)$},
+ {
+ GET => {
+ method => 'edit',
+ params => sub {
+ return {id => $_[0]};
},
+ },
+ },
- # return all the lazy-loaded data; kept in sync with the UI's
- # requirements.
- qr{^/bug_modal/edit/(\d+)$}, {
- GET => {
- method => 'edit',
- params => sub {
- return { id => $_[0] }
- },
- },
+ # returns pre-formatted html, enabling reuse of the user template
+ qr{^/bug_modal/cc/(\d+)$},
+ {
+ GET => {
+ method => 'cc',
+ params => sub {
+ return {id => $_[0]};
},
+ },
+ },
- # returns pre-formatted html, enabling reuse of the user template
- qr{^/bug_modal/cc/(\d+)$}, {
- GET => {
- method => 'cc',
- params => sub {
- return { id => $_[0] }
- },
- },
- },
+ # returns fields that require touching when the product is changed
+ qw{^/bug_modal/new_product/(\d+)$},
+ {
+ GET => {
+ method => 'new_product',
+ params => sub {
- # returns fields that require touching when the product is changed
- qw{^/bug_modal/new_product/(\d+)$}, {
- GET => {
- method => 'new_product',
- params => sub {
- # products with slashes in their name means we have to grab
- # the product from the query-string instead of the path
- return { id => $_[0], product_name => Bugzilla->input_params->{product} }
- },
- },
+ # products with slashes in their name means we have to grab
+ # the product from the query-string instead of the path
+ return {id => $_[0], product_name => Bugzilla->input_params->{product}};
},
- ]
+ },
+ },
+ ];
}
sub initial_field_values {
- my $user = Bugzilla->user;
- return {
- products => _name($user->get_enterable_products),
- keywords => _name([Bugzilla::Keyword->get_all()]),
- };
+ my $user = Bugzilla->user;
+ return {
+ products => _name($user->get_enterable_products),
+ keywords => _name([Bugzilla::Keyword->get_all()]),
+ };
}
sub product_info {
- my ( $self, $params ) = @_;
- if ( !ref $params->{product_name} ) {
- untaint( $params->{product_name} );
- }
- else {
- ThrowCodeError( 'params_required', { function => 'BugModal.components', params => ['product'] } );
- }
- my $product = Bugzilla::Product->check( { name => $params->{product_name}, cache => 1 } );
- $product = Bugzilla->user->can_enter_product( $product, 1 );
- my @components = map {
- {
- name => $_->name,
- description => $_->description,
- }
- } @{ $product->components };
- return {
- components => \@components,
- versions => _name($product->versions),
- };
+ my ($self, $params) = @_;
+ if (!ref $params->{product_name}) {
+ untaint($params->{product_name});
+ }
+ else {
+ ThrowCodeError('params_required',
+ {function => 'BugModal.components', params => ['product']});
+ }
+ my $product
+ = Bugzilla::Product->check({name => $params->{product_name}, cache => 1});
+ $product = Bugzilla->user->can_enter_product($product, 1);
+ my @components = map { {name => $_->name, description => $_->description,} }
+ @{$product->components};
+ return {components => \@components, versions => _name($product->versions),};
}
# everything we need for edit mode in a single call, returning just the fields
# that the ui requires.
sub edit {
- my ($self, $params) = @_;
- my $user = Bugzilla->user;
- my $bug = Bugzilla::Bug->check({ id => $params->{id} });
-
- # the keys of the options hash must match the field id in the ui
- my %options;
-
- my @products = @{ $user->get_enterable_products };
- unless (grep { $_->id == $bug->product_id } @products) {
- unshift @products, $bug->product_obj;
- }
- $options{product} = [ map { { name => $_->name } } @products ];
-
- $options{component} = _name($bug->product_obj->components, $bug->component);
- $options{version} = _name($bug->product_obj->versions, $bug->version);
- $options{target_milestone} = _name($bug->product_obj->milestones, $bug->target_milestone);
- $options{priority} = _name('priority', $bug->priority);
- $options{bug_severity} = _name('bug_severity', $bug->bug_severity);
- $options{rep_platform} = _name('rep_platform', $bug->rep_platform);
- $options{op_sys} = _name('op_sys', $bug->op_sys);
-
- # custom select fields
- my @custom_fields =
- grep { $_->type == FIELD_TYPE_SINGLE_SELECT || $_->type == FIELD_TYPE_MULTI_SELECT }
- Bugzilla->active_custom_fields({ product => $bug->product_obj, component => $bug->component_obj });
- foreach my $field (@custom_fields) {
- my $field_name = $field->name;
- my @values = map { { name => $_->name } }
- grep { $bug->$field_name eq $_->name
- || ($_->is_active
- && $bug->check_can_change_field($field_name, $bug->$field_name, $_->name)) }
- @{ $field->legal_values };
- $options{$field_name} = \@values;
- }
-
- # keywords
- my @keywords = grep { $_->is_active } Bugzilla::Keyword->get_all();
-
- # results
- return {
- options => \%options,
- keywords => [ map { $_->name } @keywords ],
- };
+ my ($self, $params) = @_;
+ my $user = Bugzilla->user;
+ my $bug = Bugzilla::Bug->check({id => $params->{id}});
+
+ # the keys of the options hash must match the field id in the ui
+ my %options;
+
+ my @products = @{$user->get_enterable_products};
+ unless (grep { $_->id == $bug->product_id } @products) {
+ unshift @products, $bug->product_obj;
+ }
+ $options{product} = [map { {name => $_->name} } @products];
+
+ $options{component} = _name($bug->product_obj->components, $bug->component);
+ $options{version} = _name($bug->product_obj->versions, $bug->version);
+ $options{target_milestone}
+ = _name($bug->product_obj->milestones, $bug->target_milestone);
+ $options{priority} = _name('priority', $bug->priority);
+ $options{bug_severity} = _name('bug_severity', $bug->bug_severity);
+ $options{rep_platform} = _name('rep_platform', $bug->rep_platform);
+ $options{op_sys} = _name('op_sys', $bug->op_sys);
+
+ # custom select fields
+ my @custom_fields = grep {
+ $_->type == FIELD_TYPE_SINGLE_SELECT
+ || $_->type == FIELD_TYPE_MULTI_SELECT
+ } Bugzilla->active_custom_fields(
+ {product => $bug->product_obj, component => $bug->component_obj});
+ foreach my $field (@custom_fields) {
+ my $field_name = $field->name;
+ my @values = map { {name => $_->name} } grep {
+ $bug->$field_name eq $_->name
+ || ($_->is_active
+ && $bug->check_can_change_field($field_name, $bug->$field_name, $_->name))
+ } @{$field->legal_values};
+ $options{$field_name} = \@values;
+ }
+
+ # keywords
+ my @keywords = grep { $_->is_active } Bugzilla::Keyword->get_all();
+
+ # results
+ return {options => \%options, keywords => [map { $_->name } @keywords],};
}
sub _name {
- my ($values, $current) = @_;
- # values can either be an array-ref of values, or a field name, which
- # result in that field's legal-values being used.
- if (!ref($values)) {
- $values = Bugzilla::Field->new({ name => $values, cache => 1 })->legal_values;
- }
- return [
- map { { name => $_->name } }
- grep { (defined $current && $_->name eq $current) || $_->is_active }
- @$values
- ];
+ my ($values, $current) = @_;
+
+ # values can either be an array-ref of values, or a field name, which
+ # result in that field's legal-values being used.
+ if (!ref($values)) {
+ $values = Bugzilla::Field->new({name => $values, cache => 1})->legal_values;
+ }
+ return [map { {name => $_->name} }
+ grep { (defined $current && $_->name eq $current) || $_->is_active }
+ @$values];
}
sub cc {
- my ($self, $params) = @_;
- my $template = Bugzilla->template;
- my $bug = Bugzilla::Bug->check({ id => $params->{id} });
- my $vars = {
- bug => $bug,
- cc_list => [
- sort { lc($a->identity) cmp lc($b->identity) }
- @{ $bug->cc_users }
- ]
- };
-
- my $html = '';
- $template->process('bug_modal/cc_list.html.tmpl', $vars, \$html)
- || ThrowTemplateError($template->error);
- return { html => $html };
+ my ($self, $params) = @_;
+ my $template = Bugzilla->template;
+ my $bug = Bugzilla::Bug->check({id => $params->{id}});
+ my $vars = {
+ bug => $bug,
+ cc_list => [sort { lc($a->identity) cmp lc($b->identity) } @{$bug->cc_users}]
+ };
+
+ my $html = '';
+ $template->process('bug_modal/cc_list.html.tmpl', $vars, \$html)
+ || ThrowTemplateError($template->error);
+ return {html => $html};
}
sub new_product {
- my ($self, $params) = @_;
- my $dbh = Bugzilla->dbh;
- my $user = Bugzilla->user;
- my $bug = Bugzilla::Bug->check({ id => $params->{id} });
- my $product = Bugzilla::Product->check({ name => $params->{product_name}, cache => 1 });
- my $true = $self->type('boolean', 1);
- my %result;
-
- # components
-
- my $components = _name($product->components);
- my $current_component = $bug->component;
- if (my $component = first_value { $_->{name} eq $current_component} @$components) {
- # identical component in both products
- $component->{selected} = $true;
- }
- else {
- # default to a blank value
- unshift @$components, {
- name => '',
- selected => $true,
- };
- }
- $result{component} = $components;
-
- # milestones
-
- my $milestones = _name($product->milestones);
- my $current_milestone = $bug->target_milestone;
- if ($bug->check_can_change_field('target_milestone', 0, 1)
- && (my $milestone = first_value { $_->{name} eq $current_milestone} @$milestones))
- {
- # identical milestone in both products
- $milestone->{selected} = $true;
- }
- else {
- # use default milestone
- my $default_milestone = $product->default_milestone;
- my $milestone = first_value { $_->{name} eq $default_milestone } @$milestones;
- $milestone->{selected} = $true;
+ my ($self, $params) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->user;
+ my $bug = Bugzilla::Bug->check({id => $params->{id}});
+ my $product
+ = Bugzilla::Product->check({name => $params->{product_name}, cache => 1});
+ my $true = $self->type('boolean', 1);
+ my %result;
+
+ # components
+
+ my $components = _name($product->components);
+ my $current_component = $bug->component;
+ if (my $component
+ = first_value { $_->{name} eq $current_component } @$components)
+ {
+ # identical component in both products
+ $component->{selected} = $true;
+ }
+ else {
+ # default to a blank value
+ unshift @$components, {name => '', selected => $true,};
+ }
+ $result{component} = $components;
+
+ # milestones
+
+ my $milestones = _name($product->milestones);
+ my $current_milestone = $bug->target_milestone;
+ if ($bug->check_can_change_field('target_milestone', 0, 1)
+ && (my $milestone
+ = first_value { $_->{name} eq $current_milestone } @$milestones))
+ {
+ # identical milestone in both products
+ $milestone->{selected} = $true;
+ }
+ else {
+ # use default milestone
+ my $default_milestone = $product->default_milestone;
+ my $milestone = first_value { $_->{name} eq $default_milestone } @$milestones;
+ $milestone->{selected} = $true;
+ }
+ $result{target_milestone} = $milestones;
+
+ # versions
+
+ my $versions = _name($product->versions);
+ my $current_version = $bug->version;
+ my $selected_version;
+ if (my $version = first_value { $_->{name} eq $current_version } @$versions) {
+
+ # identical version in both products
+ $version->{selected} = $true;
+ $selected_version = $version;
+ }
+ elsif ($current_version =~ /^(\d+) Branch$/
+ || $current_version =~ /^Firefox (\d+)$/
+ || $current_version =~ /^(\d+)$/)
+ {
+ # firefox, with its three version naming schemes
+ my $branch = $1;
+ foreach my $test_version ("$branch Branch", "Firefox $branch", $branch) {
+ if (my $version = first_value { $_->{name} eq $test_version } @$versions) {
+ $version->{selected} = $true;
+ $selected_version = $version;
+ last;
+ }
}
- $result{target_milestone} = $milestones;
-
- # versions
+ }
+ if (!$selected_version) {
- my $versions = _name($product->versions);
- my $current_version = $bug->version;
- my $selected_version;
- if (my $version = first_value { $_->{name} eq $current_version } @$versions) {
- # identical version in both products
+ # "unspecified", "other"
+ foreach my $test_version ("unspecified", "other") {
+ if (my $version = first_value { lc($_->{name}) eq $test_version } @$versions) {
$version->{selected} = $true;
$selected_version = $version;
+ last;
+ }
}
- elsif (
- $current_version =~ /^(\d+) Branch$/
- || $current_version =~ /^Firefox (\d+)$/
- || $current_version =~ /^(\d+)$/)
+ }
+ if (!$selected_version) {
+
+ # default to a blank value
+ unshift @$versions, {name => '', selected => $true,};
+ }
+ $result{version} = $versions;
+
+ # groups
+
+ my @groups;
+
+ # find invalid groups
+ push @groups,
+ map { {type => 'invalid', group => $_, checked => 0,} }
+ @{Bugzilla::Bug->get_invalid_groups(
+ {bug_ids => [$bug->id], product => $product})};
+
+ # logic lifted from bug/process/verify-new-product.html.tmpl
+ my $current_groups = $bug->groups_in;
+ my $group_controls = $product->group_controls;
+ foreach my $group_id (keys %$group_controls) {
+ my $group_control = $group_controls->{$group_id};
+ if (
+ $group_control->{membercontrol} == CONTROLMAPMANDATORY
+ || ($group_control->{othercontrol} == CONTROLMAPMANDATORY
+ && !$user->in_group($group_control->{name}))
+ )
{
- # firefox, with its three version naming schemes
- my $branch = $1;
- foreach my $test_version ("$branch Branch", "Firefox $branch", $branch) {
- if (my $version = first_value { $_->{name} eq $test_version } @$versions) {
- $version->{selected} = $true;
- $selected_version = $version;
- last;
- }
- }
- }
- if (!$selected_version) {
- # "unspecified", "other"
- foreach my $test_version ("unspecified", "other") {
- if (my $version = first_value { lc($_->{name}) eq $test_version } @$versions) {
- $version->{selected} = $true;
- $selected_version = $version;
- last;
- }
- }
- }
- if (!$selected_version) {
- # default to a blank value
- unshift @$versions, {
- name => '',
- selected => $true,
- };
- }
- $result{version} = $versions;
-
- # groups
-
- my @groups;
-
- # find invalid groups
- push @groups,
- map {{
- type => 'invalid',
- group => $_,
- checked => 0,
- }}
- @{ Bugzilla::Bug->get_invalid_groups({ bug_ids => [ $bug->id ], product => $product }) };
-
- # logic lifted from bug/process/verify-new-product.html.tmpl
- my $current_groups = $bug->groups_in;
- my $group_controls = $product->group_controls;
- foreach my $group_id (keys %$group_controls) {
- my $group_control = $group_controls->{$group_id};
- if ($group_control->{membercontrol} == CONTROLMAPMANDATORY
- || ($group_control->{othercontrol} == CONTROLMAPMANDATORY && !$user->in_group($group_control->{name})))
- {
- # mandatory, always checked
- push @groups, {
- type => 'mandatory',
- group => $group_control->{group},
- checked => 1,
- };
- }
- elsif (
- ($group_control->{membercontrol} != CONTROLMAPNA && $user->in_group($group_control->{name}))
- || $group_control->{othercontrol} != CONTROLMAPNA)
- {
- # optional, checked if..
- my $group = $group_control->{group};
- my $checked =
- # same group as current product
- (any { $_->id == $group->id } @$current_groups)
- # member default
- || $group_control->{membercontrol} == CONTROLMAPDEFAULT && $user->in_group($group_control->{name})
- # or other default
- || $group_control->{othercontrol} == CONTROLMAPDEFAULT && !$user->in_group($group_control->{name})
- ;
- push @groups, {
- type => 'optional',
- group => $group_control->{group},
- checked => $checked || 0,
- };
- }
+ # mandatory, always checked
+ push @groups,
+ {type => 'mandatory', group => $group_control->{group}, checked => 1,};
}
+ elsif (
+ (
+ $group_control->{membercontrol} != CONTROLMAPNA
+ && $user->in_group($group_control->{name})
+ )
+ || $group_control->{othercontrol} != CONTROLMAPNA
+ )
+ {
+ # optional, checked if..
+ my $group = $group_control->{group};
+ my $checked =
- my $default_group_name = $product->default_security_group;
- if (my $default_group = first_value { $_->{group}->name eq $default_group_name } @groups) {
- # because we always allow the default product group to be selected, it's never invalid
- $default_group->{type} = 'optional' if $default_group->{type} eq 'invalid';
- }
- else {
- # add the product's default group if it's missing
- unshift @groups, {
- type => 'optional',
- group => $product->default_security_group_obj,
- checked => 0,
- };
- }
+ # same group as current product
+ (any { $_->id == $group->id } @$current_groups)
- # if the bug is currently in a group, ensure a group is checked by default
- # by checking the product's default group if no other groups apply
- if (@$current_groups && !any { $_->{checked} } @groups) {
- foreach my $g (@groups) {
- next unless $g->{group}->name eq $default_group_name;
- $g->{checked} = 1;
- last;
- }
- }
+ # member default
+ || $group_control->{membercontrol} == CONTROLMAPDEFAULT
+ && $user->in_group($group_control->{name})
- # group by type and flatten
- my $vars = {
- product => $product,
- groups => { invalid => [], mandatory => [], optional => [] },
- };
- foreach my $g (@groups) {
- push @{ $vars->{groups}->{$g->{type}} }, {
- id => $g->{group}->id,
- name => $g->{group}->name,
- description => $g->{group}->description,
- checked => $g->{checked},
+ # or other default
+ || $group_control->{othercontrol} == CONTROLMAPDEFAULT
+ && !$user->in_group($group_control->{name});
+ push @groups,
+ {
+ type => 'optional',
+ group => $group_control->{group},
+ checked => $checked || 0,
};
}
-
- # build group selection html
- my $template = Bugzilla->template;
- $template->process('bug_modal/new_product_groups.html.tmpl', $vars, \$result{groups})
- || ThrowTemplateError($template->error);
-
- return \%result;
+ }
+
+ my $default_group_name = $product->default_security_group;
+ if (my $default_group
+ = first_value { $_->{group}->name eq $default_group_name } @groups)
+ {
+# because we always allow the default product group to be selected, it's never invalid
+ $default_group->{type} = 'optional' if $default_group->{type} eq 'invalid';
+ }
+ else {
+ # add the product's default group if it's missing
+ unshift @groups,
+ {
+ type => 'optional',
+ group => $product->default_security_group_obj,
+ checked => 0,
+ };
+ }
+
+ # if the bug is currently in a group, ensure a group is checked by default
+ # by checking the product's default group if no other groups apply
+ if (@$current_groups && !any { $_->{checked} } @groups) {
+ foreach my $g (@groups) {
+ next unless $g->{group}->name eq $default_group_name;
+ $g->{checked} = 1;
+ last;
+ }
+ }
+
+ # group by type and flatten
+ my $vars = {
+ product => $product,
+ groups => {invalid => [], mandatory => [], optional => []},
+ };
+ foreach my $g (@groups) {
+ push @{$vars->{groups}->{$g->{type}}},
+ {
+ id => $g->{group}->id,
+ name => $g->{group}->name,
+ description => $g->{group}->description,
+ checked => $g->{checked},
+ };
+ }
+
+ # build group selection html
+ my $template = Bugzilla->template;
+ $template->process('bug_modal/new_product_groups.html.tmpl',
+ $vars, \$result{groups})
+ || ThrowTemplateError($template->error);
+
+ return \%result;
}
1;
diff --git a/extensions/BugmailFilter/Config.pm b/extensions/BugmailFilter/Config.pm
index 5948c3b64..5b9585cee 100644
--- a/extensions/BugmailFilter/Config.pm
+++ b/extensions/BugmailFilter/Config.pm
@@ -11,7 +11,7 @@ use 5.10.1;
use strict;
use warnings;
-use constant NAME => 'BugmailFilter';
+use constant NAME => 'BugmailFilter';
use constant REQUIRED_MODULES => [];
use constant OPTIONAL_MODULES => [];
diff --git a/extensions/BugmailFilter/Extension.pm b/extensions/BugmailFilter/Extension.pm
index d4d7bb790..063cb273d 100644
--- a/extensions/BugmailFilter/Extension.pm
+++ b/extensions/BugmailFilter/Extension.pm
@@ -34,185 +34,172 @@ use Sys::Syslog qw(:DEFAULT);
#
sub user_preferences {
- my ($self, $args) = @_;
- return unless $args->{current_tab} eq 'bugmail_filter';
-
- if ($args->{save_changes}) {
- my $input = Bugzilla->input_params;
-
- if ($input->{add_filter}) {
-
- # add a new filter
-
- my $params = {
- user_id => Bugzilla->user->id,
- };
- $params->{field_name} = $input->{field} || IS_NULL;
- if ($params->{field_name} eq '~') {
- $params->{field_name} = '~' . $input->{field_contains};
- }
- $params->{relationship} = $input->{relationship} || IS_NULL;
- if ($input->{changer}) {
- Bugzilla::User::match_field({ changer => { type => 'single'} });
- $params->{changer_id} = Bugzilla::User->check({
- name => $input->{changer},
- cache => 1,
- })->id;
- }
- else {
- $params->{changer_id} = IS_NULL;
- }
- if (my $product_name = $input->{product}) {
- my $product = Bugzilla::Product->check({
- name => $product_name, cache => 1
- });
- $params->{product_id} = $product->id;
-
- if (my $component_name = $input->{component}) {
- $params->{component_id} = Bugzilla::Component->check({
- name => $component_name, product => $product,
- cache => 1
- })->id;
- }
- else {
- $params->{component_id} = IS_NULL;
- }
- }
- else {
- $params->{product_id} = IS_NULL;
- $params->{component_id} = IS_NULL;
- }
-
- if (@{ Bugzilla::Extension::BugmailFilter::Filter->match($params) }) {
- ThrowUserError('bugmail_filter_exists');
- }
- $params->{action} = $input->{action} eq 'Exclude' ? 1 : 0;
- foreach my $name (keys %$params) {
- $params->{$name} = undef
- if $params->{$name} eq IS_NULL;
- }
- Bugzilla::Extension::BugmailFilter::Filter->create($params);
+ my ($self, $args) = @_;
+ return unless $args->{current_tab} eq 'bugmail_filter';
+
+ if ($args->{save_changes}) {
+ my $input = Bugzilla->input_params;
+
+ if ($input->{add_filter}) {
+
+ # add a new filter
+
+ my $params = {user_id => Bugzilla->user->id,};
+ $params->{field_name} = $input->{field} || IS_NULL;
+ if ($params->{field_name} eq '~') {
+ $params->{field_name} = '~' . $input->{field_contains};
+ }
+ $params->{relationship} = $input->{relationship} || IS_NULL;
+ if ($input->{changer}) {
+ Bugzilla::User::match_field({changer => {type => 'single'}});
+ $params->{changer_id}
+ = Bugzilla::User->check({name => $input->{changer}, cache => 1,})->id;
+ }
+ else {
+ $params->{changer_id} = IS_NULL;
+ }
+ if (my $product_name = $input->{product}) {
+ my $product = Bugzilla::Product->check({name => $product_name, cache => 1});
+ $params->{product_id} = $product->id;
+
+ if (my $component_name = $input->{component}) {
+ $params->{component_id}
+ = Bugzilla::Component->check({
+ name => $component_name, product => $product, cache => 1
+ })->id;
}
-
- elsif ($input->{remove_filter}) {
-
- # remove filter(s)
-
- my $ids = ref($input->{remove}) ? $input->{remove} : [ $input->{remove} ];
- my $dbh = Bugzilla->dbh;
- my $user = Bugzilla->user;
-
- my $filters = Bugzilla::Extension::BugmailFilter::Filter->match({ id => $ids, user_id => $user->id });
- $dbh->bz_start_transaction;
- foreach my $filter (@$filters) {
- $filter->remove_from_db();
- }
- $dbh->bz_commit_transaction;
+ else {
+ $params->{component_id} = IS_NULL;
}
+ }
+ else {
+ $params->{product_id} = IS_NULL;
+ $params->{component_id} = IS_NULL;
+ }
+
+ if (@{Bugzilla::Extension::BugmailFilter::Filter->match($params)}) {
+ ThrowUserError('bugmail_filter_exists');
+ }
+ $params->{action} = $input->{action} eq 'Exclude' ? 1 : 0;
+ foreach my $name (keys %$params) {
+ $params->{$name} = undef if $params->{$name} eq IS_NULL;
+ }
+ Bugzilla::Extension::BugmailFilter::Filter->create($params);
}
- my $vars = $args->{vars};
- my $field_descs = template_var('field_descs');
+ elsif ($input->{remove_filter}) {
- # load all fields into a hash for easy manipulation
- my %fields =
- map { $_->name => $field_descs->{$_->name} }
- @{ Bugzilla->fields({ obsolete => 0 }) };
+ # remove filter(s)
- # remove time trackinger fields
- if (!Bugzilla->user->is_timetracker) {
- foreach my $field (TIMETRACKING_FIELDS) {
- delete $fields{$field};
- }
- }
+ my $ids = ref($input->{remove}) ? $input->{remove} : [$input->{remove}];
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->user;
- # remove fields which don't make any sense to filter on
- foreach my $field (IGNORE_FIELDS) {
- delete $fields{$field};
+ my $filters = Bugzilla::Extension::BugmailFilter::Filter->match(
+ {id => $ids, user_id => $user->id});
+ $dbh->bz_start_transaction;
+ foreach my $filter (@$filters) {
+ $filter->remove_from_db();
+ }
+ $dbh->bz_commit_transaction;
}
+ }
- # remove all tracking flag fields. these change too frequently to be of
- # value, so they only add noise to the list.
- foreach my $field (Bugzilla->tracking_flag_names) {
- delete $fields{$field};
- }
+ my $vars = $args->{vars};
+ my $field_descs = template_var('field_descs');
- # add tracking flag types instead
- foreach my $field (
- @{ Bugzilla::Extension::BugmailFilter::FakeField->tracking_flag_fields() }
- ) {
- $fields{$field->name} = $field->description;
- }
+ # load all fields into a hash for easy manipulation
+ my %fields = map { $_->name => $field_descs->{$_->name} }
+ @{Bugzilla->fields({obsolete => 0})};
- # adjust the description for selected fields
- foreach my $field (keys %{ FIELD_DESCRIPTION_OVERRIDE() }) {
- $fields{$field} = FIELD_DESCRIPTION_OVERRIDE->{$field};
+ # remove time trackinger fields
+ if (!Bugzilla->user->is_timetracker) {
+ foreach my $field (TIMETRACKING_FIELDS) {
+ delete $fields{$field};
}
-
- # some fields are present in the changed-fields x-header but are not real
- # bugzilla fields
- foreach my $field (
- @{ Bugzilla::Extension::BugmailFilter::FakeField->fake_fields() }
- ) {
- $fields{$field->name} = $field->description;
+ }
+
+ # remove fields which don't make any sense to filter on
+ foreach my $field (IGNORE_FIELDS) {
+ delete $fields{$field};
+ }
+
+ # remove all tracking flag fields. these change too frequently to be of
+ # value, so they only add noise to the list.
+ foreach my $field (Bugzilla->tracking_flag_names) {
+ delete $fields{$field};
+ }
+
+ # add tracking flag types instead
+ foreach my $field (
+ @{Bugzilla::Extension::BugmailFilter::FakeField->tracking_flag_fields()})
+ {
+ $fields{$field->name} = $field->description;
+ }
+
+ # adjust the description for selected fields
+ foreach my $field (keys %{FIELD_DESCRIPTION_OVERRIDE()}) {
+ $fields{$field} = FIELD_DESCRIPTION_OVERRIDE->{$field};
+ }
+
+ # some fields are present in the changed-fields x-header but are not real
+ # bugzilla fields
+ foreach
+ my $field (@{Bugzilla::Extension::BugmailFilter::FakeField->fake_fields()})
+ {
+ $fields{$field->name} = $field->description;
+ }
+
+ $vars->{fields} = \%fields;
+ $vars->{field_list} = [sort { lc($a->{description}) cmp lc($b->{description}) }
+ map { {name => $_, description => $fields{$_}} } keys %fields];
+
+ $vars->{relationships} = FILTER_RELATIONSHIPS();
+
+ $vars->{filters} = [
+ sort {
+ $a->product_name cmp $b->product_name
+ || $a->component_name cmp $b->component_name
+ || $a->field_name cmp $b->field_name
+ } @{Bugzilla::Extension::BugmailFilter::Filter->match({
+ user_id => Bugzilla->user->id,
+ })
}
+ ];
- $vars->{fields} = \%fields;
- $vars->{field_list} = [
- sort { lc($a->{description}) cmp lc($b->{description}) }
- map { { name => $_, description => $fields{$_} } }
- keys %fields
- ];
-
- $vars->{relationships} = FILTER_RELATIONSHIPS();
-
- $vars->{filters} = [
- sort {
- $a->product_name cmp $b->product_name
- || $a->component_name cmp $b->component_name
- || $a->field_name cmp $b->field_name
- }
- @{ Bugzilla::Extension::BugmailFilter::Filter->match({
- user_id => Bugzilla->user->id,
- }) }
- ];
-
- # set field_description
- foreach my $filter (@{ $vars->{filters} }) {
- my $field_name = $filter->field_name;
- if (!$field_name) {
- $filter->field_description('Any');
- }
- elsif (substr($field_name, 0, 1) eq '~') {
- $filter->field_description('~ ' . substr($field_name, 1));
- }
- else {
- $filter->field_description($fields{$field_name} || $filter->field->description);
- }
+ # set field_description
+ foreach my $filter (@{$vars->{filters}}) {
+ my $field_name = $filter->field_name;
+ if (!$field_name) {
+ $filter->field_description('Any');
}
-
- # build a list of tracking-flags, grouped by type
- require Bugzilla::Extension::TrackingFlags::Constants;
- require Bugzilla::Extension::TrackingFlags::Flag;
- my %flag_types =
- map { $_->{name} => $_->{description} }
- @{ Bugzilla::Extension::TrackingFlags::Constants::FLAG_TYPES() };
- my %tracking_flags_by_type;
- foreach my $flag (Bugzilla::Extension::TrackingFlags::Flag->get_all) {
- my $type = $flag_types{$flag->flag_type};
- $tracking_flags_by_type{$type} //= [];
- push @{ $tracking_flags_by_type{$type} }, $flag;
+ elsif (substr($field_name, 0, 1) eq '~') {
+ $filter->field_description('~ ' . substr($field_name, 1));
}
- my @tracking_flags_by_type;
- foreach my $type (sort keys %tracking_flags_by_type) {
- push @tracking_flags_by_type, {
- name => $type,
- flags => $tracking_flags_by_type{$type},
- };
+ else {
+ $filter->field_description($fields{$field_name} || $filter->field->description);
}
- $vars->{tracking_flags_by_type} = \@tracking_flags_by_type;
-
- ${ $args->{handled} } = 1;
+ }
+
+ # build a list of tracking-flags, grouped by type
+ require Bugzilla::Extension::TrackingFlags::Constants;
+ require Bugzilla::Extension::TrackingFlags::Flag;
+ my %flag_types = map { $_->{name} => $_->{description} }
+ @{Bugzilla::Extension::TrackingFlags::Constants::FLAG_TYPES()};
+ my %tracking_flags_by_type;
+ foreach my $flag (Bugzilla::Extension::TrackingFlags::Flag->get_all) {
+ my $type = $flag_types{$flag->flag_type};
+ $tracking_flags_by_type{$type} //= [];
+ push @{$tracking_flags_by_type{$type}}, $flag;
+ }
+ my @tracking_flags_by_type;
+ foreach my $type (sort keys %tracking_flags_by_type) {
+ push @tracking_flags_by_type,
+ {name => $type, flags => $tracking_flags_by_type{$type},};
+ }
+ $vars->{tracking_flags_by_type} = \@tracking_flags_by_type;
+
+ ${$args->{handled}} = 1;
}
#
@@ -220,219 +207,212 @@ sub user_preferences {
#
sub user_wants_mail {
- my ($self, $args) = @_;
- my ($user, $wants_mail, $diffs, $comments)
- = @$args{qw( user wants_mail fieldDiffs comments )};
-
- # already filtered by email prefs
- return unless $$wants_mail;
+ my ($self, $args) = @_;
+ my ($user, $wants_mail, $diffs, $comments)
+ = @$args{qw( user wants_mail fieldDiffs comments )};
+
+ # already filtered by email prefs
+ return unless $$wants_mail;
+
+ # avoid recursion
+ my $depth = 0;
+ for (my $stack = 1; my $sub = (caller($stack))[3]; $stack++) {
+ $depth++ if $sub eq 'Bugzilla::User::wants_bug_mail';
+ }
+ return if $depth > 1;
+
+ my $cache = Bugzilla->request_cache->{bugmail_filters} //= {};
+ my $filters = $cache->{$user->id}
+ //= Bugzilla::Extension::BugmailFilter::Filter->match({user_id => $user->id});
+ return unless @$filters;
+
+ my $fields = [
+ map { {
+ filter_field => $_->{field_name}, # filter's field_name
+ field_name => $_->{field_name}, # raw bugzilla field_name
+ } } grep {
+
+ # flags are added later
+ $_->{field_name} ne 'flagtypes.name'
+ } @$diffs
+ ];
+
+ # if more than one field was changed we need to check if the normal email
+ # preferences would have excluded the field.
+ if (@$fields > 1) {
+
+ # check each field individually and create filter objects if required
+ my @arg_list
+ = @$args{qw( bug relationship fieldDiffs comments dep_mail changer )};
+ foreach my $field (@$fields) {
- # avoid recursion
- my $depth = 0;
- for (my $stack = 1; my $sub = (caller($stack))[3]; $stack++) {
- $depth++ if $sub eq 'Bugzilla::User::wants_bug_mail';
- }
- return if $depth > 1;
-
- my $cache = Bugzilla->request_cache->{bugmail_filters} //= {};
- my $filters = $cache->{$user->id} //=
- Bugzilla::Extension::BugmailFilter::Filter->match({
- user_id => $user->id
- });
- return unless @$filters;
-
- my $fields = [
- map { {
- filter_field => $_->{field_name}, # filter's field_name
- field_name => $_->{field_name}, # raw bugzilla field_name
- } }
- grep {
- # flags are added later
- $_->{field_name} ne 'flagtypes.name'
- }
- @$diffs
- ];
-
- # if more than one field was changed we need to check if the normal email
- # preferences would have excluded the field.
- if (@$fields > 1) {
- # check each field individually and create filter objects if required
- my @arg_list = @$args{qw( bug relationship fieldDiffs comments dep_mail changer )};
- foreach my $field (@$fields) {
- # just a single diff
- foreach my $diff (@$diffs) {
- next unless $diff->{field_name} eq $field->{field_name};
- $arg_list[2] = [ $diff ];
- last;
- }
- if (!$user->wants_bug_mail(@arg_list)) {
- # changes to just this field would have been dropped by email
- # preferences. build a corresponding filter object so we
- # interact with email preferences correctly.
- push @$filters, Bugzilla::Extension::BugmailFilter::Filter->new_from_hash({
- field_name => $field->{field_name},
- action => 1,
- });
- }
- }
+ # just a single diff
+ foreach my $diff (@$diffs) {
+ next unless $diff->{field_name} eq $field->{field_name};
+ $arg_list[2] = [$diff];
+ last;
+ }
+ if (!$user->wants_bug_mail(@arg_list)) {
+
+ # changes to just this field would have been dropped by email
+ # preferences. build a corresponding filter object so we
+ # interact with email preferences correctly.
+ push @$filters,
+ Bugzilla::Extension::BugmailFilter::Filter->new_from_hash({
+ field_name => $field->{field_name}, action => 1,
+ });
+ }
}
+ }
- # insert fake fields for new attachments and comments
- if (@$comments) {
- if (grep { $_->type == CMT_ATTACHMENT_CREATED } @$comments) {
- push @$fields, { field_name => 'attachment.created',
- filter_field => 'attachment.created' };
- }
- if (grep { $_->type != CMT_ATTACHMENT_CREATED } @$comments) {
- push @$fields, { field_name => 'comment.created',
- filter_field => 'comment.created' };
- }
+ # insert fake fields for new attachments and comments
+ if (@$comments) {
+ if (grep { $_->type == CMT_ATTACHMENT_CREATED } @$comments) {
+ push @$fields,
+ {field_name => 'attachment.created', filter_field => 'attachment.created'};
}
-
- # insert fake fields for flags
- foreach my $diff (@$diffs) {
- next unless $diff->{field_name} eq 'flagtypes.name';
- foreach my $change (split(/, /, join(', ', ($diff->{old}, $diff->{new})))) {
- next unless $change =~ /^(.+)[\?\-+]/;
- push @$fields, {
- filter_field => $1,
- field_name => 'flagtypes.name',
- };
- }
+ if (grep { $_->type != CMT_ATTACHMENT_CREATED } @$comments) {
+ push @$fields,
+ {field_name => 'comment.created', filter_field => 'comment.created'};
}
-
- # set filter_field on tracking flags to tracking.$type
- require Bugzilla::Extension::TrackingFlags::Flag;
- my @tracking_flags = Bugzilla->tracking_flags;
- foreach my $field (@$fields) {
- next unless my $field_name = $field->{field_name};
- foreach my $tracking_flag (@tracking_flags) {
- if ($field_name eq $tracking_flag->name) {
- $field->{filter_field} = 'tracking.'. $tracking_flag->flag_type;
- }
- }
+ }
+
+ # insert fake fields for flags
+ foreach my $diff (@$diffs) {
+ next unless $diff->{field_name} eq 'flagtypes.name';
+ foreach my $change (split(/, /, join(', ', ($diff->{old}, $diff->{new})))) {
+ next unless $change =~ /^(.+)[\?\-+]/;
+ push @$fields, {filter_field => $1, field_name => 'flagtypes.name',};
}
-
- if (_should_drop($fields, $filters, $args)) {
- $$wants_mail = 0;
- openlog('apache', 'cons,pid', 'local4');
- syslog('notice', encode_utf8(sprintf(
- '[bugmail] %s (filtered) bug-%s %s',
- $args->{user}->login,
- $args->{bug}->id,
- $args->{bug}->short_desc,
- )));
- closelog();
+ }
+
+ # set filter_field on tracking flags to tracking.$type
+ require Bugzilla::Extension::TrackingFlags::Flag;
+ my @tracking_flags = Bugzilla->tracking_flags;
+ foreach my $field (@$fields) {
+ next unless my $field_name = $field->{field_name};
+ foreach my $tracking_flag (@tracking_flags) {
+ if ($field_name eq $tracking_flag->name) {
+ $field->{filter_field} = 'tracking.' . $tracking_flag->flag_type;
+ }
}
+ }
+
+ if (_should_drop($fields, $filters, $args)) {
+ $$wants_mail = 0;
+ openlog('apache', 'cons,pid', 'local4');
+ syslog(
+ 'notice',
+ encode_utf8(sprintf(
+ '[bugmail] %s (filtered) bug-%s %s',
+ $args->{user}->login,
+ $args->{bug}->id, $args->{bug}->short_desc,
+ ))
+ );
+ closelog();
+ }
}
sub _should_drop {
- my ($fields, $filters, $args) = @_;
-
- # calculate relationships
-
- my ($user, $bug, $relationship, $changer) = @$args{qw( user bug relationship changer )};
- my ($user_id, $login) = ($user->id, $user->login);
- my $bit_direct = Bugzilla::BugMail::BIT_DIRECT;
- my $bit_watching = Bugzilla::BugMail::BIT_WATCHING;
- my $bit_compwatch = 15; # from Bugzilla::Extension::ComponentWatching
-
- # the index of $rel_map corresponds to the values in FILTER_RELATIONSHIPS
- my @rel_map;
- $rel_map[1] = $bug->assigned_to->id == $user_id;
- $rel_map[2] = !$rel_map[1];
- $rel_map[3] = $bug->reporter->id == $user_id;
- $rel_map[4] = !$rel_map[3];
- if ($bug->qa_contact) {
- $rel_map[5] = $bug->qa_contact->id == $user_id;
- $rel_map[6] = !$rel_map[6];
+ my ($fields, $filters, $args) = @_;
+
+ # calculate relationships
+
+ my ($user, $bug, $relationship, $changer)
+ = @$args{qw( user bug relationship changer )};
+ my ($user_id, $login) = ($user->id, $user->login);
+ my $bit_direct = Bugzilla::BugMail::BIT_DIRECT;
+ my $bit_watching = Bugzilla::BugMail::BIT_WATCHING;
+ my $bit_compwatch = 15; # from Bugzilla::Extension::ComponentWatching
+
+ # the index of $rel_map corresponds to the values in FILTER_RELATIONSHIPS
+ my @rel_map;
+ $rel_map[1] = $bug->assigned_to->id == $user_id;
+ $rel_map[2] = !$rel_map[1];
+ $rel_map[3] = $bug->reporter->id == $user_id;
+ $rel_map[4] = !$rel_map[3];
+ if ($bug->qa_contact) {
+ $rel_map[5] = $bug->qa_contact->id == $user_id;
+ $rel_map[6] = !$rel_map[6];
+ }
+ $rel_map[7] = $bug->cc ? grep { $_ eq $login } @{$bug->cc} : 0;
+ $rel_map[8] = !$rel_map[8];
+ $rel_map[9] = ($relationship & $bit_watching or $relationship & $bit_compwatch);
+ $rel_map[10] = !$rel_map[9];
+ $rel_map[11] = $bug->is_mentor($user);
+ $rel_map[12] = !$rel_map[11];
+ foreach my $bool (@rel_map) {
+ $bool = $bool ? 1 : 0;
+ }
+
+ # exclusions
+ # drop email where we are excluding all changed fields
+
+ my $params = {
+ product_id => $bug->product_id,
+ component_id => $bug->component_id,
+ rel_map => \@rel_map,
+ changer_id => $changer->id,
+ };
+
+ foreach my $field (@$fields) {
+ $params->{field} = $field;
+ foreach my $filter (grep { $_->is_exclude } @$filters) {
+ if ($filter->matches($params)) {
+ $field->{exclude} = 1;
+ last;
+ }
}
- $rel_map[7] = $bug->cc
- ? grep { $_ eq $login } @{ $bug->cc }
- : 0;
- $rel_map[8] = !$rel_map[8];
- $rel_map[9] = (
- $relationship & $bit_watching
- or $relationship & $bit_compwatch
- );
- $rel_map[10] = !$rel_map[9];
- $rel_map[11] = $bug->is_mentor($user);
- $rel_map[12] = !$rel_map[11];
- foreach my $bool (@rel_map) {
- $bool = $bool ? 1 : 0;
- }
-
- # exclusions
- # drop email where we are excluding all changed fields
-
- my $params = {
- product_id => $bug->product_id,
- component_id => $bug->component_id,
- rel_map => \@rel_map,
- changer_id => $changer->id,
- };
-
- foreach my $field (@$fields) {
- $params->{field} = $field;
- foreach my $filter (grep { $_->is_exclude } @$filters) {
- if ($filter->matches($params)) {
- $field->{exclude} = 1;
- last;
- }
- }
+ }
+
+ # no need to process includes if nothing was excluded
+ if (!grep { $_->{exclude} } @$fields) {
+ return 0;
+ }
+
+ # inclusions
+ # flip the bit for fields that should be included
+
+ foreach my $field (@$fields) {
+ $params->{field} = $field;
+ foreach my $filter (grep { $_->is_include } @$filters) {
+ if ($filter->matches($params)) {
+ $field->{exclude} = 0;
+ last;
+ }
}
+ }
- # no need to process includes if nothing was excluded
- if (!grep { $_->{exclude} } @$fields) {
- return 0;
- }
-
- # inclusions
- # flip the bit for fields that should be included
-
- foreach my $field (@$fields) {
- $params->{field} = $field;
- foreach my $filter (grep { $_->is_include } @$filters) {
- if ($filter->matches($params)) {
- $field->{exclude} = 0;
- last;
- }
- }
- }
-
- # drop if all fields are still excluded
- return !(grep { !$_->{exclude} } @$fields);
+ # drop if all fields are still excluded
+ return !(grep { !$_->{exclude} } @$fields);
}
# catch when fields are renamed, and update the field_name entires
sub object_end_of_update {
- my ($self, $args) = @_;
- my $object = $args->{object};
+ my ($self, $args) = @_;
+ my $object = $args->{object};
- return unless $object->isa('Bugzilla::Field')
- || $object->isa('Bugzilla::Extension::TrackingFlags::Flag');
+ return
+ unless $object->isa('Bugzilla::Field')
+ || $object->isa('Bugzilla::Extension::TrackingFlags::Flag');
- return unless exists $args->{changes}->{name};
+ return unless exists $args->{changes}->{name};
- my $old_name = $args->{changes}->{name}->[0];
- my $new_name = $args->{changes}->{name}->[1];
+ my $old_name = $args->{changes}->{name}->[0];
+ my $new_name = $args->{changes}->{name}->[1];
- Bugzilla->dbh->do(
- "UPDATE bugmail_filters SET field_name=? WHERE field_name=?",
- undef,
- $new_name, $old_name);
+ Bugzilla->dbh->do("UPDATE bugmail_filters SET field_name=? WHERE field_name=?",
+ undef, $new_name, $old_name);
}
sub reorg_move_component {
- my ($self, $args) = @_;
- my $new_product = $args->{new_product};
- my $component = $args->{component};
-
- Bugzilla->dbh->do(
- "UPDATE bugmail_filters SET product_id=? WHERE component_id=?",
- undef,
- $new_product->id, $component->id,
- );
+ my ($self, $args) = @_;
+ my $new_product = $args->{new_product};
+ my $component = $args->{component};
+
+ Bugzilla->dbh->do(
+ "UPDATE bugmail_filters SET product_id=? WHERE component_id=?",
+ undef, $new_product->id, $component->id,);
}
#
@@ -440,92 +420,61 @@ sub reorg_move_component {
#
sub db_schema_abstract_schema {
- my ($self, $args) = @_;
- $args->{schema}->{bugmail_filters} = {
+ my ($self, $args) = @_;
+ $args->{schema}->{bugmail_filters} = {
+ FIELDS => [
+ id => {TYPE => 'INTSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ user_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'},
+ },
+ field_name => {
+
+ # due to fake fields, this can't be field_id
+ TYPE => 'VARCHAR(64)',
+ NOTNULL => 0,
+ },
+ product_id => {
+ TYPE => 'INT2',
+ NOTNULL => 0,
+ REFERENCES => {TABLE => 'products', COLUMN => 'id', DELETE => 'CASCADE'},
+ },
+ component_id => {
+ TYPE => 'INT2',
+ NOTNULL => 0,
+ REFERENCES => {TABLE => 'components', COLUMN => 'id', DELETE => 'CASCADE'},
+ },
+ changer_id => {
+ TYPE => 'INT3',
+ NOTNULL => 0,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'},
+ },
+ relationship => {TYPE => 'INT2', NOTNULL => 0,},
+ action => {TYPE => 'INT1', NOTNULL => 1,},
+ ],
+ INDEXES => [
+ bugmail_filters_unique_idx => {
FIELDS => [
- id => {
- TYPE => 'INTSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- user_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'CASCADE'
- },
- },
- field_name => {
- # due to fake fields, this can't be field_id
- TYPE => 'VARCHAR(64)',
- NOTNULL => 0,
- },
- product_id => {
- TYPE => 'INT2',
- NOTNULL => 0,
- REFERENCES => {
- TABLE => 'products',
- COLUMN => 'id',
- DELETE => 'CASCADE'
- },
- },
- component_id => {
- TYPE => 'INT2',
- NOTNULL => 0,
- REFERENCES => {
- TABLE => 'components',
- COLUMN => 'id',
- DELETE => 'CASCADE'
- },
- },
- changer_id => {
- TYPE => 'INT3',
- NOTNULL => 0,
- REFERENCES => {
- TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'CASCADE'
- },
- },
- relationship => {
- TYPE => 'INT2',
- NOTNULL => 0,
- },
- action => {
- TYPE => 'INT1',
- NOTNULL => 1,
- },
+ qw( user_id field_name product_id component_id
+ relationship )
],
- INDEXES => [
- bugmail_filters_unique_idx => {
- FIELDS => [ qw( user_id field_name product_id component_id
- relationship ) ],
- TYPE => 'UNIQUE',
- },
- bugmail_filters_user_idx => [
- 'user_id',
- ],
- ],
- };
+ TYPE => 'UNIQUE',
+ },
+ bugmail_filters_user_idx => ['user_id',],
+ ],
+ };
}
sub install_update_db {
- Bugzilla->dbh->bz_add_column(
- 'bugmail_filters',
- 'changer_id',
- {
- TYPE => 'INT3',
- NOTNULL => 0,
- }
- );
+ Bugzilla->dbh->bz_add_column('bugmail_filters', 'changer_id',
+ {TYPE => 'INT3', NOTNULL => 0,});
}
sub db_sanitize {
- my $dbh = Bugzilla->dbh;
- print "Deleting bugmail filters...\n";
- $dbh->do("DELETE FROM bugmail_filters");
+ my $dbh = Bugzilla->dbh;
+ print "Deleting bugmail filters...\n";
+ $dbh->do("DELETE FROM bugmail_filters");
}
__PACKAGE__->NAME;
diff --git a/extensions/BugmailFilter/lib/Constants.pm b/extensions/BugmailFilter/lib/Constants.pm
index a6636dda7..bed8a37d3 100644
--- a/extensions/BugmailFilter/lib/Constants.pm
+++ b/extensions/BugmailFilter/lib/Constants.pm
@@ -14,10 +14,10 @@ use warnings;
use base qw(Exporter);
our @EXPORT = qw(
- FAKE_FIELD_NAMES
- IGNORE_FIELDS
- FIELD_DESCRIPTION_OVERRIDE
- FILTER_RELATIONSHIPS
+ FAKE_FIELD_NAMES
+ IGNORE_FIELDS
+ FIELD_DESCRIPTION_OVERRIDE
+ FILTER_RELATIONSHIPS
);
use Bugzilla::Constants;
@@ -26,98 +26,54 @@ use Bugzilla::Constants;
# header but are not real fields
use constant FAKE_FIELD_NAMES => [
- {
- name => 'comment.created',
- description => 'Comment created',
- },
- {
- name => 'attachment.created',
- description => 'Attachment created',
- },
+ {name => 'comment.created', description => 'Comment created',},
+ {name => 'attachment.created', description => 'Attachment created',},
];
# these fields don't make any sense to filter on
use constant IGNORE_FIELDS => qw(
- assignee_last_login
- attach_data.thedata
- attachments.submitter
- cf_last_resolved
- commenter
- comment_tag
- creation_ts
- days_elapsed
- delta_ts
- everconfirmed
- last_visit_ts
- longdesc
- longdescs.count
- owner_idle_time
- reporter
- reporter_accessible
- setters.login_name
- tag
- votes
+ assignee_last_login
+ attach_data.thedata
+ attachments.submitter
+ cf_last_resolved
+ commenter
+ comment_tag
+ creation_ts
+ days_elapsed
+ delta_ts
+ everconfirmed
+ last_visit_ts
+ longdesc
+ longdescs.count
+ owner_idle_time
+ reporter
+ reporter_accessible
+ setters.login_name
+ tag
+ votes
);
# override the description of some fields
-use constant FIELD_DESCRIPTION_OVERRIDE => {
- bug_id => 'Bug Created',
-};
+use constant FIELD_DESCRIPTION_OVERRIDE => {bug_id => 'Bug Created',};
# relationship / int mappings
# _should_drop() also needs updating when this const is changed
use constant FILTER_RELATIONSHIPS => [
- {
- name => 'Assignee',
- value => 1,
- },
- {
- name => 'Not Assignee',
- value => 2,
- },
- {
- name => 'Reporter',
- value => 3,
- },
- {
- name => 'Not Reporter',
- value => 4,
- },
- {
- name => 'QA Contact',
- value => 5,
- },
- {
- name => 'Not QA Contact',
- value => 6,
- },
- {
- name => "CC'ed",
- value => 7,
- },
- {
- name => "Not CC'ed",
- value => 8,
- },
- {
- name => 'Watching',
- value => 9,
- },
- {
- name => 'Not Watching',
- value => 10,
- },
- {
- name => 'Mentoring',
- value => 11,
- },
- {
- name => 'Not Mentoring',
- value => 12,
- },
+ {name => 'Assignee', value => 1,},
+ {name => 'Not Assignee', value => 2,},
+ {name => 'Reporter', value => 3,},
+ {name => 'Not Reporter', value => 4,},
+ {name => 'QA Contact', value => 5,},
+ {name => 'Not QA Contact', value => 6,},
+ {name => "CC'ed", value => 7,},
+ {name => "Not CC'ed", value => 8,},
+ {name => 'Watching', value => 9,},
+ {name => 'Not Watching', value => 10,},
+ {name => 'Mentoring', value => 11,},
+ {name => 'Not Mentoring', value => 12,},
];
1;
diff --git a/extensions/BugmailFilter/lib/FakeField.pm b/extensions/BugmailFilter/lib/FakeField.pm
index e9f8b1808..cf82aec85 100644
--- a/extensions/BugmailFilter/lib/FakeField.pm
+++ b/extensions/BugmailFilter/lib/FakeField.pm
@@ -16,8 +16,8 @@ use Bugzilla::Extension::BugmailFilter::Constants;
# object
sub new {
- my ($class, $params) = @_;
- return bless($params, $class);
+ my ($class, $params) = @_;
+ return bless($params, $class);
}
sub name { $_[0]->{name} }
@@ -26,33 +26,35 @@ sub description { $_[0]->{description} }
# static methods
sub fake_fields {
- my $cache = Bugzilla->request_cache->{bugmail_filter};
- if (!$cache->{fake_fields}) {
- my @fields;
- foreach my $rh (@{ FAKE_FIELD_NAMES() }) {
- push @fields, Bugzilla::Extension::BugmailFilter::FakeField->new($rh);
- }
- $cache->{fake_fields} = \@fields;
+ my $cache = Bugzilla->request_cache->{bugmail_filter};
+ if (!$cache->{fake_fields}) {
+ my @fields;
+ foreach my $rh (@{FAKE_FIELD_NAMES()}) {
+ push @fields, Bugzilla::Extension::BugmailFilter::FakeField->new($rh);
}
- return $cache->{fake_fields};
+ $cache->{fake_fields} = \@fields;
+ }
+ return $cache->{fake_fields};
}
sub tracking_flag_fields {
- my $cache = Bugzilla->request_cache->{bugmail_filter};
- if (!$cache->{tracking_flag_fields}) {
- require Bugzilla::Extension::TrackingFlags::Constants;
- my @fields;
- my $tracking_types = Bugzilla::Extension::TrackingFlags::Constants::FLAG_TYPES();
- foreach my $tracking_type (@$tracking_types) {
- push @fields, Bugzilla::Extension::BugmailFilter::FakeField->new({
- name => 'tracking.' . $tracking_type->{name},
- description => $tracking_type->{description},
- sortkey => $tracking_type->{sortkey},
- });
- }
- $cache->{tracking_flag_fields} = \@fields;
+ my $cache = Bugzilla->request_cache->{bugmail_filter};
+ if (!$cache->{tracking_flag_fields}) {
+ require Bugzilla::Extension::TrackingFlags::Constants;
+ my @fields;
+ my $tracking_types
+ = Bugzilla::Extension::TrackingFlags::Constants::FLAG_TYPES();
+ foreach my $tracking_type (@$tracking_types) {
+ push @fields,
+ Bugzilla::Extension::BugmailFilter::FakeField->new({
+ name => 'tracking.' . $tracking_type->{name},
+ description => $tracking_type->{description},
+ sortkey => $tracking_type->{sortkey},
+ });
}
- return $cache->{tracking_flag_fields};
+ $cache->{tracking_flag_fields} = \@fields;
+ }
+ return $cache->{tracking_flag_fields};
}
1;
diff --git a/extensions/BugmailFilter/lib/Filter.pm b/extensions/BugmailFilter/lib/Filter.pm
index 7f2f4cb87..fa2c708cd 100644
--- a/extensions/BugmailFilter/lib/Filter.pm
+++ b/extensions/BugmailFilter/lib/Filter.pm
@@ -25,14 +25,14 @@ use Bugzilla::Util qw(trim);
use constant DB_TABLE => 'bugmail_filters';
use constant DB_COLUMNS => qw(
- id
- user_id
- product_id
- component_id
- field_name
- relationship
- changer_id
- action
+ id
+ user_id
+ product_id
+ component_id
+ field_name
+ relationship
+ changer_id
+ action
);
use constant LIST_ORDER => 'id';
@@ -40,13 +40,11 @@ use constant LIST_ORDER => 'id';
use constant UPDATE_COLUMNS => ();
use constant VALIDATORS => {
- user_id => \&_check_user,
- field_name => \&_check_field_name,
- action => \&Bugzilla::Object::check_boolean,
-};
-use constant VALIDATOR_DEPENDENCIES => {
- component_id => [ 'product_id' ],
+ user_id => \&_check_user,
+ field_name => \&_check_field_name,
+ action => \&Bugzilla::Object::check_boolean,
};
+use constant VALIDATOR_DEPENDENCIES => {component_id => ['product_id'],};
use constant AUDIT_CREATES => 0;
use constant AUDIT_UPDATES => 0;
@@ -56,163 +54,164 @@ use constant USE_MEMCACHED => 0;
# getters
sub user {
- my ($self) = @_;
- return Bugzilla::User->new({ id => $self->{user_id}, cache => 1 });
+ my ($self) = @_;
+ return Bugzilla::User->new({id => $self->{user_id}, cache => 1});
}
sub product {
- my ($self) = @_;
- return $self->{product_id}
- ? Bugzilla::Product->new({ id => $self->{product_id}, cache => 1 })
- : undef;
+ my ($self) = @_;
+ return $self->{product_id}
+ ? Bugzilla::Product->new({id => $self->{product_id}, cache => 1})
+ : undef;
}
sub product_name {
- my ($self) = @_;
- return $self->{product_name} //= $self->{product_id} ? $self->product->name : '';
+ my ($self) = @_;
+ return $self->{product_name}
+ //= $self->{product_id} ? $self->product->name : '';
}
sub component {
- my ($self) = @_;
- return $self->{component_id}
- ? Bugzilla::Component->new({ id => $self->{component_id}, cache => 1 })
- : undef;
+ my ($self) = @_;
+ return $self->{component_id}
+ ? Bugzilla::Component->new({id => $self->{component_id}, cache => 1})
+ : undef;
}
sub component_name {
- my ($self) = @_;
- return $self->{component_name} //= $self->{component_id} ? $self->component->name : '';
+ my ($self) = @_;
+ return $self->{component_name}
+ //= $self->{component_id} ? $self->component->name : '';
}
sub field_name {
- return $_[0]->{field_name} //= '';
+ return $_[0]->{field_name} //= '';
}
sub field_description {
- my ($self, $value) = @_;
- $self->{field_description} = $value if defined($value);
- return $self->{field_description};
+ my ($self, $value) = @_;
+ $self->{field_description} = $value if defined($value);
+ return $self->{field_description};
}
sub field {
- my ($self) = @_;
- return unless $self->{field_name};
- if (!$self->{field}) {
- if (substr($self->{field_name}, 0, 1) eq '~') {
- # this should never happen
- die "not implemented";
- }
- foreach my $field (
- @{ Bugzilla::Extension::BugmailFilter::FakeField->fake_fields() },
- @{ Bugzilla::Extension::BugmailFilter::FakeField->tracking_flag_fields() },
- ) {
- if ($field->{name} eq $self->{field_name}) {
- return $self->{field} = $field;
- }
- }
- $self->{field} = Bugzilla::Field->new({ name => $self->{field_name}, cache => 1 });
+ my ($self) = @_;
+ return unless $self->{field_name};
+ if (!$self->{field}) {
+ if (substr($self->{field_name}, 0, 1) eq '~') {
+
+ # this should never happen
+ die "not implemented";
}
- return $self->{field};
+ foreach my $field (
+ @{Bugzilla::Extension::BugmailFilter::FakeField->fake_fields()},
+ @{Bugzilla::Extension::BugmailFilter::FakeField->tracking_flag_fields()},
+ )
+ {
+ if ($field->{name} eq $self->{field_name}) {
+ return $self->{field} = $field;
+ }
+ }
+ $self->{field}
+ = Bugzilla::Field->new({name => $self->{field_name}, cache => 1});
+ }
+ return $self->{field};
}
sub relationship {
- return $_[0]->{relationship};
+ return $_[0]->{relationship};
}
sub changer_id {
- return $_[0]->{changer_id};
+ return $_[0]->{changer_id};
}
sub changer {
- my ($self) = @_;
- return $self->{changer_id}
- ? Bugzilla::User->new({ id => $self->{changer_id}, cache => 1 })
- : undef;
+ my ($self) = @_;
+ return $self->{changer_id}
+ ? Bugzilla::User->new({id => $self->{changer_id}, cache => 1})
+ : undef;
}
sub relationship_name {
- my ($self) = @_;
- foreach my $rel (@{ FILTER_RELATIONSHIPS() }) {
- return $rel->{name}
- if $rel->{value} == $self->{relationship};
- }
- return '?';
+ my ($self) = @_;
+ foreach my $rel (@{FILTER_RELATIONSHIPS()}) {
+ return $rel->{name} if $rel->{value} == $self->{relationship};
+ }
+ return '?';
}
sub is_exclude {
- return $_[0]->{action} == 1;
+ return $_[0]->{action} == 1;
}
sub is_include {
- return $_[0]->{action} == 0;
+ return $_[0]->{action} == 0;
}
# validators
sub _check_user {
- my ($class, $user) = @_;
- $user || ThrowCodeError('param_required', { param => 'user' });
+ my ($class, $user) = @_;
+ $user || ThrowCodeError('param_required', {param => 'user'});
}
sub _check_field_name {
- my ($class, $field_name) = @_;
- return undef unless $field_name;
- if (substr($field_name, 0, 1) eq '~') {
- $field_name = lc(trim($field_name));
- $field_name =~ /^~[a-z0-9_\.\-]+$/
- || ThrowUserError('bugmail_filter_invalid');
- length($field_name) <= 64
- || ThrowUserError('bugmail_filter_too_long');
- return $field_name;
- }
- foreach my $rh (@{ FAKE_FIELD_NAMES() }) {
- return $field_name if $rh->{name} eq $field_name;
- }
- return $field_name
- if $field_name =~ /^tracking\./;
- Bugzilla::Field->check({ name => $field_name, cache => 1});
+ my ($class, $field_name) = @_;
+ return undef unless $field_name;
+ if (substr($field_name, 0, 1) eq '~') {
+ $field_name = lc(trim($field_name));
+ $field_name =~ /^~[a-z0-9_\.\-]+$/ || ThrowUserError('bugmail_filter_invalid');
+ length($field_name) <= 64 || ThrowUserError('bugmail_filter_too_long');
return $field_name;
+ }
+ foreach my $rh (@{FAKE_FIELD_NAMES()}) {
+ return $field_name if $rh->{name} eq $field_name;
+ }
+ return $field_name if $field_name =~ /^tracking\./;
+ Bugzilla::Field->check({name => $field_name, cache => 1});
+ return $field_name;
}
# methods
sub matches {
- my ($self, $args) = @_;
-
- if (my $field_name = $self->{field_name}) {
- if ($args->{field}->{field_name} && substr($field_name, 0, 1) eq '~') {
- my $substring = quotemeta(substr($field_name, 1));
- if ($args->{field}->{filter_field} !~ /$substring/i) {
- return 0;
- }
- }
- elsif ($field_name eq 'flagtypes.name') {
- if ($args->{field}->{field_name} ne $field_name) {
- return 0;
- }
- }
- elsif ($field_name ne $args->{field}->{filter_field}) {
- return 0;
- }
- }
+ my ($self, $args) = @_;
- if ($self->{product_id} && $self->{product_id} != $args->{product_id}) {
+ if (my $field_name = $self->{field_name}) {
+ if ($args->{field}->{field_name} && substr($field_name, 0, 1) eq '~') {
+ my $substring = quotemeta(substr($field_name, 1));
+ if ($args->{field}->{filter_field} !~ /$substring/i) {
return 0;
+ }
}
-
- if ($self->{component_id} && $self->{component_id} != $args->{component_id}) {
+ elsif ($field_name eq 'flagtypes.name') {
+ if ($args->{field}->{field_name} ne $field_name) {
return 0;
+ }
}
-
- if ($self->{relationship} && !$args->{rel_map}->[$self->{relationship}]) {
- return 0;
+ elsif ($field_name ne $args->{field}->{filter_field}) {
+ return 0;
}
+ }
- if ($self->{changer_id} && $self->{changer_id} != $args->{changer_id}) {
- return 0;
- }
+ if ($self->{product_id} && $self->{product_id} != $args->{product_id}) {
+ return 0;
+ }
+
+ if ($self->{component_id} && $self->{component_id} != $args->{component_id}) {
+ return 0;
+ }
+
+ if ($self->{relationship} && !$args->{rel_map}->[$self->{relationship}]) {
+ return 0;
+ }
+
+ if ($self->{changer_id} && $self->{changer_id} != $args->{changer_id}) {
+ return 0;
+ }
- return 1;
+ return 1;
}
1;
diff --git a/extensions/BzAPI/Extension.pm b/extensions/BzAPI/Extension.pm
index ac9502fcb..e7812194e 100644
--- a/extensions/BzAPI/Extension.pm
+++ b/extensions/BzAPI/Extension.pm
@@ -33,15 +33,13 @@ our $VERSION = '0.1';
################
sub install_filesystem {
- my ($self, $args) = @_;
- my $files = $args->{'files'};
+ my ($self, $args) = @_;
+ my $files = $args->{'files'};
- my $extensionsdir = bz_locations()->{'extensionsdir'};
- my $scriptname = $extensionsdir . "/" . __PACKAGE__->NAME . "/bin/rest.cgi";
+ my $extensionsdir = bz_locations()->{'extensionsdir'};
+ my $scriptname = $extensionsdir . "/" . __PACKAGE__->NAME . "/bin/rest.cgi";
- $files->{$scriptname} = {
- perms => Bugzilla::Install::Filesystem::WS_EXECUTE
- };
+ $files->{$scriptname} = {perms => Bugzilla::Install::Filesystem::WS_EXECUTE};
}
##################
@@ -49,14 +47,14 @@ sub install_filesystem {
##################
sub template_before_process {
- my ($self, $args) = @_;
- my $vars = $args->{'vars'};
- my $file = $args->{'file'};
-
- if ($file =~ /config\.json\.tmpl$/) {
- $vars->{'initial_status'} = Bugzilla::Status->can_change_to;
- $vars->{'status_objects'} = [ Bugzilla::Status->get_all ];
- }
+ my ($self, $args) = @_;
+ my $vars = $args->{'vars'};
+ my $file = $args->{'file'};
+
+ if ($file =~ /config\.json\.tmpl$/) {
+ $vars->{'initial_status'} = Bugzilla::Status->can_change_to;
+ $vars->{'status_objects'} = [Bugzilla::Status->get_all];
+ }
}
##############
@@ -64,137 +62,136 @@ sub template_before_process {
##############
sub bug_start_of_update {
- my ($self, $args) = @_;
- my $old_bug = $args->{old_bug};
- my $params = Bugzilla->input_params;
+ my ($self, $args) = @_;
+ my $old_bug = $args->{old_bug};
+ my $params = Bugzilla->input_params;
- return if !Bugzilla->request_cache->{bzapi};
+ return if !Bugzilla->request_cache->{bzapi};
- # Check for a mid-air collision. Currently this only works when updating
- # an individual bug and if last_changed_time is provided. Otherwise it
- # allows the changes.
- my $delta_ts = $params->{last_change_time} || '';
+ # Check for a mid-air collision. Currently this only works when updating
+ # an individual bug and if last_changed_time is provided. Otherwise it
+ # allows the changes.
+ my $delta_ts = $params->{last_change_time} || '';
- if ($delta_ts && exists $params->{ids} && @{ $params->{ids} } == 1) {
- _midair_check($delta_ts, $old_bug->delta_ts);
- }
+ if ($delta_ts && exists $params->{ids} && @{$params->{ids}} == 1) {
+ _midair_check($delta_ts, $old_bug->delta_ts);
+ }
}
sub object_end_of_set_all {
- my ($self, $args) = @_;
- my $object = $args->{object};
- my $params = Bugzilla->input_params;
+ my ($self, $args) = @_;
+ my $object = $args->{object};
+ my $params = Bugzilla->input_params;
- return if !Bugzilla->request_cache->{bzapi};
- return if !$object->isa('Bugzilla::Attachment');
+ return if !Bugzilla->request_cache->{bzapi};
+ return if !$object->isa('Bugzilla::Attachment');
- # Check for a mid-air collision. Currently this only works when updating
- # an individual attachment and if last_changed_time is provided. Otherwise it
- # allows the changes.
- my $stash = Bugzilla->request_cache->{bzapi_stash} ||= {};
- my $delta_ts = $stash->{last_change_time};
+ # Check for a mid-air collision. Currently this only works when updating
+ # an individual attachment and if last_changed_time is provided. Otherwise it
+ # allows the changes.
+ my $stash = Bugzilla->request_cache->{bzapi_stash} ||= {};
+ my $delta_ts = $stash->{last_change_time};
- _midair_check($delta_ts, $object->modification_time) if $delta_ts;
+ _midair_check($delta_ts, $object->modification_time) if $delta_ts;
}
sub _midair_check {
- my ($delta_ts, $old_delta_ts) = @_;
- my $delta_ts_z = datetime_from($delta_ts)
- || ThrowCodeError('invalid_timestamp', { timestamp => $delta_ts });
- my $old_delta_tz_z = datetime_from($old_delta_ts);
- if ($old_delta_tz_z ne $delta_ts_z) {
- ThrowUserError('bzapi_midair_collision');
- }
+ my ($delta_ts, $old_delta_ts) = @_;
+ my $delta_ts_z = datetime_from($delta_ts)
+ || ThrowCodeError('invalid_timestamp', {timestamp => $delta_ts});
+ my $old_delta_tz_z = datetime_from($old_delta_ts);
+ if ($old_delta_tz_z ne $delta_ts_z) {
+ ThrowUserError('bzapi_midair_collision');
+ }
}
sub webservice_error_codes {
- my ($self, $args) = @_;
- my $error_map = $args->{error_map};
- $error_map->{'bzapi_midair_collision'} = 400;
+ my ($self, $args) = @_;
+ my $error_map = $args->{error_map};
+ $error_map->{'bzapi_midair_collision'} = 400;
}
sub webservice_fix_credentials {
- my ($self, $args) = @_;
- my $rpc = $args->{rpc};
- my $params = $args->{params};
- return if !Bugzilla->request_cache->{bzapi};
- fix_credentials($params);
+ my ($self, $args) = @_;
+ my $rpc = $args->{rpc};
+ my $params = $args->{params};
+ return if !Bugzilla->request_cache->{bzapi};
+ fix_credentials($params);
}
sub webservice_rest_request {
- my ($self, $args) = @_;
- my $rpc = $args->{rpc};
- my $params = $args->{params};
- my $cache = Bugzilla->request_cache;
+ my ($self, $args) = @_;
+ my $rpc = $args->{rpc};
+ my $params = $args->{params};
+ my $cache = Bugzilla->request_cache;
- return if !$cache->{bzapi};
+ return if !$cache->{bzapi};
- # Stash certain values for later use
- $cache->{bzapi_rpc} = $rpc;
+ # Stash certain values for later use
+ $cache->{bzapi_rpc} = $rpc;
- # Internal websevice method being used
- $cache->{bzapi_rpc_method} = $rpc->path_info . "." . $rpc->bz_method_name;
+ # Internal websevice method being used
+ $cache->{bzapi_rpc_method} = $rpc->path_info . "." . $rpc->bz_method_name;
- # Load the appropriate request handler based on path and type
- if (my $handler = _find_handler($rpc, 'request')) {
- &$handler($params);
- }
+ # Load the appropriate request handler based on path and type
+ if (my $handler = _find_handler($rpc, 'request')) {
+ &$handler($params);
+ }
}
sub webservice_rest_response {
- my ($self, $args) = @_;
- my $rpc = $args->{rpc};
- my $result = $args->{result};
- my $response = $args->{response};
- my $cache = Bugzilla->request_cache;
-
- # Stash certain values for later use
- $cache->{bzapi_rpc} ||= $rpc;
-
- return if !Bugzilla->request_cache->{bzapi}
- || ref $$result ne 'HASH';
-
- if (exists $$result->{error}) {
- $$result->{documentation} = BZAPI_DOC;
- return;
- }
-
- # Load the appropriate response handler based on path and type
- if (my $handler = _find_handler($rpc, 'response')) {
- &$handler($result, $response);
- }
-
- # Add a Location header if a newly created resource
- # such as a bug or comment.
- if ($rpc->bz_success_code
- && $rpc->bz_success_code == STATUS_CREATED
- && $$result->{ref})
- {
- $response->header("Location", $$result->{ref});
- }
+ my ($self, $args) = @_;
+ my $rpc = $args->{rpc};
+ my $result = $args->{result};
+ my $response = $args->{response};
+ my $cache = Bugzilla->request_cache;
+
+ # Stash certain values for later use
+ $cache->{bzapi_rpc} ||= $rpc;
+
+ return if !Bugzilla->request_cache->{bzapi} || ref $$result ne 'HASH';
+
+ if (exists $$result->{error}) {
+ $$result->{documentation} = BZAPI_DOC;
+ return;
+ }
+
+ # Load the appropriate response handler based on path and type
+ if (my $handler = _find_handler($rpc, 'response')) {
+ &$handler($result, $response);
+ }
+
+ # Add a Location header if a newly created resource
+ # such as a bug or comment.
+ if ( $rpc->bz_success_code
+ && $rpc->bz_success_code == STATUS_CREATED
+ && $$result->{ref})
+ {
+ $response->header("Location", $$result->{ref});
+ }
}
sub webservice_rest_resources {
- my ($self, $args) = @_;
- my $rpc = $args->{rpc};
- my $resources = $args->{resources};
+ my ($self, $args) = @_;
+ my $rpc = $args->{rpc};
+ my $resources = $args->{resources};
- return if !Bugzilla->request_cache->{bzapi};
+ return if !Bugzilla->request_cache->{bzapi};
- _add_resources($rpc, $resources);
+ _add_resources($rpc, $resources);
}
sub webservice_status_code_map {
- my ($self, $args) = @_;
- my $status_code_map = $args->{status_code_map};
- $status_code_map->{51} = STATUS_BAD_REQUEST;
+ my ($self, $args) = @_;
+ my $status_code_map = $args->{status_code_map};
+ $status_code_map->{51} = STATUS_BAD_REQUEST;
}
sub psgi_builder {
- my ($self, $args) = @_;
- my $mount = $args->{mount};
+ my ($self, $args) = @_;
+ my $mount = $args->{mount};
- $mount->{'bzapi'} = compile_cgi('extensions/BzAPI/bin/rest.cgi');
+ $mount->{'bzapi'} = compile_cgi('extensions/BzAPI/bin/rest.cgi');
}
@@ -203,93 +200,95 @@ sub psgi_builder {
#####################
sub _find_handler {
- my ($rpc, $type) = @_;
+ my ($rpc, $type) = @_;
- my $path_info = $rpc->cgi->path_info;
- my $request_method = $rpc->request->method;
+ my $path_info = $rpc->cgi->path_info;
+ my $request_method = $rpc->request->method;
- my $module = $rpc->bz_class_name || '';
- $module =~ s/^Bugzilla::WebService:://;
+ my $module = $rpc->bz_class_name || '';
+ $module =~ s/^Bugzilla::WebService:://;
- my $cache = _preload_handlers();
+ my $cache = _preload_handlers();
- return undef if !exists $cache->{$module};
+ return undef if !exists $cache->{$module};
- # Make a copy of the handler array so
- # as to not alter the actual cached data.
- my @handlers = @{ $cache->{$module} };
+ # Make a copy of the handler array so
+ # as to not alter the actual cached data.
+ my @handlers = @{$cache->{$module}};
- while (my $regex = shift @handlers) {
- my $data = shift @handlers;
- next if ref $data ne 'HASH';
- if ($path_info =~ $regex
- && exists $data->{$request_method}
- && exists $data->{$request_method}->{$type})
- {
- return $data->{$request_method}->{$type};
- }
+ while (my $regex = shift @handlers) {
+ my $data = shift @handlers;
+ next if ref $data ne 'HASH';
+ if ( $path_info =~ $regex
+ && exists $data->{$request_method}
+ && exists $data->{$request_method}->{$type})
+ {
+ return $data->{$request_method}->{$type};
}
+ }
- return undef;
+ return undef;
}
sub _add_resources {
- my ($rpc, $native_resources) = @_;
-
- my $cache = _preload_handlers();
-
- foreach my $module (keys %$cache) {
- my $native_module = "Bugzilla::WebService::$module";
- next if !$native_resources->{$native_module};
-
- # Make a copy of the handler array so
- # as to not alter the actual cached data.
- my @handlers = @{ $cache->{$module} };
-
- my @ext_resources = ();
- while (my $regex = shift @handlers) {
- my $data = shift @handlers;
- next if ref $data ne 'HASH';
- my $new_data = {};
- foreach my $request_method (keys %$data) {
- next if !exists $data->{$request_method}->{resource};
- $new_data->{$request_method} = $data->{$request_method}->{resource};
- }
- push(@ext_resources, $regex, $new_data);
- }
-
- # Places the new resources at the beginning of the list
- # so we can capture specific paths before the native resources
- unshift(@{$native_resources->{$native_module}}, @ext_resources);
+ my ($rpc, $native_resources) = @_;
+
+ my $cache = _preload_handlers();
+
+ foreach my $module (keys %$cache) {
+ my $native_module = "Bugzilla::WebService::$module";
+ next if !$native_resources->{$native_module};
+
+ # Make a copy of the handler array so
+ # as to not alter the actual cached data.
+ my @handlers = @{$cache->{$module}};
+
+ my @ext_resources = ();
+ while (my $regex = shift @handlers) {
+ my $data = shift @handlers;
+ next if ref $data ne 'HASH';
+ my $new_data = {};
+ foreach my $request_method (keys %$data) {
+ next if !exists $data->{$request_method}->{resource};
+ $new_data->{$request_method} = $data->{$request_method}->{resource};
+ }
+ push(@ext_resources, $regex, $new_data);
}
+
+ # Places the new resources at the beginning of the list
+ # so we can capture specific paths before the native resources
+ unshift(@{$native_resources->{$native_module}}, @ext_resources);
+ }
}
sub _resource_modules {
- my $extdir = bz_locations()->{extensionsdir};
- return map { basename($_, '.pm') } glob("$extdir/" . __PACKAGE__->NAME . "/lib/Resources/*.pm");
+ my $extdir = bz_locations()->{extensionsdir};
+ return
+ map { basename($_, '.pm') }
+ glob("$extdir/" . __PACKAGE__->NAME . "/lib/Resources/*.pm");
}
# preload all handlers into cache
# since we don't want to parse all
# this multiple times
sub _preload_handlers {
- my $cache = Bugzilla->request_cache;
-
- if (!exists $cache->{rest_handlers}) {
- my $all_handlers = {};
- foreach my $module (_resource_modules()) {
- my $resource_class = "Bugzilla::Extension::BzAPI::Resources::$module";
- trick_taint($resource_class);
- eval { require_module($resource_class) };
- next if ($@ || !$resource_class->can('rest_handlers'));
- my $handlers = $resource_class->rest_handlers;
- next if (ref $handlers ne 'ARRAY' || scalar @$handlers % 2 != 0);
- $all_handlers->{$module} = $handlers;
- }
- $cache->{rest_handlers} = $all_handlers;
+ my $cache = Bugzilla->request_cache;
+
+ if (!exists $cache->{rest_handlers}) {
+ my $all_handlers = {};
+ foreach my $module (_resource_modules()) {
+ my $resource_class = "Bugzilla::Extension::BzAPI::Resources::$module";
+ trick_taint($resource_class);
+ eval { require_module($resource_class) };
+ next if ($@ || !$resource_class->can('rest_handlers'));
+ my $handlers = $resource_class->rest_handlers;
+ next if (ref $handlers ne 'ARRAY' || scalar @$handlers % 2 != 0);
+ $all_handlers->{$module} = $handlers;
}
+ $cache->{rest_handlers} = $all_handlers;
+ }
- return $cache->{rest_handlers};
+ return $cache->{rest_handlers};
}
__PACKAGE__->NAME;
diff --git a/extensions/BzAPI/bin/rest.cgi b/extensions/BzAPI/bin/rest.cgi
index 5642ad550..5a895bc1d 100755
--- a/extensions/BzAPI/bin/rest.cgi
+++ b/extensions/BzAPI/bin/rest.cgi
@@ -14,12 +14,11 @@ use Bugzilla;
use Bugzilla::Constants;
use Bugzilla::Error;
use Bugzilla::WebService::Constants;
+
BEGIN {
- if (!Bugzilla->feature('rest')
- || !Bugzilla->feature('jsonrpc'))
- {
- ThrowUserError('feature_disabled', { feature => 'rest' });
- }
+ if (!Bugzilla->feature('rest') || !Bugzilla->feature('jsonrpc')) {
+ ThrowUserError('feature_disabled', {feature => 'rest'});
+ }
}
# Set request_cache bzapi value to true in order to enable the
@@ -30,9 +29,10 @@ Bugzilla->request_cache->{bzapi} = 1;
# otherwise native REST will complain
my $path_info = Bugzilla->cgi->path_info;
if ($path_info =~ s'/$'') {
- # Remove first slash as cgi->path_info expects it to
- # not be there when setting a new path.
- Bugzilla->cgi->path_info(substr($path_info, 1));
+
+ # Remove first slash as cgi->path_info expects it to
+ # not be there when setting a new path.
+ Bugzilla->cgi->path_info(substr($path_info, 1));
}
use Bugzilla::WebService::Server::REST;
diff --git a/extensions/BzAPI/lib/Constants.pm b/extensions/BzAPI/lib/Constants.pm
index fb611aae6..f31dac7a8 100644
--- a/extensions/BzAPI/lib/Constants.pm
+++ b/extensions/BzAPI/lib/Constants.pm
@@ -13,142 +13,144 @@ use warnings;
use base qw(Exporter);
our @EXPORT = qw(
- USER_FIELDS
- BUG_FIELD_MAP
- BOOLEAN_TYPE_MAP
- ATTACHMENT_FIELD_MAP
- DEFAULT_BUG_FIELDS
- DEFAULT_ATTACHMENT_FIELDS
+ USER_FIELDS
+ BUG_FIELD_MAP
+ BOOLEAN_TYPE_MAP
+ ATTACHMENT_FIELD_MAP
+ DEFAULT_BUG_FIELDS
+ DEFAULT_ATTACHMENT_FIELDS
- BZAPI_DOC
+ BZAPI_DOC
);
# These are fields that are normally exported as a single value such
# as the user's email. BzAPI needs to convert them to user objects
# where possible.
-use constant USER_FIELDS => (qw(
+use constant USER_FIELDS => (
+ qw(
assigned_to
cc
creator
qa_contact
reporter
-));
+ )
+);
# Convert old field names from old to new
use constant BUG_FIELD_MAP => {
- 'opendate' => 'creation_time', # query
- 'creation_ts' => 'creation_time',
- 'changeddate' => 'last_change_time', # query
- 'delta_ts' => 'last_change_time',
- 'bug_id' => 'id',
- 'rep_platform' => 'platform',
- 'bug_severity' => 'severity',
- 'bug_status' => 'status',
- 'short_desc' => 'summary',
- 'bug_file_loc' => 'url',
- 'status_whiteboard' => 'whiteboard',
- 'reporter' => 'creator',
- 'reporter_realname' => 'creator_realname',
- 'cclist_accessible' => 'is_cc_accessible',
- 'reporter_accessible' => 'is_creator_accessible',
- 'everconfirmed' => 'is_confirmed',
- 'dependson' => 'depends_on',
- 'blocked' => 'blocks',
- 'attachment' => 'attachments',
- 'flag' => 'flags',
- 'flagtypes.name' => 'flag',
- 'bug_group' => 'group',
- 'group' => 'groups',
- 'longdesc' => 'comment',
- 'bug_file_loc_type' => 'url_type',
- 'bugidtype' => 'id_mode',
- 'longdesc_type' => 'comment_type',
- 'short_desc_type' => 'summary_type',
- 'status_whiteboard_type' => 'whiteboard_type',
- 'emailassigned_to1' => 'email1_assigned_to',
- 'emailassigned_to2' => 'email2_assigned_to',
- 'emailcc1' => 'email1_cc',
- 'emailcc2' => 'email2_cc',
- 'emailqa_contact1' => 'email1_qa_contact',
- 'emailqa_contact2' => 'email2_qa_contact',
- 'emailreporter1' => 'email1_creator',
- 'emailreporter2' => 'email2_creator',
- 'emaillongdesc1' => 'email1_comment_creator',
- 'emaillongdesc2' => 'email2_comment_creator',
- 'emailtype1' => 'email1_type',
- 'emailtype2' => 'email2_type',
- 'chfieldfrom' => 'changed_after',
- 'chfieldto' => 'changed_before',
- 'chfield' => 'changed_field',
- 'chfieldvalue' => 'changed_field_to',
- 'deadlinefrom' => 'deadline_after',
- 'deadlineto' => 'deadline_before',
- 'attach_data.thedata' => 'attachment.data',
- 'longdescs.isprivate' => 'comment.is_private',
- 'commenter' => 'comment.creator',
- 'requestees.login_name' => 'flag.requestee',
- 'setters.login_name' => 'flag.setter',
- 'days_elapsed' => 'idle',
- 'owner_idle_time' => 'assignee_idle',
- 'dup_id' => 'dupe_of',
- 'isopened' => 'is_open',
- 'flag_type' => 'flag_types',
- 'attachments.submitter' => 'attachment.attacher',
- 'attachments.filename' => 'attachment.file_name',
- 'attachments.description' => 'attachment.description',
- 'attachments.delta_ts' => 'attachment.last_change_time',
- 'attachments.isobsolete' => 'attachment.is_obsolete',
- 'attachments.ispatch' => 'attachment.is_patch',
- 'attachments.isprivate' => 'attachment.is_private',
- 'attachments.mimetype' => 'attachment.content_type',
- 'attachments.date' => 'attachment.creation_time',
- 'attachments.attachid' => 'attachment.id',
- 'attachments.flag' => 'attachment.flags',
- 'attachments.token' => 'attachment.update_token'
+ 'opendate' => 'creation_time', # query
+ 'creation_ts' => 'creation_time',
+ 'changeddate' => 'last_change_time', # query
+ 'delta_ts' => 'last_change_time',
+ 'bug_id' => 'id',
+ 'rep_platform' => 'platform',
+ 'bug_severity' => 'severity',
+ 'bug_status' => 'status',
+ 'short_desc' => 'summary',
+ 'bug_file_loc' => 'url',
+ 'status_whiteboard' => 'whiteboard',
+ 'reporter' => 'creator',
+ 'reporter_realname' => 'creator_realname',
+ 'cclist_accessible' => 'is_cc_accessible',
+ 'reporter_accessible' => 'is_creator_accessible',
+ 'everconfirmed' => 'is_confirmed',
+ 'dependson' => 'depends_on',
+ 'blocked' => 'blocks',
+ 'attachment' => 'attachments',
+ 'flag' => 'flags',
+ 'flagtypes.name' => 'flag',
+ 'bug_group' => 'group',
+ 'group' => 'groups',
+ 'longdesc' => 'comment',
+ 'bug_file_loc_type' => 'url_type',
+ 'bugidtype' => 'id_mode',
+ 'longdesc_type' => 'comment_type',
+ 'short_desc_type' => 'summary_type',
+ 'status_whiteboard_type' => 'whiteboard_type',
+ 'emailassigned_to1' => 'email1_assigned_to',
+ 'emailassigned_to2' => 'email2_assigned_to',
+ 'emailcc1' => 'email1_cc',
+ 'emailcc2' => 'email2_cc',
+ 'emailqa_contact1' => 'email1_qa_contact',
+ 'emailqa_contact2' => 'email2_qa_contact',
+ 'emailreporter1' => 'email1_creator',
+ 'emailreporter2' => 'email2_creator',
+ 'emaillongdesc1' => 'email1_comment_creator',
+ 'emaillongdesc2' => 'email2_comment_creator',
+ 'emailtype1' => 'email1_type',
+ 'emailtype2' => 'email2_type',
+ 'chfieldfrom' => 'changed_after',
+ 'chfieldto' => 'changed_before',
+ 'chfield' => 'changed_field',
+ 'chfieldvalue' => 'changed_field_to',
+ 'deadlinefrom' => 'deadline_after',
+ 'deadlineto' => 'deadline_before',
+ 'attach_data.thedata' => 'attachment.data',
+ 'longdescs.isprivate' => 'comment.is_private',
+ 'commenter' => 'comment.creator',
+ 'requestees.login_name' => 'flag.requestee',
+ 'setters.login_name' => 'flag.setter',
+ 'days_elapsed' => 'idle',
+ 'owner_idle_time' => 'assignee_idle',
+ 'dup_id' => 'dupe_of',
+ 'isopened' => 'is_open',
+ 'flag_type' => 'flag_types',
+ 'attachments.submitter' => 'attachment.attacher',
+ 'attachments.filename' => 'attachment.file_name',
+ 'attachments.description' => 'attachment.description',
+ 'attachments.delta_ts' => 'attachment.last_change_time',
+ 'attachments.isobsolete' => 'attachment.is_obsolete',
+ 'attachments.ispatch' => 'attachment.is_patch',
+ 'attachments.isprivate' => 'attachment.is_private',
+ 'attachments.mimetype' => 'attachment.content_type',
+ 'attachments.date' => 'attachment.creation_time',
+ 'attachments.attachid' => 'attachment.id',
+ 'attachments.flag' => 'attachment.flags',
+ 'attachments.token' => 'attachment.update_token'
};
# Convert from old boolean chart type names to new names
use constant BOOLEAN_TYPE_MAP => {
- 'equals' => 'equals',
- 'not_equals' => 'notequals',
- 'equals_any' => 'anyexact',
- 'contains' => 'substring',
- 'not_contains' => 'notsubstring',
- 'case_contains' => 'casesubstring',
- 'contains_any' => 'anywordssubstr',
- 'not_contains_any' => 'nowordssubstr',
- 'contains_all' => 'allwordssubstr',
- 'contains_any_words' => 'anywords',
- 'not_contains_any_words' => 'nowords',
- 'contains_all_words' => 'allwords',
- 'regex' => 'regexp',
- 'not_regex' => 'notregexp',
- 'less_than' => 'lessthan',
- 'greater_than' => 'greaterthan',
- 'changed_before' => 'changedbefore',
- 'changed_after' => 'changedafter',
- 'changed_from' => 'changedfrom',
- 'changed_to' => 'changedto',
- 'changed_by' => 'changedby',
- 'matches' => 'matches'
+ 'equals' => 'equals',
+ 'not_equals' => 'notequals',
+ 'equals_any' => 'anyexact',
+ 'contains' => 'substring',
+ 'not_contains' => 'notsubstring',
+ 'case_contains' => 'casesubstring',
+ 'contains_any' => 'anywordssubstr',
+ 'not_contains_any' => 'nowordssubstr',
+ 'contains_all' => 'allwordssubstr',
+ 'contains_any_words' => 'anywords',
+ 'not_contains_any_words' => 'nowords',
+ 'contains_all_words' => 'allwords',
+ 'regex' => 'regexp',
+ 'not_regex' => 'notregexp',
+ 'less_than' => 'lessthan',
+ 'greater_than' => 'greaterthan',
+ 'changed_before' => 'changedbefore',
+ 'changed_after' => 'changedafter',
+ 'changed_from' => 'changedfrom',
+ 'changed_to' => 'changedto',
+ 'changed_by' => 'changedby',
+ 'matches' => 'matches'
};
# Convert old attachment field names from old to new
use constant ATTACHMENT_FIELD_MAP => {
- 'submitter' => 'attacher',
- 'description' => 'description',
- 'filename' => 'file_name',
- 'delta_ts' => 'last_change_time',
- 'isobsolete' => 'is_obsolete',
- 'ispatch' => 'is_patch',
- 'isprivate' => 'is_private',
- 'mimetype' => 'content_type',
- 'contenttypeentry' => 'content_type',
- 'date' => 'creation_time',
- 'attachid' => 'id',
- 'desc' => 'description',
- 'flag' => 'flags',
- 'type' => 'content_type',
+ 'submitter' => 'attacher',
+ 'description' => 'description',
+ 'filename' => 'file_name',
+ 'delta_ts' => 'last_change_time',
+ 'isobsolete' => 'is_obsolete',
+ 'ispatch' => 'is_patch',
+ 'isprivate' => 'is_private',
+ 'mimetype' => 'content_type',
+ 'contenttypeentry' => 'content_type',
+ 'date' => 'creation_time',
+ 'attachid' => 'id',
+ 'desc' => 'description',
+ 'flag' => 'flags',
+ 'type' => 'content_type',
};
# A base link to the current BzAPI Documentation.
diff --git a/extensions/BzAPI/lib/Resources/Bug.pm b/extensions/BzAPI/lib/Resources/Bug.pm
index 4fd59824c..ee76ddbcd 100644
--- a/extensions/BzAPI/lib/Resources/Bug.pm
+++ b/extensions/BzAPI/lib/Resources/Bug.pm
@@ -28,111 +28,86 @@ use List::Util qw(max);
#################
BEGIN {
- require Bugzilla::WebService::Bug;
- *Bugzilla::WebService::Bug::get_bug_count = \&get_bug_count_resource;
+ require Bugzilla::WebService::Bug;
+ *Bugzilla::WebService::Bug::get_bug_count = \&get_bug_count_resource;
}
sub rest_handlers {
- my $rest_handlers = [
- qr{^/bug$}, {
- GET => {
- request => \&search_bugs_request,
- response => \&search_bugs_response
- },
- POST => {
- request => \&create_bug_request,
- response => \&create_bug_response
- }
- },
- qr{^/bug/([^/]+)$}, {
- GET => {
- response => \&get_bug_response
- },
- PUT => {
- request => \&update_bug_request,
- response => \&update_bug_response
- }
- },
- qr{^/bug/([^/]+)/comment$}, {
- GET => {
- response => \&get_comments_response
- },
- POST => {
- request => \&add_comment_request,
- response => \&add_comment_response
- }
- },
- qr{^/bug/([^/]+)/history$}, {
- GET => {
- response => \&get_history_response
- }
- },
- qr{^/bug/([^/]+)/attachment$}, {
- GET => {
- response => \&get_attachments_response
- },
- POST => {
- request => \&add_attachment_request,
- response => \&add_attachment_response
- }
- },
- qr{^/bug/attachment/([^/]+)$}, {
- GET => {
- response => \&get_attachment_response
- },
- PUT => {
- request => \&update_attachment_request,
- response => \&update_attachment_response
- }
- },
- qr{^/attachment/([^/]+)$}, {
- GET => {
- response => \&get_attachment_response
- },
- PUT => {
- request => \&update_attachment_request,
- response => \&update_attachment_response
- }
- },
- qr{^/bug/([^/]+)/flag$}, {
- GET => {
- resource => {
- method => 'get',
- params => sub {
- return { ids => [ $_[0] ],
- include_fields => ['flags'] };
- }
- },
- response => \&get_bug_flags_response,
- }
- },
- qr{^/count$}, {
- GET => {
- resource => {
- method => 'get_bug_count'
- }
- }
+ my $rest_handlers = [
+ qr{^/bug$},
+ {
+ GET => {request => \&search_bugs_request, response => \&search_bugs_response},
+ POST => {request => \&create_bug_request, response => \&create_bug_response}
+ },
+ qr{^/bug/([^/]+)$},
+ {
+ GET => {response => \&get_bug_response},
+ PUT => {request => \&update_bug_request, response => \&update_bug_response}
+ },
+ qr{^/bug/([^/]+)/comment$},
+ {
+ GET => {response => \&get_comments_response},
+ POST => {request => \&add_comment_request, response => \&add_comment_response}
+ },
+ qr{^/bug/([^/]+)/history$},
+ {GET => {response => \&get_history_response}},
+ qr{^/bug/([^/]+)/attachment$},
+ {
+ GET => {response => \&get_attachments_response},
+ POST =>
+ {request => \&add_attachment_request, response => \&add_attachment_response}
+ },
+ qr{^/bug/attachment/([^/]+)$},
+ {
+ GET => {response => \&get_attachment_response},
+ PUT => {
+ request => \&update_attachment_request,
+ response => \&update_attachment_response
+ }
+ },
+ qr{^/attachment/([^/]+)$},
+ {
+ GET => {response => \&get_attachment_response},
+ PUT => {
+ request => \&update_attachment_request,
+ response => \&update_attachment_response
+ }
+ },
+ qr{^/bug/([^/]+)/flag$},
+ {
+ GET => {
+ resource => {
+ method => 'get',
+ params => sub {
+ return {ids => [$_[0]], include_fields => ['flags']};
+ }
},
- qr{^/attachment/([^/]+)$}, {
- GET => {
- resource => {
- method => 'attachments',
- params => sub {
- return { attachment_ids => [ $_[0] ] };
- }
- }
- },
- PUT => {
- resource => {
- method => 'update_attachment',
- params => sub {
- return { ids => [ $_[0] ] };
- }
- }
- }
+ response => \&get_bug_flags_response,
+ }
+ },
+ qr{^/count$},
+ {GET => {resource => {method => 'get_bug_count'}}},
+ qr{^/attachment/([^/]+)$},
+ {
+ GET => {
+ resource => {
+ method => 'attachments',
+ params => sub {
+ return {attachment_ids => [$_[0]]};
+ }
}
- ];
- return $rest_handlers;
+ },
+ PUT => {
+ resource => {
+ method => 'update_attachment',
+ params => sub {
+ return {ids => [$_[0]]};
+ }
+ }
+ }
+ }
+ ];
+ return $rest_handlers;
}
#########################
@@ -144,205 +119,203 @@ sub rest_handlers {
# this should be broken into it's own module so that report.cgi
# and here can share the same code.
sub get_bug_count_resource {
- my ($self, $params) = @_;
-
- Bugzilla->switch_to_shadow_db();
-
- my $col_field = $params->{x_axis_field} || '';
- my $row_field = $params->{y_axis_field} || '';
- my $tbl_field = $params->{z_axis_field} || '';
-
- my $dimensions = $col_field ?
- $row_field ?
- $tbl_field ? 3 : 2 : 1 : 0;
-
- if ($dimensions == 0) {
- $col_field = "bug_status";
- $params->{x_axis_field} = "bug_status";
- }
-
- # Valid bug fields that can be reported on.
- my $valid_columns = Bugzilla::Search::REPORT_COLUMNS;
-
- # Convert external names to internal if necessary
- $params = Bugzilla::Bug::map_fields($params);
- $row_field = Bugzilla::Bug::FIELD_MAP->{$row_field} || $row_field;
- $col_field = Bugzilla::Bug::FIELD_MAP->{$col_field} || $col_field;
- $tbl_field = Bugzilla::Bug::FIELD_MAP->{$tbl_field} || $tbl_field;
-
- # Validate the values in the axis fields or throw an error.
- !$row_field
- || ($valid_columns->{$row_field} && trick_taint($row_field))
- || ThrowCodeError("report_axis_invalid", { fld => "x", val => $row_field });
- !$col_field
- || ($valid_columns->{$col_field} && trick_taint($col_field))
- || ThrowCodeError("report_axis_invalid", { fld => "y", val => $col_field });
- !$tbl_field
- || ($valid_columns->{$tbl_field} && trick_taint($tbl_field))
- || ThrowCodeError("report_axis_invalid", { fld => "z", val => $tbl_field });
-
- my @axis_fields = grep { $_ } ($row_field, $col_field, $tbl_field);
-
- my $search = new Bugzilla::Search(
- fields => \@axis_fields,
- params => $params,
- allow_unlimited => 1,
- );
-
- my ($results, $extra_data) = $search->data;
-
- # We have a hash of hashes for the data itself, and a hash to hold the
- # row/col/table names.
- my %data;
- my %names;
-
- # Read the bug data and count the bugs for each possible value of row, column
- # and table.
- #
- # We detect a numerical field, and sort appropriately, if all the values are
- # numeric.
- my $col_isnumeric = 1;
- my $row_isnumeric = 1;
- my $tbl_isnumeric = 1;
-
- foreach my $result (@$results) {
- # handle empty dimension member names
- my $row = check_value($row_field, $result);
- my $col = check_value($col_field, $result);
- my $tbl = check_value($tbl_field, $result);
-
- $data{$tbl}{$col}{$row}++;
- $names{"col"}{$col}++;
- $names{"row"}{$row}++;
- $names{"tbl"}{$tbl}++;
-
- $col_isnumeric &&= ($col =~ /^-?\d+(\.\d+)?$/o);
- $row_isnumeric &&= ($row =~ /^-?\d+(\.\d+)?$/o);
- $tbl_isnumeric &&= ($tbl =~ /^-?\d+(\.\d+)?$/o);
- }
-
- my @col_names = get_names($names{"col"}, $col_isnumeric, $col_field);
- my @row_names = get_names($names{"row"}, $row_isnumeric, $row_field);
- my @tbl_names = get_names($names{"tbl"}, $tbl_isnumeric, $tbl_field);
-
- push(@tbl_names, "-total-") if (scalar(@tbl_names) > 1);
-
- my @data;
- foreach my $tbl (@tbl_names) {
- my @tbl_data;
- foreach my $row (@row_names) {
- my @col_data;
- foreach my $col (@col_names) {
- $data{$tbl}{$col}{$row} = $data{$tbl}{$col}{$row} || 0;
- push(@col_data, $data{$tbl}{$col}{$row});
- if ($tbl ne "-total-") {
- # This is a bit sneaky. We spend every loop except the last
- # building up the -total- data, and then last time round,
- # we process it as another tbl, and push() the total values
- # into the image_data array.
- $data{"-total-"}{$col}{$row} += $data{$tbl}{$col}{$row};
- }
- }
- push(@tbl_data, \@col_data);
- }
- push(@data, \@tbl_data);
- }
-
- my $result = {};
- if ($dimensions == 0) {
- my $sum = 0;
-
- # If the search returns no results, we just get an 0-byte file back
- # and so there is no data at all.
- if (@data) {
- foreach my $value (@{ $data[0][0] }) {
- $sum += $value;
- }
- }
-
- $result = {
- 'data' => $sum
- };
- }
- elsif ($dimensions == 1) {
- $result = {
- 'x_labels' => \@col_names,
- 'data' => $data[0][0] || []
- };
- }
- elsif ($dimensions == 2) {
- $result = {
- 'x_labels' => \@col_names,
- 'y_labels' => \@row_names,
- 'data' => $data[0] || [[]]
- };
- }
- elsif ($dimensions == 3) {
- if (@data > 1 && $tbl_names[-1] eq "-total-") {
- # Last table is a total, which we discard
- pop(@data);
- pop(@tbl_names);
+ my ($self, $params) = @_;
+
+ Bugzilla->switch_to_shadow_db();
+
+ my $col_field = $params->{x_axis_field} || '';
+ my $row_field = $params->{y_axis_field} || '';
+ my $tbl_field = $params->{z_axis_field} || '';
+
+ my $dimensions = $col_field ? $row_field ? $tbl_field ? 3 : 2 : 1 : 0;
+
+ if ($dimensions == 0) {
+ $col_field = "bug_status";
+ $params->{x_axis_field} = "bug_status";
+ }
+
+ # Valid bug fields that can be reported on.
+ my $valid_columns = Bugzilla::Search::REPORT_COLUMNS;
+
+ # Convert external names to internal if necessary
+ $params = Bugzilla::Bug::map_fields($params);
+ $row_field = Bugzilla::Bug::FIELD_MAP->{$row_field} || $row_field;
+ $col_field = Bugzilla::Bug::FIELD_MAP->{$col_field} || $col_field;
+ $tbl_field = Bugzilla::Bug::FIELD_MAP->{$tbl_field} || $tbl_field;
+
+ # Validate the values in the axis fields or throw an error.
+ !$row_field
+ || ($valid_columns->{$row_field} && trick_taint($row_field))
+ || ThrowCodeError("report_axis_invalid", {fld => "x", val => $row_field});
+ !$col_field
+ || ($valid_columns->{$col_field} && trick_taint($col_field))
+ || ThrowCodeError("report_axis_invalid", {fld => "y", val => $col_field});
+ !$tbl_field
+ || ($valid_columns->{$tbl_field} && trick_taint($tbl_field))
+ || ThrowCodeError("report_axis_invalid", {fld => "z", val => $tbl_field});
+
+ my @axis_fields = grep {$_} ($row_field, $col_field, $tbl_field);
+
+ my $search = new Bugzilla::Search(
+ fields => \@axis_fields,
+ params => $params,
+ allow_unlimited => 1,
+ );
+
+ my ($results, $extra_data) = $search->data;
+
+ # We have a hash of hashes for the data itself, and a hash to hold the
+ # row/col/table names.
+ my %data;
+ my %names;
+
+ # Read the bug data and count the bugs for each possible value of row, column
+ # and table.
+ #
+ # We detect a numerical field, and sort appropriately, if all the values are
+ # numeric.
+ my $col_isnumeric = 1;
+ my $row_isnumeric = 1;
+ my $tbl_isnumeric = 1;
+
+ foreach my $result (@$results) {
+
+ # handle empty dimension member names
+ my $row = check_value($row_field, $result);
+ my $col = check_value($col_field, $result);
+ my $tbl = check_value($tbl_field, $result);
+
+ $data{$tbl}{$col}{$row}++;
+ $names{"col"}{$col}++;
+ $names{"row"}{$row}++;
+ $names{"tbl"}{$tbl}++;
+
+ $col_isnumeric &&= ($col =~ /^-?\d+(\.\d+)?$/o);
+ $row_isnumeric &&= ($row =~ /^-?\d+(\.\d+)?$/o);
+ $tbl_isnumeric &&= ($tbl =~ /^-?\d+(\.\d+)?$/o);
+ }
+
+ my @col_names = get_names($names{"col"}, $col_isnumeric, $col_field);
+ my @row_names = get_names($names{"row"}, $row_isnumeric, $row_field);
+ my @tbl_names = get_names($names{"tbl"}, $tbl_isnumeric, $tbl_field);
+
+ push(@tbl_names, "-total-") if (scalar(@tbl_names) > 1);
+
+ my @data;
+ foreach my $tbl (@tbl_names) {
+ my @tbl_data;
+ foreach my $row (@row_names) {
+ my @col_data;
+ foreach my $col (@col_names) {
+ $data{$tbl}{$col}{$row} = $data{$tbl}{$col}{$row} || 0;
+ push(@col_data, $data{$tbl}{$col}{$row});
+ if ($tbl ne "-total-") {
+
+ # This is a bit sneaky. We spend every loop except the last
+ # building up the -total- data, and then last time round,
+ # we process it as another tbl, and push() the total values
+ # into the image_data array.
+ $data{"-total-"}{$col}{$row} += $data{$tbl}{$col}{$row};
}
-
- $result = {
- 'x_labels' => \@col_names,
- 'y_labels' => \@row_names,
- 'z_labels' => \@tbl_names,
- 'data' => @data ? \@data : [[[]]]
- };
- }
-
- return $result;
+ }
+ push(@tbl_data, \@col_data);
+ }
+ push(@data, \@tbl_data);
+ }
+
+ my $result = {};
+ if ($dimensions == 0) {
+ my $sum = 0;
+
+ # If the search returns no results, we just get an 0-byte file back
+ # and so there is no data at all.
+ if (@data) {
+ foreach my $value (@{$data[0][0]}) {
+ $sum += $value;
+ }
+ }
+
+ $result = {'data' => $sum};
+ }
+ elsif ($dimensions == 1) {
+ $result = {'x_labels' => \@col_names, 'data' => $data[0][0] || []};
+ }
+ elsif ($dimensions == 2) {
+ $result = {
+ 'x_labels' => \@col_names,
+ 'y_labels' => \@row_names,
+ 'data' => $data[0] || [[]]
+ };
+ }
+ elsif ($dimensions == 3) {
+ if (@data > 1 && $tbl_names[-1] eq "-total-") {
+
+ # Last table is a total, which we discard
+ pop(@data);
+ pop(@tbl_names);
+ }
+
+ $result = {
+ 'x_labels' => \@col_names,
+ 'y_labels' => \@row_names,
+ 'z_labels' => \@tbl_names,
+ 'data' => @data ? \@data : [[[]]]
+ };
+ }
+
+ return $result;
}
sub get_names {
- my ($names, $isnumeric, $field_name) = @_;
- my ($field, @sorted);
- # XXX - This is a hack to handle the actual_time/work_time field,
- # because it's named 'actual_time' in Search.pm but 'work_time' in Field.pm.
- $_[2] = $field_name = 'work_time' if $field_name eq 'actual_time';
-
- # _realname fields aren't real Bugzilla::Field objects, but they are a
- # valid axis, so we don't vailidate them as Bugzilla::Field objects.
- $field = Bugzilla::Field->check($field_name)
- if ($field_name && $field_name !~ /_realname$/);
-
- if ($field && $field->is_select) {
- foreach my $value (@{$field->legal_values}) {
- push(@sorted, $value->name) if $names->{$value->name};
- }
- unshift(@sorted, '---') if $field_name eq 'resolution';
- @sorted = uniq @sorted;
- }
- elsif ($isnumeric) {
- # It's not a field we are preserving the order of, so sort it
- # numerically...
- @sorted = sort { $a <=> $b } keys %$names;
- }
- else {
- # ...or alphabetically, as appropriate.
- @sorted = sort keys %$names;
- }
-
- return @sorted;
+ my ($names, $isnumeric, $field_name) = @_;
+ my ($field, @sorted);
+
+ # XXX - This is a hack to handle the actual_time/work_time field,
+ # because it's named 'actual_time' in Search.pm but 'work_time' in Field.pm.
+ $_[2] = $field_name = 'work_time' if $field_name eq 'actual_time';
+
+ # _realname fields aren't real Bugzilla::Field objects, but they are a
+ # valid axis, so we don't vailidate them as Bugzilla::Field objects.
+ $field = Bugzilla::Field->check($field_name)
+ if ($field_name && $field_name !~ /_realname$/);
+
+ if ($field && $field->is_select) {
+ foreach my $value (@{$field->legal_values}) {
+ push(@sorted, $value->name) if $names->{$value->name};
+ }
+ unshift(@sorted, '---') if $field_name eq 'resolution';
+ @sorted = uniq @sorted;
+ }
+ elsif ($isnumeric) {
+
+ # It's not a field we are preserving the order of, so sort it
+ # numerically...
+ @sorted = sort { $a <=> $b } keys %$names;
+ }
+ else {
+ # ...or alphabetically, as appropriate.
+ @sorted = sort keys %$names;
+ }
+
+ return @sorted;
}
sub check_value {
- my ($field, $result) = @_;
-
- my $value;
- if (!defined $field) {
- $value = '';
- }
- elsif ($field eq '') {
- $value = ' ';
- }
- else {
- $value = shift @$result;
- $value = ' ' if (!defined $value || $value eq '');
- $value = '---' if ($field eq 'resolution' && $value eq ' ');
- }
- return $value;
+ my ($field, $result) = @_;
+
+ my $value;
+ if (!defined $field) {
+ $value = '';
+ }
+ elsif ($field eq '') {
+ $value = ' ';
+ }
+ else {
+ $value = shift @$result;
+ $value = ' ' if (!defined $value || $value eq '');
+ $value = '---' if ($field eq 'resolution' && $value eq ' ');
+ }
+ return $value;
}
########################
@@ -350,282 +323,290 @@ sub check_value {
########################
sub search_bugs_request {
- my ($params) = @_;
-
- if (defined $params->{changed_field}
- && $params->{changed_field} eq "creation_time")
- {
- $params->{changed_field} = "[Bug creation]";
- }
+ my ($params) = @_;
- my $FIELD_NEW_TO_OLD = { reverse %{ BUG_FIELD_MAP() } };
+ if (defined $params->{changed_field}
+ && $params->{changed_field} eq "creation_time")
+ {
+ $params->{changed_field} = "[Bug creation]";
+ }
- # Update values of various forms.
- foreach my $key (keys %$params) {
- # First, search types. These are found in the value of any field ending
- # _type, and the value of any field matching type\d-\d-\d.
- if ($key =~ /^type(\d+)-(\d+)-(\d+)$|_type$/) {
- $params->{$key}
- = BOOLEAN_TYPE_MAP->{$params->{$key}} || $params->{$key};
- }
+ my $FIELD_NEW_TO_OLD = {reverse %{BUG_FIELD_MAP()}};
- # Field names hiding in values instead of keys: changed_field, boolean
- # charts and axis names.
- if ($key =~ /^(field\d+-\d+-\d+|
- changed_field|
- (x|y|z)_axis_field)$
- /x) {
- $params->{$key}
- = $FIELD_NEW_TO_OLD->{$params->{$key}} || $params->{$key};
- }
- }
+ # Update values of various forms.
+ foreach my $key (keys %$params) {
- # Update field names
- foreach my $field (keys %$FIELD_NEW_TO_OLD) {
- if (defined $params->{$field}) {
- $params->{$FIELD_NEW_TO_OLD->{$field}} = delete $params->{$field};
- }
- }
-
- if (exists $params->{bug_id_type}) {
- $params->{bug_id_type}
- = BOOLEAN_TYPE_MAP->{$params->{bug_id_type}} || $params->{bug_id_type};
+ # First, search types. These are found in the value of any field ending
+ # _type, and the value of any field matching type\d-\d-\d.
+ if ($key =~ /^type(\d+)-(\d+)-(\d+)$|_type$/) {
+ $params->{$key} = BOOLEAN_TYPE_MAP->{$params->{$key}} || $params->{$key};
}
- # Time field names are screwy, and got reused. We can't put this mapping
- # in NEW2OLD as everything will go haywire. actual_time has to be queried
- # as work_time even though work_time is the submit-only field for _adding_
- # to actual_time, which can't be arbitrarily manipulated.
- if (defined $params->{work_time}) {
- $params->{actual_time} = delete $params->{work_time};
- }
-
- # Other convenience search ariables used by BzAPI
- my @field_ids = grep(/^f(\d+)$/, keys %$params);
- my $last_field_id = @field_ids ? max @field_ids + 1 : 1;
- foreach my $field (qw(setters.login_name requestees.login_name)) {
- if (my $value = delete $params->{$field}) {
- $params->{"f${last_field_id}"} = $FIELD_NEW_TO_OLD->{$field} || $field;
- $params->{"o${last_field_id}"} = 'equals';
- $params->{"v${last_field_id}"} = $value;
- $last_field_id++;
- }
- }
+ # Field names hiding in values instead of keys: changed_field, boolean
+ # charts and axis names.
+ if (
+ $key =~ /^(field\d+-\d+-\d+|
+ changed_field|
+ (x|y|z)_axis_field)$
+ /x
+ )
+ {
+ $params->{$key} = $FIELD_NEW_TO_OLD->{$params->{$key}} || $params->{$key};
+ }
+ }
+
+ # Update field names
+ foreach my $field (keys %$FIELD_NEW_TO_OLD) {
+ if (defined $params->{$field}) {
+ $params->{$FIELD_NEW_TO_OLD->{$field}} = delete $params->{$field};
+ }
+ }
+
+ if (exists $params->{bug_id_type}) {
+ $params->{bug_id_type}
+ = BOOLEAN_TYPE_MAP->{$params->{bug_id_type}} || $params->{bug_id_type};
+ }
+
+ # Time field names are screwy, and got reused. We can't put this mapping
+ # in NEW2OLD as everything will go haywire. actual_time has to be queried
+ # as work_time even though work_time is the submit-only field for _adding_
+ # to actual_time, which can't be arbitrarily manipulated.
+ if (defined $params->{work_time}) {
+ $params->{actual_time} = delete $params->{work_time};
+ }
+
+ # Other convenience search ariables used by BzAPI
+ my @field_ids = grep(/^f(\d+)$/, keys %$params);
+ my $last_field_id = @field_ids ? max @field_ids + 1 : 1;
+ foreach my $field (qw(setters.login_name requestees.login_name)) {
+ if (my $value = delete $params->{$field}) {
+ $params->{"f${last_field_id}"} = $FIELD_NEW_TO_OLD->{$field} || $field;
+ $params->{"o${last_field_id}"} = 'equals';
+ $params->{"v${last_field_id}"} = $value;
+ $last_field_id++;
+ }
+ }
}
sub create_bug_request {
- my ($params) = @_;
-
- # User roles such as assigned_to and qa_contact should be just the
- # email (login) of the user you want to set to.
- foreach my $field (qw(assigned_to qa_contact)) {
- if (exists $params->{$field}) {
- $params->{$field} = $params->{$field}->{name};
- }
- }
-
- # CC should just be a list of bugzilla logins
- if (exists $params->{cc}) {
- $params->{cc} = [ map { $_->{name} } @{ $params->{cc} } ];
- }
-
- # Comment
- if (exists $params->{comments}) {
- $params->{comment_is_private} = $params->{comments}->[0]->{is_private};
- $params->{description} = $params->{comments}->[0]->{text};
- delete $params->{comments};
- }
-
- # Some fields are not supported by Bugzilla::Bug->create but are supported
- # by Bugzilla::Bug->update :(
- my $cache = Bugzilla->request_cache->{bzapi_bug_create_extra} ||= {};
- foreach my $field (qw(remaining_time)) {
- next if !exists $params->{$field};
- $cache->{$field} = delete $params->{$field};
- }
-
- # remove username/password
- delete $params->{username};
- delete $params->{password};
+ my ($params) = @_;
+
+ # User roles such as assigned_to and qa_contact should be just the
+ # email (login) of the user you want to set to.
+ foreach my $field (qw(assigned_to qa_contact)) {
+ if (exists $params->{$field}) {
+ $params->{$field} = $params->{$field}->{name};
+ }
+ }
+
+ # CC should just be a list of bugzilla logins
+ if (exists $params->{cc}) {
+ $params->{cc} = [map { $_->{name} } @{$params->{cc}}];
+ }
+
+ # Comment
+ if (exists $params->{comments}) {
+ $params->{comment_is_private} = $params->{comments}->[0]->{is_private};
+ $params->{description} = $params->{comments}->[0]->{text};
+ delete $params->{comments};
+ }
+
+ # Some fields are not supported by Bugzilla::Bug->create but are supported
+ # by Bugzilla::Bug->update :(
+ my $cache = Bugzilla->request_cache->{bzapi_bug_create_extra} ||= {};
+ foreach my $field (qw(remaining_time)) {
+ next if !exists $params->{$field};
+ $cache->{$field} = delete $params->{$field};
+ }
+
+ # remove username/password
+ delete $params->{username};
+ delete $params->{password};
}
sub update_bug_request {
- my ($params) = @_;
-
- my $bug_id = ref $params->{ids} ? $params->{ids}->[0] : $params->{ids};
- my $bug = Bugzilla::Bug->check($bug_id);
-
- # Convert groups to proper add/remove lists
- if (exists $params->{groups}) {
- my @new_groups = map { $_->{name} } @{ $params->{groups} };
- my @old_groups = map { $_->name } @{ $bug->groups_in };
- my ($removed, $added) = diff_arrays(\@old_groups, \@new_groups);
- if (@$added || @$removed) {
- my $groups_data = {};
- $groups_data->{add} = $added if @$added;
- $groups_data->{remove} = $removed if @$removed;
- $params->{groups} = $groups_data;
- }
- else {
- delete $params->{groups};
- }
+ my ($params) = @_;
+
+ my $bug_id = ref $params->{ids} ? $params->{ids}->[0] : $params->{ids};
+ my $bug = Bugzilla::Bug->check($bug_id);
+
+ # Convert groups to proper add/remove lists
+ if (exists $params->{groups}) {
+ my @new_groups = map { $_->{name} } @{$params->{groups}};
+ my @old_groups = map { $_->name } @{$bug->groups_in};
+ my ($removed, $added) = diff_arrays(\@old_groups, \@new_groups);
+ if (@$added || @$removed) {
+ my $groups_data = {};
+ $groups_data->{add} = $added if @$added;
+ $groups_data->{remove} = $removed if @$removed;
+ $params->{groups} = $groups_data;
}
-
- # Other fields such as keywords, blocks depends_on
- # support 'set' which will make the list exactly what
- # the user passes in.
- foreach my $field (qw(blocks depends_on dependson keywords)) {
- if (exists $params->{$field}) {
- $params->{$field} = { set => $params->{$field} };
- }
+ else {
+ delete $params->{groups};
+ }
+ }
+
+ # Other fields such as keywords, blocks depends_on
+ # support 'set' which will make the list exactly what
+ # the user passes in.
+ foreach my $field (qw(blocks depends_on dependson keywords)) {
+ if (exists $params->{$field}) {
+ $params->{$field} = {set => $params->{$field}};
+ }
+ }
+
+ # User roles such as assigned_to and qa_contact should be just the
+ # email (login) of the user you want to change to. Also if defined
+ # but set to NULL then we reset them to default
+ foreach my $field (qw(assigned_to qa_contact)) {
+ if (exists $params->{$field}) {
+ if (!$params->{$field}) {
+ $params->{"reset_$field"} = 1;
+ delete $params->{$field};
+ }
+ else {
+ $params->{$field} = $params->{$field}->{name};
+ }
+ }
+ }
+
+ # CC is treated like groups in that we need 'add' and 'remove' keys
+ if (exists $params->{cc}) {
+ my $new_cc = [map { $_->{name} } @{$params->{cc}}];
+ my ($removed, $added) = diff_arrays($bug->cc, $new_cc);
+ if (@$added || @$removed) {
+ my $cc_data = {};
+ $cc_data->{add} = $added if @$added;
+ $cc_data->{remove} = $removed if @$removed;
+ $params->{cc} = $cc_data;
}
-
- # User roles such as assigned_to and qa_contact should be just the
- # email (login) of the user you want to change to. Also if defined
- # but set to NULL then we reset them to default
- foreach my $field (qw(assigned_to qa_contact)) {
- if (exists $params->{$field}) {
- if (!$params->{$field}) {
- $params->{"reset_$field"} = 1;
- delete $params->{$field};
- }
- else {
- $params->{$field} = $params->{$field}->{name};
- }
- }
+ else {
+ delete $params->{cc};
}
+ }
- # CC is treated like groups in that we need 'add' and 'remove' keys
- if (exists $params->{cc}) {
- my $new_cc = [ map { $_->{name} } @{ $params->{cc} } ];
- my ($removed, $added) = diff_arrays($bug->cc, $new_cc);
- if (@$added || @$removed) {
- my $cc_data = {};
- $cc_data->{add} = $added if @$added;
- $cc_data->{remove} = $removed if @$removed;
- $params->{cc} = $cc_data;
- }
- else {
- delete $params->{cc};
- }
+ # see_also is treated like groups in that we need 'add' and 'remove' keys
+ if (exists $params->{see_also}) {
+ my $old_see_also = [map { $_->name } @{$bug->see_also}];
+ my ($removed, $added) = diff_arrays($old_see_also, $params->{see_also});
+ if (@$added || @$removed) {
+ my $data = {};
+ $data->{add} = $added if @$added;
+ $data->{remove} = $removed if @$removed;
+ $params->{see_also} = $data;
}
-
- # see_also is treated like groups in that we need 'add' and 'remove' keys
- if (exists $params->{see_also}) {
- my $old_see_also = [ map { $_->name } @{ $bug->see_also } ];
- my ($removed, $added) = diff_arrays($old_see_also, $params->{see_also});
- if (@$added || @$removed) {
- my $data = {};
- $data->{add} = $added if @$added;
- $data->{remove} = $removed if @$removed;
- $params->{see_also} = $data;
- }
- else {
- delete $params->{see_also};
- }
+ else {
+ delete $params->{see_also};
}
+ }
- # BzAPI allows for adding comments by appending to the list of current
- # comments and passing the whole list back.
- # 1. If a comment id is specified, the user can update the comment privacy
- # 2. If no id is specified it is considered a new comment but only the last
- # one will be accepted.
- my %comment_is_private;
- foreach my $comment (@{ $params->{'comments'} }) {
- if (my $id = $comment->{'id'}) {
- # Existing comment; tweak privacy flags if necessary
- $comment_is_private{$id}
- = ($comment->{'is_private'} && $comment->{'is_private'} eq "true") ? 1 : 0;
- }
- else {
- # New comment to be added
- # If multiple new comments are specified, only the last one will be
- # added.
- $params->{comment} = {
- body => $comment->{text},
- is_private => ($comment->{'is_private'} &&
- $comment->{'is_private'} eq "true") ? 1 : 0
- };
- }
- }
- $params->{comment_is_private} = \%comment_is_private if %comment_is_private;
-
- # Remove setter and convert requestee to just name
- if (exists $params->{flags}) {
- foreach my $flag (@{ $params->{flags} }) {
- delete $flag->{setter}; # Always use logged in user
- if (exists $flag->{requestee} && ref $flag->{requestee}) {
- $flag->{requestee} = $flag->{requestee}->{name};
- }
- # If no flag id provided, assume it is new
- if (!exists $flag->{id}) {
- $flag->{new} = 1;
- }
- }
+ # BzAPI allows for adding comments by appending to the list of current
+ # comments and passing the whole list back.
+ # 1. If a comment id is specified, the user can update the comment privacy
+ # 2. If no id is specified it is considered a new comment but only the last
+ # one will be accepted.
+ my %comment_is_private;
+ foreach my $comment (@{$params->{'comments'}}) {
+ if (my $id = $comment->{'id'}) {
+
+ # Existing comment; tweak privacy flags if necessary
+ $comment_is_private{$id}
+ = ($comment->{'is_private'} && $comment->{'is_private'} eq "true") ? 1 : 0;
}
+ else {
+ # New comment to be added
+ # If multiple new comments are specified, only the last one will be
+ # added.
+ $params->{comment} = {
+ body => $comment->{text},
+ is_private => ($comment->{'is_private'} && $comment->{'is_private'} eq "true")
+ ? 1
+ : 0
+ };
+ }
+ }
+ $params->{comment_is_private} = \%comment_is_private if %comment_is_private;
+
+ # Remove setter and convert requestee to just name
+ if (exists $params->{flags}) {
+ foreach my $flag (@{$params->{flags}}) {
+ delete $flag->{setter}; # Always use logged in user
+ if (exists $flag->{requestee} && ref $flag->{requestee}) {
+ $flag->{requestee} = $flag->{requestee}->{name};
+ }
+
+ # If no flag id provided, assume it is new
+ if (!exists $flag->{id}) {
+ $flag->{new} = 1;
+ }
+ }
+ }
}
sub add_comment_request {
- my ($params) = @_;
- $params->{comment} = delete $params->{text} if $params->{text};
+ my ($params) = @_;
+ $params->{comment} = delete $params->{text} if $params->{text};
}
sub add_attachment_request {
- my ($params) = @_;
-
- # Bug.add_attachment uses 'summary' for description.
- if ($params->{description}) {
- $params->{summary} = $params->{description};
- delete $params->{description};
- }
-
- # Remove setter and convert requestee to just name
- if (exists $params->{flags}) {
- foreach my $flag (@{ $params->{flags} }) {
- delete $flag->{setter}; # Always use logged in user
- if (exists $flag->{requestee} && ref $flag->{requestee}) {
- $flag->{requestee} = $flag->{requestee}->{name};
- }
- }
- }
-
- # Add comment if one is provided
- if (exists $params->{comments} && scalar @{ $params->{comments} }) {
- $params->{comment} = $params->{comments}->[0]->{text};
- delete $params->{comments};
- }
+ my ($params) = @_;
+
+ # Bug.add_attachment uses 'summary' for description.
+ if ($params->{description}) {
+ $params->{summary} = $params->{description};
+ delete $params->{description};
+ }
+
+ # Remove setter and convert requestee to just name
+ if (exists $params->{flags}) {
+ foreach my $flag (@{$params->{flags}}) {
+ delete $flag->{setter}; # Always use logged in user
+ if (exists $flag->{requestee} && ref $flag->{requestee}) {
+ $flag->{requestee} = $flag->{requestee}->{name};
+ }
+ }
+ }
+
+ # Add comment if one is provided
+ if (exists $params->{comments} && scalar @{$params->{comments}}) {
+ $params->{comment} = $params->{comments}->[0]->{text};
+ delete $params->{comments};
+ }
}
sub update_attachment_request {
- my ($params) = @_;
-
- # Stash away for midair checking later
- if ($params->{last_change_time}) {
- my $stash = Bugzilla->request_cache->{bzapi_stash} ||= {};
- $stash->{last_change_time} = delete $params->{last_change_time};
- }
-
- # Immutable values
- foreach my $key (qw(attacher bug_id bug_ref creation_time
- encoding id ref size update_token)) {
- delete $params->{$key};
- }
-
- # Convert setter and requestee to standard values
- if (exists $params->{flags}) {
- foreach my $flag (@{ $params->{flags} }) {
- delete $flag->{setter}; # Always use logged in user
- if (exists $flag->{requestee} && ref $flag->{requestee}) {
- $flag->{requestee} = $flag->{requestee}->{name};
- }
- }
- }
-
- # Add comment if one is provided
- if (exists $params->{comments} && scalar @{ $params->{comments} }) {
- $params->{comment} = $params->{comments}->[0]->{text};
- delete $params->{comments};
- }
+ my ($params) = @_;
+
+ # Stash away for midair checking later
+ if ($params->{last_change_time}) {
+ my $stash = Bugzilla->request_cache->{bzapi_stash} ||= {};
+ $stash->{last_change_time} = delete $params->{last_change_time};
+ }
+
+ # Immutable values
+ foreach my $key (
+ qw(attacher bug_id bug_ref creation_time
+ encoding id ref size update_token)
+ )
+ {
+ delete $params->{$key};
+ }
+
+ # Convert setter and requestee to standard values
+ if (exists $params->{flags}) {
+ foreach my $flag (@{$params->{flags}}) {
+ delete $flag->{setter}; # Always use logged in user
+ if (exists $flag->{requestee} && ref $flag->{requestee}) {
+ $flag->{requestee} = $flag->{requestee}->{name};
+ }
+ }
+ }
+
+ # Add comment if one is provided
+ if (exists $params->{comments} && scalar @{$params->{comments}}) {
+ $params->{comment} = $params->{comments}->[0]->{text};
+ delete $params->{comments};
+ }
}
#########################
@@ -633,237 +614,247 @@ sub update_attachment_request {
#########################
sub search_bugs_response {
- my ($result, $response) = @_;
- my $cache = Bugzilla->request_cache;
- my $params = Bugzilla->input_params;
+ my ($result, $response) = @_;
+ my $cache = Bugzilla->request_cache;
+ my $params = Bugzilla->input_params;
- return if !exists $$result->{bugs};
+ return if !exists $$result->{bugs};
- my $bug_objs = $cache->{bzapi_search_bugs};
+ my $bug_objs = $cache->{bzapi_search_bugs};
- my @fixed_bugs;
- my $stash = {};
- foreach my $bug_data (@{$$result->{bugs}}) {
- my $bug_obj = shift @$bug_objs;
- my $fixed = fix_bug($bug_data, $bug_obj, $stash);
+ my @fixed_bugs;
+ my $stash = {};
+ foreach my $bug_data (@{$$result->{bugs}}) {
+ my $bug_obj = shift @$bug_objs;
+ my $fixed = fix_bug($bug_data, $bug_obj, $stash);
- # CC count and Dupe count
- if (filter_wants_nocache($params, 'cc_count')) {
- $fixed->{cc_count} = scalar @{ $bug_obj->cc }
- if $bug_obj->cc;
- }
- if (filter_wants_nocache($params, 'dupe_count')) {
- $fixed->{dupe_count} = scalar @{ $bug_obj->duplicate_ids }
- if $bug_obj->duplicate_ids;
- }
-
- push(@fixed_bugs, $fixed);
+ # CC count and Dupe count
+ if (filter_wants_nocache($params, 'cc_count')) {
+ $fixed->{cc_count} = scalar @{$bug_obj->cc} if $bug_obj->cc;
+ }
+ if (filter_wants_nocache($params, 'dupe_count')) {
+ $fixed->{dupe_count} = scalar @{$bug_obj->duplicate_ids}
+ if $bug_obj->duplicate_ids;
}
- $$result->{bugs} = \@fixed_bugs;
+ push(@fixed_bugs, $fixed);
+ }
+
+ $$result->{bugs} = \@fixed_bugs;
}
sub create_bug_response {
- my ($result, $response) = @_;
- my $rpc = Bugzilla->request_cache->{bzapi_rpc};
+ my ($result, $response) = @_;
+ my $rpc = Bugzilla->request_cache->{bzapi_rpc};
- return if !exists $$result->{id};
- my $bug_id = $$result->{id};
+ return if !exists $$result->{id};
+ my $bug_id = $$result->{id};
- $$result->{ref} = $rpc->type('string', ref_urlbase() . "/bug/$bug_id");
- $response->code(STATUS_CREATED);
+ $$result->{ref} = $rpc->type('string', ref_urlbase() . "/bug/$bug_id");
+ $response->code(STATUS_CREATED);
}
sub get_bug_response {
- my ($result) = @_;
- my $rpc = Bugzilla->request_cache->{bzapi_rpc};
+ my ($result) = @_;
+ my $rpc = Bugzilla->request_cache->{bzapi_rpc};
- return if !exists $$result->{bugs};
- my $bug_data = $$result->{bugs}->[0];
+ return if !exists $$result->{bugs};
+ my $bug_data = $$result->{bugs}->[0];
- my $bug_id = $rpc->bz_rest_params->{ids}->[0];
- my $bug_obj = Bugzilla::Bug->check($bug_id);
- my $fixed = fix_bug($bug_data, $bug_obj);
+ my $bug_id = $rpc->bz_rest_params->{ids}->[0];
+ my $bug_obj = Bugzilla::Bug->check($bug_id);
+ my $fixed = fix_bug($bug_data, $bug_obj);
- $$result = $fixed;
+ $$result = $fixed;
}
sub update_bug_response {
- my ($result) = @_;
- return if !exists $$result->{bugs}
- || !scalar @{$$result->{bugs}};
- $$result = { ok => 1 };
+ my ($result) = @_;
+ return if !exists $$result->{bugs} || !scalar @{$$result->{bugs}};
+ $$result = {ok => 1};
}
# Get all comments for a bug
sub get_comments_response {
- my ($result) = @_;
- my $rpc = Bugzilla->request_cache->{bzapi_rpc};
- my $params = Bugzilla->input_params;
+ my ($result) = @_;
+ my $rpc = Bugzilla->request_cache->{bzapi_rpc};
+ my $params = Bugzilla->input_params;
- return if !exists $$result->{bugs};
+ return if !exists $$result->{bugs};
- my $bug_id = $rpc->bz_rest_params->{ids}->[0];
- my $bug = Bugzilla::Bug->check($bug_id);
+ my $bug_id = $rpc->bz_rest_params->{ids}->[0];
+ my $bug = Bugzilla::Bug->check($bug_id);
- my $comment_objs = $bug->comments({ order => 'oldest_to_newest',
- after => $params->{new_since} });
- my @filtered_comment_objs;
- foreach my $comment (@$comment_objs) {
- next if $comment->is_private && !Bugzilla->user->is_insider;
- push(@filtered_comment_objs, $comment);
- }
+ my $comment_objs
+ = $bug->comments({order => 'oldest_to_newest', after => $params->{new_since}
+ });
+ my @filtered_comment_objs;
+ foreach my $comment (@$comment_objs) {
+ next if $comment->is_private && !Bugzilla->user->is_insider;
+ push(@filtered_comment_objs, $comment);
+ }
- my $comments_data = $$result->{bugs}->{$bug_id}->{comments};
+ my $comments_data = $$result->{bugs}->{$bug_id}->{comments};
- my @fixed_comments;
- foreach my $comment_data (@$comments_data) {
- my $comment_obj = shift @filtered_comment_objs;
- my $fixed = fix_comment($comment_data, $comment_obj);
+ my @fixed_comments;
+ foreach my $comment_data (@$comments_data) {
+ my $comment_obj = shift @filtered_comment_objs;
+ my $fixed = fix_comment($comment_data, $comment_obj);
- if (exists $fixed->{creator}) {
- # /bug/<ID>/comment returns full login for creator but not for /bug/<ID>?include_fields=comments :(
- $fixed->{creator}->{name} = $rpc->type('string', $comment_obj->author->login);
- # /bug/<ID>/comment does not return real_name for creator but returns ref
- $fixed->{creator}->{'ref'} = $rpc->type('string', ref_urlbase() . "/user/" . $comment_obj->author->login);
- delete $fixed->{creator}->{real_name};
- }
+ if (exists $fixed->{creator}) {
+
+# /bug/<ID>/comment returns full login for creator but not for /bug/<ID>?include_fields=comments :(
+ $fixed->{creator}->{name} = $rpc->type('string', $comment_obj->author->login);
- push(@fixed_comments, filter($params, $fixed));
+ # /bug/<ID>/comment does not return real_name for creator but returns ref
+ $fixed->{creator}->{'ref'} = $rpc->type('string',
+ ref_urlbase() . "/user/" . $comment_obj->author->login);
+ delete $fixed->{creator}->{real_name};
}
- $$result = { comments => \@fixed_comments };
+ push(@fixed_comments, filter($params, $fixed));
+ }
+
+ $$result = {comments => \@fixed_comments};
}
# Format the return response on successful comment creation
sub add_comment_response {
- my ($result, $response) = @_;
- my $rpc = Bugzilla->request_cache->{bzapi_rpc};
+ my ($result, $response) = @_;
+ my $rpc = Bugzilla->request_cache->{bzapi_rpc};
- return if !exists $$result->{id};
- my $bug_id = $rpc->bz_rest_params->{id};
+ return if !exists $$result->{id};
+ my $bug_id = $rpc->bz_rest_params->{id};
- $$result = { ref => $rpc->type('string', ref_urlbase() . "/bug/$bug_id/comment") };
- $response->code(STATUS_CREATED);
+ $$result
+ = {ref => $rpc->type('string', ref_urlbase() . "/bug/$bug_id/comment")};
+ $response->code(STATUS_CREATED);
}
# Get the history for a bug
sub get_history_response {
- my ($result) = @_;
- my $params = Bugzilla->input_params;
+ my ($result) = @_;
+ my $params = Bugzilla->input_params;
- return if !exists $$result->{bugs};
- my $history = $$result->{bugs}->[0]->{history};
+ return if !exists $$result->{bugs};
+ my $history = $$result->{bugs}->[0]->{history};
- my @new_history;
- foreach my $changeset (@$history) {
- $changeset = fix_changeset($changeset);
- push(@new_history, filter($params, $changeset));
- }
+ my @new_history;
+ foreach my $changeset (@$history) {
+ $changeset = fix_changeset($changeset);
+ push(@new_history, filter($params, $changeset));
+ }
- $$result = { history => \@new_history };
+ $$result = {history => \@new_history};
}
# Get all attachments for a bug
sub get_attachments_response {
- my ($result) = @_;
- my $rpc = Bugzilla->request_cache->{bzapi_rpc};
- my $params = Bugzilla->input_params;
-
- return if !exists $$result->{bugs};
- my $bug_id = $rpc->bz_rest_params->{ids}->[0];
- my $bug = Bugzilla::Bug->check($bug_id);
- my $attachment_objs = $bug->attachments;
-
- my $attachments_data = $$result->{bugs}->{$bug_id};
-
- my @fixed_attachments;
- foreach my $attachment (@$attachments_data) {
- my $attachment_obj = shift @$attachment_objs;
- my $fixed = fix_attachment($attachment, $attachment_obj);
-
- if ((filter_wants_nocache($params, 'data', 'extra')
- || filter_wants_nocache($params, 'encoding', 'extra')
- || $params->{attachmentdata}))
- {
- if (!$fixed->{data}) {
- $fixed->{data} = $rpc->type('base64', $attachment_obj->data);
- $fixed->{encoding} = $rpc->type('string', 'base64');
- }
- }
- else {
- delete $fixed->{data};
- delete $fixed->{encoding};
- }
-
- push(@fixed_attachments, filter($params, $fixed));
+ my ($result) = @_;
+ my $rpc = Bugzilla->request_cache->{bzapi_rpc};
+ my $params = Bugzilla->input_params;
+
+ return if !exists $$result->{bugs};
+ my $bug_id = $rpc->bz_rest_params->{ids}->[0];
+ my $bug = Bugzilla::Bug->check($bug_id);
+ my $attachment_objs = $bug->attachments;
+
+ my $attachments_data = $$result->{bugs}->{$bug_id};
+
+ my @fixed_attachments;
+ foreach my $attachment (@$attachments_data) {
+ my $attachment_obj = shift @$attachment_objs;
+ my $fixed = fix_attachment($attachment, $attachment_obj);
+
+ if ((
+ filter_wants_nocache($params, 'data', 'extra')
+ || filter_wants_nocache($params, 'encoding', 'extra')
+ || $params->{attachmentdata}
+ ))
+ {
+ if (!$fixed->{data}) {
+ $fixed->{data} = $rpc->type('base64', $attachment_obj->data);
+ $fixed->{encoding} = $rpc->type('string', 'base64');
+ }
+ }
+ else {
+ delete $fixed->{data};
+ delete $fixed->{encoding};
}
- $$result = { attachments => \@fixed_attachments };
+ push(@fixed_attachments, filter($params, $fixed));
+ }
+
+ $$result = {attachments => \@fixed_attachments};
}
# Format the return response on successful attachment creation
sub add_attachment_response {
- my ($result, $response) = @_;
- my $rpc = Bugzilla->request_cache->{bzapi_rpc};
+ my ($result, $response) = @_;
+ my $rpc = Bugzilla->request_cache->{bzapi_rpc};
- my ($attach_id) = keys %{ $$result->{attachments} };
+ my ($attach_id) = keys %{$$result->{attachments}};
- $$result = { ref => $rpc->type('string', ref_urlbase() . "/attachment/$attach_id"), id => $attach_id };
- $response->code(STATUS_CREATED);
+ $$result = {
+ ref => $rpc->type('string', ref_urlbase() . "/attachment/$attach_id"),
+ id => $attach_id
+ };
+ $response->code(STATUS_CREATED);
}
# Update an attachment's metadata
sub update_attachment_response {
- my ($result) = @_;
- $$result = { ok => 1 };
+ my ($result) = @_;
+ $$result = {ok => 1};
}
# Get a single attachment by attachment_id
sub get_attachment_response {
- my ($result) = @_;
- my $rpc = Bugzilla->request_cache->{bzapi_rpc};
- my $params = Bugzilla->input_params;
-
- return if !exists $$result->{attachments};
- my $attach_id = $rpc->bz_rest_params->{attachment_ids}->[0];
- my $attachment_data = $$result->{attachments}->{$attach_id};
- my $attachment_obj = Bugzilla::Attachment->new($attach_id);
- my $fixed = fix_attachment($attachment_data, $attachment_obj);
-
- if ((filter_wants_nocache($params, 'data', 'extra')
- || filter_wants_nocache($params, 'encoding', 'extra')
- || $params->{attachmentdata}))
- {
- if (!$fixed->{data}) {
- $fixed->{data} = $rpc->type('base64', $attachment_obj->data);
- $fixed->{encoding} = $rpc->type('string', 'base64');
- }
- }
- else {
- delete $fixed->{data};
- delete $fixed->{encoding};
- }
-
- $fixed = filter($params, $fixed);
-
- $$result = $fixed;
+ my ($result) = @_;
+ my $rpc = Bugzilla->request_cache->{bzapi_rpc};
+ my $params = Bugzilla->input_params;
+
+ return if !exists $$result->{attachments};
+ my $attach_id = $rpc->bz_rest_params->{attachment_ids}->[0];
+ my $attachment_data = $$result->{attachments}->{$attach_id};
+ my $attachment_obj = Bugzilla::Attachment->new($attach_id);
+ my $fixed = fix_attachment($attachment_data, $attachment_obj);
+
+ if ((
+ filter_wants_nocache($params, 'data', 'extra')
+ || filter_wants_nocache($params, 'encoding', 'extra')
+ || $params->{attachmentdata}
+ ))
+ {
+ if (!$fixed->{data}) {
+ $fixed->{data} = $rpc->type('base64', $attachment_obj->data);
+ $fixed->{encoding} = $rpc->type('string', 'base64');
+ }
+ }
+ else {
+ delete $fixed->{data};
+ delete $fixed->{encoding};
+ }
+
+ $fixed = filter($params, $fixed);
+
+ $$result = $fixed;
}
# Get a list of flags for a bug
sub get_bug_flags_response {
- my ($result) = @_;
- my $params = Bugzilla->input_params;
+ my ($result) = @_;
+ my $params = Bugzilla->input_params;
- return if !exists $$result->{bugs};
- my $flags = $$result->{bugs}->[0]->{flags};
+ return if !exists $$result->{bugs};
+ my $flags = $$result->{bugs}->[0]->{flags};
- my @new_flags;
- foreach my $flag (@$flags) {
- push(@new_flags, fix_flag($flag));
- }
+ my @new_flags;
+ foreach my $flag (@$flags) {
+ push(@new_flags, fix_flag($flag));
+ }
- $$result = { flags => \@new_flags };
+ $$result = {flags => \@new_flags};
}
1;
diff --git a/extensions/BzAPI/lib/Resources/Bugzilla.pm b/extensions/BzAPI/lib/Resources/Bugzilla.pm
index 6e350d839..23d423d5a 100644
--- a/extensions/BzAPI/lib/Resources/Bugzilla.pm
+++ b/extensions/BzAPI/lib/Resources/Bugzilla.pm
@@ -29,124 +29,118 @@ use Digest::MD5 qw(md5_base64);
#########################
BEGIN {
- require Bugzilla::WebService::Bugzilla;
- *Bugzilla::WebService::Bugzilla::get_configuration = \&get_configuration;
- *Bugzilla::WebService::Bugzilla::get_empty = \&get_empty;
+ require Bugzilla::WebService::Bugzilla;
+ *Bugzilla::WebService::Bugzilla::get_configuration = \&get_configuration;
+ *Bugzilla::WebService::Bugzilla::get_empty = \&get_empty;
}
sub rest_handlers {
- my $rest_handlers = [
- qr{^/$}, {
- GET => {
- resource => {
- method => 'get_empty'
- }
- }
- },
- qr{^/configuration$}, {
- GET => {
- resource => {
- method => 'get_configuration'
- }
- }
- }
- ];
- return $rest_handlers;
+ my $rest_handlers = [
+ qr{^/$}, {GET => {resource => {method => 'get_empty'}}},
+ qr{^/configuration$}, {GET => {resource => {method => 'get_configuration'}}}
+ ];
+ return $rest_handlers;
}
sub get_configuration {
- my ($self) = @_;
- my $user = Bugzilla->user;
- my $params = Bugzilla->input_params;
-
- my $can_cache = !exists $params->{product} && !exists $params->{flags};
- my $cache_key = 'bzapi_get_configuration';
-
- if ($can_cache) {
- my $result = Bugzilla->memcached->get_config({key => $cache_key});
- return $result if defined $result;
- }
-
- # Get data from the shadow DB as they don't change very often.
- Bugzilla->switch_to_shadow_db;
-
- # Pass a bunch of Bugzilla configuration to the templates.
- my $vars = {};
- $vars->{'priority'} = get_legal_field_values('priority');
- $vars->{'severity'} = get_legal_field_values('bug_severity');
- $vars->{'platform'} = get_legal_field_values('rep_platform');
- $vars->{'op_sys'} = get_legal_field_values('op_sys');
- $vars->{'keyword'} = [ map($_->name, Bugzilla::Keyword->get_all) ];
- $vars->{'resolution'} = get_legal_field_values('resolution');
- $vars->{'status'} = get_legal_field_values('bug_status');
- $vars->{'custom_fields'} =
- [ grep {$_->is_select} Bugzilla->active_custom_fields ];
-
- # Include a list of product objects.
- if ($params->{'product'}) {
- my @products = $params->{'product'};
- foreach my $product_name (@products) {
- my $product = new Bugzilla::Product({ name => $product_name });
- if ($product && $user->can_see_product($product->name)) {
- push (@{$vars->{'products'}}, $product);
- }
- }
- } else {
- $vars->{'products'} = $user->get_selectable_products;
- }
-
- # We set the 2nd argument to 1 to also preload flag types.
- Bugzilla::Product::preload($vars->{'products'}, 1, { is_active => 1 });
-
- # Allow consumers to specify whether or not they want flag data.
- if (defined $params->{'flags'}) {
- $vars->{'show_flags'} = $params->{'flags'};
- }
- else {
- # We default to sending flag data.
- $vars->{'show_flags'} = 1;
- }
-
- # Create separate lists of open versus resolved statuses. This should really
- # be made part of the configuration.
- my @open_status;
- my @closed_status;
- foreach my $status (@{$vars->{'status'}}) {
- is_open_state($status) ? push(@open_status, $status)
- : push(@closed_status, $status);
+ my ($self) = @_;
+ my $user = Bugzilla->user;
+ my $params = Bugzilla->input_params;
+
+ my $can_cache = !exists $params->{product} && !exists $params->{flags};
+ my $cache_key = 'bzapi_get_configuration';
+
+ if ($can_cache) {
+ my $result = Bugzilla->memcached->get_config({key => $cache_key});
+ return $result if defined $result;
+ }
+
+ # Get data from the shadow DB as they don't change very often.
+ Bugzilla->switch_to_shadow_db;
+
+ # Pass a bunch of Bugzilla configuration to the templates.
+ my $vars = {};
+ $vars->{'priority'} = get_legal_field_values('priority');
+ $vars->{'severity'} = get_legal_field_values('bug_severity');
+ $vars->{'platform'} = get_legal_field_values('rep_platform');
+ $vars->{'op_sys'} = get_legal_field_values('op_sys');
+ $vars->{'keyword'} = [map($_->name, Bugzilla::Keyword->get_all)];
+ $vars->{'resolution'} = get_legal_field_values('resolution');
+ $vars->{'status'} = get_legal_field_values('bug_status');
+ $vars->{'custom_fields'}
+ = [grep { $_->is_select } Bugzilla->active_custom_fields];
+
+ # Include a list of product objects.
+ if ($params->{'product'}) {
+ my @products = $params->{'product'};
+ foreach my $product_name (@products) {
+ my $product = new Bugzilla::Product({name => $product_name});
+ if ($product && $user->can_see_product($product->name)) {
+ push(@{$vars->{'products'}}, $product);
+ }
}
- $vars->{'open_status'} = \@open_status;
- $vars->{'closed_status'} = \@closed_status;
-
- # Generate a list of fields that can be queried.
- my @fields = @{Bugzilla::Field->match({obsolete => 0})};
- # Exclude fields the user cannot query.
- if (!Bugzilla->user->is_timetracker) {
- @fields = grep { $_->name !~ /^(estimated_time|remaining_time|work_time|percentage_complete|deadline)$/ } @fields;
- }
- $vars->{'field'} = \@fields;
-
- my $json;
- Bugzilla->template->process('config.json.tmpl', $vars, \$json);
- if ($json) {
- my $result = $self->json->decode($json);
- if ($can_cache) {
- Bugzilla->memcached->set_config({key => $cache_key, data => $result});
- }
- return $result;
- }
- else {
- return {};
+ }
+ else {
+ $vars->{'products'} = $user->get_selectable_products;
+ }
+
+ # We set the 2nd argument to 1 to also preload flag types.
+ Bugzilla::Product::preload($vars->{'products'}, 1, {is_active => 1});
+
+ # Allow consumers to specify whether or not they want flag data.
+ if (defined $params->{'flags'}) {
+ $vars->{'show_flags'} = $params->{'flags'};
+ }
+ else {
+ # We default to sending flag data.
+ $vars->{'show_flags'} = 1;
+ }
+
+ # Create separate lists of open versus resolved statuses. This should really
+ # be made part of the configuration.
+ my @open_status;
+ my @closed_status;
+ foreach my $status (@{$vars->{'status'}}) {
+ is_open_state($status)
+ ? push(@open_status, $status)
+ : push(@closed_status, $status);
+ }
+ $vars->{'open_status'} = \@open_status;
+ $vars->{'closed_status'} = \@closed_status;
+
+ # Generate a list of fields that can be queried.
+ my @fields = @{Bugzilla::Field->match({obsolete => 0})};
+
+ # Exclude fields the user cannot query.
+ if (!Bugzilla->user->is_timetracker) {
+ @fields = grep {
+ $_->name
+ !~ /^(estimated_time|remaining_time|work_time|percentage_complete|deadline)$/
+ } @fields;
+ }
+ $vars->{'field'} = \@fields;
+
+ my $json;
+ Bugzilla->template->process('config.json.tmpl', $vars, \$json);
+ if ($json) {
+ my $result = $self->json->decode($json);
+ if ($can_cache) {
+ Bugzilla->memcached->set_config({key => $cache_key, data => $result});
}
+ return $result;
+ }
+ else {
+ return {};
+ }
}
sub get_empty {
- my ($self) = @_;
- return {
- ref => $self->type('string', Bugzilla->localconfig->{urlbase} . "bzapi/"),
- documentation => $self->type('string', BZAPI_DOC),
- version => $self->type('string', BUGZILLA_VERSION)
- };
+ my ($self) = @_;
+ return {
+ ref => $self->type('string', Bugzilla->localconfig->{urlbase} . "bzapi/"),
+ documentation => $self->type('string', BZAPI_DOC),
+ version => $self->type('string', BUGZILLA_VERSION)
+ };
}
1;
diff --git a/extensions/BzAPI/lib/Resources/User.pm b/extensions/BzAPI/lib/Resources/User.pm
index 550a61d28..7a7a183a9 100644
--- a/extensions/BzAPI/lib/Resources/User.pm
+++ b/extensions/BzAPI/lib/Resources/User.pm
@@ -14,67 +14,58 @@ use warnings;
use Bugzilla::Extension::BzAPI::Util;
sub rest_handlers {
- my $rest_handlers = [
- qr{/user$}, {
- GET => {
- response => \&get_users,
- },
- },
- qr{/user/([^/]+)$}, {
- GET => {
- response => \&get_user,
- },
- }
- ];
- return $rest_handlers;
+ my $rest_handlers = [
+ qr{/user$}, {GET => {response => \&get_users,},},
+ qr{/user/([^/]+)$}, {GET => {response => \&get_user,},}
+ ];
+ return $rest_handlers;
}
sub get_users {
- my ($result) = @_;
- my $rpc = Bugzilla->request_cache->{bzapi_rpc};
- my $params = Bugzilla->input_params;
+ my ($result) = @_;
+ my $rpc = Bugzilla->request_cache->{bzapi_rpc};
+ my $params = Bugzilla->input_params;
- return if !exists $$result->{users};
+ return if !exists $$result->{users};
- my @users;
- foreach my $user (@{$$result->{users}}) {
- my $object = Bugzilla::User->new(
- { id => $user->{id}, cache => 1 });
+ my @users;
+ foreach my $user (@{$$result->{users}}) {
+ my $object = Bugzilla::User->new({id => $user->{id}, cache => 1});
- $user = fix_user($user, $object);
+ $user = fix_user($user, $object);
- # Use userid instead of email for 'ref' for /user calls
- $user->{'ref'} = $rpc->type('string', ref_urlbase . "/user/" . $object->id);
+ # Use userid instead of email for 'ref' for /user calls
+ $user->{'ref'} = $rpc->type('string', ref_urlbase . "/user/" . $object->id);
- # Emails are not filtered even if user is not logged in
- $user->{name} = $rpc->type('string', $object->login);
+ # Emails are not filtered even if user is not logged in
+ $user->{name} = $rpc->type('string', $object->login);
- push(@users, filter($params, $user));
- }
+ push(@users, filter($params, $user));
+ }
- $$result->{users} = \@users;
+ $$result->{users} = \@users;
}
sub get_user {
- my ($result) = @_;
- my $rpc = Bugzilla->request_cache->{bzapi_rpc};
- my $params = Bugzilla->input_params;
+ my ($result) = @_;
+ my $rpc = Bugzilla->request_cache->{bzapi_rpc};
+ my $params = Bugzilla->input_params;
- return if !exists $$result->{users};
- my $user = $$result->{users}->[0] || return;
- my $object = Bugzilla::User->new({ id => $user->{id}, cache => 1 });
+ return if !exists $$result->{users};
+ my $user = $$result->{users}->[0] || return;
+ my $object = Bugzilla::User->new({id => $user->{id}, cache => 1});
- $user = fix_user($user, $object);
+ $user = fix_user($user, $object);
- # Use userid instead of email for 'ref' for /user calls
- $user->{'ref'} = $rpc->type('string', ref_urlbase . "/user/" . $object->id);
+ # Use userid instead of email for 'ref' for /user calls
+ $user->{'ref'} = $rpc->type('string', ref_urlbase . "/user/" . $object->id);
- # Emails are not filtered even if user is not logged in
- $user->{name} = $rpc->type('string', $object->login);
+ # Emails are not filtered even if user is not logged in
+ $user->{name} = $rpc->type('string', $object->login);
- $user = filter($params, $user);
+ $user = filter($params, $user);
- $$result = $user;
+ $$result = $user;
}
1;
diff --git a/extensions/BzAPI/lib/Util.pm b/extensions/BzAPI/lib/Util.pm
index d50679a6c..6f7e2c025 100644
--- a/extensions/BzAPI/lib/Util.pm
+++ b/extensions/BzAPI/lib/Util.pm
@@ -28,425 +28,437 @@ use MIME::Base64;
use base qw(Exporter);
our @EXPORT = qw(
- ref_urlbase
- fix_bug
- fix_user
- fix_flag
- fix_comment
- fix_changeset
- fix_attachment
- filter_wants_nocache
- filter
- fix_credentials
- filter_email
+ ref_urlbase
+ fix_bug
+ fix_user
+ fix_flag
+ fix_comment
+ fix_changeset
+ fix_attachment
+ filter_wants_nocache
+ filter
+ fix_credentials
+ filter_email
);
# Return an URL base appropriate for constructing a ref link
# normally required by REST API calls.
sub ref_urlbase {
- return Bugzilla->localconfig->{urlbase} . "bzapi";
+ return Bugzilla->localconfig->{urlbase} . "bzapi";
}
# convert certain fields within a bug object
# from a simple scalar value to their respective objects
sub fix_bug {
- my ($data, $bug, $stash) = @_;
- my $dbh = $stash->{dbh} //= Bugzilla->dbh;
- my $params = $stash->{params} //= Bugzilla->input_params;
- my $rpc = $stash->{rpc} //= Bugzilla->request_cache->{bzapi_rpc};
- my $method = $stash->{method} //= Bugzilla->request_cache->{bzapi_rpc_method};
-
- $bug = ref $bug ? $bug : Bugzilla::Bug->check($bug || $data->{id});
-
- # Add REST API reference to the individual bug
- if ($stash->{wants_ref} //= filter_wants_nocache($params, 'ref')) {
- $data->{'ref'} = ref_urlbase() . "/bug/" . $bug->id;
+ my ($data, $bug, $stash) = @_;
+ my $dbh = $stash->{dbh} //= Bugzilla->dbh;
+ my $params = $stash->{params} //= Bugzilla->input_params;
+ my $rpc = $stash->{rpc} //= Bugzilla->request_cache->{bzapi_rpc};
+ my $method = $stash->{method} //= Bugzilla->request_cache->{bzapi_rpc_method};
+
+ $bug = ref $bug ? $bug : Bugzilla::Bug->check($bug || $data->{id});
+
+ # Add REST API reference to the individual bug
+ if ($stash->{wants_ref} //= filter_wants_nocache($params, 'ref')) {
+ $data->{'ref'} = ref_urlbase() . "/bug/" . $bug->id;
+ }
+
+ # User fields
+ foreach my $field (USER_FIELDS) {
+ next if !exists $data->{$field};
+ if ($field eq 'cc') {
+ my @new_cc;
+ foreach my $cc (@{$bug->cc_users}) {
+ my $cc_data = {name => filter_email($cc->email)};
+ push(@new_cc, fix_user($cc_data, $cc));
+ }
+ $data->{$field} = \@new_cc;
}
-
- # User fields
- foreach my $field (USER_FIELDS) {
- next if !exists $data->{$field};
- if ($field eq 'cc') {
- my @new_cc;
- foreach my $cc (@{ $bug->cc_users }) {
- my $cc_data = { name => filter_email($cc->email) };
- push(@new_cc, fix_user($cc_data, $cc));
- }
- $data->{$field} = \@new_cc;
- }
- else {
- my $field_name = $field;
- if ($field eq 'creator') {
- $field_name = 'reporter';
- }
- $data->{$field}
- = fix_user($data->{"${field}_detail"}, $bug->$field_name);
- delete $data->{$field}->{id};
- delete $data->{$field}->{email};
- $data->{$field} = filter($params, $data->{$field}, undef, $field);
- }
-
- # Get rid of extra detail hash if exists since redundant
- delete $data->{"${field}_detail"} if exists $data->{"${field}_detail"};
+ else {
+ my $field_name = $field;
+ if ($field eq 'creator') {
+ $field_name = 'reporter';
+ }
+ $data->{$field} = fix_user($data->{"${field}_detail"}, $bug->$field_name);
+ delete $data->{$field}->{id};
+ delete $data->{$field}->{email};
+ $data->{$field} = filter($params, $data->{$field}, undef, $field);
}
- # Groups
- if ($stash->{wants_groups} //= filter_wants_nocache($params, 'groups')) {
- my @new_groups;
- foreach my $group (@{ $data->{groups} }) {
- if (my $object = Bugzilla::Group->new({ name => $group, cache => 1 })) {
- $group = {
- id => $rpc->type('int', $object->id),
- name => $rpc->type('string', $object->name),
- };
- }
- push(@new_groups, $group);
- }
- $data->{groups} = \@new_groups;
+ # Get rid of extra detail hash if exists since redundant
+ delete $data->{"${field}_detail"} if exists $data->{"${field}_detail"};
+ }
+
+ # Groups
+ if ($stash->{wants_groups} //= filter_wants_nocache($params, 'groups')) {
+ my @new_groups;
+ foreach my $group (@{$data->{groups}}) {
+ if (my $object = Bugzilla::Group->new({name => $group, cache => 1})) {
+ $group = {
+ id => $rpc->type('int', $object->id),
+ name => $rpc->type('string', $object->name),
+ };
+ }
+ push(@new_groups, $group);
}
-
- # Flags
- if (exists $data->{flags}) {
- my @new_flags;
- foreach my $flag (@{ $data->{flags} }) {
- push(@new_flags, fix_flag($flag));
- }
- $data->{flags} = \@new_flags;
+ $data->{groups} = \@new_groups;
+ }
+
+ # Flags
+ if (exists $data->{flags}) {
+ my @new_flags;
+ foreach my $flag (@{$data->{flags}}) {
+ push(@new_flags, fix_flag($flag));
}
-
- # Attachment metadata is included by default but not data
- if ($stash->{wants_attachments} //= filter_wants_nocache($params, 'attachments')) {
- my $attachment_params = { ids => $bug->id };
- if (!filter_wants_nocache($params, 'data', 'extra', 'attachments')
- && !$params->{attachmentdata})
- {
- $attachment_params->{exclude_fields} = ['data'];
- }
-
- my $attachments = $rpc->attachments($attachment_params);
-
- my @fixed_attachments;
- foreach my $attachment (@{ $attachments->{bugs}->{$bug->id} }) {
- my $fixed = fix_attachment($attachment);
- push(@fixed_attachments, filter($params, $fixed, undef, 'attachments'));
- }
-
- $data->{attachments} = \@fixed_attachments;
+ $data->{flags} = \@new_flags;
+ }
+
+ # Attachment metadata is included by default but not data
+ if ($stash->{wants_attachments}
+ //= filter_wants_nocache($params, 'attachments'))
+ {
+ my $attachment_params = {ids => $bug->id};
+ if ( !filter_wants_nocache($params, 'data', 'extra', 'attachments')
+ && !$params->{attachmentdata})
+ {
+ $attachment_params->{exclude_fields} = ['data'];
}
- # Comments and history are not part of _default and have to be requested
+ my $attachments = $rpc->attachments($attachment_params);
- # Comments
- if ($stash->{wants_comments} //= filter_wants_nocache($params, 'comments', 'extra', 'comments')) {
- my $comments = $rpc->comments({ ids => $bug->id });
- $comments = $comments->{bugs}->{$bug->id}->{comments};
- my @new_comments;
- foreach my $comment (@$comments) {
- $comment = fix_comment($comment);
- push(@new_comments, filter($params, $comment, 'extra', 'comments'));
- }
- $data->{comments} = \@new_comments;
+ my @fixed_attachments;
+ foreach my $attachment (@{$attachments->{bugs}->{$bug->id}}) {
+ my $fixed = fix_attachment($attachment);
+ push(@fixed_attachments, filter($params, $fixed, undef, 'attachments'));
}
- # History
- if ($stash->{wants_history} //= filter_wants_nocache($params, 'history', 'extra', 'history')) {
- my $history = $rpc->history({ ids => [ $bug->id ] });
- my @new_history;
- foreach my $changeset (@{ $history->{bugs}->[0]->{history} }) {
- push(@new_history, fix_changeset($changeset, $bug));
- }
- $data->{history} = \@new_history;
+ $data->{attachments} = \@fixed_attachments;
+ }
+
+ # Comments and history are not part of _default and have to be requested
+
+ # Comments
+ if ($stash->{wants_comments}
+ //= filter_wants_nocache($params, 'comments', 'extra', 'comments'))
+ {
+ my $comments = $rpc->comments({ids => $bug->id});
+ $comments = $comments->{bugs}->{$bug->id}->{comments};
+ my @new_comments;
+ foreach my $comment (@$comments) {
+ $comment = fix_comment($comment);
+ push(@new_comments, filter($params, $comment, 'extra', 'comments'));
+ }
+ $data->{comments} = \@new_comments;
+ }
+
+ # History
+ if ($stash->{wants_history}
+ //= filter_wants_nocache($params, 'history', 'extra', 'history'))
+ {
+ my $history = $rpc->history({ids => [$bug->id]});
+ my @new_history;
+ foreach my $changeset (@{$history->{bugs}->[0]->{history}}) {
+ push(@new_history, fix_changeset($changeset, $bug));
+ }
+ $data->{history} = \@new_history;
+ }
+
+ # Add in all custom fields even if not set or visible on this bug
+ my $custom_fields = $stash->{custom_fields}
+ //= Bugzilla->fields({custom => 1, obsolete => 0, by_name => 1});
+ foreach my $field (values %$custom_fields) {
+ my $name = $field->name;
+ my $type = $field->type;
+ if (!filter_wants_nocache($params, $name, ['default', 'custom'])) {
+ delete $custom_fields->{$name};
+ next;
+ }
+ if ($type == FIELD_TYPE_BUG_ID) {
+ $data->{$name} = $rpc->type('int', $bug->$name);
+ }
+ elsif ($type == FIELD_TYPE_DATETIME || $type == FIELD_TYPE_DATE) {
+ $data->{$name} = $rpc->type('dateTime', $bug->$name);
}
+ elsif ($type == FIELD_TYPE_MULTI_SELECT) {
+
+# Bug.search, when include_fields=_all, returns array, otherwise return as comma delimited string :(
+ if ($method eq 'Bug.search'
+ && !grep($_ eq '_all', @{$params->{include_fields}}))
+ {
+ $data->{$name} = $rpc->type('string', join(', ', @{$bug->$name}));
+ }
+ else {
+ my @values = map { $rpc->type('string', $_) } @{$bug->$name};
+ $data->{$name} = \@values;
+ }
+ }
+ else {
+ $data->{$name} = $rpc->type('string', $bug->$name);
+ }
+ }
- # Add in all custom fields even if not set or visible on this bug
- my $custom_fields = $stash->{custom_fields} //=
- Bugzilla->fields({ custom => 1, obsolete => 0, by_name => 1 });
- foreach my $field (values %$custom_fields) {
- my $name = $field->name;
- my $type = $field->type;
- if (!filter_wants_nocache($params, $name, ['default','custom'])) {
- delete $custom_fields->{$name};
- next;
- }
- if ($type == FIELD_TYPE_BUG_ID) {
- $data->{$name} = $rpc->type('int', $bug->$name);
- }
- elsif ($type == FIELD_TYPE_DATETIME
- || $type == FIELD_TYPE_DATE)
- {
- $data->{$name} = $rpc->type('dateTime', $bug->$name);
- }
- elsif ($type == FIELD_TYPE_MULTI_SELECT) {
- # Bug.search, when include_fields=_all, returns array, otherwise return as comma delimited string :(
- if ($method eq 'Bug.search' && !grep($_ eq '_all', @{ $params->{include_fields} })) {
- $data->{$name} = $rpc->type('string', join(', ', @{ $bug->$name }));
- }
- else {
- my @values = map { $rpc->type('string', $_) } @{ $bug->$name };
- $data->{$name} = \@values;
- }
- }
- else {
- $data->{$name} = $rpc->type('string', $bug->$name);
- }
+ # Remove empty values in some cases
+ foreach my $key (keys %$data) {
+
+ # QA Contact is null if single bug or "" if doing search
+ if ($key eq 'qa_contact' && !$data->{$key}->{name}) {
+ if ($method eq 'Bug.search') {
+ $data->{$key}->{name} = $rpc->type('string', '');
+ }
+ next;
}
- # Remove empty values in some cases
- foreach my $key (keys %$data) {
- # QA Contact is null if single bug or "" if doing search
- if ($key eq 'qa_contact' && !$data->{$key}->{name}) {
- if ($method eq 'Bug.search') {
- $data->{$key}->{name} = $rpc->type('string', '');
- }
- next;
- }
+ next if $method eq 'Bug.search' && $key eq 'url'; # Return url even if empty
+ next if $method eq 'Bug.search' && $key eq 'keywords'; # Return keywords even if empty
+ next if $method eq 'Bug.search' && $key eq 'whiteboard'; # Return whiteboard even if empty
+ next if $method eq 'Bug.get' && grep($_ eq $key, TIMETRACKING_FIELDS);
- next if $method eq 'Bug.search' && $key eq 'url'; # Return url even if empty
- next if $method eq 'Bug.search' && $key eq 'keywords'; # Return keywords even if empty
- next if $method eq 'Bug.search' && $key eq 'whiteboard'; # Return whiteboard even if empty
- next if $method eq 'Bug.get' && grep($_ eq $key, TIMETRACKING_FIELDS);
+ next
+ if ($method eq 'Bug.search'
+ && $key =~ /^(resolution|cc_count|dupe_count)$/
+ && !grep($_ eq '_all', @{$params->{include_fields}}));
- next if ($method eq 'Bug.search'
- && $key =~ /^(resolution|cc_count|dupe_count)$/
- && !grep($_ eq '_all', @{ $params->{include_fields} }));
+ if (!ref $data->{$key}) {
+ delete $data->{$key} if !$data->{$key};
+ }
+ else {
+ if (ref $data->{$key} eq 'ARRAY' && !@{$data->{$key}}) {
- if (!ref $data->{$key}) {
- delete $data->{$key} if !$data->{$key};
+ # Return empty string if blocks or depends_on is empty
+ if ($method eq 'Bug.search' && ($key eq 'depends_on' || $key eq 'blocks')) {
+ $data->{$key} = '';
}
else {
- if (ref $data->{$key} eq 'ARRAY' && !@{$data->{$key}}) {
- # Return empty string if blocks or depends_on is empty
- if ($method eq 'Bug.search' && ($key eq 'depends_on' || $key eq 'blocks')) {
- $data->{$key} = '';
- }
- else {
- delete $data->{$key};
- }
- }
- elsif (ref $data->{$key} eq 'HASH' && !%{$data->{$key}}) {
- delete $data->{$key};
- }
+ delete $data->{$key};
}
+ }
+ elsif (ref $data->{$key} eq 'HASH' && !%{$data->{$key}}) {
+ delete $data->{$key};
+ }
}
+ }
- return $data;
+ return $data;
}
# convert a user related field from being just login
# names to user objects
sub fix_user {
- my ($data, $object) = @_;
- my $user = Bugzilla->user;
- my $rpc = Bugzilla->request_cache->{bzapi_rpc};
+ my ($data, $object) = @_;
+ my $user = Bugzilla->user;
+ my $rpc = Bugzilla->request_cache->{bzapi_rpc};
- return { name => undef } if !$data;
+ return {name => undef} if !$data;
- if (!ref $data) {
- $data = {
- name => filter_email($object->login)
- };
- $data->{real_name} = $rpc->type('string', $object->name);
- }
- else {
- $data->{name} = filter_email($data->{name});
- }
+ if (!ref $data) {
+ $data = {name => filter_email($object->login)};
+ $data->{real_name} = $rpc->type('string', $object->name);
+ }
+ else {
+ $data->{name} = filter_email($data->{name});
+ }
- if ($user->id) {
- $data->{ref} = $rpc->type('string', ref_urlbase . "/user/" . $object->login);
- }
+ if ($user->id) {
+ $data->{ref} = $rpc->type('string', ref_urlbase . "/user/" . $object->login);
+ }
- return $data;
+ return $data;
}
# convert certain attributes of a comment to objects
# and also remove other unwanted key/values.
sub fix_comment {
- my ($data, $object) = @_;
- my $rpc = Bugzilla->request_cache->{bzapi_rpc};
- my $method = Bugzilla->request_cache->{bzapi_rpc_method};
+ my ($data, $object) = @_;
+ my $rpc = Bugzilla->request_cache->{bzapi_rpc};
+ my $method = Bugzilla->request_cache->{bzapi_rpc_method};
- $object ||= Bugzilla::Comment->new({ id => $data->{id}, cache => 1 });
+ $object ||= Bugzilla::Comment->new({id => $data->{id}, cache => 1});
- if (exists $data->{creator}) {
- $data->{creator} = fix_user($data->{creator}, $object->author);
- }
+ if (exists $data->{creator}) {
+ $data->{creator} = fix_user($data->{creator}, $object->author);
+ }
- if ($data->{attachment_id} && $method ne 'Bug.search') {
- $data->{attachment_ref} = $rpc->type('string', ref_urlbase() .
- "/attachment/" . $object->extra_data);
- }
- else {
- delete $data->{attachment_id};
- }
+ if ($data->{attachment_id} && $method ne 'Bug.search') {
+ $data->{attachment_ref}
+ = $rpc->type('string', ref_urlbase() . "/attachment/" . $object->extra_data);
+ }
+ else {
+ delete $data->{attachment_id};
+ }
- delete $data->{author};
- delete $data->{time};
- delete $data->{raw_text};
+ delete $data->{author};
+ delete $data->{time};
+ delete $data->{raw_text};
- return $data;
+ return $data;
}
# convert certain attributes of a changeset object from
# scalar values to related objects. Also remove other unwanted
# key/values.
sub fix_changeset {
- my ($data, $object) = @_;
- my $user = Bugzilla->user;
- my $rpc = Bugzilla->request_cache->{bzapi_rpc};
-
- if ($data->{who}) {
- $data->{changer} = {
- name => $rpc->type('string', $data->{who}),
- ref => $rpc->type('string', ref_urlbase() . "/user/" . $data->{who})
- };
- delete $data->{who};
- }
-
- if ($data->{when}) {
- $data->{change_time} = $rpc->type('dateTime', $data->{when});
- delete $data->{when};
- }
-
- foreach my $change (@{ $data->{changes} }) {
- $change->{field_name} = 'flag' if $change->{field_name} eq 'flagtypes.name';
- }
-
- return $data;
+ my ($data, $object) = @_;
+ my $user = Bugzilla->user;
+ my $rpc = Bugzilla->request_cache->{bzapi_rpc};
+
+ if ($data->{who}) {
+ $data->{changer} = {
+ name => $rpc->type('string', $data->{who}),
+ ref => $rpc->type('string', ref_urlbase() . "/user/" . $data->{who})
+ };
+ delete $data->{who};
+ }
+
+ if ($data->{when}) {
+ $data->{change_time} = $rpc->type('dateTime', $data->{when});
+ delete $data->{when};
+ }
+
+ foreach my $change (@{$data->{changes}}) {
+ $change->{field_name} = 'flag' if $change->{field_name} eq 'flagtypes.name';
+ }
+
+ return $data;
}
# convert certain attributes of an attachment object from
# scalar values to related objects. Also add in additional
# key/values.
sub fix_attachment {
- my ($data, $object) = @_;
- my $rpc = Bugzilla->request_cache->{bzapi_rpc};
- my $method = Bugzilla->request_cache->{bzapi_rpc_method};
- my $params = Bugzilla->input_params;
- my $user = Bugzilla->user;
-
- $object ||= Bugzilla::Attachment->new({ id => $data->{id}, cache => 1 });
-
- if (exists $data->{attacher}) {
- $data->{attacher} = fix_user($data->{attacher}, $object->attacher);
- if ($method eq 'Bug.search') {
- delete $data->{attacher}->{real_name};
- }
- else {
- $data->{attacher}->{real_name} = $rpc->type('string', $object->attacher->name);
- }
+ my ($data, $object) = @_;
+ my $rpc = Bugzilla->request_cache->{bzapi_rpc};
+ my $method = Bugzilla->request_cache->{bzapi_rpc_method};
+ my $params = Bugzilla->input_params;
+ my $user = Bugzilla->user;
+
+ $object ||= Bugzilla::Attachment->new({id => $data->{id}, cache => 1});
+
+ if (exists $data->{attacher}) {
+ $data->{attacher} = fix_user($data->{attacher}, $object->attacher);
+ if ($method eq 'Bug.search') {
+ delete $data->{attacher}->{real_name};
}
-
- if (exists $data->{data}) {
- $data->{encoding} = $rpc->type('string', 'base64');
- if ($params->{attachmentdata}
- || filter_wants_nocache($params, 'attachments.data'))
- {
- $data->{encoding} = $rpc->type('string', 'base64');
- }
- else {
- delete $data->{data};
- }
- }
-
- if (exists $data->{bug_id}) {
- $data->{bug_ref} = $rpc->type('string', ref_urlbase() . "/bug/" . $object->bug_id);
- }
-
- # Upstream API returns these as integers where bzapi returns as booleans
- if (exists $data->{is_patch}) {
- $data->{is_patch} = $rpc->type('boolean', $data->{is_patch});
- }
- if (exists $data->{is_obsolete}) {
- $data->{is_obsolete} = $rpc->type('boolean', $data->{is_obsolete});
- }
- if (exists $data->{is_private}) {
- $data->{is_private} = $rpc->type('boolean', $data->{is_private});
+ else {
+ $data->{attacher}->{real_name} = $rpc->type('string', $object->attacher->name);
}
-
- if (exists $data->{flags} && @{ $data->{flags} }) {
- my @new_flags;
- foreach my $flag (@{ $data->{flags} }) {
- push(@new_flags, fix_flag($flag));
- }
- $data->{flags} = \@new_flags;
+ }
+
+ if (exists $data->{data}) {
+ $data->{encoding} = $rpc->type('string', 'base64');
+ if ($params->{attachmentdata}
+ || filter_wants_nocache($params, 'attachments.data'))
+ {
+ $data->{encoding} = $rpc->type('string', 'base64');
}
else {
- delete $data->{flags};
+ delete $data->{data};
}
+ }
+
+ if (exists $data->{bug_id}) {
+ $data->{bug_ref}
+ = $rpc->type('string', ref_urlbase() . "/bug/" . $object->bug_id);
+ }
+
+ # Upstream API returns these as integers where bzapi returns as booleans
+ if (exists $data->{is_patch}) {
+ $data->{is_patch} = $rpc->type('boolean', $data->{is_patch});
+ }
+ if (exists $data->{is_obsolete}) {
+ $data->{is_obsolete} = $rpc->type('boolean', $data->{is_obsolete});
+ }
+ if (exists $data->{is_private}) {
+ $data->{is_private} = $rpc->type('boolean', $data->{is_private});
+ }
+
+ if (exists $data->{flags} && @{$data->{flags}}) {
+ my @new_flags;
+ foreach my $flag (@{$data->{flags}}) {
+ push(@new_flags, fix_flag($flag));
+ }
+ $data->{flags} = \@new_flags;
+ }
+ else {
+ delete $data->{flags};
+ }
- $data->{ref} = $rpc->type('string', ref_urlbase() . "/attachment/" . $object->id);
+ $data->{ref}
+ = $rpc->type('string', ref_urlbase() . "/attachment/" . $object->id);
- # Add update token if we are getting an attachment outside of Bug.get and user is logged in
- if ($user->id && ($method eq 'Bug.attachments'|| $method eq 'Bug.search')) {
- $data->{update_token} = issue_hash_token([ $object->id, $object->modification_time ]);
- }
+# Add update token if we are getting an attachment outside of Bug.get and user is logged in
+ if ($user->id && ($method eq 'Bug.attachments' || $method eq 'Bug.search')) {
+ $data->{update_token}
+ = issue_hash_token([$object->id, $object->modification_time]);
+ }
- delete $data->{creator};
- delete $data->{summary};
+ delete $data->{creator};
+ delete $data->{summary};
- return $data;
+ return $data;
}
# convert certain attributes of a flag object from
# scalar values to related objects. Also remove other unwanted
# key/values.
sub fix_flag {
- my ($data, $object) = @_;
- my $rpc = Bugzilla->request_cache->{bzapi_rpc};
+ my ($data, $object) = @_;
+ my $rpc = Bugzilla->request_cache->{bzapi_rpc};
- $object ||= Bugzilla::Flag->new({ id => $data->{id}, cache => 1 });
+ $object ||= Bugzilla::Flag->new({id => $data->{id}, cache => 1});
- if (exists $data->{setter}) {
- $data->{setter} = fix_user($data->{setter}, $object->setter);
- delete $data->{setter}->{real_name};
- }
+ if (exists $data->{setter}) {
+ $data->{setter} = fix_user($data->{setter}, $object->setter);
+ delete $data->{setter}->{real_name};
+ }
- if (exists $data->{requestee}) {
- $data->{requestee} = fix_user($data->{requestee}, $object->requestee);
- delete $data->{requestee}->{real_name};
- }
+ if (exists $data->{requestee}) {
+ $data->{requestee} = fix_user($data->{requestee}, $object->requestee);
+ delete $data->{requestee}->{real_name};
+ }
- return $data;
+ return $data;
}
# Calls Bugzilla::WebService::Util::filter_wants but disables caching
# as we make several webservice calls in a single REST call and the
# caching can cause unexpected results.
sub filter_wants_nocache {
- my ($params, $field, $types, $prefix) = @_;
- delete Bugzilla->request_cache->{filter_wants};
- return filter_wants($params, $field, $types, $prefix);
+ my ($params, $field, $types, $prefix) = @_;
+ delete Bugzilla->request_cache->{filter_wants};
+ return filter_wants($params, $field, $types, $prefix);
}
sub filter {
- my ($params, $hash, $types, $prefix) = @_;
- my %newhash = %$hash;
- foreach my $key (keys %$hash) {
- delete $newhash{$key} if !filter_wants_nocache($params, $key, $types, $prefix);
- }
- return \%newhash;
+ my ($params, $hash, $types, $prefix) = @_;
+ my %newhash = %$hash;
+ foreach my $key (keys %$hash) {
+ delete $newhash{$key} if !filter_wants_nocache($params, $key, $types, $prefix);
+ }
+ return \%newhash;
}
sub fix_credentials {
- my ($params) = @_;
- # Allow user to pass in username=foo&password=bar to be compatible
- $params->{'Bugzilla_login'} = $params->{'login'} = delete $params->{'username'}
- if exists $params->{'username'};
- $params->{'Bugzilla_password'} = $params->{'password'} if exists $params->{'password'};
-
- # Allow user to pass userid=1&cookie=3iYGuKZdyz for compatibility with BzAPI
- if (exists $params->{'userid'} && exists $params->{'cookie'}) {
- my $userid = delete $params->{'userid'};
- my $cookie = delete $params->{'cookie'};
- $params->{'Bugzilla_token'} = "${userid}-${cookie}";
- }
+ my ($params) = @_;
+
+ # Allow user to pass in username=foo&password=bar to be compatible
+ $params->{'Bugzilla_login'} = $params->{'login'} = delete $params->{'username'}
+ if exists $params->{'username'};
+ $params->{'Bugzilla_password'} = $params->{'password'}
+ if exists $params->{'password'};
+
+ # Allow user to pass userid=1&cookie=3iYGuKZdyz for compatibility with BzAPI
+ if (exists $params->{'userid'} && exists $params->{'cookie'}) {
+ my $userid = delete $params->{'userid'};
+ my $cookie = delete $params->{'cookie'};
+ $params->{'Bugzilla_token'} = "${userid}-${cookie}";
+ }
}
# Filter email addresses by default ignoring the system
# webservice_email_filter setting
sub filter_email {
- my $rpc = Bugzilla->request_cache->{bzapi_rpc};
- return $rpc->type('string', email_filter($_[0]));
+ my $rpc = Bugzilla->request_cache->{bzapi_rpc};
+ return $rpc->type('string', email_filter($_[0]));
}
1;
diff --git a/extensions/ComponentWatching/Extension.pm b/extensions/ComponentWatching/Extension.pm
index fdeedff98..654094191 100644
--- a/extensions/ComponentWatching/Extension.pm
+++ b/extensions/ComponentWatching/Extension.pm
@@ -32,92 +32,58 @@ use constant REL_COMPONENT_WATCHER => 15;
#
sub db_schema_abstract_schema {
- my ($self, $args) = @_;
- my $dbh = Bugzilla->dbh;
-
- # Bugzilla 5.0+, the components.id type
- # is INT3, while earlier versions used INT2
- my $component_id_type = 'INT2';
- my $len = scalar @{ $args->{schema}->{components}->{FIELDS} };
- for (my $i = 0; $i < $len - 1; $i+=2) {
- next if $args->{schema}->{components}->{FIELDS}->[$i] ne 'id';
- $component_id_type = 'INT3'
- if $args->{schema}->{components}->{FIELDS}->[$i+1]->{TYPE} eq 'MEDIUMSERIAL';
- last;
- }
- $args->{'schema'}->{'component_watch'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- user_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'CASCADE',
- }
- },
- component_id => {
- TYPE => $component_id_type,
- NOTNULL => 0,
- REFERENCES => {
- TABLE => 'components',
- COLUMN => 'id',
- DELETE => 'CASCADE',
- }
- },
- product_id => {
- TYPE => 'INT2',
- NOTNULL => 0,
- REFERENCES => {
- TABLE => 'products',
- COLUMN => 'id',
- DELETE => 'CASCADE',
- }
- },
- component_prefix => {
- TYPE => 'VARCHAR(64)',
- NOTNULL => 0,
- },
- ],
- };
+ my ($self, $args) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ # Bugzilla 5.0+, the components.id type
+ # is INT3, while earlier versions used INT2
+ my $component_id_type = 'INT2';
+ my $len = scalar @{$args->{schema}->{components}->{FIELDS}};
+ for (my $i = 0; $i < $len - 1; $i += 2) {
+ next if $args->{schema}->{components}->{FIELDS}->[$i] ne 'id';
+ $component_id_type = 'INT3'
+ if $args->{schema}->{components}->{FIELDS}->[$i + 1]->{TYPE} eq
+ 'MEDIUMSERIAL';
+ last;
+ }
+ $args->{'schema'}->{'component_watch'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ user_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE',}
+ },
+ component_id => {
+ TYPE => $component_id_type,
+ NOTNULL => 0,
+ REFERENCES => {TABLE => 'components', COLUMN => 'id', DELETE => 'CASCADE',}
+ },
+ product_id => {
+ TYPE => 'INT2',
+ NOTNULL => 0,
+ REFERENCES => {TABLE => 'products', COLUMN => 'id', DELETE => 'CASCADE',}
+ },
+ component_prefix => {TYPE => 'VARCHAR(64)', NOTNULL => 0,},
+ ],
+ };
}
sub install_update_db {
- my $dbh = Bugzilla->dbh;
- $dbh->bz_add_column(
- 'components',
- 'watch_user',
- {
- TYPE => 'INT3',
- REFERENCES => {
- TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'SET NULL',
- }
- }
- );
- $dbh->bz_add_column(
- 'component_watch',
- 'id',
- {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- );
- $dbh->bz_add_column(
- 'component_watch',
- 'component_prefix',
- {
- TYPE => 'VARCHAR(64)',
- NOTNULL => 0,
- }
- );
+ my $dbh = Bugzilla->dbh;
+ $dbh->bz_add_column(
+ 'components',
+ 'watch_user',
+ {
+ TYPE => 'INT3',
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'SET NULL',}
+ }
+ );
+ $dbh->bz_add_column('component_watch', 'id',
+ {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ );
+ $dbh->bz_add_column('component_watch', 'component_prefix',
+ {TYPE => 'VARCHAR(64)', NOTNULL => 0,});
}
#
@@ -125,16 +91,16 @@ sub install_update_db {
#
sub template_before_create {
- my ($self, $args) = @_;
- my $config = $args->{config};
- my $constants = $config->{VARIABLES}{constants};
- $constants->{REL_COMPONENT_WATCHER} = REL_COMPONENT_WATCHER;
+ my ($self, $args) = @_;
+ my $config = $args->{config};
+ my $constants = $config->{VARIABLES}{constants};
+ $constants->{REL_COMPONENT_WATCHER} = REL_COMPONENT_WATCHER;
}
sub template_before_process {
- my ($self, $args) = @_;
- return unless $args->{file} eq 'admin/components/create.html.tmpl';
- $args->{vars}{comp}{default_assignee}{login} = DEFAULT_ASSIGNEE;
+ my ($self, $args) = @_;
+ return unless $args->{file} eq 'admin/components/create.html.tmpl';
+ $args->{vars}{comp}{default_assignee}{login} = DEFAULT_ASSIGNEE;
}
#
@@ -142,147 +108,147 @@ sub template_before_process {
#
BEGIN {
- *Bugzilla::Component::watch_user = \&_component_watch_user;
+ *Bugzilla::Component::watch_user = \&_component_watch_user;
}
sub _component_watch_user {
- my ($self) = @_;
- return unless $self->{watch_user};
- $self->{watch_user_object} ||= Bugzilla::User->new($self->{watch_user});
- return $self->{watch_user_object};
+ my ($self) = @_;
+ return unless $self->{watch_user};
+ $self->{watch_user_object} ||= Bugzilla::User->new($self->{watch_user});
+ return $self->{watch_user_object};
}
sub object_columns {
- my ($self, $args) = @_;
- my $class = $args->{class};
- my $columns = $args->{columns};
- return unless $class->isa('Bugzilla::Component');
-
- if (Bugzilla->dbh->bz_column_info($class->DB_TABLE, 'watch_user')) {
- push @$columns, 'watch_user';
- }
+ my ($self, $args) = @_;
+ my $class = $args->{class};
+ my $columns = $args->{columns};
+ return unless $class->isa('Bugzilla::Component');
+
+ if (Bugzilla->dbh->bz_column_info($class->DB_TABLE, 'watch_user')) {
+ push @$columns, 'watch_user';
+ }
}
sub object_update_columns {
- my ($self, $args) = @_;
- my $object = $args->{object};
- my $columns = $args->{columns};
- return unless $object->isa('Bugzilla::Component');
+ my ($self, $args) = @_;
+ my $object = $args->{object};
+ my $columns = $args->{columns};
+ return unless $object->isa('Bugzilla::Component');
- push(@$columns, 'watch_user');
+ push(@$columns, 'watch_user');
- # add the user if not yet exists and user chooses 'automatic'
- $self->_create_watch_user();
+ # add the user if not yet exists and user chooses 'automatic'
+ $self->_create_watch_user();
- # editcomponents.cgi doesn't call set_all, so we have to do this here
- my $input = Bugzilla->input_params;
- $object->set('watch_user', $input->{watch_user});
+ # editcomponents.cgi doesn't call set_all, so we have to do this here
+ my $input = Bugzilla->input_params;
+ $object->set('watch_user', $input->{watch_user});
}
sub object_validators {
- my ($self, $args) = @_;
- my $class = $args->{class};
- my $validators = $args->{validators};
- return unless $class->isa('Bugzilla::Component');
+ my ($self, $args) = @_;
+ my $class = $args->{class};
+ my $validators = $args->{validators};
+ return unless $class->isa('Bugzilla::Component');
- $validators->{watch_user} = \&_check_watch_user;
+ $validators->{watch_user} = \&_check_watch_user;
}
sub object_before_create {
- my ($self, $args) = @_;
- my $class = $args->{class};
- my $params = $args->{params};
- return unless $class->isa('Bugzilla::Component');
-
- # We need to create a watch user for the default product/component
- # if we are creating the database for the first time.
- my $dbh = Bugzilla->dbh;
- if (Bugzilla->usage_mode == USAGE_MODE_CMDLINE
- && !$dbh->selectrow_array('SELECT 1 FROM components'))
- {
- my $watch_user = Bugzilla::User->create({
- login_name => 'testcomponent@testproduct.bugs',
- cryptpassword => '*',
- disable_mail => 1
- });
- $params->{watch_user} = $watch_user->login;
- }
- else {
- my $input = Bugzilla->input_params;
- $params->{watch_user} = $input->{watch_user};
- $self->_create_watch_user();
- }
+ my ($self, $args) = @_;
+ my $class = $args->{class};
+ my $params = $args->{params};
+ return unless $class->isa('Bugzilla::Component');
+
+ # We need to create a watch user for the default product/component
+ # if we are creating the database for the first time.
+ my $dbh = Bugzilla->dbh;
+ if (Bugzilla->usage_mode == USAGE_MODE_CMDLINE
+ && !$dbh->selectrow_array('SELECT 1 FROM components'))
+ {
+ my $watch_user = Bugzilla::User->create({
+ login_name => 'testcomponent@testproduct.bugs',
+ cryptpassword => '*',
+ disable_mail => 1
+ });
+ $params->{watch_user} = $watch_user->login;
+ }
+ else {
+ my $input = Bugzilla->input_params;
+ $params->{watch_user} = $input->{watch_user};
+ $self->_create_watch_user();
+ }
}
sub object_end_of_update {
- my ($self, $args) = @_;
- my $object = $args->{object};
- my $old_object = $args->{old_object};
- my $changes = $args->{changes};
- return unless $object->isa('Bugzilla::Component');
-
- my $old_id = $old_object->watch_user ? $old_object->watch_user->id : 0;
- my $new_id = $object->watch_user ? $object->watch_user->id : 0;
- if ($old_id != $new_id) {
- $changes->{watch_user} = [ $old_id ? $old_id : undef, $new_id ? $new_id : undef ];
- }
-
- # when a component is renamed, update the watch-user to follow
- # this only happens when the user appears to have been auto-generated from the old name
- if ($changes->{name}
- && $old_object->watch_user
- && $object->watch_user
- && $old_object->watch_user->id == $object->watch_user->id
- && _generate_watch_user_name($old_object) eq $object->watch_user->login
- )
- {
- my $old_login = $object->watch_user->login;
- $object->watch_user->set_login(_generate_watch_user_name($object));
- $object->watch_user->update();
- $changes->{watch_user_login} = [ $old_login, $object->watch_user->login ];
- }
+ my ($self, $args) = @_;
+ my $object = $args->{object};
+ my $old_object = $args->{old_object};
+ my $changes = $args->{changes};
+ return unless $object->isa('Bugzilla::Component');
+
+ my $old_id = $old_object->watch_user ? $old_object->watch_user->id : 0;
+ my $new_id = $object->watch_user ? $object->watch_user->id : 0;
+ if ($old_id != $new_id) {
+ $changes->{watch_user} = [$old_id ? $old_id : undef, $new_id ? $new_id : undef];
+ }
+
+# when a component is renamed, update the watch-user to follow
+# this only happens when the user appears to have been auto-generated from the old name
+ if ( $changes->{name}
+ && $old_object->watch_user
+ && $object->watch_user
+ && $old_object->watch_user->id == $object->watch_user->id
+ && _generate_watch_user_name($old_object) eq $object->watch_user->login)
+ {
+ my $old_login = $object->watch_user->login;
+ $object->watch_user->set_login(_generate_watch_user_name($object));
+ $object->watch_user->update();
+ $changes->{watch_user_login} = [$old_login, $object->watch_user->login];
+ }
}
sub _generate_watch_user_name {
- # this is mirrored in template/en/default/hook/admin/components/edit-common-rows.html.tmpl
- # that javascript needs to be kept in sync with this perl
- my ($component) = @_;
- return _sanitise_name($component->name)
- . '@' . _sanitise_name($component->product->name) . '.bugs';
+
+# this is mirrored in template/en/default/hook/admin/components/edit-common-rows.html.tmpl
+# that javascript needs to be kept in sync with this perl
+ my ($component) = @_;
+ return
+ _sanitise_name($component->name) . '@'
+ . _sanitise_name($component->product->name) . '.bugs';
}
sub _sanitise_name {
- my ($name) = @_;
- $name = lc($name);
- $name =~ s/[^a-z0-9_]/-/g;
- $name =~ s/-+/-/g;
- $name =~ s/(^-|-$)//g;
- return $name;
+ my ($name) = @_;
+ $name = lc($name);
+ $name =~ s/[^a-z0-9_]/-/g;
+ $name =~ s/-+/-/g;
+ $name =~ s/(^-|-$)//g;
+ return $name;
}
sub _create_watch_user {
- my $input = Bugzilla->input_params;
- if ($input->{watch_user_auto}
- && !Bugzilla::User->new({ name => $input->{watch_user} }))
- {
- Bugzilla::User->create({
- login_name => $input->{watch_user},
- cryptpassword => '*',
- });
- }
+ my $input = Bugzilla->input_params;
+ if ($input->{watch_user_auto}
+ && !Bugzilla::User->new({name => $input->{watch_user}}))
+ {
+ Bugzilla::User->create({
+ login_name => $input->{watch_user}, cryptpassword => '*',
+ });
+ }
}
sub _check_watch_user {
- my ($self, $value, $field) = @_;
- $value = trim($value || '');
- return undef if !REQUIRE_WATCH_USER && $value eq '';
- if ($value eq '') {
- ThrowUserError('component_watch_missing_watch_user');
- }
- if ($value !~ /\.bugs$/i) {
- ThrowUserError('component_watch_invalid_watch_user');
- }
- return Bugzilla::User->check($value)->id;
+ my ($self, $value, $field) = @_;
+ $value = trim($value || '');
+ return undef if !REQUIRE_WATCH_USER && $value eq '';
+ if ($value eq '') {
+ ThrowUserError('component_watch_missing_watch_user');
+ }
+ if ($value !~ /\.bugs$/i) {
+ ThrowUserError('component_watch_invalid_watch_user');
+ }
+ return Bugzilla::User->check($value)->id;
}
#
@@ -290,74 +256,79 @@ sub _check_watch_user {
#
sub user_preferences {
- my ($self, $args) = @_;
- my $tab = $args->{'current_tab'};
- return unless $tab eq 'component_watch';
-
- my $save = $args->{'save_changes'};
- my $handled = $args->{'handled'};
- my $vars = $args->{'vars'};
- my $user = Bugzilla->user;
- my $input = Bugzilla->input_params;
+ my ($self, $args) = @_;
+ my $tab = $args->{'current_tab'};
+ return unless $tab eq 'component_watch';
- if ($save) {
- if ($input->{'add'} && $input->{'add_product'}) {
- # add watch
+ my $save = $args->{'save_changes'};
+ my $handled = $args->{'handled'};
+ my $vars = $args->{'vars'};
+ my $user = Bugzilla->user;
+ my $input = Bugzilla->input_params;
- # load product and verify access
- my $productName = $input->{'add_product'};
- my $product = Bugzilla::Product->new({ name => $productName, cache => 1 });
- unless ($product && $user->can_access_product($product)) {
- ThrowUserError('product_access_denied', { product => $productName });
- }
+ if ($save) {
+ if ($input->{'add'} && $input->{'add_product'}) {
- # starting-with
- if (my $prefix = $input->{add_starting}) {
- _addPrefixWatch($user, $product, $prefix);
-
- } else {
- my $ra_componentNames = $input->{'add_component'};
- $ra_componentNames = [$ra_componentNames || ''] unless ref($ra_componentNames);
-
- if (grep { $_ eq '' } @$ra_componentNames) {
- # watching a product
- _addProductWatch($user, $product);
-
- } else {
- # watching specific components
- foreach my $componentName (@$ra_componentNames) {
- my $component = Bugzilla::Component->new({
- name => $componentName, product => $product, cache => 1
- });
- unless ($component) {
- ThrowUserError('product_access_denied', { product => $productName });
- }
- _addComponentWatch($user, $component);
- }
- }
- }
+ # add watch
+
+ # load product and verify access
+ my $productName = $input->{'add_product'};
+ my $product = Bugzilla::Product->new({name => $productName, cache => 1});
+ unless ($product && $user->can_access_product($product)) {
+ ThrowUserError('product_access_denied', {product => $productName});
+ }
- _addDefaultSettings($user);
+ # starting-with
+ if (my $prefix = $input->{add_starting}) {
+ _addPrefixWatch($user, $product, $prefix);
- } else {
- # remove watch(s)
+ }
+ else {
+ my $ra_componentNames = $input->{'add_component'};
+ $ra_componentNames = [$ra_componentNames || ''] unless ref($ra_componentNames);
- my $delete = ref $input->{del_watch}
- ? $input->{del_watch}
- : [ $input->{del_watch} ];
- foreach my $id (@$delete) {
- _deleteWatch($user, $id);
+ if (grep { $_ eq '' } @$ra_componentNames) {
+
+ # watching a product
+ _addProductWatch($user, $product);
+
+ }
+ else {
+ # watching specific components
+ foreach my $componentName (@$ra_componentNames) {
+ my $component
+ = Bugzilla::Component->new({
+ name => $componentName, product => $product, cache => 1
+ });
+ unless ($component) {
+ ThrowUserError('product_access_denied', {product => $productName});
}
+ _addComponentWatch($user, $component);
+ }
}
+ }
+
+ _addDefaultSettings($user);
}
+ else {
+ # remove watch(s)
- $vars->{'add_product'} = $input->{'product'};
- $vars->{'add_component'} = $input->{'component'};
- $vars->{'watches'} = _getWatches($user);
- $vars->{'user_watches'} = _getUserWatches($user);
+ my $delete
+ = ref $input->{del_watch} ? $input->{del_watch} : [$input->{del_watch}];
+ foreach my $id (@$delete) {
+ _deleteWatch($user, $id);
+ }
+ }
+
+ }
- $$handled = 1;
+ $vars->{'add_product'} = $input->{'product'};
+ $vars->{'add_component'} = $input->{'component'};
+ $vars->{'watches'} = _getWatches($user);
+ $vars->{'user_watches'} = _getUserWatches($user);
+
+ $$handled = 1;
}
#
@@ -365,44 +336,45 @@ sub user_preferences {
#
sub bugmail_recipients {
- my ($self, $args) = @_;
- my $bug = $args->{'bug'};
- my $recipients = $args->{'recipients'};
- my $diffs = $args->{'diffs'};
-
- my ($oldProductId, $newProductId) = ($bug->product_id, $bug->product_id);
- my ($oldComponentId, $newComponentId) = ($bug->component_id, $bug->component_id);
-
- # notify when the product/component is switch from one being watched
- if (@$diffs) {
- # we need the product to process the component, so scan for that first
- my $product;
- foreach my $ra (@$diffs) {
- next if !(exists $ra->{'old'}
- && exists $ra->{'field_name'});
- if ($ra->{'field_name'} eq 'product') {
- $product = Bugzilla::Product->new({ name => $ra->{'old'}, cache => 1 });
- $oldProductId = $product->id;
- }
- }
- if (!$product) {
- $product = Bugzilla::Product->new({ id => $oldProductId, cache => 1 });
- }
- foreach my $ra (@$diffs) {
- next if !(exists $ra->{'old'}
- && exists $ra->{'field_name'});
- if ($ra->{'field_name'} eq 'component') {
- my $component = Bugzilla::Component->new({
- name => $ra->{'old'}, product => $product, cache => 1
- });
- $oldComponentId = $component->id;
- }
- }
+ my ($self, $args) = @_;
+ my $bug = $args->{'bug'};
+ my $recipients = $args->{'recipients'};
+ my $diffs = $args->{'diffs'};
+
+ my ($oldProductId, $newProductId) = ($bug->product_id, $bug->product_id);
+ my ($oldComponentId, $newComponentId)
+ = ($bug->component_id, $bug->component_id);
+
+ # notify when the product/component is switch from one being watched
+ if (@$diffs) {
+
+ # we need the product to process the component, so scan for that first
+ my $product;
+ foreach my $ra (@$diffs) {
+ next if !(exists $ra->{'old'} && exists $ra->{'field_name'});
+ if ($ra->{'field_name'} eq 'product') {
+ $product = Bugzilla::Product->new({name => $ra->{'old'}, cache => 1});
+ $oldProductId = $product->id;
+ }
}
+ if (!$product) {
+ $product = Bugzilla::Product->new({id => $oldProductId, cache => 1});
+ }
+ foreach my $ra (@$diffs) {
+ next if !(exists $ra->{'old'} && exists $ra->{'field_name'});
+ if ($ra->{'field_name'} eq 'component') {
+ my $component
+ = Bugzilla::Component->new({
+ name => $ra->{'old'}, product => $product, cache => 1
+ });
+ $oldComponentId = $component->id;
+ }
+ }
+ }
- # add component watchers
- my $dbh = Bugzilla->dbh;
- my $sth = $dbh->prepare("
+ # add component watchers
+ my $dbh = Bugzilla->dbh;
+ my $sth = $dbh->prepare("
SELECT user_id
FROM component_watch
WHERE ((product_id = ? OR product_id = ?) AND component_id IS NULL)
@@ -416,52 +388,52 @@ sub bugmail_recipients {
AND components.name LIKE @{[$dbh->sql_string_concat('component_prefix', q{'%'})]}
AND (components.id = ? OR components.id = ?)
");
- $sth->execute(
- $oldProductId, $newProductId,
- $oldComponentId, $newComponentId,
- $oldProductId, $newProductId,
- $oldComponentId, $newComponentId,
- );
- while (my ($uid) = $sth->fetchrow_array) {
- if (!exists $recipients->{$uid}) {
- $recipients->{$uid}->{+REL_COMPONENT_WATCHER} = Bugzilla::BugMail::BIT_WATCHING();
- }
+ $sth->execute(
+ $oldProductId, $newProductId, $oldComponentId, $newComponentId,
+ $oldProductId, $newProductId, $oldComponentId, $newComponentId,
+ );
+ while (my ($uid) = $sth->fetchrow_array) {
+ if (!exists $recipients->{$uid}) {
+ $recipients->{$uid}->{+REL_COMPONENT_WATCHER}
+ = Bugzilla::BugMail::BIT_WATCHING();
}
+ }
- # add component watchers from watch-users
- my $uidList = join(',', keys %$recipients);
- $sth = $dbh->prepare("
+ # add component watchers from watch-users
+ my $uidList = join(',', keys %$recipients);
+ $sth = $dbh->prepare("
SELECT component_watch.user_id
FROM components
INNER JOIN component_watch ON component_watch.component_id = components.id
WHERE components.watch_user in ($uidList)
");
- $sth->execute();
- while (my ($uid) = $sth->fetchrow_array) {
- if (!exists $recipients->{$uid}) {
- $recipients->{$uid}->{+REL_COMPONENT_WATCHER} = Bugzilla::BugMail::BIT_WATCHING();
- }
+ $sth->execute();
+ while (my ($uid) = $sth->fetchrow_array) {
+ if (!exists $recipients->{$uid}) {
+ $recipients->{$uid}->{+REL_COMPONENT_WATCHER}
+ = Bugzilla::BugMail::BIT_WATCHING();
}
+ }
- # add watch-users from component watchers
- $sth = $dbh->prepare("
+ # add watch-users from component watchers
+ $sth = $dbh->prepare("
SELECT watch_user
FROM components
WHERE (id = ? OR id = ?)
AND (watch_user IS NOT NULL)
");
- $sth->execute($oldComponentId, $newComponentId);
- while (my ($uid) = $sth->fetchrow_array) {
- if (!exists $recipients->{$uid}) {
- $recipients->{$uid}->{+REL_COMPONENT_WATCHER} = Bugzilla::BugMail::BIT_DIRECT();
- }
+ $sth->execute($oldComponentId, $newComponentId);
+ while (my ($uid) = $sth->fetchrow_array) {
+ if (!exists $recipients->{$uid}) {
+ $recipients->{$uid}->{+REL_COMPONENT_WATCHER} = Bugzilla::BugMail::BIT_DIRECT();
}
+ }
}
sub bugmail_relationships {
- my ($self, $args) = @_;
- my $relationships = $args->{relationships};
- $relationships->{+REL_COMPONENT_WATCHER} = 'Component-Watcher';
+ my ($self, $args) = @_;
+ my $relationships = $args->{relationships};
+ $relationships->{+REL_COMPONENT_WATCHER} = 'Component-Watcher';
}
#
@@ -469,141 +441,140 @@ sub bugmail_relationships {
#
sub _getWatches {
- my ($user, $watch_id) = @_;
- my $dbh = Bugzilla->dbh;
+ my ($user, $watch_id) = @_;
+ my $dbh = Bugzilla->dbh;
- $watch_id = (defined $watch_id && $watch_id =~ /^(\d+)$/) ? $1 : undef;
+ $watch_id = (defined $watch_id && $watch_id =~ /^(\d+)$/) ? $1 : undef;
- my $sth = $dbh->prepare("
+ my $sth = $dbh->prepare("
SELECT id, product_id, component_id, component_prefix
FROM component_watch
- WHERE user_id = ?" . ($watch_id ? " AND id = ?" : "")
+ WHERE user_id = ?" . ($watch_id ? " AND id = ?" : ""));
+ $watch_id ? $sth->execute($user->id, $watch_id) : $sth->execute($user->id);
+
+ my @watches;
+ while (my ($id, $productId, $componentId, $prefix) = $sth->fetchrow_array) {
+ my $product = Bugzilla::Product->new({id => $productId, cache => 1});
+ next unless $product && $user->can_access_product($product);
+
+ my %watch = (
+ id => $id,
+ product => $product,
+ product_name => $product->name,
+ component_name => '',
+ component_prefix => $prefix,
);
- $watch_id ? $sth->execute($user->id, $watch_id) : $sth->execute($user->id);
-
- my @watches;
- while (my ($id, $productId, $componentId, $prefix) = $sth->fetchrow_array) {
- my $product = Bugzilla::Product->new({ id => $productId, cache => 1 });
- next unless $product && $user->can_access_product($product);
-
- my %watch = (
- id => $id,
- product => $product,
- product_name => $product->name,
- component_name => '',
- component_prefix => $prefix,
- );
- if ($componentId) {
- my $component = Bugzilla::Component->new({ id => $componentId, cache => 1 });
- next unless $component;
- $watch{'component'} = $component;
- $watch{'component_name'} = $component->name;
- }
-
- push @watches, \%watch;
+ if ($componentId) {
+ my $component = Bugzilla::Component->new({id => $componentId, cache => 1});
+ next unless $component;
+ $watch{'component'} = $component;
+ $watch{'component_name'} = $component->name;
}
- if ($watch_id) {
- return $watches[0] || {};
- }
+ push @watches, \%watch;
+ }
+
+ if ($watch_id) {
+ return $watches[0] || {};
+ }
- @watches = sort {
- $a->{'product_name'} cmp $b->{'product_name'}
- || $a->{'component_name'} cmp $b->{'component_name'}
- || $a->{'component_prefix'} cmp $b->{'component_prefix'}
- } @watches;
+ @watches = sort {
+ $a->{'product_name'} cmp $b->{'product_name'}
+ || $a->{'component_name'} cmp $b->{'component_name'}
+ || $a->{'component_prefix'} cmp $b->{'component_prefix'}
+ } @watches;
- return \@watches;
+ return \@watches;
}
sub _getUserWatches {
- my ($user) = @_;
- my $dbh = Bugzilla->dbh;
+ my ($user) = @_;
+ my $dbh = Bugzilla->dbh;
- my $sth = $dbh->prepare("
+ my $sth = $dbh->prepare("
SELECT components.product_id, components.id as component, profiles.login_name
FROM watch
INNER JOIN components ON components.watch_user = watched
INNER JOIN profiles ON profiles.userid = watched
WHERE watcher = ?
");
- $sth->execute($user->id);
- my @watches;
- while (my ($productId, $componentId, $login) = $sth->fetchrow_array) {
- my $product = Bugzilla::Product->new({ id => $productId, cache => 1 });
- next unless $product && $user->can_access_product($product);
-
- my %watch = (
- product => $product,
- component => Bugzilla::Component->new({ id => $componentId, cache => 1 }),
- user => Bugzilla::User->check($login),
- );
- push @watches, \%watch;
- }
+ $sth->execute($user->id);
+ my @watches;
+ while (my ($productId, $componentId, $login) = $sth->fetchrow_array) {
+ my $product = Bugzilla::Product->new({id => $productId, cache => 1});
+ next unless $product && $user->can_access_product($product);
+
+ my %watch = (
+ product => $product,
+ component => Bugzilla::Component->new({id => $componentId, cache => 1}),
+ user => Bugzilla::User->check($login),
+ );
+ push @watches, \%watch;
+ }
- @watches = sort {
- $a->{'product'}->name cmp $b->{'product'}->name
- || $a->{'component'}->name cmp $b->{'component'}->name
- } @watches;
+ @watches = sort {
+ $a->{'product'}->name cmp $b->{'product'}->name
+ || $a->{'component'}->name cmp $b->{'component'}->name
+ } @watches;
- return \@watches;
+ return \@watches;
}
sub _addProductWatch {
- my ($user, $product) = @_;
- my $dbh = Bugzilla->dbh;
+ my ($user, $product) = @_;
+ my $dbh = Bugzilla->dbh;
- my $sth = $dbh->prepare("
+ my $sth = $dbh->prepare("
SELECT 1
FROM component_watch
WHERE user_id = ? AND product_id = ? AND component_id IS NULL
");
- $sth->execute($user->id, $product->id);
- return if $sth->fetchrow_array;
+ $sth->execute($user->id, $product->id);
+ return if $sth->fetchrow_array;
- $sth = $dbh->prepare("
+ $sth = $dbh->prepare("
DELETE FROM component_watch
WHERE user_id = ? AND product_id = ?
");
- $sth->execute($user->id, $product->id);
+ $sth->execute($user->id, $product->id);
- $sth = $dbh->prepare("
+ $sth = $dbh->prepare("
INSERT INTO component_watch(user_id, product_id)
VALUES (?, ?)
");
- $sth->execute($user->id, $product->id);
+ $sth->execute($user->id, $product->id);
- return _getWatches($user, $dbh->bz_last_key());
+ return _getWatches($user, $dbh->bz_last_key());
}
sub _addComponentWatch {
- my ($user, $component) = @_;
- my $dbh = Bugzilla->dbh;
+ my ($user, $component) = @_;
+ my $dbh = Bugzilla->dbh;
- my $sth = $dbh->prepare("
+ my $sth = $dbh->prepare("
SELECT 1
FROM component_watch
WHERE user_id = ?
AND (component_id = ? OR (product_id = ? AND component_id IS NULL))
");
- $sth->execute($user->id, $component->id, $component->product_id);
- return if $sth->fetchrow_array;
+ $sth->execute($user->id, $component->id, $component->product_id);
+ return if $sth->fetchrow_array;
- $sth = $dbh->prepare("
+ $sth = $dbh->prepare("
INSERT INTO component_watch(user_id, product_id, component_id)
VALUES (?, ?, ?)
");
- $sth->execute($user->id, $component->product_id, $component->id);
+ $sth->execute($user->id, $component->product_id, $component->id);
- return _getWatches($user, $dbh->bz_last_key());
+ return _getWatches($user, $dbh->bz_last_key());
}
sub _addPrefixWatch {
- my ($user, $product, $prefix) = @_;
- my $dbh = Bugzilla->dbh;
+ my ($user, $product, $prefix) = @_;
+ my $dbh = Bugzilla->dbh;
- trick_taint($prefix);
- my $sth = $dbh->prepare("
+ trick_taint($prefix);
+ my $sth = $dbh->prepare("
SELECT 1
FROM component_watch
WHERE user_id = ?
@@ -612,121 +583,102 @@ sub _addPrefixWatch {
OR (product_id = ? AND component_id IS NULL)
)
");
- $sth->execute(
- $user->id,
- $product->id, $prefix,
- $product->id
- );
- return if $sth->fetchrow_array;
+ $sth->execute($user->id, $product->id, $prefix, $product->id);
+ return if $sth->fetchrow_array;
- $sth = $dbh->prepare("
+ $sth = $dbh->prepare("
INSERT INTO component_watch(user_id, product_id, component_prefix)
VALUES (?, ?, ?)
");
- $sth->execute($user->id, $product->id, $prefix);
+ $sth->execute($user->id, $product->id, $prefix);
}
sub _deleteWatch {
- my ($user, $id) = @_;
- my $dbh = Bugzilla->dbh;
+ my ($user, $id) = @_;
+ my $dbh = Bugzilla->dbh;
- detaint_natural($id) || ThrowCodeError("component_watch_invalid_id");
+ detaint_natural($id) || ThrowCodeError("component_watch_invalid_id");
- return $dbh->do("DELETE FROM component_watch WHERE id=? AND user_id=?",
- undef, $id, $user->id);
+ return $dbh->do("DELETE FROM component_watch WHERE id=? AND user_id=?",
+ undef, $id, $user->id);
}
sub _addDefaultSettings {
- my ($user) = @_;
- my $dbh = Bugzilla->dbh;
+ my ($user) = @_;
+ my $dbh = Bugzilla->dbh;
- my $sth = $dbh->prepare("
+ my $sth = $dbh->prepare("
SELECT 1
FROM email_setting
WHERE user_id = ? AND relationship = ?
");
- $sth->execute($user->id, REL_COMPONENT_WATCHER);
- return if $sth->fetchrow_array;
-
- my @defaultEvents = (
- EVT_OTHER,
- EVT_COMMENT,
- EVT_ATTACHMENT,
- EVT_ATTACHMENT_DATA,
- EVT_PROJ_MANAGEMENT,
- EVT_OPENED_CLOSED,
- EVT_KEYWORD,
- EVT_DEPEND_BLOCK,
- EVT_BUG_CREATED,
- );
- foreach my $event (@defaultEvents) {
- $dbh->do(
- "INSERT INTO email_setting(user_id,relationship,event) VALUES (?,?,?)",
- undef,
- $user->id, REL_COMPONENT_WATCHER, $event
- );
- }
+ $sth->execute($user->id, REL_COMPONENT_WATCHER);
+ return if $sth->fetchrow_array;
+
+ my @defaultEvents = (EVT_OTHER, EVT_COMMENT,
+ EVT_ATTACHMENT, EVT_ATTACHMENT_DATA,
+ EVT_PROJ_MANAGEMENT, EVT_OPENED_CLOSED,
+ EVT_KEYWORD, EVT_DEPEND_BLOCK,
+ EVT_BUG_CREATED,
+ );
+ foreach my $event (@defaultEvents) {
+ $dbh->do("INSERT INTO email_setting(user_id,relationship,event) VALUES (?,?,?)",
+ undef, $user->id, REL_COMPONENT_WATCHER, $event);
+ }
}
sub reorg_move_component {
- my ($self, $args) = @_;
- my $new_product = $args->{new_product};
- my $component = $args->{component};
-
- Bugzilla->dbh->do(
- "UPDATE component_watch SET product_id=? WHERE component_id=?",
- undef,
- $new_product->id, $component->id,
- );
+ my ($self, $args) = @_;
+ my $new_product = $args->{new_product};
+ my $component = $args->{component};
+
+ Bugzilla->dbh->do(
+ "UPDATE component_watch SET product_id=? WHERE component_id=?",
+ undef, $new_product->id, $component->id,);
}
sub sanitycheck_check {
- my ($self, $args) = @_;
- my $status = $args->{status};
+ my ($self, $args) = @_;
+ my $status = $args->{status};
- $status->('component_watching_check');
+ $status->('component_watching_check');
- my ($count) = Bugzilla->dbh->selectrow_array("
+ my ($count) = Bugzilla->dbh->selectrow_array("
SELECT COUNT(*)
FROM component_watch
INNER JOIN components ON components.id = component_watch.component_id
WHERE component_watch.product_id <> components.product_id
");
- if ($count) {
- $status->('component_watching_alert', undef, 'alert');
- $status->('component_watching_repair');
- }
+ if ($count) {
+ $status->('component_watching_alert', undef, 'alert');
+ $status->('component_watching_repair');
+ }
}
sub sanitycheck_repair {
- my ($self, $args) = @_;
- return unless Bugzilla->cgi->param('component_watching_repair');
+ my ($self, $args) = @_;
+ return unless Bugzilla->cgi->param('component_watching_repair');
- my $status = $args->{'status'};
- my $dbh = Bugzilla->dbh;
- $status->('component_watching_repairing');
+ my $status = $args->{'status'};
+ my $dbh = Bugzilla->dbh;
+ $status->('component_watching_repairing');
- my $rows = $dbh->selectall_arrayref("
+ my $rows = $dbh->selectall_arrayref("
SELECT DISTINCT component_watch.product_id AS bad_product_id,
components.product_id AS good_product_id,
component_watch.component_id
FROM component_watch
INNER JOIN components ON components.id = component_watch.component_id
WHERE component_watch.product_id <> components.product_id
- ",
- { Slice => {} }
- );
- foreach my $row (@$rows) {
- $dbh->do("
+ ", {Slice => {}});
+ foreach my $row (@$rows) {
+ $dbh->do("
UPDATE component_watch
SET product_id=?
WHERE product_id=? AND component_id=?
- ", undef,
- $row->{good_product_id},
- $row->{bad_product_id},
- $row->{component_id},
- );
- }
+ ", undef, $row->{good_product_id}, $row->{bad_product_id},
+ $row->{component_id},);
+ }
}
#
@@ -734,8 +686,9 @@ sub sanitycheck_repair {
#
sub webservice {
- my ($self, $args) = @_;
- $args->{dispatch}->{ComponentWatching} = "Bugzilla::Extension::ComponentWatching::WebService";
+ my ($self, $args) = @_;
+ $args->{dispatch}->{ComponentWatching}
+ = "Bugzilla::Extension::ComponentWatching::WebService";
}
__PACKAGE__->NAME;
diff --git a/extensions/ComponentWatching/lib/WebService.pm b/extensions/ComponentWatching/lib/WebService.pm
index ba4cb0225..331842f7b 100644
--- a/extensions/ComponentWatching/lib/WebService.pm
+++ b/extensions/ComponentWatching/lib/WebService.pm
@@ -21,30 +21,25 @@ use Bugzilla::Product;
use Bugzilla::User;
sub rest_resources {
- return [
- qr{^/component-watching$}, {
- GET => {
- method => 'list',
- },
- POST => {
- method => 'add',
- },
+ return [
+ qr{^/component-watching$},
+ {GET => {method => 'list',}, POST => {method => 'add',},},
+ qr{^/component-watching/(\d+)$},
+ {
+ GET => {
+ method => 'get',
+ params => sub {
+ return {id => $_[0]};
},
- qr{^/component-watching/(\d+)$}, {
- GET => {
- method => 'get',
- params => sub {
- return { id => $_[0] }
- },
- },
- DELETE => {
- method => 'remove',
- params => sub {
- return { id => $_[0] }
- },
- },
+ },
+ DELETE => {
+ method => 'remove',
+ params => sub {
+ return {id => $_[0]};
},
- ];
+ },
+ },
+ ];
}
#
@@ -52,62 +47,69 @@ sub rest_resources {
#
sub list {
- my ($self, $params) = @_;
- my $user = Bugzilla->login(LOGIN_REQUIRED);
+ my ($self, $params) = @_;
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
- return Bugzilla::Extension::ComponentWatching::_getWatches($user);
+ return Bugzilla::Extension::ComponentWatching::_getWatches($user);
}
sub add {
- my ($self, $params) = @_;
- my $user = Bugzilla->login(LOGIN_REQUIRED);
- my $result;
-
- # load product and verify access
- my $productName = $params->{'product'};
- my $product = Bugzilla::Product->new({ name => $productName, cache => 1 });
- unless ($product && $user->can_access_product($product)) {
- ThrowUserError('product_access_denied', { product => $productName });
+ my ($self, $params) = @_;
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
+ my $result;
+
+ # load product and verify access
+ my $productName = $params->{'product'};
+ my $product = Bugzilla::Product->new({name => $productName, cache => 1});
+ unless ($product && $user->can_access_product($product)) {
+ ThrowUserError('product_access_denied', {product => $productName});
+ }
+
+ my $ra_componentNames = $params->{'component'};
+ $ra_componentNames = [$ra_componentNames || ''] unless ref($ra_componentNames);
+
+ if (grep { $_ eq '' } @$ra_componentNames) {
+
+ # watching a product
+ $result
+ = Bugzilla::Extension::ComponentWatching::_addProductWatch($user, $product);
+
+ }
+ else {
+ # watching specific components
+ foreach my $componentName (@$ra_componentNames) {
+ my $component
+ = Bugzilla::Component->new({
+ name => $componentName, product => $product, cache => 1
+ });
+ unless ($component) {
+ ThrowUserError('product_access_denied', {product => $productName});
+ }
+ $result = Bugzilla::Extension::ComponentWatching::_addComponentWatch($user,
+ $component);
}
+ }
- my $ra_componentNames = $params->{'component'};
- $ra_componentNames = [$ra_componentNames || ''] unless ref($ra_componentNames);
-
- if (grep { $_ eq '' } @$ra_componentNames) {
- # watching a product
- $result = Bugzilla::Extension::ComponentWatching::_addProductWatch($user, $product);
-
- } else {
- # watching specific components
- foreach my $componentName (@$ra_componentNames) {
- my $component = Bugzilla::Component->new({
- name => $componentName, product => $product, cache => 1
- });
- unless ($component) {
- ThrowUserError('product_access_denied', { product => $productName });
- }
- $result = Bugzilla::Extension::ComponentWatching::_addComponentWatch($user, $component);
- }
- }
-
- Bugzilla::Extension::ComponentWatching::_addDefaultSettings($user);
+ Bugzilla::Extension::ComponentWatching::_addDefaultSettings($user);
- return $result;
+ return $result;
}
sub get {
- my ($self, $params) = @_;
- my $user = Bugzilla->login(LOGIN_REQUIRED);
+ my ($self, $params) = @_;
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
- return Bugzilla::Extension::ComponentWatching::_getWatches($user, $params->{'id'});
+ return Bugzilla::Extension::ComponentWatching::_getWatches($user,
+ $params->{'id'});
}
sub remove {
- my ($self, $params) = @_;
- my $user = Bugzilla->login(LOGIN_REQUIRED);
- my %result = (status => Bugzilla::Extension::ComponentWatching::_deleteWatch($user, $params->{'id'}));
+ my ($self, $params) = @_;
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
+ my %result = (status =>
+ Bugzilla::Extension::ComponentWatching::_deleteWatch($user, $params->{'id'}));
- return \%result;
+ return \%result;
}
1;
diff --git a/extensions/ContributorEngagement/Config.pm b/extensions/ContributorEngagement/Config.pm
index d48de3bc6..5f46efdb0 100644
--- a/extensions/ContributorEngagement/Config.pm
+++ b/extensions/ContributorEngagement/Config.pm
@@ -13,10 +13,8 @@ use warnings;
use constant NAME => 'ContributorEngagement';
-use constant REQUIRED_MODULES => [
-];
+use constant REQUIRED_MODULES => [];
-use constant OPTIONAL_MODULES => [
-];
+use constant OPTIONAL_MODULES => [];
__PACKAGE__->NAME;
diff --git a/extensions/ContributorEngagement/Extension.pm b/extensions/ContributorEngagement/Extension.pm
index 35eba24ab..ae5c1b809 100644
--- a/extensions/ContributorEngagement/Extension.pm
+++ b/extensions/ContributorEngagement/Extension.pm
@@ -23,105 +23,110 @@ use Bugzilla::Extension::ContributorEngagement::Constants;
our $VERSION = '2.0';
BEGIN {
- *Bugzilla::User::first_patch_reviewed_id = \&_first_patch_reviewed_id;
+ *Bugzilla::User::first_patch_reviewed_id = \&_first_patch_reviewed_id;
}
sub _first_patch_reviewed_id { return $_[0]->{'first_patch_reviewed_id'}; }
sub install_update_db {
- my ($self) = @_;
- my $dbh = Bugzilla->dbh;
-
- if ($dbh->bz_column_info('profiles', 'first_patch_approved_id')) {
- $dbh->bz_drop_column('profiles', 'first_patch_approved_id');
- }
- if (!$dbh->bz_column_info('profiles', 'first_patch_reviewed_id')) {
- $dbh->bz_add_column('profiles', 'first_patch_reviewed_id', { TYPE => 'INT3' });
- _populate_first_reviewed_ids();
- }
+ my ($self) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ if ($dbh->bz_column_info('profiles', 'first_patch_approved_id')) {
+ $dbh->bz_drop_column('profiles', 'first_patch_approved_id');
+ }
+ if (!$dbh->bz_column_info('profiles', 'first_patch_reviewed_id')) {
+ $dbh->bz_add_column('profiles', 'first_patch_reviewed_id', {TYPE => 'INT3'});
+ _populate_first_reviewed_ids();
+ }
}
sub _populate_first_reviewed_ids {
- my $dbh = Bugzilla->dbh;
+ my $dbh = Bugzilla->dbh;
- my $sth = $dbh->prepare('UPDATE profiles SET first_patch_reviewed_id = ? WHERE userid = ?');
- my $ra = $dbh->selectall_arrayref("SELECT attachments.submitter_id,
+ my $sth = $dbh->prepare(
+ 'UPDATE profiles SET first_patch_reviewed_id = ? WHERE userid = ?');
+ my $ra = $dbh->selectall_arrayref(
+ "SELECT attachments.submitter_id,
attachments.attach_id
FROM attachments
INNER JOIN flags ON attachments.attach_id = flags.attach_id
INNER JOIN flagtypes ON flags.type_id = flagtypes.id
WHERE flagtypes.name LIKE 'review%' AND flags.status = '+'
- ORDER BY flags.modification_date");
- my $count = 1;
- my $total = scalar @$ra;
- my %user_seen;
- foreach my $ra_row (@$ra) {
- my ($user_id, $attach_id) = @$ra_row;
- indicate_progress({ current => $count++, total => $total, every => 25 });
- next if $user_seen{$user_id};
- $sth->execute($attach_id, $user_id);
- $user_seen{$user_id} = 1;
- }
-
- print "done\n";
+ ORDER BY flags.modification_date"
+ );
+ my $count = 1;
+ my $total = scalar @$ra;
+ my %user_seen;
+ foreach my $ra_row (@$ra) {
+ my ($user_id, $attach_id) = @$ra_row;
+ indicate_progress({current => $count++, total => $total, every => 25});
+ next if $user_seen{$user_id};
+ $sth->execute($attach_id, $user_id);
+ $user_seen{$user_id} = 1;
+ }
+
+ print "done\n";
}
sub object_columns {
- my ($self, $args) = @_;
- my ($class, $columns) = @$args{qw(class columns)};
- if ($class->isa('Bugzilla::User')) {
- my $dbh = Bugzilla->dbh;
- if ($dbh->bz_column_info($class->DB_TABLE, 'first_patch_reviewed_id')) {
- push @$columns, 'first_patch_reviewed_id';
- }
+ my ($self, $args) = @_;
+ my ($class, $columns) = @$args{qw(class columns)};
+ if ($class->isa('Bugzilla::User')) {
+ my $dbh = Bugzilla->dbh;
+ if ($dbh->bz_column_info($class->DB_TABLE, 'first_patch_reviewed_id')) {
+ push @$columns, 'first_patch_reviewed_id';
}
+ }
}
sub flag_end_of_update {
- my ($self, $args) = @_;
- my ($object, $timestamp, $new_flags) = @$args{qw(object timestamp new_flags)};
-
- if ($object->isa('Bugzilla::Attachment')
- && @$new_flags
- && !$object->attacher->first_patch_reviewed_id
- && grep($_ eq $object->bug->product, ENABLED_PRODUCTS))
- {
- my $attachment = $object;
-
- foreach my $orig_change (@$new_flags) {
- my $change = $orig_change;
- $change =~ s/^[^:]+://; # get rid of setter
- $change =~ s/\([^\)]+\)$//; # get rid of requestee
- my ($name, $value) = $change =~ /^(.+)(.)$/;
-
- # Only interested in review flags set to +
- next unless $name =~ /^review/ && $value eq '+';
-
- _send_mail($attachment, $timestamp);
-
- Bugzilla->dbh->do("UPDATE profiles SET first_patch_reviewed_id = ? WHERE userid = ?",
- undef, $attachment->id, $attachment->attacher->id);
- Bugzilla->memcached->clear({ table => 'profiles', id => $attachment->attacher->id });
- last;
- }
+ my ($self, $args) = @_;
+ my ($object, $timestamp, $new_flags) = @$args{qw(object timestamp new_flags)};
+
+ if ( $object->isa('Bugzilla::Attachment')
+ && @$new_flags
+ && !$object->attacher->first_patch_reviewed_id
+ && grep($_ eq $object->bug->product, ENABLED_PRODUCTS))
+ {
+ my $attachment = $object;
+
+ foreach my $orig_change (@$new_flags) {
+ my $change = $orig_change;
+ $change =~ s/^[^:]+://; # get rid of setter
+ $change =~ s/\([^\)]+\)$//; # get rid of requestee
+ my ($name, $value) = $change =~ /^(.+)(.)$/;
+
+ # Only interested in review flags set to +
+ next unless $name =~ /^review/ && $value eq '+';
+
+ _send_mail($attachment, $timestamp);
+
+ Bugzilla->dbh->do(
+ "UPDATE profiles SET first_patch_reviewed_id = ? WHERE userid = ?",
+ undef, $attachment->id, $attachment->attacher->id);
+ Bugzilla->memcached->clear(
+ {table => 'profiles', id => $attachment->attacher->id});
+ last;
}
+ }
}
sub _send_mail {
- my ($attachment, $timestamp) = @_;
+ my ($attachment, $timestamp) = @_;
- my $vars = {
- date => format_time($timestamp, '%a, %d %b %Y %T %z', 'UTC'),
- attachment => $attachment,
- from_user => EMAIL_FROM,
- };
+ my $vars = {
+ date => format_time($timestamp, '%a, %d %b %Y %T %z', 'UTC'),
+ attachment => $attachment,
+ from_user => EMAIL_FROM,
+ };
- my $msg;
- my $template = Bugzilla->template_inner($attachment->attacher->setting('lang'));
- $template->process("contributor/email.txt.tmpl", $vars, \$msg)
- || ThrowTemplateError($template->error());
+ my $msg;
+ my $template = Bugzilla->template_inner($attachment->attacher->setting('lang'));
+ $template->process("contributor/email.txt.tmpl", $vars, \$msg)
+ || ThrowTemplateError($template->error());
- MessageToMTA($msg);
+ MessageToMTA($msg);
}
__PACKAGE__->NAME;
diff --git a/extensions/ContributorEngagement/lib/Constants.pm b/extensions/ContributorEngagement/lib/Constants.pm
index dd379adcd..030a463a4 100644
--- a/extensions/ContributorEngagement/lib/Constants.pm
+++ b/extensions/ContributorEngagement/lib/Constants.pm
@@ -14,20 +14,17 @@ use warnings;
use base qw(Exporter);
our @EXPORT = qw(
- EMAIL_FROM
- ENABLED_PRODUCTS
+ EMAIL_FROM
+ ENABLED_PRODUCTS
);
use constant EMAIL_FROM => 'bugzilla-daemon@mozilla.org';
use constant ENABLED_PRODUCTS => (
- "Cloud Services",
- "Core",
- "Firefox for Android",
- "Firefox for Metro",
- "Firefox",
- "Testing",
- "Toolkit",
+ "Cloud Services", "Core",
+ "Firefox for Android", "Firefox for Metro",
+ "Firefox", "Testing",
+ "Toolkit",
);
1;
diff --git a/extensions/EditComments/Config.pm b/extensions/EditComments/Config.pm
index b95647940..e310dda84 100644
--- a/extensions/EditComments/Config.pm
+++ b/extensions/EditComments/Config.pm
@@ -11,7 +11,7 @@ use 5.10.1;
use strict;
use warnings;
-use constant NAME => 'EditComments';
+use constant NAME => 'EditComments';
use constant REQUIRED_MODULES => [];
use constant OPTIONAL_MODULES => [];
diff --git a/extensions/EditComments/Extension.pm b/extensions/EditComments/Extension.pm
index e2ace3f23..b7c398967 100644
--- a/extensions/EditComments/Extension.pm
+++ b/extensions/EditComments/Extension.pm
@@ -28,33 +28,36 @@ our $VERSION = '0.01';
################
sub db_schema_abstract_schema {
- my ($self, $args) = @_;
- my $schema = $args->{schema};
-
- $schema->{'longdescs_activity'} = {
- FIELDS => [
- comment_id => {TYPE => 'INT', NOTNULL => 1,
- REFERENCES => {TABLE => 'longdescs',
- COLUMN => 'comment_id',
- DELETE => 'CASCADE'}},
- who => {TYPE => 'INT3', NOTNULL => 1,
- REFERENCES => {TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'CASCADE'}},
- change_when => {TYPE => 'DATETIME', NOTNULL => 1},
- old_comment => {TYPE => 'LONGTEXT', NOTNULL => 1},
- ],
- INDEXES => [
- longdescs_activity_comment_id_idx => ['comment_id'],
- longdescs_activity_change_when_idx => ['change_when'],
- longdescs_activity_comment_id_change_when_idx => [qw(comment_id change_when)],
- ],
- };
+ my ($self, $args) = @_;
+ my $schema = $args->{schema};
+
+ $schema->{'longdescs_activity'} = {
+ FIELDS => [
+ comment_id => {
+ TYPE => 'INT',
+ NOTNULL => 1,
+ REFERENCES =>
+ {TABLE => 'longdescs', COLUMN => 'comment_id', DELETE => 'CASCADE'}
+ },
+ who => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'}
+ },
+ change_when => {TYPE => 'DATETIME', NOTNULL => 1},
+ old_comment => {TYPE => 'LONGTEXT', NOTNULL => 1},
+ ],
+ INDEXES => [
+ longdescs_activity_comment_id_idx => ['comment_id'],
+ longdescs_activity_change_when_idx => ['change_when'],
+ longdescs_activity_comment_id_change_when_idx => [qw(comment_id change_when)],
+ ],
+ };
}
sub install_update_db {
- my $dbh = Bugzilla->dbh;
- $dbh->bz_add_column('longdescs', 'edit_count', { TYPE => 'INT3', DEFAULT => 0 });
+ my $dbh = Bugzilla->dbh;
+ $dbh->bz_add_column('longdescs', 'edit_count', {TYPE => 'INT3', DEFAULT => 0});
}
####################
@@ -62,36 +65,33 @@ sub install_update_db {
####################
sub page_before_template {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- return if $args->{'page_id'} ne 'editcomments.html';
+ return if $args->{'page_id'} ne 'editcomments.html';
- my $vars = $args->{'vars'};
- my $user = Bugzilla->user;
- my $params = Bugzilla->input_params;
+ my $vars = $args->{'vars'};
+ my $user = Bugzilla->user;
+ my $params = Bugzilla->input_params;
- # validate group membership
- my $edit_comments_group = Bugzilla->params->{edit_comments_group};
- if (!$edit_comments_group || !$user->in_group($edit_comments_group)) {
- ThrowUserError('auth_failure', { group => $edit_comments_group,
- action => 'view',
- object => 'editcomments' });
- }
+ # validate group membership
+ my $edit_comments_group = Bugzilla->params->{edit_comments_group};
+ if (!$edit_comments_group || !$user->in_group($edit_comments_group)) {
+ ThrowUserError('auth_failure',
+ {group => $edit_comments_group, action => 'view', object => 'editcomments'});
+ }
- my $bug_id = $params->{bug_id};
- my $bug = Bugzilla::Bug->check($bug_id);
+ my $bug_id = $params->{bug_id};
+ my $bug = Bugzilla::Bug->check($bug_id);
- my $comment_id = $params->{comment_id};
+ my $comment_id = $params->{comment_id};
- my ($comment) = grep($_->id == $comment_id, @{ $bug->comments });
- if (!$comment
- || ($comment->is_private && !$user->is_insider))
- {
- ThrowUserError("edit_comment_invalid_comment_id", { comment_id => $comment_id });
- }
+ my ($comment) = grep($_->id == $comment_id, @{$bug->comments});
+ if (!$comment || ($comment->is_private && !$user->is_insider)) {
+ ThrowUserError("edit_comment_invalid_comment_id", {comment_id => $comment_id});
+ }
- $vars->{'bug'} = $bug;
- $vars->{'comment'} = $comment;
+ $vars->{'bug'} = $bug;
+ $vars->{'comment'} = $comment;
}
##################
@@ -99,88 +99,94 @@ sub page_before_template {
##################
BEGIN {
- no warnings 'redefine';
- *Bugzilla::Comment::activity = \&_get_activity;
- *Bugzilla::Comment::edit_count = \&_edit_count;
- *Bugzilla::WebService::Bug::_super_translate_comment = \&Bugzilla::WebService::Bug::_translate_comment;
- *Bugzilla::WebService::Bug::_translate_comment = \&_new_translate_comment;
+ no warnings 'redefine';
+ *Bugzilla::Comment::activity = \&_get_activity;
+ *Bugzilla::Comment::edit_count = \&_edit_count;
+ *Bugzilla::WebService::Bug::_super_translate_comment
+ = \&Bugzilla::WebService::Bug::_translate_comment;
+ *Bugzilla::WebService::Bug::_translate_comment = \&_new_translate_comment;
}
sub _new_translate_comment {
- my ($self, $comment, $filters) = @_;
+ my ($self, $comment, $filters) = @_;
- my $comment_hash = $self->_super_translate_comment($comment, $filters);
+ my $comment_hash = $self->_super_translate_comment($comment, $filters);
- if (filter_wants $filters, 'raw_text') {
- $comment_hash->{raw_text} = $self->type('string', $comment->body);
- }
+ if (filter_wants $filters, 'raw_text') {
+ $comment_hash->{raw_text} = $self->type('string', $comment->body);
+ }
- return $comment_hash;
+ return $comment_hash;
}
sub _edit_count { return $_[0]->{'edit_count'}; }
sub _get_activity {
- my ($self, $activity_sort_order) = @_;
+ my ($self, $activity_sort_order) = @_;
- return $self->{'activity'} if $self->{'activity'};
+ return $self->{'activity'} if $self->{'activity'};
- my $dbh = Bugzilla->dbh;
- my $query = 'SELECT longdescs_activity.comment_id AS id, profiles.userid, ' .
- $dbh->sql_date_format('longdescs_activity.change_when', '%Y.%m.%d %H:%i:%s') . '
+ my $dbh = Bugzilla->dbh;
+ my $query
+ = 'SELECT longdescs_activity.comment_id AS id, profiles.userid, '
+ . $dbh->sql_date_format('longdescs_activity.change_when', '%Y.%m.%d %H:%i:%s')
+ . '
AS time, longdescs_activity.old_comment AS old
FROM longdescs_activity
INNER JOIN profiles
ON profiles.userid = longdescs_activity.who
WHERE longdescs_activity.comment_id = ?';
- $query .= " ORDER BY longdescs_activity.change_when DESC";
- my $sth = $dbh->prepare($query);
- $sth->execute($self->id);
-
- # We are shifting each comment activity body 1 back. The reason this
- # has to be done is that the longdescs_activity table stores the comment
- # body that the comment was before the edit, not the actual new version
- # of the comment.
- my @activity;
- my $new_comment;
- my $last_old_comment;
- my $count = 0;
- while (my $change_ref = $sth->fetchrow_hashref()) {
- my %change = %$change_ref;
- $change{'author'} = Bugzilla::User->new({ id => $change{'userid'}, cache => 1 });
- if ($count == 0) {
- $change{new} = $self->body;
- }
- else {
- $change{new} = $new_comment;
- }
- $new_comment = $change{old};
- $last_old_comment = $change{old};
- push (@activity, \%change);
- $count++;
+ $query .= " ORDER BY longdescs_activity.change_when DESC";
+ my $sth = $dbh->prepare($query);
+ $sth->execute($self->id);
+
+ # We are shifting each comment activity body 1 back. The reason this
+ # has to be done is that the longdescs_activity table stores the comment
+ # body that the comment was before the edit, not the actual new version
+ # of the comment.
+ my @activity;
+ my $new_comment;
+ my $last_old_comment;
+ my $count = 0;
+ while (my $change_ref = $sth->fetchrow_hashref()) {
+ my %change = %$change_ref;
+ $change{'author'} = Bugzilla::User->new({id => $change{'userid'}, cache => 1});
+ if ($count == 0) {
+ $change{new} = $self->body;
}
+ else {
+ $change{new} = $new_comment;
+ }
+ $new_comment = $change{old};
+ $last_old_comment = $change{old};
+ push(@activity, \%change);
+ $count++;
+ }
+
+ return [] if !@activity;
+
+ # Store the original comment as the first or last entry
+ # depending on sort order
+ push(
+ @activity,
+ {
+ author => $self->author,
+ body => $last_old_comment,
+ time => $self->creation_ts,
+ original => 1
+ }
+ );
- return [] if !@activity;
-
- # Store the original comment as the first or last entry
- # depending on sort order
- push(@activity, {
- author => $self->author,
- body => $last_old_comment,
- time => $self->creation_ts,
- original => 1
- });
-
- $activity_sort_order
- ||= Bugzilla->user->settings->{'comment_sort_order'}->{'value'};
+ $activity_sort_order
+ ||= Bugzilla->user->settings->{'comment_sort_order'}->{'value'};
- if ($activity_sort_order eq "oldest_to_newest") {
- @activity = reverse @activity;
- }
+ if ($activity_sort_order eq "oldest_to_newest") {
+ @activity = reverse @activity;
+ }
- $self->{'activity'} = \@activity;
+ $self->{'activity'} = \@activity;
- return $self->{'activity'};
+ return $self->{'activity'};
}
#########
@@ -188,86 +194,91 @@ sub _get_activity {
#########
sub object_columns {
- my ($self, $args) = @_;
- my ($class, $columns) = @$args{qw(class columns)};
- if ($class->isa('Bugzilla::Comment')) {
- if (Bugzilla->dbh->bz_column_info($class->DB_TABLE, 'edit_count')) {
- push @$columns, 'edit_count';
- }
+ my ($self, $args) = @_;
+ my ($class, $columns) = @$args{qw(class columns)};
+ if ($class->isa('Bugzilla::Comment')) {
+ if (Bugzilla->dbh->bz_column_info($class->DB_TABLE, 'edit_count')) {
+ push @$columns, 'edit_count';
}
+ }
}
sub bug_end_of_update {
- my ($self, $args) = @_;
-
- # Silently return if not in the proper group
- # or if editing comments is disabled
- my $user = Bugzilla->user;
- my $edit_comments_group = Bugzilla->params->{edit_comments_group};
- return if (!$edit_comments_group || !$user->in_group($edit_comments_group));
-
- my $bug = $args->{bug};
- my $timestamp = $args->{timestamp};
- my $params = Bugzilla->input_params;
- my $dbh = Bugzilla->dbh;
-
- my $updated = 0;
- foreach my $param (grep(/^edit_comment_textarea_/, keys %$params)) {
- my ($comment_id) = $param =~ /edit_comment_textarea_(\d+)$/;
- next if !detaint_natural($comment_id);
-
- # The comment ID must belong to this bug.
- my ($comment_obj) = grep($_->id == $comment_id, @{ $bug->comments});
- next if (!$comment_obj || ($comment_obj->is_private && !$user->is_insider));
-
- my $new_comment = $comment_obj->_check_thetext($params->{$param});
-
- my $old_comment = $comment_obj->body;
- next if $old_comment eq $new_comment;
-
- trick_taint($new_comment);
- $dbh->do("UPDATE longdescs SET thetext = ?, edit_count = edit_count + 1
- WHERE comment_id = ?",
- undef, $new_comment, $comment_id);
- Bugzilla->memcached->clear({ table => 'longdescs', id => $comment_id });
-
- # Log old comment to the longdescs activity table
- $timestamp ||= $dbh->selectrow_array("SELECT NOW()");
- $dbh->do("INSERT INTO longdescs_activity " .
- "(comment_id, who, change_when, old_comment) " .
- "VALUES (?, ?, ?, ?)",
- undef, ($comment_id, $user->id, $timestamp, $old_comment));
-
- $comment_obj->{thetext} = $new_comment;
-
- $updated = 1;
- }
-
- $bug->_sync_fulltext( update_comments => 1 ) if $updated;
+ my ($self, $args) = @_;
+
+ # Silently return if not in the proper group
+ # or if editing comments is disabled
+ my $user = Bugzilla->user;
+ my $edit_comments_group = Bugzilla->params->{edit_comments_group};
+ return if (!$edit_comments_group || !$user->in_group($edit_comments_group));
+
+ my $bug = $args->{bug};
+ my $timestamp = $args->{timestamp};
+ my $params = Bugzilla->input_params;
+ my $dbh = Bugzilla->dbh;
+
+ my $updated = 0;
+ foreach my $param (grep(/^edit_comment_textarea_/, keys %$params)) {
+ my ($comment_id) = $param =~ /edit_comment_textarea_(\d+)$/;
+ next if !detaint_natural($comment_id);
+
+ # The comment ID must belong to this bug.
+ my ($comment_obj) = grep($_->id == $comment_id, @{$bug->comments});
+ next if (!$comment_obj || ($comment_obj->is_private && !$user->is_insider));
+
+ my $new_comment = $comment_obj->_check_thetext($params->{$param});
+
+ my $old_comment = $comment_obj->body;
+ next if $old_comment eq $new_comment;
+
+ trick_taint($new_comment);
+ $dbh->do(
+ "UPDATE longdescs SET thetext = ?, edit_count = edit_count + 1
+ WHERE comment_id = ?", undef, $new_comment, $comment_id
+ );
+ Bugzilla->memcached->clear({table => 'longdescs', id => $comment_id});
+
+ # Log old comment to the longdescs activity table
+ $timestamp ||= $dbh->selectrow_array("SELECT NOW()");
+ $dbh->do(
+ "INSERT INTO longdescs_activity "
+ . "(comment_id, who, change_when, old_comment) "
+ . "VALUES (?, ?, ?, ?)",
+ undef,
+ ($comment_id, $user->id, $timestamp, $old_comment)
+ );
+
+ $comment_obj->{thetext} = $new_comment;
+
+ $updated = 1;
+ }
+
+ $bug->_sync_fulltext(update_comments => 1) if $updated;
}
sub config_modify_panels {
- my ($self, $args) = @_;
- push @{ $args->{panels}->{groupsecurity}->{params} }, {
- name => 'edit_comments_group',
- type => 's',
- choices => \&get_all_group_names,
- default => 'admin',
- checker => \&check_group
+ my ($self, $args) = @_;
+ push @{$args->{panels}->{groupsecurity}->{params}},
+ {
+ name => 'edit_comments_group',
+ type => 's',
+ choices => \&get_all_group_names,
+ default => 'admin',
+ checker => \&check_group
};
}
sub webservice {
- my ($self, $args) = @_;
- my $dispatch = $args->{dispatch};
- $dispatch->{EditComments} = "Bugzilla::Extension::EditComments::WebService";
+ my ($self, $args) = @_;
+ my $dispatch = $args->{dispatch};
+ $dispatch->{EditComments} = "Bugzilla::Extension::EditComments::WebService";
}
sub db_sanitize {
- my $dbh = Bugzilla->dbh;
- print "Deleting edited comment histories...\n";
- $dbh->do("DELETE FROM longdescs_activity");
- $dbh->do("UPDATE longdescs SET edit_count=0");
+ my $dbh = Bugzilla->dbh;
+ print "Deleting edited comment histories...\n";
+ $dbh->do("DELETE FROM longdescs_activity");
+ $dbh->do("UPDATE longdescs SET edit_count=0");
}
__PACKAGE__->NAME;
diff --git a/extensions/EditComments/lib/WebService.pm b/extensions/EditComments/lib/WebService.pm
index 6969ca742..97e40b8b6 100644
--- a/extensions/EditComments/lib/WebService.pm
+++ b/extensions/EditComments/lib/WebService.pm
@@ -18,64 +18,61 @@ use Bugzilla::Util qw(trim);
use Bugzilla::WebService::Util qw(validate);
use constant PUBLIC_METHODS => qw(
- comments
+ comments
);
sub comments {
- my ($self, $params) = validate(@_, 'comment_ids');
- my $dbh = Bugzilla->switch_to_shadow_db();
- my $user = Bugzilla->user;
-
- if (!defined $params->{comment_ids}) {
- ThrowCodeError('param_required',
- { function => 'Bug.comments',
- param => 'comment_ids' });
+ my ($self, $params) = validate(@_, 'comment_ids');
+ my $dbh = Bugzilla->switch_to_shadow_db();
+ my $user = Bugzilla->user;
+
+ if (!defined $params->{comment_ids}) {
+ ThrowCodeError('param_required',
+ {function => 'Bug.comments', param => 'comment_ids'});
+ }
+
+ my @ids = map { trim($_) } @{$params->{comment_ids} || []};
+ my $comment_data = Bugzilla::Comment->new_from_list(\@ids);
+
+ # See if we were passed any invalid comment ids.
+ my %got_ids = map { $_->id => 1 } @$comment_data;
+ foreach my $comment_id (@ids) {
+ if (!$got_ids{$comment_id}) {
+ ThrowUserError('comment_id_invalid', {id => $comment_id});
}
+ }
- my @ids = map { trim($_) } @{ $params->{comment_ids} || [] };
- my $comment_data = Bugzilla::Comment->new_from_list(\@ids);
+ # Now make sure that we can see all the associated bugs.
+ my %got_bug_ids = map { $_->bug_id => 1 } @$comment_data;
+ $user->visible_bugs([keys %got_bug_ids]); # preload cache for visibility check
+ Bugzilla::Bug->check($_) foreach (keys %got_bug_ids);
- # See if we were passed any invalid comment ids.
- my %got_ids = map { $_->id => 1 } @$comment_data;
- foreach my $comment_id (@ids) {
- if (!$got_ids{$comment_id}) {
- ThrowUserError('comment_id_invalid', { id => $comment_id });
- }
+ my %comments;
+ foreach my $comment (@$comment_data) {
+ if ($comment->is_private && !$user->is_insider) {
+ ThrowUserError('comment_is_private', {id => $comment->id});
}
+ $comments{$comment->id} = $comment->body;
+ }
- # Now make sure that we can see all the associated bugs.
- my %got_bug_ids = map { $_->bug_id => 1 } @$comment_data;
- $user->visible_bugs([ keys %got_bug_ids ]); # preload cache for visibility check
- Bugzilla::Bug->check($_) foreach (keys %got_bug_ids);
-
- my %comments;
- foreach my $comment (@$comment_data) {
- if ($comment->is_private && !$user->is_insider) {
- ThrowUserError('comment_is_private', { id => $comment->id });
- }
- $comments{$comment->id} = $comment->body;
- }
-
- return { comments => \%comments };
+ return {comments => \%comments};
}
sub rest_resources {
- return [
- qr{^/editcomments/comment/(\d+)$}, {
- GET => {
- method => 'comments',
- params => sub {
- return { comment_ids => $_[0] };
- },
- },
+ return [
+ qr{^/editcomments/comment/(\d+)$},
+ {
+ GET => {
+ method => 'comments',
+ params => sub {
+ return {comment_ids => $_[0]};
},
- qr{^/editcomments/comment$}, {
- GET => {
- method => 'comments',
- },
- },
- ];
-};
+ },
+ },
+ qr{^/editcomments/comment$},
+ {GET => {method => 'comments',},},
+ ];
+}
1;
diff --git a/extensions/EditTable/Config.pm b/extensions/EditTable/Config.pm
index b9bd003b2..f62e5f0c0 100644
--- a/extensions/EditTable/Config.pm
+++ b/extensions/EditTable/Config.pm
@@ -11,7 +11,7 @@ use 5.10.1;
use strict;
use warnings;
-use constant NAME => 'EditTable';
+use constant NAME => 'EditTable';
use constant REQUIRED_MODULES => [];
use constant OPTIONAL_MODULES => [];
diff --git a/extensions/EditTable/Extension.pm b/extensions/EditTable/Extension.pm
index 1eb0d82f9..a6d01be65 100644
--- a/extensions/EditTable/Extension.pm
+++ b/extensions/EditTable/Extension.pm
@@ -49,137 +49,128 @@ our $VERSION = '1';
# },
sub EDITABLE_TABLES {
- my $tables = {};
- Bugzilla::Hook::process("editable_tables", { tables => $tables });
- return $tables;
+ my $tables = {};
+ Bugzilla::Hook::process("editable_tables", {tables => $tables});
+ return $tables;
}
sub page_before_template {
- my ($self, $args) = @_;
- my ($vars, $page) = @$args{qw(vars page_id)};
- return unless $page eq 'edit_table.html';
- my $input = Bugzilla->input_params;
-
- # we only support editing a particular set of tables
- my $table_name = $input->{table};
- exists $self->EDITABLE_TABLES()->{$table_name}
- || ThrowUserError('edittable_unsupported', { table => $table_name } );
- my $table = $self->EDITABLE_TABLES()->{$table_name};
- my $id_field = $table->{id_field};
- my $order_by = $table->{order_by} || $id_field;
- my $group = $table->{group} || 'admin';
- trick_taint($table_name);
-
- Bugzilla->user->in_group($group)
- || ThrowUserError('auth_failure', { group => $group,
- action => 'edit',
- object => 'tables' });
-
- # load columns
- my $dbh = Bugzilla->dbh;
- my @fields = sort
- grep { $_ ne $id_field && $_ ne $order_by; }
- $dbh->bz_table_columns($table_name);
- if ($order_by ne $id_field) {
- unshift @fields, $order_by;
- }
+ my ($self, $args) = @_;
+ my ($vars, $page) = @$args{qw(vars page_id)};
+ return unless $page eq 'edit_table.html';
+ my $input = Bugzilla->input_params;
+
+ # we only support editing a particular set of tables
+ my $table_name = $input->{table};
+ exists $self->EDITABLE_TABLES()->{$table_name}
+ || ThrowUserError('edittable_unsupported', {table => $table_name});
+ my $table = $self->EDITABLE_TABLES()->{$table_name};
+ my $id_field = $table->{id_field};
+ my $order_by = $table->{order_by} || $id_field;
+ my $group = $table->{group} || 'admin';
+ trick_taint($table_name);
+
+ Bugzilla->user->in_group($group)
+ || ThrowUserError('auth_failure',
+ {group => $group, action => 'edit', object => 'tables'});
+
+ # load columns
+ my $dbh = Bugzilla->dbh;
+ my @fields = sort grep { $_ ne $id_field && $_ ne $order_by; }
+ $dbh->bz_table_columns($table_name);
+ if ($order_by ne $id_field) {
+ unshift @fields, $order_by;
+ }
+
+ # update table
+ my $data = $input->{table_data};
+ my $edits = [];
+ if ($data) {
+ check_hash_token($input->{token}, [$table_name]);
+
+ $data = from_json($data)->{data};
+ $edits = dclone($data);
+ eval {
+ $dbh->bz_start_transaction;
+
+ foreach my $row (@$data) {
+ map { trick_taint($_) } @$row;
+ if ($row->[0] eq '-') {
+
+ # add
+ shift @$row;
+ next unless grep { $_ ne '' } @$row;
+ my $placeholders = join(',', split(//, '?' x scalar(@fields)));
+ $dbh->do(
+ "INSERT INTO $table_name("
+ . join(',', @fields) . ") "
+ . "VALUES ($placeholders)",
+ undef, @$row
+ );
+ }
+ elsif ($row->[0] < 0) {
- # update table
- my $data = $input->{table_data};
- my $edits = [];
- if ($data) {
- check_hash_token($input->{token}, [$table_name]);
-
- $data = from_json($data)->{data};
- $edits = dclone($data);
- eval {
- $dbh->bz_start_transaction;
-
- foreach my $row (@$data) {
- map { trick_taint($_) } @$row;
- if ($row->[0] eq '-') {
- # add
- shift @$row;
- next unless grep { $_ ne '' } @$row;
- my $placeholders = join(',', split(//, '?' x scalar(@fields)));
- $dbh->do(
- "INSERT INTO $table_name(" . join(',', @fields) . ") " .
- "VALUES ($placeholders)",
- undef,
- @$row
- );
- }
- elsif ($row->[0] < 0) {
- # delete
- $dbh->do(
- "DELETE FROM $table_name WHERE $id_field=?",
- undef,
- -$row->[0]
- );
- }
- else {
- # update
- my $id = shift @$row;
- $dbh->do(
- "UPDATE $table_name " .
- "SET " . join(',', map { "$_ = ?" } @fields) . " " .
- "WHERE $id_field = ?",
- undef,
- @$row, $id
- );
- }
- }
-
- $dbh->bz_commit_transaction;
- $vars->{updated} = 1;
- $edits = [];
- };
- if ($@) {
- my $error = $@;
- $error =~ s/^DBD::[^:]+::db do failed: //;
- $error =~ s/^(.+) \[for Statement ".+$/$1/s;
- $vars->{error} = $error;
- $dbh->bz_rollback_transaction;
+ # delete
+ $dbh->do("DELETE FROM $table_name WHERE $id_field=?", undef, -$row->[0]);
+ }
+ else {
+ # update
+ my $id = shift @$row;
+ $dbh->do(
+ "UPDATE $table_name " . "SET "
+ . join(',', map {"$_ = ?"} @fields) . " "
+ . "WHERE $id_field = ?",
+ undef, @$row, $id
+ );
}
+ }
+
+ $dbh->bz_commit_transaction;
+ $vars->{updated} = 1;
+ $edits = [];
+ };
+ if ($@) {
+ my $error = $@;
+ $error =~ s/^DBD::[^:]+::db do failed: //;
+ $error =~ s/^(.+) \[for Statement ".+$/$1/s;
+ $vars->{error} = $error;
+ $dbh->bz_rollback_transaction;
}
+ }
- # load data from table
- unshift @fields, $id_field;
- $data = $dbh->selectall_arrayref(
- "SELECT " . join(',', @fields) . " FROM $table_name ORDER BY $order_by"
- );
+ # load data from table
+ unshift @fields, $id_field;
+ $data = $dbh->selectall_arrayref(
+ "SELECT " . join(',', @fields) . " FROM $table_name ORDER BY $order_by");
- # we don't support nulls currently
- foreach my $row (@$data) {
- if (grep { !defined($_) } @$row) {
- ThrowUserError('edittable_nulls', { table => $table_name } );
- }
+ # we don't support nulls currently
+ foreach my $row (@$data) {
+ if (grep { !defined($_) } @$row) {
+ ThrowUserError('edittable_nulls', {table => $table_name});
}
+ }
- # apply failed edits
- foreach my $edit (@$edits) {
- if ($edit->[0] eq '-') {
- push @$data, $edit;
- }
- else {
- my $id = $edit->[0];
- foreach my $row (@$data) {
- if ($row->[0] == $id) {
- @$row = @$edit;
- last;
- }
- }
+ # apply failed edits
+ foreach my $edit (@$edits) {
+ if ($edit->[0] eq '-') {
+ push @$data, $edit;
+ }
+ else {
+ my $id = $edit->[0];
+ foreach my $row (@$data) {
+ if ($row->[0] == $id) {
+ @$row = @$edit;
+ last;
}
+ }
}
+ }
- $vars->{table_name} = $table_name;
- $vars->{blurb} = $table->{blurb};
- $vars->{token} = issue_hash_token([$table_name]);
- $vars->{table_data} = to_json({
- fields => \@fields,
- id_field => $id_field,
- data => $data,
- });
+ $vars->{table_name} = $table_name;
+ $vars->{blurb} = $table->{blurb};
+ $vars->{token} = issue_hash_token([$table_name]);
+ $vars->{table_data}
+ = to_json({fields => \@fields, id_field => $id_field, data => $data,});
}
__PACKAGE__->NAME;
diff --git a/extensions/Ember/Extension.pm b/extensions/Ember/Extension.pm
index 8bb558c6f..d3e322919 100644
--- a/extensions/Ember/Extension.pm
+++ b/extensions/Ember/Extension.pm
@@ -16,9 +16,9 @@ use parent qw(Bugzilla::Extension);
our $VERSION = '0.01';
sub webservice {
- my ($self, $args) = @_;
- my $dispatch = $args->{dispatch};
- $dispatch->{Ember} = "Bugzilla::Extension::Ember::WebService";
+ my ($self, $args) = @_;
+ my $dispatch = $args->{dispatch};
+ $dispatch->{Ember} = "Bugzilla::Extension::Ember::WebService";
}
__PACKAGE__->NAME;
diff --git a/extensions/Ember/lib/FakeBug.pm b/extensions/Ember/lib/FakeBug.pm
index 46fef4ea7..32cbb5287 100644
--- a/extensions/Ember/lib/FakeBug.pm
+++ b/extensions/Ember/lib/FakeBug.pm
@@ -16,62 +16,64 @@ use Bugzilla::Bug;
our $AUTOLOAD;
sub new {
- my $class = shift;
- my $self = shift;
- bless $self, $class;
- return $self;
+ my $class = shift;
+ my $self = shift;
+ bless $self, $class;
+ return $self;
}
sub AUTOLOAD {
- my $self = shift;
- my $name = $AUTOLOAD;
- $name =~ s/.*://;
- return exists $self->{$name} ? $self->{$name} : undef;
+ my $self = shift;
+ my $name = $AUTOLOAD;
+ $name =~ s/.*://;
+ return exists $self->{$name} ? $self->{$name} : undef;
}
sub check_can_change_field {
- return Bugzilla::Bug::check_can_change_field(@_);
+ return Bugzilla::Bug::check_can_change_field(@_);
}
-sub id { return undef; }
+sub id { return undef; }
sub product_obj { return $_[0]->{product_obj}; }
-sub reporter { return Bugzilla->user; }
+sub reporter { return Bugzilla->user; }
sub choices {
- my $self = shift;
- return $self->{'choices'} if exists $self->{'choices'};
- return {} if $self->{'error'};
- my $user = Bugzilla->user;
-
- my @products = @{ $user->get_enterable_products };
- # The current product is part of the popup, even if new bugs are no longer
- # allowed for that product
- if (!grep($_->name eq $self->product_obj->name, @products)) {
- unshift(@products, $self->product_obj);
- }
-
- my @statuses = @{ Bugzilla::Status->can_change_to };
-
- # UNCONFIRMED is only a valid status if it is enabled in this product.
- if (!$self->product_obj->allows_unconfirmed) {
- @statuses = grep { $_->name ne 'UNCONFIRMED' } @statuses;
- }
-
- my %choices = (
- bug_status => \@statuses,
- product => \@products,
- component => $self->product_obj->components,
- version => $self->product_obj->versions,
- target_milestone => $self->product_obj->milestones,
- );
-
- my $resolution_field = new Bugzilla::Field({ name => 'resolution' });
- # Don't include the empty resolution in drop-downs.
- my @resolutions = grep($_->name, @{ $resolution_field->legal_values });
- $choices{'resolution'} = \@resolutions;
-
- $self->{'choices'} = \%choices;
- return $self->{'choices'};
+ my $self = shift;
+ return $self->{'choices'} if exists $self->{'choices'};
+ return {} if $self->{'error'};
+ my $user = Bugzilla->user;
+
+ my @products = @{$user->get_enterable_products};
+
+ # The current product is part of the popup, even if new bugs are no longer
+ # allowed for that product
+ if (!grep($_->name eq $self->product_obj->name, @products)) {
+ unshift(@products, $self->product_obj);
+ }
+
+ my @statuses = @{Bugzilla::Status->can_change_to};
+
+ # UNCONFIRMED is only a valid status if it is enabled in this product.
+ if (!$self->product_obj->allows_unconfirmed) {
+ @statuses = grep { $_->name ne 'UNCONFIRMED' } @statuses;
+ }
+
+ my %choices = (
+ bug_status => \@statuses,
+ product => \@products,
+ component => $self->product_obj->components,
+ version => $self->product_obj->versions,
+ target_milestone => $self->product_obj->milestones,
+ );
+
+ my $resolution_field = new Bugzilla::Field({name => 'resolution'});
+
+ # Don't include the empty resolution in drop-downs.
+ my @resolutions = grep($_->name, @{$resolution_field->legal_values});
+ $choices{'resolution'} = \@resolutions;
+
+ $self->{'choices'} = \%choices;
+ return $self->{'choices'};
}
1;
diff --git a/extensions/Ember/lib/WebService.pm b/extensions/Ember/lib/WebService.pm
index 10c828537..6ad33cd81 100644
--- a/extensions/Ember/lib/WebService.pm
+++ b/extensions/Ember/lib/WebService.pm
@@ -12,8 +12,8 @@ use strict;
use warnings;
use parent qw(Bugzilla::WebService
- Bugzilla::WebService::Bug
- Bugzilla::WebService::Product);
+ Bugzilla::WebService::Bug
+ Bugzilla::WebService::Product);
use Bugzilla::Bug;
use Bugzilla::Component;
@@ -29,69 +29,68 @@ use Scalar::Util qw(blessed);
use Storable qw(dclone);
use constant PUBLIC_METHODS => qw(
- bug
- create
- get_attachments
- search
- show
+ bug
+ create
+ get_attachments
+ search
+ show
);
-use constant DATE_FIELDS => {
- show => ['last_updated'],
-};
+use constant DATE_FIELDS => {show => ['last_updated'],};
use constant FIELD_TYPE_MAP => {
- 0 => 'unknown',
- 1 => 'freetext',
- 2 => 'single_select',
- 3 => 'multiple_select',
- 4 => 'textarea',
- 5 => 'datetime',
- 6 => 'date',
- 7 => 'bug_id',
- 8 => 'bug_urls',
- 9 => 'keywords',
- 99 => 'extension'
+ 0 => 'unknown',
+ 1 => 'freetext',
+ 2 => 'single_select',
+ 3 => 'multiple_select',
+ 4 => 'textarea',
+ 5 => 'datetime',
+ 6 => 'date',
+ 7 => 'bug_id',
+ 8 => 'bug_urls',
+ 9 => 'keywords',
+ 99 => 'extension'
};
use constant NON_EDIT_FIELDS => qw(
- assignee_accessible
- bug_group
- bug_id
- commenter
- cclist_accessible
- content
- creation_ts
- days_elapsed
- everconfirmed
- qacontact_accessible
- reporter
- reporter_accessible
- restrict_comments
- tag
- votes
+ assignee_accessible
+ bug_group
+ bug_id
+ commenter
+ cclist_accessible
+ content
+ creation_ts
+ days_elapsed
+ everconfirmed
+ qacontact_accessible
+ reporter
+ reporter_accessible
+ restrict_comments
+ tag
+ votes
);
use constant BUG_CHOICE_FIELDS => qw(
- bug_status
- component
- product
- resolution
- target_milestone
- version
+ bug_status
+ component
+ product
+ resolution
+ target_milestone
+ version
);
use constant DEFAULT_VALUE_MAP => {
- op_sys => 'defaultopsys',
- rep_platform => 'defaultplatform',
- priority => 'defaultpriority',
- bug_severity => 'defaultseverity'
+ op_sys => 'defaultopsys',
+ rep_platform => 'defaultplatform',
+ priority => 'defaultpriority',
+ bug_severity => 'defaultseverity'
};
sub API_NAMES {
- # Internal field names converted to the API equivalents
- my %api_names = reverse %{ Bugzilla::Bug::FIELD_MAP() };
- return \%api_names;
+
+ # Internal field names converted to the API equivalents
+ my %api_names = reverse %{Bugzilla::Bug::FIELD_MAP()};
+ return \%api_names;
}
###############
@@ -99,227 +98,221 @@ sub API_NAMES {
###############
sub create {
- my ($self, $params) = @_;
+ my ($self, $params) = @_;
- Bugzilla->login(LOGIN_REQUIRED);
- Bugzilla->switch_to_shadow_db();
+ Bugzilla->login(LOGIN_REQUIRED);
+ Bugzilla->switch_to_shadow_db();
- my $product = delete $params->{product};
- $product || ThrowCodeError('params_required',
- { function => 'Ember.create', params => ['product'] });
+ my $product = delete $params->{product};
+ $product
+ || ThrowCodeError('params_required',
+ {function => 'Ember.create', params => ['product']});
- my $product_obj = Bugzilla::Product->check($product);
+ my $product_obj = Bugzilla::Product->check($product);
- my $fake_bug = Bugzilla::Extension::Ember::FakeBug->new(
- { product_obj => $product_obj, reporter_id => Bugzilla->user->id });
+ my $fake_bug = Bugzilla::Extension::Ember::FakeBug->new(
+ {product_obj => $product_obj, reporter_id => Bugzilla->user->id});
- my @fields = $self->_get_fields($fake_bug);
+ my @fields = $self->_get_fields($fake_bug);
- return {
- fields => \@fields
- };
+ return {fields => \@fields};
}
sub show {
- my ($self, $params) = @_;
- my (@fields, $attachments, $comments, $data);
- my $dbh = Bugzilla->dbh;
- my $user = Bugzilla->user;
+ my ($self, $params) = @_;
+ my (@fields, $attachments, $comments, $data);
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->user;
- Bugzilla->switch_to_shadow_db();
+ Bugzilla->switch_to_shadow_db();
- # Throw error if token was provided and user is not logged
- # in meaning token was invalid/expired.
- if (exists $params->{token} && !$user->id) {
- ThrowUserError('invalid_token');
- }
+ # Throw error if token was provided and user is not logged
+ # in meaning token was invalid/expired.
+ if (exists $params->{token} && !$user->id) {
+ ThrowUserError('invalid_token');
+ }
- my $bug_id = delete $params->{id};
- $bug_id || ThrowCodeError('params_required',
- { function => 'Ember.show', params => ['id'] });
+ my $bug_id = delete $params->{id};
+ $bug_id
+ || ThrowCodeError('params_required',
+ {function => 'Ember.show', params => ['id']});
- my $bug = Bugzilla::Bug->check($bug_id);
+ my $bug = Bugzilla::Bug->check($bug_id);
- my $bug_hash = $self->_bug_to_hash($bug, $params);
+ my $bug_hash = $self->_bug_to_hash($bug, $params);
- # Only return changes since last_updated if provided
- my $last_updated = delete $params->{last_updated};
- if ($last_updated) {
- trick_taint($last_updated);
+ # Only return changes since last_updated if provided
+ my $last_updated = delete $params->{last_updated};
+ if ($last_updated) {
+ trick_taint($last_updated);
- my $updated_fields =
- $dbh->selectcol_arrayref('SELECT fieldid FROM bugs_activity
- WHERE bug_when > ? AND bug_id = ?',
- undef, ($last_updated, $bug->id));
+ my $updated_fields = $dbh->selectcol_arrayref(
+ 'SELECT fieldid FROM bugs_activity
+ WHERE bug_when > ? AND bug_id = ?', undef,
+ ($last_updated, $bug->id)
+ );
- if (@$updated_fields) {
- # Also add in the delta_ts value which is in the bugs_activity
- # entries
- push(@$updated_fields, get_field_id('delta_ts'));
- @fields = $self->_get_fields($bug, $updated_fields);
- }
- }
- # Return all the things
- else {
- @fields = $self->_get_fields($bug);
- }
+ if (@$updated_fields) {
- # Place the fields current value along with the field definition
- foreach my $field (@fields) {
- if (($field->{name} eq 'depends_on'
- || $field->{name} eq 'blocks')
- && scalar @{ $bug_hash->{$field->{name}} })
- {
- my $bug_ids = delete $bug_hash->{$field->{name}};
- $user->visible_bugs($bug_ids);
- my $bug_objs = Bugzilla::Bug->new_from_list($bug_ids);
-
- my @new_list;
- foreach my $bug (@$bug_objs) {
- my $data;
- if ($user->can_see_bug($bug)) {
- $data = {
- id => $bug->id,
- status => $bug->bug_status,
- summary => $bug->short_desc
- };
- }
- else {
- $data = { id => $bug->id };
- }
- push(@new_list, $data);
- }
- $field->{current_value} = \@new_list;
+ # Also add in the delta_ts value which is in the bugs_activity
+ # entries
+ push(@$updated_fields, get_field_id('delta_ts'));
+ @fields = $self->_get_fields($bug, $updated_fields);
+ }
+ }
+
+ # Return all the things
+ else {
+ @fields = $self->_get_fields($bug);
+ }
+
+ # Place the fields current value along with the field definition
+ foreach my $field (@fields) {
+ if (($field->{name} eq 'depends_on' || $field->{name} eq 'blocks')
+ && scalar @{$bug_hash->{$field->{name}}})
+ {
+ my $bug_ids = delete $bug_hash->{$field->{name}};
+ $user->visible_bugs($bug_ids);
+ my $bug_objs = Bugzilla::Bug->new_from_list($bug_ids);
+
+ my @new_list;
+ foreach my $bug (@$bug_objs) {
+ my $data;
+ if ($user->can_see_bug($bug)) {
+ $data
+ = {id => $bug->id, status => $bug->bug_status, summary => $bug->short_desc};
}
else {
- $field->{current_value} = delete $bug_hash->{$field->{name}} || '';
+ $data = {id => $bug->id};
}
+ push(@new_list, $data);
+ }
+ $field->{current_value} = \@new_list;
}
-
- # Any left over bug values will be added to the field list
- # These are extra fields that do not have a corresponding
- # Field.pm object
- if (!$last_updated) {
- foreach my $key (keys %$bug_hash) {
- my $field = {
- name => $key,
- current_value => $bug_hash->{$key}
- };
- my $name = Bugzilla::Bug::FIELD_MAP()->{$key} || $key;
- $field->{can_edit} = $self->_can_change_field($name, $bug);
- push(@fields, $field);
- }
+ else {
+ $field->{current_value} = delete $bug_hash->{$field->{name}} || '';
+ }
+ }
+
+ # Any left over bug values will be added to the field list
+ # These are extra fields that do not have a corresponding
+ # Field.pm object
+ if (!$last_updated) {
+ foreach my $key (keys %$bug_hash) {
+ my $field = {name => $key, current_value => $bug_hash->{$key}};
+ my $name = Bugzilla::Bug::FIELD_MAP()->{$key} || $key;
+ $field->{can_edit} = $self->_can_change_field($name, $bug);
+ push(@fields, $field);
}
+ }
- # Complete the return data
- my $data = { id => $bug->id, fields => \@fields };
+ # Complete the return data
+ my $data = {id => $bug->id, fields => \@fields};
- return $data;
+ return $data;
}
sub search {
- my ($self, $params) = @_;
-
- my $total;
- if (exists $params->{offset} && exists $params->{limit}) {
- my $count_params = dclone($params);
- delete $count_params->{offset};
- delete $count_params->{limit};
- $count_params->{count_only} = 1;
- $total = $self->SUPER::search($count_params);
- }
-
- my $result = $self->SUPER::search($params);
- $result->{total} = defined $total ? $total : scalar(@{ $result->{bugs} });
- return $result;
+ my ($self, $params) = @_;
+
+ my $total;
+ if (exists $params->{offset} && exists $params->{limit}) {
+ my $count_params = dclone($params);
+ delete $count_params->{offset};
+ delete $count_params->{limit};
+ $count_params->{count_only} = 1;
+ $total = $self->SUPER::search($count_params);
+ }
+
+ my $result = $self->SUPER::search($params);
+ $result->{total} = defined $total ? $total : scalar(@{$result->{bugs}});
+ return $result;
}
sub bug {
- my ($self, $params) = @_;
- my $dbh = Bugzilla->dbh;
-
- my $bug_id = delete $params->{id};
- $bug_id || ThrowCodeError('param_required',
- { function => 'Ember.bug', param => 'id' });
-
- my ($comments, $attachments) = ([], []);
- my $bug = $self->get({ ids => [ $bug_id ] });
- $bug = $bug->{bugs}->[0];
-
- # Only return changes since last_updated if provided
- my $last_updated = delete $params->{last_updated};
- if ($last_updated) {
- trick_taint($last_updated);
- my $updated_fields = $dbh->selectcol_arrayref('SELECT fielddefs.name
+ my ($self, $params) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ my $bug_id = delete $params->{id};
+ $bug_id
+ || ThrowCodeError('param_required', {function => 'Ember.bug', param => 'id'});
+
+ my ($comments, $attachments) = ([], []);
+ my $bug = $self->get({ids => [$bug_id]});
+ $bug = $bug->{bugs}->[0];
+
+ # Only return changes since last_updated if provided
+ my $last_updated = delete $params->{last_updated};
+ if ($last_updated) {
+ trick_taint($last_updated);
+ my $updated_fields = $dbh->selectcol_arrayref(
+ 'SELECT fielddefs.name
FROM fielddefs INNER JOIN bugs_activity
ON fielddefs.id = bugs_activity.fieldid
WHERE bugs_activity.bug_when > ?
AND bugs_activity.bug_id = ?',
- undef, ($last_updated, $bug->{id}));
-
- my %field_map = reverse %{ Bugzilla::Bug::FIELD_MAP() };
- $field_map{'flagtypes.name'} = 'flags';
-
- my $changed_bug = {};
- foreach my $field (@$updated_fields) {
- my $field_name = $field_map{$field} || $field;
- if ($bug->{$field_name}) {
- $changed_bug->{$field_name} = $bug->{$field_name};
- }
- }
- $bug = $changed_bug;
+ undef, ($last_updated, $bug->{id})
+ );
+
+ my %field_map = reverse %{Bugzilla::Bug::FIELD_MAP()};
+ $field_map{'flagtypes.name'} = 'flags';
+
+ my $changed_bug = {};
+ foreach my $field (@$updated_fields) {
+ my $field_name = $field_map{$field} || $field;
+ if ($bug->{$field_name}) {
+ $changed_bug->{$field_name} = $bug->{$field_name};
+ }
+ }
+ $bug = $changed_bug;
- # Find any comments created since the last_updated date
- $comments = $self->comments({ ids => $bug_id, new_since => $last_updated });
+ # Find any comments created since the last_updated date
+ $comments = $self->comments({ids => $bug_id, new_since => $last_updated});
- # Find any new attachments or modified attachments since the
- # last_updated date
- my $updated_attachments =
- $dbh->selectcol_arrayref('SELECT attach_id FROM attachments
+ # Find any new attachments or modified attachments since the
+ # last_updated date
+ my $updated_attachments = $dbh->selectcol_arrayref(
+ 'SELECT attach_id FROM attachments
WHERE (creation_ts > ? OR modification_time > ?)
- AND bug_id = ?',
- undef, ($last_updated, $last_updated, $bug->{id}));
- if ($updated_attachments) {
- $attachments = $self->_get_attachments({ attachment_ids => $updated_attachments,
- exclude_fields => ['data'] });
- }
+ AND bug_id = ?', undef,
+ ($last_updated, $last_updated, $bug->{id})
+ );
+ if ($updated_attachments) {
+ $attachments
+ = $self->_get_attachments({
+ attachment_ids => $updated_attachments, exclude_fields => ['data']
+ });
}
- else {
- $comments = $self->comments({ ids => [ $bug_id ] });
- $attachments = $self->_get_attachments({ ids => [ $bug_id ],
- exclude_fields => ['data'] });
+ }
+ else {
+ $comments = $self->comments({ids => [$bug_id]});
+ $attachments
+ = $self->_get_attachments({ids => [$bug_id], exclude_fields => ['data']});
- }
+ }
- $comments = $comments->{bugs}->{$bug_id}->{comments};
+ $comments = $comments->{bugs}->{$bug_id}->{comments};
- return {
- bug => $bug,
- comments => $comments,
- attachments => $attachments,
- };
+ return {bug => $bug, comments => $comments, attachments => $attachments,};
}
sub get_attachments {
- my ($self, $params) = @_;
- my $attachments = $self->_get_attachments($params);
- my $flag_types = [];
- my $bug;
- if ($params->{ids}) {
- $bug = Bugzilla::Bug->check($params->{ids}->[0]);
- $flag_types = $self->_get_flag_types_bug($bug, 'attachment');
- }
- elsif ($params->{attachment_ids} && @$attachments) {
- $bug = Bugzilla::Bug->check($attachments->[0]->{bug_id});
- $flag_types = $self->_get_flag_types_all($bug, 'attachment')->{attachment};
- }
- if (@$flag_types) {
- @$flag_types = map { $self->_flagtype_to_hash($_, $bug) } @$flag_types;
- }
- return {
- attachments => $attachments,
- flag_types => $flag_types
- };
+ my ($self, $params) = @_;
+ my $attachments = $self->_get_attachments($params);
+ my $flag_types = [];
+ my $bug;
+ if ($params->{ids}) {
+ $bug = Bugzilla::Bug->check($params->{ids}->[0]);
+ $flag_types = $self->_get_flag_types_bug($bug, 'attachment');
+ }
+ elsif ($params->{attachment_ids} && @$attachments) {
+ $bug = Bugzilla::Bug->check($attachments->[0]->{bug_id});
+ $flag_types = $self->_get_flag_types_all($bug, 'attachment')->{attachment};
+ }
+ if (@$flag_types) {
+ @$flag_types = map { $self->_flagtype_to_hash($_, $bug) } @$flag_types;
+ }
+ return {attachments => $attachments, flag_types => $flag_types};
}
###################
@@ -327,449 +320,477 @@ sub get_attachments {
###################
sub _get_attachments {
- my ($self, $params) = @_;
- my $user = Bugzilla->user;
-
- my $attachments = $self->attachments($params);
-
- if ($params->{ids}) {
- $attachments = [ map { @{ $attachments->{bugs}->{$_} } }
- keys %{ $attachments->{bugs} } ];
- }
- elsif ($params->{attachment_ids}) {
- $attachments = [ map { $attachments->{attachments}->{$_} }
- keys %{ $attachments->{attachments} } ];
- }
-
- foreach my $attachment (@$attachments) {
- $attachment->{can_edit}
- = ($user->login eq $attachment->{creator} || $user->in_group('editbugs')) ? 1 : 0;
- }
-
- return $attachments;
+ my ($self, $params) = @_;
+ my $user = Bugzilla->user;
+
+ my $attachments = $self->attachments($params);
+
+ if ($params->{ids}) {
+ $attachments
+ = [map { @{$attachments->{bugs}->{$_}} } keys %{$attachments->{bugs}}];
+ }
+ elsif ($params->{attachment_ids}) {
+ $attachments = [map { $attachments->{attachments}->{$_} }
+ keys %{$attachments->{attachments}}];
+ }
+
+ foreach my $attachment (@$attachments) {
+ $attachment->{can_edit}
+ = ($user->login eq $attachment->{creator} || $user->in_group('editbugs'))
+ ? 1
+ : 0;
+ }
+
+ return $attachments;
}
sub _get_fields {
- my ($self, $bug, $field_ids) = @_;
- my $user = Bugzilla->user;
-
- # Load the field objects we need
- my @field_objs;
- if ($field_ids) {
- # Load just the fields that match the ids provided
- @field_objs = @{ Bugzilla::Field->match({ id => $field_ids }) };
-
+ my ($self, $bug, $field_ids) = @_;
+ my $user = Bugzilla->user;
+
+ # Load the field objects we need
+ my @field_objs;
+ if ($field_ids) {
+
+ # Load just the fields that match the ids provided
+ @field_objs = @{Bugzilla::Field->match({id => $field_ids})};
+
+ }
+ else {
+ # load up standard fields
+ @field_objs = @{Bugzilla->fields({custom => 0})};
+
+ # Load custom fields
+ my $cf_params = {product => $bug->product_obj};
+ $cf_params->{component} = $bug->component_obj if $bug->can('component_obj');
+ $cf_params->{bug_id} = $bug->id if $bug->id;
+ push(@field_objs, Bugzilla->active_custom_fields($cf_params));
+ }
+
+ my $return_groups = my $return_flags = $field_ids ? 0 : 1;
+ my @fields;
+ foreach my $field (@field_objs) {
+ $return_groups = 1 if $field->name eq 'bug_group';
+ $return_flags = 1 if $field->name eq 'flagtypes.name';
+
+ # Skip any special fields containing . in the name such as
+ # for attachments.*, etc.
+ next if $field->name =~ /\./;
+
+ # Remove time tracking fields if the user is privileged
+ next
+ if (grep($field->name eq $_, TIMETRACKING_FIELDS)
+ && !Bugzilla->user->is_timetracker);
+
+ # These fields should never be set by the user
+ next if grep($field->name eq $_, NON_EDIT_FIELDS);
+
+ # We already selected a product so no need to display all choices
+ # Might as well skip classification for new bugs as well.
+ next
+ if (!$bug->id
+ && ($field->name eq 'product' || $field->name eq 'classification'));
+
+ # Skip assigned_to and qa_contact for new bugs if user not in
+ # editbugs group
+ next
+ if (!$bug->id
+ && ($field->name eq 'assigned_to' || $field->name eq 'qa_contact')
+ && !$user->in_group('editbugs', $bug->product_obj->id));
+
+# Do not display obsolete fields or fields that should be displayed for create bug form
+ next
+ if (!$bug->id && $field->custom && ($field->obsolete || !$field->enter_bug));
+
+ push(@fields, $self->_field_to_hash($field, $bug));
+ }
+
+ # Add group information as separate field
+ if ($return_groups) {
+ push(
+ @fields,
+ {
+ description => $self->type('string', 'Groups'),
+ is_custom => $self->type('boolean', 0),
+ is_mandatory => $self->type('boolean', 0),
+ name => $self->type('string', 'groups'),
+ values => [
+ map { $self->_group_to_hash($_, $bug) } @{$bug->product_obj->groups_available}
+ ]
+ }
+ );
+ }
+
+ # Add flag information as separate field
+ if ($return_flags) {
+ my $flag_hash;
+ if ($bug->id) {
+ foreach my $flag_type ('bug', 'attachment') {
+ $flag_hash->{$flag_type} = $self->_get_flag_types_bug($bug, $flag_type);
+ }
}
else {
- # load up standard fields
- @field_objs = @{ Bugzilla->fields({ custom => 0 }) };
-
- # Load custom fields
- my $cf_params = { product => $bug->product_obj };
- $cf_params->{component} = $bug->component_obj if $bug->can('component_obj');
- $cf_params->{bug_id} = $bug->id if $bug->id;
- push(@field_objs, Bugzilla->active_custom_fields($cf_params));
- }
-
- my $return_groups = my $return_flags = $field_ids ? 0 : 1;
- my @fields;
- foreach my $field (@field_objs) {
- $return_groups = 1 if $field->name eq 'bug_group';
- $return_flags = 1 if $field->name eq 'flagtypes.name';
-
- # Skip any special fields containing . in the name such as
- # for attachments.*, etc.
- next if $field->name =~ /\./;
-
- # Remove time tracking fields if the user is privileged
- next if (grep($field->name eq $_, TIMETRACKING_FIELDS)
- && !Bugzilla->user->is_timetracker);
-
- # These fields should never be set by the user
- next if grep($field->name eq $_, NON_EDIT_FIELDS);
-
- # We already selected a product so no need to display all choices
- # Might as well skip classification for new bugs as well.
- next if (!$bug->id && ($field->name eq 'product' || $field->name eq 'classification'));
-
- # Skip assigned_to and qa_contact for new bugs if user not in
- # editbugs group
- next if (!$bug->id
- && ($field->name eq 'assigned_to' || $field->name eq 'qa_contact')
- && !$user->in_group('editbugs', $bug->product_obj->id));
-
- # Do not display obsolete fields or fields that should be displayed for create bug form
- next if (!$bug->id && $field->custom
- && ($field->obsolete || !$field->enter_bug));
-
- push(@fields, $self->_field_to_hash($field, $bug));
+ $flag_hash = $self->_get_flag_types_all($bug);
}
-
- # Add group information as separate field
- if ($return_groups) {
- push(@fields, {
- description => $self->type('string', 'Groups'),
- is_custom => $self->type('boolean', 0),
- is_mandatory => $self->type('boolean', 0),
- name => $self->type('string', 'groups'),
- values => [ map { $self->_group_to_hash($_, $bug) }
- @{ $bug->product_obj->groups_available } ]
- });
- }
-
- # Add flag information as separate field
- if ($return_flags) {
- my $flag_hash;
- if ($bug->id) {
- foreach my $flag_type ('bug', 'attachment') {
- $flag_hash->{$flag_type} = $self->_get_flag_types_bug($bug, $flag_type);
- }
- }
- else {
- $flag_hash = $self->_get_flag_types_all($bug);
- }
- my @flag_values;
- foreach my $flag_type ('bug', 'attachment') {
- foreach my $flag (@{ $flag_hash->{$flag_type} }) {
- push(@flag_values, $self->_flagtype_to_hash($flag, $bug));
- }
- }
-
- push(@fields, {
- description => $self->type('string', 'Flags'),
- is_custom => $self->type('boolean', 0),
- is_mandatory => $self->type('boolean', 0),
- name => $self->type('string', 'flags'),
- values => \@flag_values
- });
+ my @flag_values;
+ foreach my $flag_type ('bug', 'attachment') {
+ foreach my $flag (@{$flag_hash->{$flag_type}}) {
+ push(@flag_values, $self->_flagtype_to_hash($flag, $bug));
+ }
}
- return @fields;
+ push(
+ @fields,
+ {
+ description => $self->type('string', 'Flags'),
+ is_custom => $self->type('boolean', 0),
+ is_mandatory => $self->type('boolean', 0),
+ name => $self->type('string', 'flags'),
+ values => \@flag_values
+ }
+ );
+ }
+
+ return @fields;
}
sub _get_flag_types_all {
- my ($self, $bug, $type) = @_;
- my $params = { is_active => 1 };
- $params->{target_type} = $type if $type;
- return $bug->product_obj->flag_types($params);
+ my ($self, $bug, $type) = @_;
+ my $params = {is_active => 1};
+ $params->{target_type} = $type if $type;
+ return $bug->product_obj->flag_types($params);
}
sub _get_flag_types_bug {
- my ($self, $bug, $type) = @_;
- my $params = {
- target_type => $type,
- product_id => $bug->product_obj->id,
- component_id => $bug->component_obj->id,
- bug_id => $bug->id,
- active_or_has_flags => $bug->id,
- };
- return Bugzilla::Flag->_flag_types($params);
+ my ($self, $bug, $type) = @_;
+ my $params = {
+ target_type => $type,
+ product_id => $bug->product_obj->id,
+ component_id => $bug->component_obj->id,
+ bug_id => $bug->id,
+ active_or_has_flags => $bug->id,
+ };
+ return Bugzilla::Flag->_flag_types($params);
}
sub _group_to_hash {
- my ($self, $group, $bug) = @_;
+ my ($self, $group, $bug) = @_;
- my $data = {
- description => $self->type('string', $group->description),
- name => $self->type('string', $group->name)
- };
+ my $data = {
+ description => $self->type('string', $group->description),
+ name => $self->type('string', $group->name)
+ };
- if ($group->name eq $bug->product_obj->default_security_group) {
- $data->{security_default} = $self->type('boolean', 1);
- }
+ if ($group->name eq $bug->product_obj->default_security_group) {
+ $data->{security_default} = $self->type('boolean', 1);
+ }
- return $data;
+ return $data;
}
sub _field_to_hash {
- my ($self, $field, $bug) = @_;
-
- my $data = {
- is_custom => $self->type('boolean', $field->custom),
- description => $self->type('string', $field->description),
- is_mandatory => $self->type('boolean', $field->is_mandatory),
- };
-
- if ($field->custom) {
- $data->{type} = $self->type('string', FIELD_TYPE_MAP->{$field->type});
- }
-
- # Use the API name if one is present instead of the internal field name
- my $field_name = $field->name;
- $field_name = API_NAMES->{$field_name} || $field_name;
-
- if ($field_name eq 'longdesc') {
- $field_name = $bug->id ? 'comment' : 'description';
- }
-
- $data->{name} = $self->type('string', $field_name);
-
- # Set can_edit true or false if we are editing a current bug
- if ($bug->id) {
- # 'delta_ts's can_edit is incorrectly set in fielddefs
- $data->{can_edit} = $field->name eq 'delta_ts'
- ? $self->type('boolean', 0)
- : $self->_can_change_field($field, $bug);
- }
-
- # description for creating a new bug, otherwise comment
-
- # FIXME 'version' and 'target_milestone' types are incorrectly set in fielddefs
- if ($field->is_select || $field->name eq 'version' || $field->name eq 'target_milestone') {
- $data->{values} = [ $self->_get_field_values($field, $bug) ];
- }
-
- # Add default values for specific fields if new bug
- if (!$bug->id && DEFAULT_VALUE_MAP->{$field->name}) {
- my $default_value = Bugzilla->params->{DEFAULT_VALUE_MAP->{$field->name}};
- $data->{default_value} = $default_value;
- }
-
- return $data;
+ my ($self, $field, $bug) = @_;
+
+ my $data = {
+ is_custom => $self->type('boolean', $field->custom),
+ description => $self->type('string', $field->description),
+ is_mandatory => $self->type('boolean', $field->is_mandatory),
+ };
+
+ if ($field->custom) {
+ $data->{type} = $self->type('string', FIELD_TYPE_MAP->{$field->type});
+ }
+
+ # Use the API name if one is present instead of the internal field name
+ my $field_name = $field->name;
+ $field_name = API_NAMES->{$field_name} || $field_name;
+
+ if ($field_name eq 'longdesc') {
+ $field_name = $bug->id ? 'comment' : 'description';
+ }
+
+ $data->{name} = $self->type('string', $field_name);
+
+ # Set can_edit true or false if we are editing a current bug
+ if ($bug->id) {
+
+ # 'delta_ts's can_edit is incorrectly set in fielddefs
+ $data->{can_edit}
+ = $field->name eq 'delta_ts'
+ ? $self->type('boolean', 0)
+ : $self->_can_change_field($field, $bug);
+ }
+
+ # description for creating a new bug, otherwise comment
+
+ # FIXME 'version' and 'target_milestone' types are incorrectly set in fielddefs
+ if ( $field->is_select
+ || $field->name eq 'version'
+ || $field->name eq 'target_milestone')
+ {
+ $data->{values} = [$self->_get_field_values($field, $bug)];
+ }
+
+ # Add default values for specific fields if new bug
+ if (!$bug->id && DEFAULT_VALUE_MAP->{$field->name}) {
+ my $default_value = Bugzilla->params->{DEFAULT_VALUE_MAP->{$field->name}};
+ $data->{default_value} = $default_value;
+ }
+
+ return $data;
}
sub _value_to_hash {
- my ($self, $value, $bug) = @_;
+ my ($self, $value, $bug) = @_;
- my $data = { name=> $self->type('string', $value->name) };
+ my $data = {name => $self->type('string', $value->name)};
- if ($bug->{bug_id}) {
- $data->{is_active} = $self->type('boolean', $value->is_active);
- }
+ if ($bug->{bug_id}) {
+ $data->{is_active} = $self->type('boolean', $value->is_active);
+ }
- if ($value->can('sortkey')) {
- $data->{sort_key} = $self->type('int', $value->sortkey || 0);
- }
+ if ($value->can('sortkey')) {
+ $data->{sort_key} = $self->type('int', $value->sortkey || 0);
+ }
- if ($value->isa('Bugzilla::Component')) {
- $data->{default_assignee} = $self->_user_to_hash($value->default_assignee);
- $data->{initial_cc} = [ map { $self->_user_to_hash($_) } @{ $value->initial_cc } ];
- if (Bugzilla->params->{useqacontact} && $value->default_qa_contact) {
- $data->{default_qa_contact} = $self->_user_to_hash($value->default_qa_contact);
- }
+ if ($value->isa('Bugzilla::Component')) {
+ $data->{default_assignee} = $self->_user_to_hash($value->default_assignee);
+ $data->{initial_cc} = [map { $self->_user_to_hash($_) } @{$value->initial_cc}];
+ if (Bugzilla->params->{useqacontact} && $value->default_qa_contact) {
+ $data->{default_qa_contact} = $self->_user_to_hash($value->default_qa_contact);
}
+ }
- if ($value->can('description')) {
- $data->{description} = $self->type('string', $value->description);
- }
+ if ($value->can('description')) {
+ $data->{description} = $self->type('string', $value->description);
+ }
- return $data;
+ return $data;
}
sub _user_to_hash {
- my ($self, $user) = @_;
+ my ($self, $user) = @_;
- my $data = {
- real_name => $self->type('string', $user->name)
- };
+ my $data = {real_name => $self->type('string', $user->name)};
- if (Bugzilla->user->id) {
- $data->{email} = $self->type('string', $user->email);
- }
+ if (Bugzilla->user->id) {
+ $data->{email} = $self->type('string', $user->email);
+ }
- return $data;
+ return $data;
}
sub _get_field_values {
- my ($self, $field, $bug) = @_;
-
- # Certain fields are special and should use $bug->choices
- # to determine editability and not $bug->check_can_change_field
- my @values;
- if (grep($field->name eq $_, BUG_CHOICE_FIELDS)) {
- @values = @{ $bug->choices->{$field->name} };
+ my ($self, $field, $bug) = @_;
+
+ # Certain fields are special and should use $bug->choices
+ # to determine editability and not $bug->check_can_change_field
+ my @values;
+ if (grep($field->name eq $_, BUG_CHOICE_FIELDS)) {
+ @values = @{$bug->choices->{$field->name}};
+ }
+ else {
+ # We need to get the values from the product for
+ # component, version, and milestones.
+ if ($field->name eq 'component') {
+ @values = @{$bug->product_obj->components};
+ }
+ elsif ($field->name eq 'target_milestone') {
+ @values = @{$bug->product_obj->milestones};
+ }
+ elsif ($field->name eq 'version') {
+ @values = @{$bug->product_obj->versions};
}
else {
- # We need to get the values from the product for
- # component, version, and milestones.
- if ($field->name eq 'component') {
- @values = @{ $bug->product_obj->components };
- }
- elsif ($field->name eq 'target_milestone') {
- @values = @{ $bug->product_obj->milestones };
- }
- elsif ($field->name eq 'version') {
- @values = @{ $bug->product_obj->versions };
- }
- else {
- @values = @{ $field->legal_values };
- }
+ @values = @{$field->legal_values};
}
+ }
- my @filtered_values;
- foreach my $value (@values) {
- next if !$bug->id && !$value->is_active;
- next if $bug->id && !$self->_can_change_field($field, $bug, $value->name);
- push(@filtered_values, $value);
- }
+ my @filtered_values;
+ foreach my $value (@values) {
+ next if !$bug->id && !$value->is_active;
+ next if $bug->id && !$self->_can_change_field($field, $bug, $value->name);
+ push(@filtered_values, $value);
+ }
- return map { $self->_value_to_hash($_, $bug) } @filtered_values;
+ return map { $self->_value_to_hash($_, $bug) } @filtered_values;
}
sub _can_change_field {
- my ($self, $field, $bug, $value) = @_;
- my $user = Bugzilla->user;
- my $field_name = blessed $field ? $field->name : $field;
-
- # Cannot set resolution on bug creation
- return $self->type('boolean', 0) if ($field_name eq 'resolution' && !$bug->{bug_id});
-
- # Cannot edit an obsolete or inactive custom field
- return $self->type('boolean', 0) if (blessed $field && $field->custom && $field->obsolete);
-
- # If not a multi-select or single-select, value is not provided
- # and we just check if the field itself is editable by the user.
- if (!defined $value) {
- return $self->type('boolean', $bug->check_can_change_field($field_name, 0, 1));
- }
-
- return $self->type('boolean', $bug->check_can_change_field($field_name, '', $value));
+ my ($self, $field, $bug, $value) = @_;
+ my $user = Bugzilla->user;
+ my $field_name = blessed $field ? $field->name : $field;
+
+ # Cannot set resolution on bug creation
+ return $self->type('boolean', 0)
+ if ($field_name eq 'resolution' && !$bug->{bug_id});
+
+ # Cannot edit an obsolete or inactive custom field
+ return $self->type('boolean', 0)
+ if (blessed $field && $field->custom && $field->obsolete);
+
+ # If not a multi-select or single-select, value is not provided
+ # and we just check if the field itself is editable by the user.
+ if (!defined $value) {
+ return $self->type('boolean', $bug->check_can_change_field($field_name, 0, 1));
+ }
+
+ return $self->type('boolean',
+ $bug->check_can_change_field($field_name, '', $value));
}
sub _flag_to_hash {
- my ($self, $flag) = @_;
-
- my $data = {
- id => $self->type('int', $flag->id),
- name => $self->type('string', $flag->name),
- type_id => $self->type('int', $flag->type_id),
- creation_date => $self->type('dateTime', $flag->creation_date),
- modification_date => $self->type('dateTime', $flag->modification_date),
- status => $self->type('string', $flag->status)
- };
-
- foreach my $field (qw(setter requestee)) {
- my $field_id = $field . "_id";
- $data->{$field} = $self->_user_to_hash($flag->$field) if $flag->$field_id;
- }
-
- $data->{type} = $flag->attach_id ? 'attachment' : 'bug';
- $data->{attach_id} = $flag->attach_id if $flag->attach_id;
-
- return $data;
+ my ($self, $flag) = @_;
+
+ my $data = {
+ id => $self->type('int', $flag->id),
+ name => $self->type('string', $flag->name),
+ type_id => $self->type('int', $flag->type_id),
+ creation_date => $self->type('dateTime', $flag->creation_date),
+ modification_date => $self->type('dateTime', $flag->modification_date),
+ status => $self->type('string', $flag->status)
+ };
+
+ foreach my $field (qw(setter requestee)) {
+ my $field_id = $field . "_id";
+ $data->{$field} = $self->_user_to_hash($flag->$field) if $flag->$field_id;
+ }
+
+ $data->{type} = $flag->attach_id ? 'attachment' : 'bug';
+ $data->{attach_id} = $flag->attach_id if $flag->attach_id;
+
+ return $data;
}
sub _flagtype_to_hash {
- my ($self, $flagtype, $bug) = @_;
- my $user = Bugzilla->user;
-
- my $cansetflag = $user->can_set_flag($flagtype);
- my $canrequestflag = $user->can_request_flag($flagtype);
-
- my $data = {
- id => $self->type('int' , $flagtype->id),
- name => $self->type('string' , $flagtype->name),
- description => $self->type('string' , $flagtype->description),
- type => $self->type('string' , $flagtype->target_type),
- is_requestable => $self->type('boolean', $flagtype->is_requestable),
- is_requesteeble => $self->type('boolean', $flagtype->is_requesteeble),
- is_multiplicable => $self->type('boolean', $flagtype->is_multiplicable),
- can_set_flag => $self->type('boolean', $cansetflag),
- can_request_flag => $self->type('boolean', $canrequestflag)
- };
-
- my @values;
- foreach my $value ('?','+','-') {
- push(@values, $self->type('string', $value));
- }
- $data->{values} = \@values;
-
- # if we're creating a bug, we need to return all valid flags for
- # this product, as well as inclusions & exclusions so ember can
- # display relevant flags once the component is selected
- if (!$bug->id) {
- my $inclusions = $self->_flagtype_clusions_to_hash($flagtype->inclusions, $bug->product_obj->id);
- my $exclusions = $self->_flagtype_clusions_to_hash($flagtype->exclusions, $bug->product_obj->id);
- # if we have both inclusions and exclusions, the exclusions are redundant
- $exclusions = [] if @$inclusions && @$exclusions;
- # no need to return anything if there's just "any component"
- $data->{inclusions} = $inclusions if @$inclusions && $inclusions->[0] ne '';
- $data->{exclusions} = $exclusions if @$exclusions && $exclusions->[0] ne '';
- }
-
- return $data;
+ my ($self, $flagtype, $bug) = @_;
+ my $user = Bugzilla->user;
+
+ my $cansetflag = $user->can_set_flag($flagtype);
+ my $canrequestflag = $user->can_request_flag($flagtype);
+
+ my $data = {
+ id => $self->type('int', $flagtype->id),
+ name => $self->type('string', $flagtype->name),
+ description => $self->type('string', $flagtype->description),
+ type => $self->type('string', $flagtype->target_type),
+ is_requestable => $self->type('boolean', $flagtype->is_requestable),
+ is_requesteeble => $self->type('boolean', $flagtype->is_requesteeble),
+ is_multiplicable => $self->type('boolean', $flagtype->is_multiplicable),
+ can_set_flag => $self->type('boolean', $cansetflag),
+ can_request_flag => $self->type('boolean', $canrequestflag)
+ };
+
+ my @values;
+ foreach my $value ('?', '+', '-') {
+ push(@values, $self->type('string', $value));
+ }
+ $data->{values} = \@values;
+
+ # if we're creating a bug, we need to return all valid flags for
+ # this product, as well as inclusions & exclusions so ember can
+ # display relevant flags once the component is selected
+ if (!$bug->id) {
+ my $inclusions = $self->_flagtype_clusions_to_hash($flagtype->inclusions,
+ $bug->product_obj->id);
+ my $exclusions = $self->_flagtype_clusions_to_hash($flagtype->exclusions,
+ $bug->product_obj->id);
+
+ # if we have both inclusions and exclusions, the exclusions are redundant
+ $exclusions = [] if @$inclusions && @$exclusions;
+
+ # no need to return anything if there's just "any component"
+ $data->{inclusions} = $inclusions if @$inclusions && $inclusions->[0] ne '';
+ $data->{exclusions} = $exclusions if @$exclusions && $exclusions->[0] ne '';
+ }
+
+ return $data;
}
sub _flagtype_clusions_to_hash {
- my ($self, $clusions, $product_id) = @_;
- my $result = [];
- foreach my $key (keys %$clusions) {
- my ($prod_id, $comp_id) = split(/:/, $clusions->{$key}, 2);
- if ($prod_id == 0 || $prod_id == $product_id) {
- if ($comp_id) {
- my $component = Bugzilla::Component->new({ id => $comp_id, cache => 1 });
- push @$result, $component->name;
- }
- else {
- return [ '' ];
- }
- }
+ my ($self, $clusions, $product_id) = @_;
+ my $result = [];
+ foreach my $key (keys %$clusions) {
+ my ($prod_id, $comp_id) = split(/:/, $clusions->{$key}, 2);
+ if ($prod_id == 0 || $prod_id == $product_id) {
+ if ($comp_id) {
+ my $component = Bugzilla::Component->new({id => $comp_id, cache => 1});
+ push @$result, $component->name;
+ }
+ else {
+ return [''];
+ }
}
- return $result;
+ }
+ return $result;
}
sub rest_resources {
- return [
- # create page - single product name
- qr{^/ember/create/(.*)$}, {
- GET => {
- method => 'create',
- params => sub {
- return { product => $_[0] };
- }
- }
- },
- # create page - one or more products
- qr{^/ember/create$}, {
- GET => {
- method => 'create'
- }
- },
- # show bug page - single bug id
- qr{^/ember/show/(\d+)$}, {
- GET => {
- method => 'show',
- params => sub {
- return { id => $_[0] };
- }
- }
- },
- # search - wrapper around SUPER::search which also includes the total
- # number of bugs when using pagination
- qr{^/ember/search$}, {
- GET => {
- method => 'search',
- },
- },
- # get current bug attributes without field information - single bug id
- qr{^/ember/bug/(\d+)$}, {
- GET => {
- method => 'bug',
- params => sub {
- return { id => $_[0] };
- }
- }
- },
- # attachments - wrapper around SUPER::attachments that also includes
- # can_edit attribute
- qr{^/ember/bug/(\d+)/attachments$}, {
- GET => {
- method => 'get_attachments',
- params => sub {
- return { ids => $_[0] };
- }
- }
- },
- qr{^/ember/bug/attachments/(\d+)$}, {
- GET => {
- method => 'get_attachments',
- params => sub {
- return { attachment_ids => $_[0] };
- }
- }
+ return [
+ # create page - single product name
+ qr{^/ember/create/(.*)$},
+ {
+ GET => {
+ method => 'create',
+ params => sub {
+ return {product => $_[0]};
}
- ];
-};
+ }
+ },
+
+ # create page - one or more products
+ qr{^/ember/create$},
+ {GET => {method => 'create'}},
+
+ # show bug page - single bug id
+ qr{^/ember/show/(\d+)$},
+ {
+ GET => {
+ method => 'show',
+ params => sub {
+ return {id => $_[0]};
+ }
+ }
+ },
+
+ # search - wrapper around SUPER::search which also includes the total
+ # number of bugs when using pagination
+ qr{^/ember/search$},
+ {GET => {method => 'search',},},
+
+ # get current bug attributes without field information - single bug id
+ qr{^/ember/bug/(\d+)$},
+ {
+ GET => {
+ method => 'bug',
+ params => sub {
+ return {id => $_[0]};
+ }
+ }
+ },
+
+ # attachments - wrapper around SUPER::attachments that also includes
+ # can_edit attribute
+ qr{^/ember/bug/(\d+)/attachments$},
+ {
+ GET => {
+ method => 'get_attachments',
+ params => sub {
+ return {ids => $_[0]};
+ }
+ }
+ },
+ qr{^/ember/bug/attachments/(\d+)$},
+ {
+ GET => {
+ method => 'get_attachments',
+ params => sub {
+ return {attachment_ids => $_[0]};
+ }
+ }
+ }
+ ];
+}
1;
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 22c4042b5..9ecbf25a6 100644
--- a/extensions/Example/Extension.pm
+++ b/extensions/Example/Extension.pm
@@ -33,338 +33,355 @@ use constant REL_EXAMPLE => -127;
our $VERSION = '1.0';
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'};
+ # 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!";
+ 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!";
}
sub bug_end_of_create_validators {
- 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_params = $args->{'params'};
+ # 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);
+ # 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} = [];
+ # This would remove all ccs from the bug, preventing ANY ccs from being
+ # added on bug creation.
+ # $bug_params->{cc} = [];
}
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 ($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.";
- 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) = @_;
+ my ($self, $args) = @_;
- # This replaces every occurrence of the word "foo" with the word
- # "bar"
+ # This replaces every occurrence of the word "foo" with the word
+ # "bar"
- my $regexes = $args->{'regexes'};
- push(@$regexes, { match => qr/\bfoo\b/, replace => '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 });
+ # 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 ($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 $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 ($self, $args) = @_;
- my $operators = $args->{'operators'};
+ my $operators = $args->{'operators'};
- my $original = $operators->{component}->{_non_changed};
- $operators->{component} = {
- _non_changed => sub { _component_nonchanged($original, @_) }
- };
+ 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 he 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 he 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 config_add_panels {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my $modules = $args->{panel_modules};
- $modules->{Example} = "Bugzilla::Extension::Example::Config";
+ my $modules = $args->{panel_modules};
+ $modules->{Example} = "Bugzilla::Extension::Example::Config";
}
sub config_modify_panels {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my $panels = $args->{panels};
+ 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);
+ # 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(@{ $info_class->{choices} }, 'CGI,Example');
- push(@{ $verify_class->{choices} }, 'Example');
+ push(@{$info_class->{choices}}, 'CGI,Example');
+ push(@{$verify_class->{choices}}, 'Example');
- push(@$auth_params, { name => 'param_example',
- type => 't',
- default => 0,
- checker => \&check_numeric });
+ 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,
@@ -383,639 +400,662 @@ 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 him,
- # but he 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 him,
+ # but he 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 ($self, $args) = @_;
- my $vars = $args->{vars};
- $vars->{'example'} = 1;
+ 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;
- # 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!";
+ 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!";
}
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) = @_;
+
+ # 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_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!";
+ # 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.";
- 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;
+ # 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};
+ my ($self, $args) = @_;
+ print "Install-before_final_checks hook\n" unless $args->{silent};
- # Add a new user setting like this:
- #
- # add_setting({
- # name => 'product_chooser', # setting name
- # options => ['pretty', 'full', 'small'], # options
- # category => '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 .
+ # Add a new user setting like this:
+ #
+ # add_setting({
+ # name => 'product_chooser', # setting name
+ # options => ['pretty', 'full', 'small'], # options
+ # category => '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;
+ my $dbh = Bugzilla->dbh;
+
# $dbh->bz_add_column('fielddefs', 'example_column',
# {TYPE => 'MEDIUMTEXT', NOTNULL => 1, DEFAULT => ''});
}
sub job_map {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my $job_map = $args->{job_map};
+ 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';
+ # 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 });
+ # 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 ($self, $args) = @_;
+
+ my $email = $args->{email};
- 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');
+ # 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 ($self, $args) = @_;
- my $class = $args->{'class'};
- my $object_params = $args->{'params'};
+ 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));
- }
+ # 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 ($self, $args) = @_;
- my ($object, $field, $value) = @$args{qw(object field value)};
+ 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!";
- }
+ # 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 ($self, $args) = @_;
- my $class = $args->{'class'};
- my $object = $args->{'object'};
+ 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 ($self, $args) = @_;
- my $class = $args->{'class'};
- my $object_params = $args->{'params'};
+ 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;
- }
+ # 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 ($self, $args) = @_;
- my $object = $args->{'object'};
- my $object_params = $args->{'params'};
+ 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;
- }
+ # 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 ($self, $args) = @_;
- my ($object, $old_object, $changes) =
- @$args{qw(object old_object changes)};
+ 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!";
- }
+ # 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 ($self, $args) = @_;
- my ($vars, $page) = @$args{qw(vars page_id)};
+ 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 };
- }
+ # 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 ($self, $args) = @_;
- my $vars = $args->{vars};
- $vars->{'example'} = 1;
+ my $vars = $args->{vars};
+ $vars->{'example'} = 1;
}
sub product_confirm_delete {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my $vars = $args->{vars};
- $vars->{'example'} = 1;
+ 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 ' .
# '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 ($self, $args) = @_;
- my $dbh = Bugzilla->dbh;
- my $sth;
+ my $dbh = Bugzilla->dbh;
+ my $sth;
- my $status = $args->{'status'};
+ my $status = $args->{'status'};
- # Check that all users are Australian
- $status->('example_check_au_user');
+ # Check that all users are Australian
+ $status->('example_check_au_user');
- $sth = $dbh->prepare("SELECT userid, login_name
+ $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;
- }
+ 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;
+ $status->('example_check_au_user_prompt') if $seen_nonau;
}
sub sanitycheck_repair {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my $cgi = Bugzilla->cgi;
- my $dbh = Bugzilla->dbh;
+ my $cgi = Bugzilla->cgi;
+ my $dbh = Bugzilla->dbh;
- my $status = $args->{'status'};
+ my $status = $args->{'status'};
- if ($cgi->param('example_repair_au_user')) {
- $status->('example_repair_au_user_start');
+ 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'");
+ #$dbh->do("UPDATE profiles
+ # SET login_name = CONCAT(login_name, '.au')
+ # WHERE login_name NOT LIKE '%.au'");
- $status->('example_repair_au_user_end');
- }
+ $status->('example_repair_au_user_end');
+ }
}
sub template_before_create {
- my ($self, $args) = @_;
+ 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 $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_before_process {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my ($vars, $file, $context) = @$args{qw(vars file context)};
+ my ($vars, $file, $context) = @$args{qw(vars file context)};
- if ($file eq 'bug/edit.html.tmpl') {
- $vars->{'viewing_the_bug_form'} = 1;
- }
+ if ($file eq 'bug/edit.html.tmpl') {
+ $vars->{'viewing_the_bug_form'} = 1;
+ }
}
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};
- 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;
+ # 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, "\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, "\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 376fe21a8..10be62f85 100644
--- a/extensions/Example/lib/Auth/Login.pm
+++ b/extensions/Example/lib/Auth/Login.pm
@@ -15,7 +15,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 cac6d1019..4170b93c3 100644
--- a/extensions/Example/lib/Auth/Verify.pm
+++ b/extensions/Example/lib/Auth/Verify.pm
@@ -14,7 +14,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 7b1940462..f50c6e6cb 100644
--- a/extensions/Example/lib/WebService.pm
+++ b/extensions/Example/lib/WebService.pm
@@ -14,8 +14,8 @@ use base 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 7b19a9f4d..6ee4b5fdc 100644
--- a/extensions/Example/template/en/default/setup/strings.txt.pl
+++ b/extensions/Example/template/en/default/setup/strings.txt.pl
@@ -17,8 +17,6 @@
# Contributor(s):
# Max Kanat-Alexander <mkanat@bugzilla.org>
-%strings = (
- feature_example_acme => 'Example Extension: Acme Feature',
-);
+%strings = (feature_example_acme => 'Example Extension: Acme Feature',);
1;
diff --git a/extensions/FlagDefaultRequestee/Extension.pm b/extensions/FlagDefaultRequestee/Extension.pm
index b534f9904..df23d9cc4 100644
--- a/extensions/FlagDefaultRequestee/Extension.pm
+++ b/extensions/FlagDefaultRequestee/Extension.pm
@@ -27,14 +27,16 @@ our $VERSION = '1';
################
sub install_update_db {
- my $dbh = Bugzilla->dbh;
- $dbh->bz_add_column('flagtypes', 'default_requestee', {
- TYPE => 'INT3',
- NOTNULL => 0,
- REFERENCES => { TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'SET NULL' }
- });
+ my $dbh = Bugzilla->dbh;
+ $dbh->bz_add_column(
+ 'flagtypes',
+ 'default_requestee',
+ {
+ TYPE => 'INT3',
+ NOTNULL => 0,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'SET NULL'}
+ }
+ );
}
#############
@@ -42,51 +44,51 @@ sub install_update_db {
#############
sub template_before_process {
- my ($self, $args) = @_;
- return unless Bugzilla->user->id;
- my ($vars, $file) = @$args{qw(vars file)};
- return unless grep { $_ eq $file } FLAGTYPE_TEMPLATES;
-
- my $flag_types = [];
- if (exists $vars->{bug} || exists $vars->{attachment}) {
- my $bug;
- if (exists $vars->{bug}) {
- $bug = $vars->{'bug'};
- } elsif (exists $vars->{'attachment'}) {
- $bug = $vars->{'attachment'}->{bug};
- }
-
- $flag_types = Bugzilla::FlagType::match({
- 'target_type' => ($file =~ /^bug/ ? 'bug' : 'attachment'),
- 'product_id' => $bug->product_id,
- 'component_id' => $bug->component_id,
- 'bug_id' => $bug->id,
- 'active_or_has_flags' => $bug->id,
- });
-
- $vars->{flag_currently_requested} ||= {};
- foreach my $type (@$flag_types) {
- my $flags = Bugzilla::Flag->match({
- type_id => $type->id,
- bug_id => $bug->id,
- status => '?'
- });
- map { $vars->{flag_currently_requested}->{$_->id} = 1 } @$flags;
- }
+ my ($self, $args) = @_;
+ return unless Bugzilla->user->id;
+ my ($vars, $file) = @$args{qw(vars file)};
+ return unless grep { $_ eq $file } FLAGTYPE_TEMPLATES;
+
+ my $flag_types = [];
+ if (exists $vars->{bug} || exists $vars->{attachment}) {
+ my $bug;
+ if (exists $vars->{bug}) {
+ $bug = $vars->{'bug'};
}
- elsif ($file =~ /^bug\/create/ && exists $vars->{product}) {
- my $bug_flags = $vars->{product}->flag_types->{bug};
- my $attachment_flags = $vars->{product}->flag_types->{attachment};
- $flag_types = [ map { $_ } (@$bug_flags, @$attachment_flags) ];
+ elsif (exists $vars->{'attachment'}) {
+ $bug = $vars->{'attachment'}->{bug};
}
- return if !@$flag_types;
+ $flag_types = Bugzilla::FlagType::match({
+ 'target_type' => ($file =~ /^bug/ ? 'bug' : 'attachment'),
+ 'product_id' => $bug->product_id,
+ 'component_id' => $bug->component_id,
+ 'bug_id' => $bug->id,
+ 'active_or_has_flags' => $bug->id,
+ });
- $vars->{flag_default_requestees} ||= {};
+ $vars->{flag_currently_requested} ||= {};
foreach my $type (@$flag_types) {
- next if !$type->default_requestee;
- $vars->{flag_default_requestees}->{$type->id} = $type->default_requestee->login;
+ my $flags
+ = Bugzilla::Flag->match({
+ type_id => $type->id, bug_id => $bug->id, status => '?'
+ });
+ map { $vars->{flag_currently_requested}->{$_->id} = 1 } @$flags;
}
+ }
+ elsif ($file =~ /^bug\/create/ && exists $vars->{product}) {
+ my $bug_flags = $vars->{product}->flag_types->{bug};
+ my $attachment_flags = $vars->{product}->flag_types->{attachment};
+ $flag_types = [map {$_} (@$bug_flags, @$attachment_flags)];
+ }
+
+ return if !@$flag_types;
+
+ $vars->{flag_default_requestees} ||= {};
+ foreach my $type (@$flag_types) {
+ next if !$type->default_requestee;
+ $vars->{flag_default_requestees}->{$type->id} = $type->default_requestee->login;
+ }
}
##################
@@ -94,83 +96,79 @@ sub template_before_process {
##################
BEGIN {
- *Bugzilla::FlagType::default_requestee = \&_default_requestee;
+ *Bugzilla::FlagType::default_requestee = \&_default_requestee;
}
sub object_columns {
- my ($self, $args) = @_;
- my ($class, $columns) = @$args{qw(class columns)};
- if ($class->isa('Bugzilla::FlagType')) {
- push(@$columns, 'default_requestee');
- }
+ my ($self, $args) = @_;
+ my ($class, $columns) = @$args{qw(class columns)};
+ if ($class->isa('Bugzilla::FlagType')) {
+ push(@$columns, 'default_requestee');
+ }
}
sub object_update_columns {
- my ($self, $args) = @_;
- my $object = $args->{object};
- return unless $object->isa('Bugzilla::FlagType');
+ my ($self, $args) = @_;
+ my $object = $args->{object};
+ return unless $object->isa('Bugzilla::FlagType');
- my $columns = $args->{columns};
- push(@$columns, 'default_requestee');
+ my $columns = $args->{columns};
+ push(@$columns, 'default_requestee');
- # editflagtypes.cgi doesn't call set_all, so we have to do this here
- my $input = Bugzilla->input_params;
- $object->set('default_requestee', $input->{default_requestee})
- if exists $input->{default_requestee};
+ # editflagtypes.cgi doesn't call set_all, so we have to do this here
+ my $input = Bugzilla->input_params;
+ $object->set('default_requestee', $input->{default_requestee})
+ if exists $input->{default_requestee};
}
sub object_validators {
- my ($self, $args) = @_;
- my $class = $args->{class};
- return unless $class->isa('Bugzilla::FlagType');
+ my ($self, $args) = @_;
+ my $class = $args->{class};
+ return unless $class->isa('Bugzilla::FlagType');
- my $validators = $args->{validators};
- $validators->{default_requestee} = \&_check_default_requestee;
+ my $validators = $args->{validators};
+ $validators->{default_requestee} = \&_check_default_requestee;
}
sub object_before_create {
- my ($self, $args) = @_;
- my $class = $args->{class};
- return unless $class->isa('Bugzilla::FlagType');
-
- my $params = $args->{params};
- my $input = Bugzilla->input_params;
- $params->{default_requestee} = $input->{default_requestee}
- if exists $params->{default_requestee};
+ my ($self, $args) = @_;
+ my $class = $args->{class};
+ return unless $class->isa('Bugzilla::FlagType');
+
+ my $params = $args->{params};
+ my $input = Bugzilla->input_params;
+ $params->{default_requestee} = $input->{default_requestee}
+ if exists $params->{default_requestee};
}
sub object_end_of_update {
- my ($self, $args) = @_;
- my $object = $args->{object};
- return unless $object->isa('Bugzilla::FlagType');
-
- my $old_object = $args->{old_object};
- my $changes = $args->{changes};
- my $old_id = $old_object->default_requestee
- ? $old_object->default_requestee->id
- : 0;
- my $new_id = $object->default_requestee
- ? $object->default_requestee->id
- : 0;
- return if $old_id == $new_id;
-
- $changes->{default_requestee} = [ $old_id, $new_id ];
+ my ($self, $args) = @_;
+ my $object = $args->{object};
+ return unless $object->isa('Bugzilla::FlagType');
+
+ my $old_object = $args->{old_object};
+ my $changes = $args->{changes};
+ my $old_id
+ = $old_object->default_requestee ? $old_object->default_requestee->id : 0;
+ my $new_id = $object->default_requestee ? $object->default_requestee->id : 0;
+ return if $old_id == $new_id;
+
+ $changes->{default_requestee} = [$old_id, $new_id];
}
sub _check_default_requestee {
- my ($self, $value, $field) = @_;
- $value = trim($value // '');
- return undef if $value eq '';
- ThrowUserError("flag_default_requestee_review")
- if $self->name eq 'review';
- return Bugzilla::User->check($value)->id;
+ my ($self, $value, $field) = @_;
+ $value = trim($value // '');
+ return undef if $value eq '';
+ ThrowUserError("flag_default_requestee_review") if $self->name eq 'review';
+ return Bugzilla::User->check($value)->id;
}
sub _default_requestee {
- my ($self) = @_;
- return $self->{default_requestee}
- ? Bugzilla::User->new({ id => $self->{default_requestee}, cache => 1 })
- : undef;
+ my ($self) = @_;
+ return $self->{default_requestee}
+ ? Bugzilla::User->new({id => $self->{default_requestee}, cache => 1})
+ : undef;
}
__PACKAGE__->NAME;
diff --git a/extensions/FlagDefaultRequestee/lib/Constants.pm b/extensions/FlagDefaultRequestee/lib/Constants.pm
index 2c2cdf35c..26d3e0d9e 100644
--- a/extensions/FlagDefaultRequestee/lib/Constants.pm
+++ b/extensions/FlagDefaultRequestee/lib/Constants.pm
@@ -14,14 +14,12 @@ use warnings;
use base qw(Exporter);
our @EXPORT = qw(
- FLAGTYPE_TEMPLATES
+ FLAGTYPE_TEMPLATES
);
use constant FLAGTYPE_TEMPLATES => (
- "attachment/edit.html.tmpl",
- "attachment/createformcontents.html.tmpl",
- "bug/edit.html.tmpl",
- "bug/create/create.html.tmpl"
+ "attachment/edit.html.tmpl", "attachment/createformcontents.html.tmpl",
+ "bug/edit.html.tmpl", "bug/create/create.html.tmpl"
);
1;
diff --git a/extensions/FlagTypeComment/Extension.pm b/extensions/FlagTypeComment/Extension.pm
index e7b34113d..e26b08b27 100644
--- a/extensions/FlagTypeComment/Extension.pm
+++ b/extensions/FlagTypeComment/Extension.pm
@@ -39,31 +39,19 @@ our $VERSION = '1';
################
sub db_schema_abstract_schema {
- my ($self, $args) = @_;
- $args->{'schema'}->{'flagtype_comments'} = {
- FIELDS => [
- type_id => {
- TYPE => 'SMALLINT(6)',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'flagtypes',
- COLUMN => 'id',
- DELETE => 'CASCADE'
- }
- },
- on_status => {
- TYPE => 'CHAR(1)',
- NOTNULL => 1
- },
- comment => {
- TYPE => 'MEDIUMTEXT',
- NOTNULL => 1
- },
- ],
- INDEXES => [
- flagtype_comments_idx => ['type_id'],
- ],
- };
+ my ($self, $args) = @_;
+ $args->{'schema'}->{'flagtype_comments'} = {
+ FIELDS => [
+ type_id => {
+ TYPE => 'SMALLINT(6)',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'flagtypes', COLUMN => 'id', DELETE => 'CASCADE'}
+ },
+ on_status => {TYPE => 'CHAR(1)', NOTNULL => 1},
+ comment => {TYPE => 'MEDIUMTEXT', NOTNULL => 1},
+ ],
+ INDEXES => [flagtype_comments_idx => ['type_id'],],
+ };
}
#############
@@ -71,83 +59,89 @@ sub db_schema_abstract_schema {
#############
sub template_before_process {
- my ($self, $args) = @_;
- my ($vars, $file) = @$args{qw(vars file)};
+ my ($self, $args) = @_;
+ my ($vars, $file) = @$args{qw(vars file)};
- return unless Bugzilla->user->id;
- if (grep { $_ eq $file } FLAGTYPE_COMMENT_TEMPLATES) {
- _set_ftc_states($file, $vars);
- }
+ return unless Bugzilla->user->id;
+ if (grep { $_ eq $file } FLAGTYPE_COMMENT_TEMPLATES) {
+ _set_ftc_states($file, $vars);
+ }
}
sub _set_ftc_states {
- my ($file, $vars) = @_;
- my $dbh = Bugzilla->dbh;
-
- my $ftc_flags;
- my $db_result;
- if ($file =~ /^admin\//) {
- # admin
- my $type = $vars->{'type'} || return;
- my ($target_type, $id);
- if (blessed($type)) {
- ($target_type, $id) = ($type->target_type, $type->id);
- } else {
- ($target_type, $id) = ($type->{target_type}, $type->{id});
- trick_taint($id) if $id;
- }
- if ($target_type eq 'bug') {
- return unless FLAGTYPE_COMMENT_BUG_FLAGS;
- } else {
- return unless FLAGTYPE_COMMENT_ATTACHMENT_FLAGS;
- }
- if ($id) {
- $db_result = $dbh->selectall_arrayref(
- "SELECT type_id AS flagtype, on_status AS state, comment AS text
+ my ($file, $vars) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ my $ftc_flags;
+ my $db_result;
+ if ($file =~ /^admin\//) {
+
+ # admin
+ my $type = $vars->{'type'} || return;
+ my ($target_type, $id);
+ if (blessed($type)) {
+ ($target_type, $id) = ($type->target_type, $type->id);
+ }
+ else {
+ ($target_type, $id) = ($type->{target_type}, $type->{id});
+ trick_taint($id) if $id;
+ }
+ if ($target_type eq 'bug') {
+ return unless FLAGTYPE_COMMENT_BUG_FLAGS;
+ }
+ else {
+ return unless FLAGTYPE_COMMENT_ATTACHMENT_FLAGS;
+ }
+ if ($id) {
+ $db_result = $dbh->selectall_arrayref(
+ "SELECT type_id AS flagtype, on_status AS state, comment AS text
FROM flagtype_comments
- WHERE type_id = ?",
- { Slice => {} }, $id);
- }
- } else {
- # creating/editing attachment / viewing bug
- my $bug;
- if (exists $vars->{'bug'}) {
- $bug = $vars->{'bug'};
- } elsif (exists $vars->{'attachment'}) {
- $bug = $vars->{'attachment'}->{bug};
- } else {
- return;
- }
-
- my $flag_types = Bugzilla::FlagType::match({
- 'target_type' => ($file =~ /^bug/ ? 'bug' : 'attachment'),
- 'product_id' => $bug->product_id,
- 'component_id' => $bug->component_id,
- 'bug_id' => $bug->id,
- 'active_or_has_flags' => $bug->id,
- });
-
- if (@$flag_types) {
- my $types = join(',', map { $_->id } @$flag_types);
- my $states = "'" . join("','", FLAGTYPE_COMMENT_STATES) . "'";
- $db_result = $dbh->selectall_arrayref(
- "SELECT type_id AS flagtype, on_status AS state, comment AS text
- FROM flagtype_comments
- WHERE type_id IN ($types) AND on_status IN ($states)",
- { Slice => {} });
- }
- else {
- $db_result = [];
- }
+ WHERE type_id = ?", {Slice => {}}, $id
+ );
+ }
+ }
+ else {
+ # creating/editing attachment / viewing bug
+ my $bug;
+ if (exists $vars->{'bug'}) {
+ $bug = $vars->{'bug'};
+ }
+ elsif (exists $vars->{'attachment'}) {
+ $bug = $vars->{'attachment'}->{bug};
+ }
+ else {
+ return;
}
- foreach my $row (@$db_result) {
- $ftc_flags->{$row->{'flagtype'}} ||= {};
- $ftc_flags->{$row->{'flagtype'}}{$row->{'state'}} = $row->{text};
+ my $flag_types = Bugzilla::FlagType::match({
+ 'target_type' => ($file =~ /^bug/ ? 'bug' : 'attachment'),
+ 'product_id' => $bug->product_id,
+ 'component_id' => $bug->component_id,
+ 'bug_id' => $bug->id,
+ 'active_or_has_flags' => $bug->id,
+ });
+
+ if (@$flag_types) {
+ my $types = join(',', map { $_->id } @$flag_types);
+ my $states = "'" . join("','", FLAGTYPE_COMMENT_STATES) . "'";
+ $db_result = $dbh->selectall_arrayref(
+ "SELECT type_id AS flagtype, on_status AS state, comment AS text
+ FROM flagtype_comments
+ WHERE type_id IN ($types) AND on_status IN ($states)", {Slice => {}}
+ );
+ }
+ else {
+ $db_result = [];
}
+ }
- $vars->{'ftc_states'} = [ FLAGTYPE_COMMENT_STATES ];
- $vars->{'ftc_flags'} = $ftc_flags;
+ foreach my $row (@$db_result) {
+ $ftc_flags->{$row->{'flagtype'}} ||= {};
+ $ftc_flags->{$row->{'flagtype'}}{$row->{'state'}} = $row->{text};
+ }
+
+ $vars->{'ftc_states'} = [FLAGTYPE_COMMENT_STATES];
+ $vars->{'ftc_flags'} = $ftc_flags;
}
#########
@@ -155,55 +149,55 @@ sub _set_ftc_states {
#########
sub flagtype_end_of_create {
- my ($self, $args) = @_;
- _set_flagtypes($args->{type});
+ my ($self, $args) = @_;
+ _set_flagtypes($args->{type});
}
sub flagtype_end_of_update {
- my ($self, $args) = @_;
- _set_flagtypes($args->{type});
+ my ($self, $args) = @_;
+ _set_flagtypes($args->{type});
}
sub _set_flagtypes {
- my $flag_type = shift;
- my $flagtype_id = $flag_type->id;
- my $input = Bugzilla->input_params;
- my $dbh = Bugzilla->dbh;
-
- foreach my $state (FLAGTYPE_COMMENT_STATES) {
- next if (!defined $input->{"ftc_${flagtype_id}_$state"}
- && !defined $input->{"ftc_new_$state"});
-
- my $text = $input->{"ftc_${flagtype_id}_$state"} || $input->{"ftc_new_$state"} || '';
- $text =~ s/\r\n/\n/g;
- trick_taint($text);
-
- if ($text ne '') {
- if ($dbh->selectrow_array(
- "SELECT 1 FROM flagtype_comments WHERE type_id=? AND on_status=?",
- undef,
- $flagtype_id, $state)
- ) {
- $dbh->do(
- "UPDATE flagtype_comments SET comment=?
- WHERE type_id=? AND on_status=?",
- undef,
- $text, $flagtype_id, $state);
- } else {
- $dbh->do(
- "INSERT INTO flagtype_comments(type_id, on_status, comment)
- VALUES (?, ?, ?)",
- undef,
- $flagtype_id, $state, $text);
- }
-
- } else {
- $dbh->do(
- "DELETE FROM flagtype_comments WHERE type_id=? AND on_status=?",
- undef,
- $flagtype_id, $state);
- }
+ my $flag_type = shift;
+ my $flagtype_id = $flag_type->id;
+ my $input = Bugzilla->input_params;
+ my $dbh = Bugzilla->dbh;
+
+ foreach my $state (FLAGTYPE_COMMENT_STATES) {
+ next
+ if (!defined $input->{"ftc_${flagtype_id}_$state"}
+ && !defined $input->{"ftc_new_$state"});
+
+ my $text
+ = $input->{"ftc_${flagtype_id}_$state"} || $input->{"ftc_new_$state"} || '';
+ $text =~ s/\r\n/\n/g;
+ trick_taint($text);
+
+ if ($text ne '') {
+ if ($dbh->selectrow_array(
+ "SELECT 1 FROM flagtype_comments WHERE type_id=? AND on_status=?", undef,
+ $flagtype_id, $state
+ ))
+ {
+ $dbh->do(
+ "UPDATE flagtype_comments SET comment=?
+ WHERE type_id=? AND on_status=?", undef, $text, $flagtype_id, $state
+ );
+ }
+ else {
+ $dbh->do(
+ "INSERT INTO flagtype_comments(type_id, on_status, comment)
+ VALUES (?, ?, ?)", undef, $flagtype_id, $state, $text
+ );
+ }
+
+ }
+ else {
+ $dbh->do("DELETE FROM flagtype_comments WHERE type_id=? AND on_status=?",
+ undef, $flagtype_id, $state);
}
+ }
}
__PACKAGE__->NAME;
diff --git a/extensions/FlagTypeComment/lib/Constants.pm b/extensions/FlagTypeComment/lib/Constants.pm
index d6242b78b..ee3a54076 100644
--- a/extensions/FlagTypeComment/lib/Constants.pm
+++ b/extensions/FlagTypeComment/lib/Constants.pm
@@ -26,28 +26,26 @@ use warnings;
use base qw(Exporter);
our @EXPORT = qw(
- FLAGTYPE_COMMENT_TEMPLATES
- FLAGTYPE_COMMENT_STATES
- FLAGTYPE_COMMENT_BUG_FLAGS
- FLAGTYPE_COMMENT_ATTACHMENT_FLAGS
+ FLAGTYPE_COMMENT_TEMPLATES
+ FLAGTYPE_COMMENT_STATES
+ FLAGTYPE_COMMENT_BUG_FLAGS
+ FLAGTYPE_COMMENT_ATTACHMENT_FLAGS
);
-use constant FLAGTYPE_COMMENT_STATES => ("?", "+", "-");
-use constant FLAGTYPE_COMMENT_BUG_FLAGS => 0;
+use constant FLAGTYPE_COMMENT_STATES => ("?", "+", "-");
+use constant FLAGTYPE_COMMENT_BUG_FLAGS => 0;
use constant FLAGTYPE_COMMENT_ATTACHMENT_FLAGS => 1;
sub FLAGTYPE_COMMENT_TEMPLATES {
- my @result = ("admin/flag-type/edit.html.tmpl");
- if (FLAGTYPE_COMMENT_BUG_FLAGS) {
- push @result, ("bug/comments.html.tmpl");
- }
- if (FLAGTYPE_COMMENT_ATTACHMENT_FLAGS) {
- push @result, (
- "attachment/edit.html.tmpl",
- "attachment/createformcontents.html.tmpl",
- );
- }
- return @result;
+ my @result = ("admin/flag-type/edit.html.tmpl");
+ if (FLAGTYPE_COMMENT_BUG_FLAGS) {
+ push @result, ("bug/comments.html.tmpl");
+ }
+ if (FLAGTYPE_COMMENT_ATTACHMENT_FLAGS) {
+ push @result,
+ ("attachment/edit.html.tmpl", "attachment/createformcontents.html.tmpl",);
+ }
+ return @result;
}
1;
diff --git a/extensions/GitHubAuth/Extension.pm b/extensions/GitHubAuth/Extension.pm
index d0d9f42f1..85d81b02a 100644
--- a/extensions/GitHubAuth/Extension.pm
+++ b/extensions/GitHubAuth/Extension.pm
@@ -24,69 +24,73 @@ use URI::QueryParam;
our $VERSION = '0.01';
BEGIN {
- # Monkey-patch can() on Bugzilla::Auth::Login::CGI so that our own fail_nodata gets called.
- # Our fail_nodata behaves like CGI's, so this shouldn't be a problem for CGI-based logins.
+# Monkey-patch can() on Bugzilla::Auth::Login::CGI so that our own fail_nodata gets called.
+# Our fail_nodata behaves like CGI's, so this shouldn't be a problem for CGI-based logins.
- *Bugzilla::Auth::Login::CGI::can = sub {
- my ($stack, $method) = @_;
+ *Bugzilla::Auth::Login::CGI::can = sub {
+ my ($stack, $method) = @_;
- return undef if $method eq 'fail_nodata';
- return $stack->SUPER::can($method);
- };
+ return undef if $method eq 'fail_nodata';
+ return $stack->SUPER::can($method);
+ };
}
sub install_before_final_checks {
- Bugzilla::Group->create({
- name => 'no-github-auth',
- description => 'Group containing groups whose members may not use GitHubAuth to log in',
- isbuggroup => 0,
- }) unless Bugzilla::Group->new({ name => 'no-github-auth' });
+ Bugzilla::Group->create({
+ name => 'no-github-auth',
+ description =>
+ 'Group containing groups whose members may not use GitHubAuth to log in',
+ isbuggroup => 0,
+ })
+ unless Bugzilla::Group->new({name => 'no-github-auth'});
}
sub attachment_should_redirect_login {
- my ($self, $args) = @_;
- my $cgi = Bugzilla->cgi;
+ my ($self, $args) = @_;
+ my $cgi = Bugzilla->cgi;
- if ($cgi->param('github_state') || $cgi->param('github_email')) {
- ${$args->{do_redirect}} = 1;
- }
+ if ($cgi->param('github_state') || $cgi->param('github_email')) {
+ ${$args->{do_redirect}} = 1;
+ }
}
sub auth_login_methods {
- my ($self, $args) = @_;
- my $modules = $args->{'modules'};
- if (exists $modules->{'GitHubAuth'}) {
- $modules->{'GitHubAuth'} = 'Bugzilla/Extension/GitHubAuth/Login.pm';
- }
+ my ($self, $args) = @_;
+ my $modules = $args->{'modules'};
+ if (exists $modules->{'GitHubAuth'}) {
+ $modules->{'GitHubAuth'} = 'Bugzilla/Extension/GitHubAuth/Login.pm';
+ }
}
sub auth_verify_methods {
- my ($self, $args) = @_;
- my $modules = $args->{'modules'};
- if (exists $modules->{'GitHubAuth'}) {
- $modules->{'GitHubAuth'} = 'Bugzilla/Extension/GitHubAuth/Verify.pm';
- }
+ my ($self, $args) = @_;
+ my $modules = $args->{'modules'};
+ if (exists $modules->{'GitHubAuth'}) {
+ $modules->{'GitHubAuth'} = 'Bugzilla/Extension/GitHubAuth/Verify.pm';
+ }
}
sub config_modify_panels {
- my ($self, $args) = @_;
- my $auth_panel_params = $args->{panels}{auth}{params};
-
- my $user_info_class = first { $_->{name} eq 'user_info_class' } @$auth_panel_params;
- if ($user_info_class) {
- push @{ $user_info_class->{choices} }, "GitHubAuth,CGI";
- }
-
- my $user_verify_class = first { $_->{name} eq 'user_verify_class' } @$auth_panel_params;
- if ($user_verify_class) {
- unshift @{ $user_verify_class->{choices} }, "GitHubAuth";
- }
+ my ($self, $args) = @_;
+ my $auth_panel_params = $args->{panels}{auth}{params};
+
+ my $user_info_class
+ = first { $_->{name} eq 'user_info_class' } @$auth_panel_params;
+ if ($user_info_class) {
+ push @{$user_info_class->{choices}}, "GitHubAuth,CGI";
+ }
+
+ my $user_verify_class
+ = first { $_->{name} eq 'user_verify_class' } @$auth_panel_params;
+ if ($user_verify_class) {
+ unshift @{$user_verify_class->{choices}}, "GitHubAuth";
+ }
}
sub config_add_panels {
- my ($self, $args) = @_;
- my $modules = $args->{panel_modules};
- $modules->{GitHubAuth} = "Bugzilla::Extension::GitHubAuth::Config";
+ my ($self, $args) = @_;
+ my $modules = $args->{panel_modules};
+ $modules->{GitHubAuth} = "Bugzilla::Extension::GitHubAuth::Config";
}
__PACKAGE__->NAME;
diff --git a/extensions/GitHubAuth/lib/Client.pm b/extensions/GitHubAuth/lib/Client.pm
index 291501961..328bab48f 100644
--- a/extensions/GitHubAuth/lib/Client.pm
+++ b/extensions/GitHubAuth/lib/Client.pm
@@ -16,7 +16,8 @@ use URI;
use URI::QueryParam;
use Digest;
-use Bugzilla::Extension::GitHubAuth::Client::Error qw(ThrowUserError ThrowCodeError);
+use Bugzilla::Extension::GitHubAuth::Client::Error
+ qw(ThrowUserError ThrowCodeError);
use Bugzilla::Util qw(remote_ip);
use constant DIGEST_HASH => 'SHA1';
@@ -24,107 +25,108 @@ use constant DIGEST_HASH => 'SHA1';
use fields qw(user_agent);
use constant {
- GH_ACCESS_TOKEN_URI => 'https://github.com/login/oauth/access_token',
- GH_AUTHORIZE_URI => 'https://github.com/login/oauth/authorize',
- GH_USER_EMAILS_URI => 'https://api.github.com/user/emails',
+ GH_ACCESS_TOKEN_URI => 'https://github.com/login/oauth/access_token',
+ GH_AUTHORIZE_URI => 'https://github.com/login/oauth/authorize',
+ GH_USER_EMAILS_URI => 'https://api.github.com/user/emails',
};
sub new {
- my ($class, %init) = @_;
- my $self = $class->fields::new();
+ my ($class, %init) = @_;
+ my $self = $class->fields::new();
- return $self;
+ return $self;
}
sub login_uri {
- my ($class, $target_uri) = @_;
+ my ($class, $target_uri) = @_;
- my $uri = URI->new(Bugzilla->localconfig->{urlbase} . "github.cgi");
- $uri->query_form(target_uri => $target_uri);
- return $uri;
+ my $uri = URI->new(Bugzilla->localconfig->{urlbase} . "github.cgi");
+ $uri->query_form(target_uri => $target_uri);
+ return $uri;
}
sub authorize_uri {
- my ($class, $state) = @_;
+ my ($class, $state) = @_;
- my $uri = URI->new(GH_AUTHORIZE_URI);
- $uri->query_form(
- client_id => Bugzilla->params->{github_client_id},
- scope => 'user:email',
- state => $state,
- redirect_uri => Bugzilla->localconfig->{urlbase} . "github.cgi",
- );
+ my $uri = URI->new(GH_AUTHORIZE_URI);
+ $uri->query_form(
+ client_id => Bugzilla->params->{github_client_id},
+ scope => 'user:email',
+ state => $state,
+ redirect_uri => Bugzilla->localconfig->{urlbase} . "github.cgi",
+ );
- return $uri;
+ return $uri;
}
sub get_email_key {
- my ($class, $email) = @_;
-
- my $cgi = Bugzilla->cgi;
- my $digest = Digest->new(DIGEST_HASH);
- $digest->add($email);
- $digest->add(remote_ip());
- $digest->add($cgi->cookie('Bugzilla_github_token') // Bugzilla->request_cache->{github_token} // '');
- $digest->add(Bugzilla->localconfig->{site_wide_secret});
- return $digest->hexdigest;
+ my ($class, $email) = @_;
+
+ my $cgi = Bugzilla->cgi;
+ my $digest = Digest->new(DIGEST_HASH);
+ $digest->add($email);
+ $digest->add(remote_ip());
+ $digest->add($cgi->cookie('Bugzilla_github_token')
+ // Bugzilla->request_cache->{github_token} // '');
+ $digest->add(Bugzilla->localconfig->{site_wide_secret});
+ return $digest->hexdigest;
}
sub _handle_response {
- my ($self, $response) = @_;
- my $data = eval {
- decode_json($response->content);
- };
- if ($@) {
- ThrowCodeError("github_bad_response", { message => "Unable to parse json response" });
- }
-
- unless ($response->is_success) {
- ThrowCodeError("github_error", { response => $response });
- }
- return $data;
+ my ($self, $response) = @_;
+ my $data = eval { decode_json($response->content); };
+ if ($@) {
+ ThrowCodeError("github_bad_response",
+ {message => "Unable to parse json response"});
+ }
+
+ unless ($response->is_success) {
+ ThrowCodeError("github_error", {response => $response});
+ }
+ return $data;
}
sub get_access_token {
- my ($self, $code) = @_;
-
- my $response = $self->user_agent->post(
- GH_ACCESS_TOKEN_URI,
- { client_id => Bugzilla->params->{github_client_id},
- client_secret => Bugzilla->params->{github_client_secret},
- code => $code },
- Accept => 'application/json',
- );
- my $data = $self->_handle_response($response);
- return $data->{access_token} if exists $data->{access_token};
+ my ($self, $code) = @_;
+
+ my $response = $self->user_agent->post(GH_ACCESS_TOKEN_URI,
+ {
+ client_id => Bugzilla->params->{github_client_id},
+ client_secret => Bugzilla->params->{github_client_secret},
+ code => $code
+ },
+ Accept => 'application/json',
+ );
+ my $data = $self->_handle_response($response);
+ return $data->{access_token} if exists $data->{access_token};
}
sub get_user_emails {
- my ($self, $access_token) = @_;
- my $uri = URI->new(GH_USER_EMAILS_URI);
- $uri->query_form(access_token => $access_token);
+ my ($self, $access_token) = @_;
+ my $uri = URI->new(GH_USER_EMAILS_URI);
+ $uri->query_form(access_token => $access_token);
- my $response = $self->user_agent->get($uri, Accept => 'application/json');
+ my $response = $self->user_agent->get($uri, Accept => 'application/json');
- return $self->_handle_response($response);
+ return $self->_handle_response($response);
}
sub user_agent {
- my ($self) = @_;
- $self->{user_agent} //= $self->_build_user_agent;
+ my ($self) = @_;
+ $self->{user_agent} //= $self->_build_user_agent;
- return $self->{user_agent};
+ return $self->{user_agent};
}
sub _build_user_agent {
- my ($self) = @_;
- my $ua = LWP::UserAgent->new( timeout => 10 );
+ my ($self) = @_;
+ my $ua = LWP::UserAgent->new(timeout => 10);
- if (Bugzilla->params->{proxy_url}) {
- $ua->proxy('https', Bugzilla->params->{proxy_url});
- }
+ if (Bugzilla->params->{proxy_url}) {
+ $ua->proxy('https', Bugzilla->params->{proxy_url});
+ }
- return $ua;
+ return $ua;
}
1;
diff --git a/extensions/GitHubAuth/lib/Client/Error.pm b/extensions/GitHubAuth/lib/Client/Error.pm
index adb6ec07b..00e8415d1 100644
--- a/extensions/GitHubAuth/lib/Client/Error.pm
+++ b/extensions/GitHubAuth/lib/Client/Error.pm
@@ -16,39 +16,39 @@ use Bugzilla::Error ();
use base qw(Exporter);
use fields qw(type error vars);
-our @EXPORT = qw(ThrowUserError ThrowCodeError);
+our @EXPORT = qw(ThrowUserError ThrowCodeError);
our $USE_EXCEPTION_OBJECTS = 0;
sub _new {
- my ($class, $type, $error, $vars) = @_;
- my $self = $class->fields::new();
- $self->{type} = $type;
- $self->{error} = $error;
- $self->{vars} = $vars // {};
+ my ($class, $type, $error, $vars) = @_;
+ my $self = $class->fields::new();
+ $self->{type} = $type;
+ $self->{error} = $error;
+ $self->{vars} = $vars // {};
- return $self;
+ return $self;
}
-sub type { $_[0]->{type} }
+sub type { $_[0]->{type} }
sub error { $_[0]->{error} }
-sub vars { $_[0]->{vars} }
+sub vars { $_[0]->{vars} }
sub ThrowUserError {
- if ($USE_EXCEPTION_OBJECTS) {
- die __PACKAGE__->_new('user', @_);
- }
- else {
- Bugzilla::Error::ThrowUserError(@_);
- }
+ if ($USE_EXCEPTION_OBJECTS) {
+ die __PACKAGE__->_new('user', @_);
+ }
+ else {
+ Bugzilla::Error::ThrowUserError(@_);
+ }
}
sub ThrowCodeError {
- if ($USE_EXCEPTION_OBJECTS) {
- die __PACKAGE__->_new('code', @_);
- }
- else {
- Bugzilla::Error::ThrowCodeError(@_);
- }
+ if ($USE_EXCEPTION_OBJECTS) {
+ die __PACKAGE__->_new('code', @_);
+ }
+ else {
+ Bugzilla::Error::ThrowCodeError(@_);
+ }
}
1;
diff --git a/extensions/GitHubAuth/lib/Config.pm b/extensions/GitHubAuth/lib/Config.pm
index 0c8874129..b718b4be5 100644
--- a/extensions/GitHubAuth/lib/Config.pm
+++ b/extensions/GitHubAuth/lib/Config.pm
@@ -16,22 +16,14 @@ use Bugzilla::Config::Common;
our $sortkey = 1350;
sub get_param_list {
- my ($class) = @_;
-
- my @params = (
- {
- name => 'github_client_id',
- type => 't',
- default => '',
- },
- {
- name => 'github_client_secret',
- type => 't',
- default => '',
- },
- );
-
- return @params;
+ my ($class) = @_;
+
+ my @params = (
+ {name => 'github_client_id', type => 't', default => '',},
+ {name => 'github_client_secret', type => 't', default => '',},
+ );
+
+ return @params;
}
1;
diff --git a/extensions/GitHubAuth/lib/Login.pm b/extensions/GitHubAuth/lib/Login.pm
index 073fbfeea..df3195bc7 100644
--- a/extensions/GitHubAuth/lib/Login.pm
+++ b/extensions/GitHubAuth/lib/Login.pm
@@ -24,187 +24,208 @@ use Bugzilla::Extension::GitHubAuth::Client;
use Bugzilla::Extension::GitHubAuth::Client::Error ();
use Bugzilla::Error;
-use constant { requires_verification => 1,
- is_automatic => 1,
- user_can_create_account => 1 };
+use constant {requires_verification => 1, is_automatic => 1,
+ user_can_create_account => 1};
sub get_login_info {
- my ($self) = @_;
- my $cgi = Bugzilla->cgi;
- my $github_action = Bugzilla->request_cache->{github_action};
-
- return { failure => AUTH_NODATA } unless $github_action;
-
- my $response;
- if ($github_action eq 'login') {
- $response = $self->_get_login_info_from_github();
- }
- elsif ($github_action eq 'email') {
- $response = $self->_get_login_info_from_email();
- }
-
- if (!exists $response->{failure}) {
- if (exists $response->{user}) {
- # existing account
- my $user = $response->{user};
- return { failure => AUTH_ERROR,
- user_error => 'github_auth_account_too_powerful' } if $user->in_group('no-github-auth');
- return { failure => AUTH_ERROR,
- user_error => 'mfa_prevents_login',
- details => { provider => 'GitHub' } } if $user->mfa;
- $response = {
- username => $user->login,
- user_id => $user->id,
- github_auth => 1,
- };
+ my ($self) = @_;
+ my $cgi = Bugzilla->cgi;
+ my $github_action = Bugzilla->request_cache->{github_action};
+
+ return {failure => AUTH_NODATA} unless $github_action;
+
+ my $response;
+ if ($github_action eq 'login') {
+ $response = $self->_get_login_info_from_github();
+ }
+ elsif ($github_action eq 'email') {
+ $response = $self->_get_login_info_from_email();
+ }
+
+ if (!exists $response->{failure}) {
+ if (exists $response->{user}) {
+
+ # existing account
+ my $user = $response->{user};
+ return {
+ failure => AUTH_ERROR,
+ user_error => 'github_auth_account_too_powerful'
}
- else {
- # new account
- my $email = $response->{email};
- $response = {
- username => $email,
- github_auth => 1,
- };
+ if $user->in_group('no-github-auth');
+ return {
+ failure => AUTH_ERROR,
+ user_error => 'mfa_prevents_login',
+ details => {provider => 'GitHub'}
}
+ if $user->mfa;
+ $response = {username => $user->login, user_id => $user->id, github_auth => 1,};
}
- return $response;
+ else {
+ # new account
+ my $email = $response->{email};
+ $response = {username => $email, github_auth => 1,};
+ }
+ }
+ return $response;
}
sub _get_login_info_from_github {
- my ($self) = @_;
- my $cgi = Bugzilla->cgi;
- my $template = Bugzilla->template;
- my $code = $cgi->param('code');
-
- return { failure => AUTH_ERROR, error => 'github_missing_code' } unless $code;
-
- trick_taint($code);
-
- my $client = Bugzilla::Extension::GitHubAuth::Client->new;
-
- my ($access_token, $emails);
- eval {
- # The following variable lets us catch and return (rather than throw) errors
- # from our github client code, as required by the Auth API.
- local $Bugzilla::Extension::GitHubAuth::Client::Error::USE_EXCEPTION_OBJECTS = 1;
- $access_token = $client->get_access_token($code);
- $emails = $client->get_user_emails($access_token);
- };
- my $e = $@;
- if (blessed $e && $e->isa('Bugzilla::Extension::GitHubAuth::Client::Error')) {
- my $key = $e->type eq 'user' ? 'user_error' : 'error';
- return { failure => AUTH_ERROR, $key => $e->error, details => $e->vars };
- }
- elsif ($e) {
- die $e;
- }
-
- my @emails = map { $_->{email} }
- grep { $_->{verified} && $_->{email} !~ /\@users\.noreply\.github\.com$/ } @$emails;
-
- my @bugzilla_users;
- my @github_emails;
- foreach my $email (@emails) {
- my $user = Bugzilla::User->new({name => $email, cache => 1});
- if ($user) {
- push @bugzilla_users, $user;
- }
- else {
- push @github_emails, $email;
- }
- }
- my @allowed_bugzilla_users = grep { not $_->in_group('no-github-auth') } @bugzilla_users;
-
- if (@allowed_bugzilla_users == 1) {
- my ($user) = @allowed_bugzilla_users;
- return { user => $user };
- }
- elsif (@allowed_bugzilla_users > 1) {
- $self->{github_failure} = {
- template => 'account/auth/github-verify-account.html.tmpl',
- vars => {
- bugzilla_users => \@allowed_bugzilla_users,
- choose_email => _mk_choose_email(\@emails),
- },
- };
- return { failure => AUTH_NODATA };
- }
- elsif (@allowed_bugzilla_users == 0 && @bugzilla_users > 0 && @github_emails == 0) {
- return { failure => AUTH_ERROR,
- user_error => 'github_auth_account_too_powerful' };
- }
- elsif (@github_emails) {
- $self->{github_failure} = {
- template => 'account/auth/github-verify-account.html.tmpl',
- vars => {
- github_emails => \@github_emails,
- choose_email => _mk_choose_email(\@emails),
- },
- };
- return { failure => AUTH_NODATA };
+ my ($self) = @_;
+ my $cgi = Bugzilla->cgi;
+ my $template = Bugzilla->template;
+ my $code = $cgi->param('code');
+
+ return {failure => AUTH_ERROR, error => 'github_missing_code'} unless $code;
+
+ trick_taint($code);
+
+ my $client = Bugzilla::Extension::GitHubAuth::Client->new;
+
+ my ($access_token, $emails);
+ eval {
+ # The following variable lets us catch and return (rather than throw) errors
+ # from our github client code, as required by the Auth API.
+ local $Bugzilla::Extension::GitHubAuth::Client::Error::USE_EXCEPTION_OBJECTS
+ = 1;
+ $access_token = $client->get_access_token($code);
+ $emails = $client->get_user_emails($access_token);
+ };
+ my $e = $@;
+ if (blessed $e && $e->isa('Bugzilla::Extension::GitHubAuth::Client::Error')) {
+ my $key = $e->type eq 'user' ? 'user_error' : 'error';
+ return {failure => AUTH_ERROR, $key => $e->error, details => $e->vars};
+ }
+ elsif ($e) {
+ die $e;
+ }
+
+ my @emails
+ = map { $_->{email} }
+ grep { $_->{verified} && $_->{email} !~ /\@users\.noreply\.github\.com$/ }
+ @$emails;
+
+ my @bugzilla_users;
+ my @github_emails;
+ foreach my $email (@emails) {
+ my $user = Bugzilla::User->new({name => $email, cache => 1});
+ if ($user) {
+ push @bugzilla_users, $user;
}
else {
- return { failure => AUTH_ERROR, user_error => 'github_no_emails' };
+ push @github_emails, $email;
}
+ }
+ my @allowed_bugzilla_users
+ = grep { not $_->in_group('no-github-auth') } @bugzilla_users;
+
+ if (@allowed_bugzilla_users == 1) {
+ my ($user) = @allowed_bugzilla_users;
+ return {user => $user};
+ }
+ elsif (@allowed_bugzilla_users > 1) {
+ $self->{github_failure} = {
+ template => 'account/auth/github-verify-account.html.tmpl',
+ vars => {
+ bugzilla_users => \@allowed_bugzilla_users,
+ choose_email => _mk_choose_email(\@emails),
+ },
+ };
+ return {failure => AUTH_NODATA};
+ }
+ elsif (@allowed_bugzilla_users == 0
+ && @bugzilla_users > 0
+ && @github_emails == 0)
+ {
+ return {
+ failure => AUTH_ERROR,
+ user_error => 'github_auth_account_too_powerful'
+ };
+ }
+ elsif (@github_emails) {
+ $self->{github_failure} = {
+ template => 'account/auth/github-verify-account.html.tmpl',
+ vars => {
+ github_emails => \@github_emails,
+ choose_email => _mk_choose_email(\@emails),
+ },
+ };
+ return {failure => AUTH_NODATA};
+ }
+ else {
+ return {failure => AUTH_ERROR, user_error => 'github_no_emails'};
+ }
}
sub _get_login_info_from_email {
- my ($self) = @_;
- my $cgi = Bugzilla->cgi;
- my $email = $cgi->param('email') or return { failure => AUTH_ERROR,
- user_error => 'github_invalid_email',
- details => { email => '' } };
- trick_taint($email);
-
- unless (any { $_ eq $email } @{ Bugzilla->request_cache->{github_emails} }) {
- return { failure => AUTH_ERROR,
- user_error => 'github_invalid_email',
- details => { email => $email }};
- }
+ my ($self) = @_;
+ my $cgi = Bugzilla->cgi;
+ my $email = $cgi->param('email')
+ or return {
+ failure => AUTH_ERROR,
+ user_error => 'github_invalid_email',
+ details => {email => ''}
+ };
+ trick_taint($email);
- my $user = Bugzilla::User->new({name => $email, cache => 1});
- $cgi->remove_cookie('Bugzilla_github_token');
- return $user ? { user => $user } : { email => $email };
+ unless (any { $_ eq $email } @{Bugzilla->request_cache->{github_emails}}) {
+ return {
+ failure => AUTH_ERROR,
+ user_error => 'github_invalid_email',
+ details => {email => $email}
+ };
+ }
+
+ my $user = Bugzilla::User->new({name => $email, cache => 1});
+ $cgi->remove_cookie('Bugzilla_github_token');
+ return $user ? {user => $user} : {email => $email};
}
sub fail_nodata {
- my ($self) = @_;
- my $cgi = Bugzilla->cgi;
- my $template = Bugzilla->template;
+ my ($self) = @_;
+ my $cgi = Bugzilla->cgi;
+ my $template = Bugzilla->template;
- ThrowUserError('login_required') if Bugzilla->usage_mode != USAGE_MODE_BROWSER;
+ ThrowUserError('login_required') if Bugzilla->usage_mode != USAGE_MODE_BROWSER;
- my $file = $self->{github_failure}{template} // "account/auth/login.html.tmpl";
- my $vars = $self->{github_failure}{vars} // { target => $cgi->url(-relative=>1) };
+ my $file = $self->{github_failure}{template} // "account/auth/login.html.tmpl";
+ my $vars = $self->{github_failure}{vars}
+ // {target => $cgi->url(-relative => 1)};
- print $cgi->header();
- $template->process($file, $vars) or ThrowTemplateError($template->error());
- exit;
+ print $cgi->header();
+ $template->process($file, $vars) or ThrowTemplateError($template->error());
+ exit;
}
sub _store_emails {
- my ($emails) = @_;
- my $state = issue_short_lived_session_token("github_email");
- set_token_extra_data($state, { type => 'github_email',
- emails => $emails,
- target_uri => Bugzilla->request_cache->{github_target_uri} });
-
- Bugzilla->cgi->send_cookie(-name => 'github_state',
- -value => $state,
- -httponly => 1);
- return $state;
+ my ($emails) = @_;
+ my $state = issue_short_lived_session_token("github_email");
+ set_token_extra_data(
+ $state,
+ {
+ type => 'github_email',
+ emails => $emails,
+ target_uri => Bugzilla->request_cache->{github_target_uri}
+ }
+ );
+
+ Bugzilla->cgi->send_cookie(
+ -name => 'github_state',
+ -value => $state,
+ -httponly => 1
+ );
+ return $state;
}
sub _mk_choose_email {
- my ($emails) = @_;
- my $state = _store_emails($emails);
-
- return sub {
- my $email = shift;
- my $uri = URI->new(Bugzilla->localconfig->{urlbase} . "github.cgi");
- $uri->query_form( state => $state, email => $email );
- return $uri;
- };
+ my ($emails) = @_;
+ my $state = _store_emails($emails);
+
+ return sub {
+ my $email = shift;
+ my $uri = URI->new(Bugzilla->localconfig->{urlbase} . "github.cgi");
+ $uri->query_form(state => $state, email => $email);
+ return $uri;
+ };
}
1;
diff --git a/extensions/GitHubAuth/lib/Verify.pm b/extensions/GitHubAuth/lib/Verify.pm
index f399af02e..078353c80 100644
--- a/extensions/GitHubAuth/lib/Verify.pm
+++ b/extensions/GitHubAuth/lib/Verify.pm
@@ -16,11 +16,11 @@ use base qw(Bugzilla::Auth::Verify);
use Bugzilla::Constants qw( AUTH_NO_SUCH_USER );
sub check_credentials {
- my ($self, $login_data) = @_;
+ my ($self, $login_data) = @_;
- return { failure => AUTH_NO_SUCH_USER } unless $login_data->{github_auth};
+ return {failure => AUTH_NO_SUCH_USER} unless $login_data->{github_auth};
- return $login_data;
+ return $login_data;
}
1;
diff --git a/extensions/GoogleAnalytics/Extension.pm b/extensions/GoogleAnalytics/Extension.pm
index e9b144da4..fb7e8adae 100644
--- a/extensions/GoogleAnalytics/Extension.pm
+++ b/extensions/GoogleAnalytics/Extension.pm
@@ -15,9 +15,9 @@ use parent qw(Bugzilla::Extension);
our $VERSION = '0.1';
sub config_add_panels {
- my ($self, $args) = @_;
- my $modules = $args->{panel_modules};
- $modules->{GoogleAnalytics} = "Bugzilla::Extension::GoogleAnalytics::Config";
+ my ($self, $args) = @_;
+ my $modules = $args->{panel_modules};
+ $modules->{GoogleAnalytics} = "Bugzilla::Extension::GoogleAnalytics::Config";
}
__PACKAGE__->NAME;
diff --git a/extensions/GoogleAnalytics/lib/Config.pm b/extensions/GoogleAnalytics/lib/Config.pm
index f9e003ce0..1c453ff74 100644
--- a/extensions/GoogleAnalytics/lib/Config.pm
+++ b/extensions/GoogleAnalytics/lib/Config.pm
@@ -14,28 +14,25 @@ use warnings;
use Bugzilla::Config::Common;
sub get_param_list {
- my ($class) = @_;
-
- my @params = (
- {
- name => 'google_analytics_tracking_id',
- type => 't',
- default => '',
- checker => sub {
- my ($tracking_id) = (@_);
-
- return 'must be like UA-XXXXXX-X' unless $tracking_id =~ m{^(UA-[[:xdigit:]]+-[[:xdigit:]]+)?$};
- return '';
- }
- },
- {
- name => 'google_analytics_debug',
- type => 'b',
- default => 0
- },
- );
-
- return @params;
+ my ($class) = @_;
+
+ my @params = (
+ {
+ name => 'google_analytics_tracking_id',
+ type => 't',
+ default => '',
+ checker => sub {
+ my ($tracking_id) = (@_);
+
+ return 'must be like UA-XXXXXX-X'
+ unless $tracking_id =~ m{^(UA-[[:xdigit:]]+-[[:xdigit:]]+)?$};
+ return '';
+ }
+ },
+ {name => 'google_analytics_debug', type => 'b', default => 0},
+ );
+
+ return @params;
}
1;
diff --git a/extensions/Gravatar/Config.pm b/extensions/Gravatar/Config.pm
index e0c684c9b..8651966b5 100644
--- a/extensions/Gravatar/Config.pm
+++ b/extensions/Gravatar/Config.pm
@@ -11,7 +11,7 @@ use 5.10.1;
use strict;
use warnings;
-use constant NAME => 'Gravatar';
+use constant NAME => 'Gravatar';
use constant REQUIRED_MODULES => [];
use constant OPTIONAL_MODULES => [];
diff --git a/extensions/Gravatar/Extension.pm b/extensions/Gravatar/Extension.pm
index 97cf23b00..04d5f3090 100644
--- a/extensions/Gravatar/Extension.pm
+++ b/extensions/Gravatar/Extension.pm
@@ -20,37 +20,39 @@ use Digest::MD5 qw(md5_hex);
use constant DEFAULT_URL => 'extensions/Gravatar/web/default.jpg';
BEGIN {
- *Bugzilla::User::gravatar = \&_user_gravatar;
+ *Bugzilla::User::gravatar = \&_user_gravatar;
}
sub _user_gravatar {
- my ($self, $size) = @_;
- if ($self->setting('show_my_gravatar') eq 'Off') {
- return DEFAULT_URL;
- }
- if (!$self->{gravatar}) {
- my $email = $self->email;
- $email = $gravatar_user_map{$self->email} if exists $gravatar_user_map{$self->email};
- $self->{gravatar} = 'https://secure.gravatar.com/avatar/' . md5_hex(lc($email)) . '?d=mm';
- }
- $size ||= 64;
- return $self->{gravatar} . '&amp;size=' . $size;
+ my ($self, $size) = @_;
+ if ($self->setting('show_my_gravatar') eq 'Off') {
+ return DEFAULT_URL;
+ }
+ if (!$self->{gravatar}) {
+ my $email = $self->email;
+ $email = $gravatar_user_map{$self->email}
+ if exists $gravatar_user_map{$self->email};
+ $self->{gravatar}
+ = 'https://secure.gravatar.com/avatar/' . md5_hex(lc($email)) . '?d=mm';
+ }
+ $size ||= 64;
+ return $self->{gravatar} . '&amp;size=' . $size;
}
sub install_before_final_checks {
- my ($self, $args) = @_;
- add_setting({
- name => 'show_gravatars',
- options => ['On', 'Off'],
- default => 'Off',
- category => 'Bug Editing'
- });
- add_setting({
- name => 'show_my_gravatar',
- options => ['On', 'Off'],
- default => 'On',
- category => 'Bug Editing'
- });
+ my ($self, $args) = @_;
+ add_setting({
+ name => 'show_gravatars',
+ options => ['On', 'Off'],
+ default => 'Off',
+ category => 'Bug Editing'
+ });
+ add_setting({
+ name => 'show_my_gravatar',
+ options => ['On', 'Off'],
+ default => 'On',
+ category => 'Bug Editing'
+ });
}
__PACKAGE__->NAME;
diff --git a/extensions/Gravatar/lib/Data.pm b/extensions/Gravatar/lib/Data.pm
index 763dba85b..13b004fa5 100644
--- a/extensions/Gravatar/lib/Data.pm
+++ b/extensions/Gravatar/lib/Data.pm
@@ -13,11 +13,9 @@ use warnings;
use base 'Exporter';
our @EXPORT_OK = qw(
- %gravatar_user_map
+ %gravatar_user_map
);
-our %gravatar_user_map = (
- 'orangefactor@bots.tld' => 'tbplbot@gmail.com',
-);
+our %gravatar_user_map = ('orangefactor@bots.tld' => 'tbplbot@gmail.com',);
1;
diff --git a/extensions/GuidedBugEntry/Config.pm b/extensions/GuidedBugEntry/Config.pm
index 316fc6cdc..7fca2ccf0 100644
--- a/extensions/GuidedBugEntry/Config.pm
+++ b/extensions/GuidedBugEntry/Config.pm
@@ -13,10 +13,8 @@ use warnings;
use constant NAME => 'GuidedBugEntry';
-use constant REQUIRED_MODULES => [
-];
+use constant REQUIRED_MODULES => [];
-use constant OPTIONAL_MODULES => [
-];
+use constant OPTIONAL_MODULES => [];
__PACKAGE__->NAME;
diff --git a/extensions/GuidedBugEntry/Extension.pm b/extensions/GuidedBugEntry/Extension.pm
index 2d58d506a..72bae0d84 100644
--- a/extensions/GuidedBugEntry/Extension.pm
+++ b/extensions/GuidedBugEntry/Extension.pm
@@ -23,111 +23,103 @@ use Bugzilla::Extension::BMO::Data;
our $VERSION = '1';
sub enter_bug_start {
- my ($self, $args) = @_;
- my $vars = $args->{vars};
- my $template = Bugzilla->template;
- my $cgi = Bugzilla->cgi;
- my $user = Bugzilla->user;
-
- # hack for skipping old guided code when enabled
- $vars->{'disable_guided'} = 1;
-
- # force guided format for new users
- my $format = $cgi->param('format') || '';
- if ($cgi->param('maketemplate')) {
- $format = '__default__';
+ my ($self, $args) = @_;
+ my $vars = $args->{vars};
+ my $template = Bugzilla->template;
+ my $cgi = Bugzilla->cgi;
+ my $user = Bugzilla->user;
+
+ # hack for skipping old guided code when enabled
+ $vars->{'disable_guided'} = 1;
+
+ # force guided format for new users
+ my $format = $cgi->param('format') || '';
+ if ($cgi->param('maketemplate')) {
+ $format = '__default__';
+ }
+
+ if ($format eq 'guided' || ($format eq '' && !$user->in_group('editbugs'))) {
+
+ # skip the first step if a product is provided
+ if ($cgi->param('product')) {
+ print $cgi->redirect('enter_bug.cgi?format=guided'
+ . ($cgi->param('format_forced') ? '&format_forced=1' : '')
+ . '#h=dupes' . '|'
+ . url_quote($cgi->param('product')) . '|'
+ . url_quote($cgi->param('component') || ''));
+ exit;
}
- if (
- $format eq 'guided' ||
- (
- $format eq '' &&
- !$user->in_group('editbugs')
- )
- ) {
- # skip the first step if a product is provided
- if ($cgi->param('product')) {
- print $cgi->redirect('enter_bug.cgi?format=guided' .
- ($cgi->param('format_forced') ? '&format_forced=1' : '') .
- '#h=dupes' .
- '|' . url_quote($cgi->param('product')) .
- '|' . url_quote($cgi->param('component') || '')
- );
- exit;
- }
-
- # Do not redirect to product forms if we came from there already
- $vars->{'format_forced'} = 1 if $cgi->param('format_forced');
-
- $self->_init_vars($vars);
- print $cgi->header();
- $template->process('guided/guided.html.tmpl', $vars)
- || ThrowTemplateError($template->error());
- exit;
- }
-
- # we use the __default__ format to bypass the guided entry
- # it isn't understood upstream, so remove it once a product
- # has been selected.
- if (
- ($cgi->param('format') && $cgi->param('format') eq "__default__")
- && ($cgi->param('product') && $cgi->param('product') ne '')
- ) {
- $cgi->delete('format');
- }
+ # Do not redirect to product forms if we came from there already
+ $vars->{'format_forced'} = 1 if $cgi->param('format_forced');
+
+ $self->_init_vars($vars);
+ print $cgi->header();
+ $template->process('guided/guided.html.tmpl', $vars)
+ || ThrowTemplateError($template->error());
+ exit;
+ }
+
+ # we use the __default__ format to bypass the guided entry
+ # it isn't understood upstream, so remove it once a product
+ # has been selected.
+ if ( ($cgi->param('format') && $cgi->param('format') eq "__default__")
+ && ($cgi->param('product') && $cgi->param('product') ne ''))
+ {
+ $cgi->delete('format');
+ }
}
sub _init_vars {
- my ($self, $vars) = @_;
- my $user = Bugzilla->user;
-
- my @enterable_products = @{$user->get_enterable_products};
- ThrowUserError('no_products') unless scalar(@enterable_products);
-
- my @classifications = ({object => undef, products => \@enterable_products});
-
- my $class;
- foreach my $product (@enterable_products) {
- $class->{$product->classification_id}->{'object'} ||=
- new Bugzilla::Classification($product->classification_id);
- push(@{$class->{$product->classification_id}->{'products'}}, $product);
- }
- @classifications =
- sort {
- $a->{'object'}->sortkey <=> $b->{'object'}->sortkey
- || lc($a->{'object'}->name) cmp lc($b->{'object'}->name)
- } (values %$class);
- $vars->{'classifications'} = \@classifications;
-
- my @open_states = BUG_STATE_OPEN();
- $vars->{'open_states'} = \@open_states;
-
- $vars->{'token'} = issue_session_token('create_bug');
-
- $vars->{'platform'} = detect_platform();
- $vars->{'op_sys'} = detect_op_sys();
- $vars->{'webdev'} = Bugzilla->cgi->param('webdev');
+ my ($self, $vars) = @_;
+ my $user = Bugzilla->user;
+
+ my @enterable_products = @{$user->get_enterable_products};
+ ThrowUserError('no_products') unless scalar(@enterable_products);
+
+ my @classifications = ({object => undef, products => \@enterable_products});
+
+ my $class;
+ foreach my $product (@enterable_products) {
+ $class->{$product->classification_id}->{'object'}
+ ||= new Bugzilla::Classification($product->classification_id);
+ push(@{$class->{$product->classification_id}->{'products'}}, $product);
+ }
+ @classifications = sort {
+ $a->{'object'}->sortkey <=> $b->{'object'}->sortkey
+ || lc($a->{'object'}->name) cmp lc($b->{'object'}->name)
+ } (values %$class);
+ $vars->{'classifications'} = \@classifications;
+
+ my @open_states = BUG_STATE_OPEN();
+ $vars->{'open_states'} = \@open_states;
+
+ $vars->{'token'} = issue_session_token('create_bug');
+
+ $vars->{'platform'} = detect_platform();
+ $vars->{'op_sys'} = detect_op_sys();
+ $vars->{'webdev'} = Bugzilla->cgi->param('webdev');
}
sub page_before_template {
- my ($self, $args) = @_;
- my $page = $args->{'page_id'};
- my $vars = $args->{'vars'};
- my $cgi = Bugzilla->cgi;
-
- return unless $page eq 'guided_products.js';
-
- if (!$cgi->param('format_forced')) {
- my %bug_formats;
- foreach my $product (keys %create_bug_formats) {
- if (my $format = Bugzilla::Extension::BMO::forced_format($product)) {
- $bug_formats{$product} = $format;
- }
- }
- $vars->{'create_bug_formats'} = \%bug_formats;
+ my ($self, $args) = @_;
+ my $page = $args->{'page_id'};
+ my $vars = $args->{'vars'};
+ my $cgi = Bugzilla->cgi;
+
+ return unless $page eq 'guided_products.js';
+
+ if (!$cgi->param('format_forced')) {
+ my %bug_formats;
+ foreach my $product (keys %create_bug_formats) {
+ if (my $format = Bugzilla::Extension::BMO::forced_format($product)) {
+ $bug_formats{$product} = $format;
+ }
}
+ $vars->{'create_bug_formats'} = \%bug_formats;
+ }
- $vars->{'webdev'} = $cgi->param('webdev');
+ $vars->{'webdev'} = $cgi->param('webdev');
}
__PACKAGE__->NAME;
diff --git a/extensions/InlineHistory/Extension.pm b/extensions/InlineHistory/Extension.pm
index adbfa4c74..45f1120f8 100644
--- a/extensions/InlineHistory/Extension.pm
+++ b/extensions/InlineHistory/Extension.pm
@@ -24,223 +24,227 @@ our $VERSION = '1.5';
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;
- }
-
- # allow other extensions to alter history
- Bugzilla::Hook::process('inline_history_activtiy', { activity => $activity });
-
- 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
- $operation->{who} =
- Bugzilla::User->new({ name => $operation->{who}, cache => 1 });
-
- 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 });
- $change->{fieldtype} = $field_obj->type;
- }
-
- # 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 see-also
- if ($change->{fieldname} eq 'see_also') {
- my $url_base = Bugzilla->localconfig->{urlbase};
- foreach my $f (qw( added removed )) {
- my @values;
- foreach my $value (split(/, /, $change->{$f})) {
- my ($bug_id) = substr($value, 0, length($url_base)) eq $url_base
- ? $value =~ /id=(\d+)$/
- : undef;
- push @values, {
- url => $value,
- bug_id => $bug_id,
- };
- }
- $change->{$f} = \@values;
- }
+ 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;
+ }
+
+ # allow other extensions to alter history
+ Bugzilla::Hook::process('inline_history_activtiy', {activity => $activity});
+
+ 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
+ $operation->{who}
+ = Bugzilla::User->new({name => $operation->{who}, cache => 1});
+
+ 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});
+ $change->{fieldtype} = $field_obj->type;
+ }
+
+ # 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 see-also
+ if ($change->{fieldname} eq 'see_also') {
+ my $url_base = Bugzilla->localconfig->{urlbase};
+ foreach my $f (qw( added removed )) {
+ my @values;
+ foreach my $value (split(/, /, $change->{$f})) {
+ my ($bug_id)
+ = substr($value, 0, length($url_base)) eq $url_base
+ ? $value =~ /id=(\d+)$/
+ : undef;
+ push @values, {url => $value, bug_id => $bug_id,};
+ }
+ $change->{$f} = \@values;
+ }
+ }
+
+ # 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 =~ /^((.+).)$/;
+ next unless defined $name;
+ $flags{$name}{added} = $value;
+ $flags{$name}{removed} |= '';
+ }
+ foreach my $removed (@removed) {
+ my ($value, $name) = $removed =~ /^((.+).)$/;
+ next unless defined $name;
+ $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 =~ /^((.+).)$/;
- next unless defined $name;
- $flags{$name}{added} = $value;
- $flags{$name}{removed} |= '';
- }
- foreach my $removed (@removed) {
- my ($value, $name) = $removed =~ /^((.+).)$/;
- next unless defined $name;
- $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({
- name => 'inline_history',
- options => ['on', 'off'],
- default => 'off',
- category => 'Bug Editing'
- });
+ my ($self, $args) = @_;
+ add_setting({
+ name => 'inline_history',
+ options => ['on', 'off'],
+ default => 'off',
+ category => 'Bug Editing'
+ });
}
__PACKAGE__->NAME;
diff --git a/extensions/LastResolved/Config.pm b/extensions/LastResolved/Config.pm
index 8fd8f106f..c981db7f0 100644
--- a/extensions/LastResolved/Config.pm
+++ b/extensions/LastResolved/Config.pm
@@ -13,10 +13,8 @@ use warnings;
use constant NAME => 'LastResolved';
-use constant REQUIRED_MODULES => [
-];
+use constant REQUIRED_MODULES => [];
-use constant OPTIONAL_MODULES => [
-];
+use constant OPTIONAL_MODULES => [];
__PACKAGE__->NAME;
diff --git a/extensions/LastResolved/Extension.pm b/extensions/LastResolved/Extension.pm
index 197bb15d9..798506ae9 100644
--- a/extensions/LastResolved/Extension.pm
+++ b/extensions/LastResolved/Extension.pm
@@ -22,94 +22,94 @@ use Bugzilla::Install::Util qw(indicate_progress);
our $VERSION = '0.01';
sub install_update_db {
- my ($self, $args) = @_;
- my $last_resolved = Bugzilla::Field->new({'name' => 'cf_last_resolved'});
- if (!$last_resolved) {
- Bugzilla::Field->create({
- name => 'cf_last_resolved',
- description => 'Last Resolved',
- type => FIELD_TYPE_DATETIME,
- mailhead => 0,
- enter_bug => 0,
- obsolete => 0,
- custom => 1,
- buglist => 1,
- });
- _migrate_last_resolved();
- }
+ my ($self, $args) = @_;
+ my $last_resolved = Bugzilla::Field->new({'name' => 'cf_last_resolved'});
+ if (!$last_resolved) {
+ Bugzilla::Field->create({
+ name => 'cf_last_resolved',
+ description => 'Last Resolved',
+ type => FIELD_TYPE_DATETIME,
+ mailhead => 0,
+ enter_bug => 0,
+ obsolete => 0,
+ custom => 1,
+ buglist => 1,
+ });
+ _migrate_last_resolved();
+ }
}
sub _migrate_last_resolved {
- my $dbh = Bugzilla->dbh;
- my $field_id = get_field_id('bug_status');
- my $resolved_activity = $dbh->selectall_arrayref(
- "SELECT bugs_activity.bug_id, bugs_activity.bug_when, bugs_activity.who
+ my $dbh = Bugzilla->dbh;
+ my $field_id = get_field_id('bug_status');
+ my $resolved_activity = $dbh->selectall_arrayref(
+ "SELECT bugs_activity.bug_id, bugs_activity.bug_when, bugs_activity.who
FROM bugs_activity
WHERE bugs_activity.fieldid = ?
AND bugs_activity.added = 'RESOLVED'
- ORDER BY bugs_activity.bug_when",
- undef, $field_id);
+ ORDER BY bugs_activity.bug_when", undef, $field_id
+ );
- my $count = 1;
- my $total = scalar @$resolved_activity;
- my %current_last_resolved;
- foreach my $activity (@$resolved_activity) {
- indicate_progress({ current => $count++, total => $total, every => 25 });
- my ($id, $new, $who) = @$activity;
- my $old = $current_last_resolved{$id} ? $current_last_resolved{$id} : "";
- $dbh->do("UPDATE bugs SET cf_last_resolved = ? WHERE bug_id = ?", undef, $new, $id);
- LogActivityEntry($id, 'cf_last_resolved', $old, $new, $who, $new);
- $current_last_resolved{$id} = $new;
- }
+ my $count = 1;
+ my $total = scalar @$resolved_activity;
+ my %current_last_resolved;
+ foreach my $activity (@$resolved_activity) {
+ indicate_progress({current => $count++, total => $total, every => 25});
+ my ($id, $new, $who) = @$activity;
+ my $old = $current_last_resolved{$id} ? $current_last_resolved{$id} : "";
+ $dbh->do("UPDATE bugs SET cf_last_resolved = ? WHERE bug_id = ?",
+ undef, $new, $id);
+ LogActivityEntry($id, 'cf_last_resolved', $old, $new, $who, $new);
+ $current_last_resolved{$id} = $new;
+ }
}
sub bug_check_can_change_field {
- my ($self, $args) = @_;
- my ($field, $priv_results) = @$args{qw(field priv_results)};
- if ($field eq 'cf_last_resolved') {
- push (@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
- }
+ my ($self, $args) = @_;
+ my ($field, $priv_results) = @$args{qw(field priv_results)};
+ if ($field eq 'cf_last_resolved') {
+ push(@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
+ }
}
sub bug_end_of_update {
- my ($self, $args) = @_;
- my $dbh = Bugzilla->dbh;
- my ($bug, $old_bug, $timestamp, $changes) =
- @$args{qw(bug old_bug timestamp changes)};
- if ($changes->{'bug_status'}) {
- # If the bug has been resolved then update the cf_last_resolved
- # value to the current timestamp if cf_last_resolved exists
- if ($bug->bug_status eq 'RESOLVED') {
- $dbh->do("UPDATE bugs SET cf_last_resolved = ? WHERE bug_id = ?",
- undef, $timestamp, $bug->id);
- my $old_value = $bug->cf_last_resolved || '';
- LogActivityEntry($bug->id, 'cf_last_resolved', $old_value,
- $timestamp, Bugzilla->user->id, $timestamp);
- }
+ my ($self, $args) = @_;
+ my $dbh = Bugzilla->dbh;
+ my ($bug, $old_bug, $timestamp, $changes)
+ = @$args{qw(bug old_bug timestamp changes)};
+ if ($changes->{'bug_status'}) {
+
+ # If the bug has been resolved then update the cf_last_resolved
+ # value to the current timestamp if cf_last_resolved exists
+ if ($bug->bug_status eq 'RESOLVED') {
+ $dbh->do("UPDATE bugs SET cf_last_resolved = ? WHERE bug_id = ?",
+ undef, $timestamp, $bug->id);
+ my $old_value = $bug->cf_last_resolved || '';
+ LogActivityEntry($bug->id, 'cf_last_resolved', $old_value, $timestamp,
+ Bugzilla->user->id, $timestamp);
}
+ }
}
sub bug_fields {
- my ($self, $args) = @_;
- my $fields = $args->{'fields'};
- push (@$fields, 'cf_last_resolved')
+ my ($self, $args) = @_;
+ my $fields = $args->{'fields'};
+ push(@$fields, 'cf_last_resolved');
}
sub object_columns {
- my ($self, $args) = @_;
- my ($class, $columns) = @$args{qw(class columns)};
- if ($class->isa('Bugzilla::Bug')) {
- push(@$columns, 'cf_last_resolved');
- }
+ my ($self, $args) = @_;
+ my ($class, $columns) = @$args{qw(class columns)};
+ if ($class->isa('Bugzilla::Bug')) {
+ push(@$columns, 'cf_last_resolved');
+ }
}
sub buglist_columns {
- my ($self, $args) = @_;
- my $columns = $args->{columns};
- $columns->{'cf_last_resolved'} = {
- name => 'bugs.cf_last_resolved',
- title => 'Last Resolved',
- };
+ my ($self, $args) = @_;
+ my $columns = $args->{columns};
+ $columns->{'cf_last_resolved'}
+ = {name => 'bugs.cf_last_resolved', title => 'Last Resolved',};
}
__PACKAGE__->NAME;
diff --git a/extensions/LimitedEmail/Config.pm b/extensions/LimitedEmail/Config.pm
index 94b9b10eb..d47fb4d91 100644
--- a/extensions/LimitedEmail/Config.pm
+++ b/extensions/LimitedEmail/Config.pm
@@ -11,15 +11,15 @@ use 5.10.1;
use strict;
use warnings;
-use constant NAME => 'LimitedEmail';
-use constant REQUIRED_MODULES => [ ];
-use constant OPTIONAL_MODULES => [ ];
+use constant NAME => 'LimitedEmail';
+use constant REQUIRED_MODULES => [];
+use constant OPTIONAL_MODULES => [];
use constant FILTERS => [
- qr/^(?:glob|dkl|justdave|shyam)\@mozilla\.com$/i,
- qr/^byron\.jones\@gmail\.com$/i,
- qr/^gerv\@mozilla\.org$/i,
- qr/^reed\@reedloden\.com$/i,
+ qr/^(?:glob|dkl|justdave|shyam)\@mozilla\.com$/i,
+ qr/^byron\.jones\@gmail\.com$/i,
+ qr/^gerv\@mozilla\.org$/i,
+ qr/^reed\@reedloden\.com$/i,
];
__PACKAGE__->NAME;
diff --git a/extensions/LimitedEmail/Extension.pm b/extensions/LimitedEmail/Extension.pm
index 9b504db91..3831ac878 100644
--- a/extensions/LimitedEmail/Extension.pm
+++ b/extensions/LimitedEmail/Extension.pm
@@ -21,46 +21,46 @@ use Encode qw(encode_utf8);
use Bugzilla::Constants qw(bz_locations);
sub mailer_before_send {
- my ($self, $args) = @_;
- my $email = $args->{email};
- my $header = $email->{header};
- return if $header->header('to') eq '';
+ my ($self, $args) = @_;
+ my $email = $args->{email};
+ my $header = $email->{header};
+ return if $header->header('to') eq '';
- my $blocked = '';
- if (!deliver_to($header->header('to'))) {
- $blocked = $header->header('to');
- $header->header_set(to => '');
- }
+ my $blocked = '';
+ if (!deliver_to($header->header('to'))) {
+ $blocked = $header->header('to');
+ $header->header_set(to => '');
+ }
- my $log_filename = bz_locations->{'datadir'} . '/mail.log';
- my $fh = FileHandle->new(">>$log_filename");
- if ($fh) {
- print $fh encode_utf8(sprintf(
- "[%s] %s%s %s : %s\n",
- time2str('%D %T', time),
- ($blocked eq '' ? '' : '(blocked) '),
- ($blocked eq '' ? $header->header('to') : $blocked),
- $header->header('X-Bugzilla-Reason') || '-',
- $header->header('subject')
- ));
- $fh->close();
- }
+ my $log_filename = bz_locations->{'datadir'} . '/mail.log';
+ my $fh = FileHandle->new(">>$log_filename");
+ if ($fh) {
+ print $fh encode_utf8(sprintf(
+ "[%s] %s%s %s : %s\n",
+ time2str('%D %T', time),
+ ($blocked eq '' ? '' : '(blocked) '),
+ ($blocked eq '' ? $header->header('to') : $blocked),
+ $header->header('X-Bugzilla-Reason') || '-',
+ $header->header('subject')
+ ));
+ $fh->close();
+ }
}
sub deliver_to {
- my $email = address_of(shift);
- my $ra_filters = Bugzilla::Extension::LimitedEmail::FILTERS;
- foreach my $re (@$ra_filters) {
- if ($email =~ $re) {
- return 1;
- }
+ my $email = address_of(shift);
+ my $ra_filters = Bugzilla::Extension::LimitedEmail::FILTERS;
+ foreach my $re (@$ra_filters) {
+ if ($email =~ $re) {
+ return 1;
}
- return 0;
+ }
+ return 0;
}
sub address_of {
- my $email = shift;
- return $email =~ /<([^>]+)>/ ? $1 : $email;
+ my $email = shift;
+ return $email =~ /<([^>]+)>/ ? $1 : $email;
}
__PACKAGE__->NAME;
diff --git a/extensions/MozProjectReview/Config.pm b/extensions/MozProjectReview/Config.pm
index 1a5e14f3d..41d761a35 100644
--- a/extensions/MozProjectReview/Config.pm
+++ b/extensions/MozProjectReview/Config.pm
@@ -12,10 +12,8 @@ use warnings;
use constant NAME => 'MozProjectReview';
-use constant REQUIRED_MODULES => [
-];
+use constant REQUIRED_MODULES => [];
-use constant OPTIONAL_MODULES => [
-];
+use constant OPTIONAL_MODULES => [];
__PACKAGE__->NAME;
diff --git a/extensions/MozProjectReview/Extension.pm b/extensions/MozProjectReview/Extension.pm
index 29d709ff4..c5cd27d21 100644
--- a/extensions/MozProjectReview/Extension.pm
+++ b/extensions/MozProjectReview/Extension.pm
@@ -22,139 +22,151 @@ use Bugzilla::Constants;
use List::MoreUtils qw(any);
sub post_bug_after_creation {
- my ($self, $args) = @_;
- my $vars = $args->{'vars'};
- my $bug = $vars->{'bug'};
- my $timestamp = $args->{'timestamp'};
- my $user = Bugzilla->user;
- my $params = Bugzilla->input_params;
- my $template = Bugzilla->template;
-
- return if !($params->{format} && $params->{format} eq 'moz-project-review');
-
- # do a match if applicable
- Bugzilla::User::match_field({
- 'sow_vendor_mozcontact' => { 'type' => 'single' },
- });
-
- my $do_sec_review = 0;
- my @sec_review_needed = (
- 'Engaging a new vendor company',
- 'Adding a new SOW with a vendor',
- 'Extending a SOW or renewing a contract',
- 'Purchasing software',
- 'Signing up for an online service',
- 'Other'
- );
- if ((any { $_ eq $params->{contract_type} } @sec_review_needed)
- || $params->{mozilla_data} eq 'Yes') {
- $do_sec_review = 1;
- }
-
- my ($sec_review_bug, $finance_bug, $error, @dep_comment, @dep_errors, @send_mail);
-
- # Common parameters always passed to _file_child_bug
- # bug_data and template_suffix will be different for each bug
- my $child_params = {
- parent_bug => $bug,
- template_vars => $vars,
- dep_comment => \@dep_comment,
- dep_errors => \@dep_errors,
- send_mail => \@send_mail,
- };
-
- if ($do_sec_review) {
- $child_params->{'bug_data'} = {
- short_desc => 'RRA: ' . $params->{contract_type} . ' with ' . $params->{other_party},
- product => 'Enterprise Information Security',
- component => 'Rapid Risk Analysis',
- bug_severity => 'normal',
- groups => [ 'mozilla-employee-confidential' ],
- op_sys => 'All',
- rep_platform => 'All',
- version => 'unspecified',
- blocked => $bug->bug_id,
- cc => $params->{cc},
- };
- $child_params->{'template_suffix'} = 'sec-review';
- _file_child_bug($child_params);
- }
-
+ my ($self, $args) = @_;
+ my $vars = $args->{'vars'};
+ my $bug = $vars->{'bug'};
+ my $timestamp = $args->{'timestamp'};
+ my $user = Bugzilla->user;
+ my $params = Bugzilla->input_params;
+ my $template = Bugzilla->template;
+
+ return if !($params->{format} && $params->{format} eq 'moz-project-review');
+
+ # do a match if applicable
+ Bugzilla::User::match_field({'sow_vendor_mozcontact' => {'type' => 'single'},});
+
+ my $do_sec_review = 0;
+ my @sec_review_needed = (
+ 'Engaging a new vendor company',
+ 'Adding a new SOW with a vendor',
+ 'Extending a SOW or renewing a contract',
+ 'Purchasing software',
+ 'Signing up for an online service',
+ 'Other'
+ );
+ if ((any { $_ eq $params->{contract_type} } @sec_review_needed)
+ || $params->{mozilla_data} eq 'Yes')
+ {
+ $do_sec_review = 1;
+ }
+
+ my ($sec_review_bug, $finance_bug, $error, @dep_comment, @dep_errors,
+ @send_mail);
+
+ # Common parameters always passed to _file_child_bug
+ # bug_data and template_suffix will be different for each bug
+ my $child_params = {
+ parent_bug => $bug,
+ template_vars => $vars,
+ dep_comment => \@dep_comment,
+ dep_errors => \@dep_errors,
+ send_mail => \@send_mail,
+ };
+
+ if ($do_sec_review) {
$child_params->{'bug_data'} = {
- short_desc => 'Finance Review: ' . $params->{contract_type} . ' with ' . $params->{other_party},
- product => 'Finance',
- component => 'Purchase Request Form',
- bug_severity => 'normal',
- priority => '--',
- groups => [ 'finance' ],
- op_sys => 'All',
- rep_platform => 'All',
- version => 'unspecified',
- blocked => $bug->bug_id,
- cc => $params->{cc},
+ short_desc => 'RRA: '
+ . $params->{contract_type}
+ . ' with '
+ . $params->{other_party},
+ product => 'Enterprise Information Security',
+ component => 'Rapid Risk Analysis',
+ bug_severity => 'normal',
+ groups => ['mozilla-employee-confidential'],
+ op_sys => 'All',
+ rep_platform => 'All',
+ version => 'unspecified',
+ blocked => $bug->bug_id,
+ cc => $params->{cc},
};
- $child_params->{'template_suffix'} = 'finance';
+ $child_params->{'template_suffix'} = 'sec-review';
_file_child_bug($child_params);
-
+ }
+
+ $child_params->{'bug_data'} = {
+ short_desc => 'Finance Review: '
+ . $params->{contract_type}
+ . ' with '
+ . $params->{other_party},
+ product => 'Finance',
+ component => 'Purchase Request Form',
+ bug_severity => 'normal',
+ priority => '--',
+ groups => ['finance'],
+ op_sys => 'All',
+ rep_platform => 'All',
+ version => 'unspecified',
+ blocked => $bug->bug_id,
+ cc => $params->{cc},
+ };
+ $child_params->{'template_suffix'} = 'finance';
+ _file_child_bug($child_params);
+
+ if (scalar @dep_errors) {
+ warn "[Bug "
+ . $bug->id
+ . "] Failed to create additional moz-project-review bugs:\n"
+ . join("\n", @dep_errors);
+ $vars->{'message'} = 'moz_project_review_creation_failed';
+ }
+
+ if (scalar @dep_comment) {
+ my $comment = join("\n", @dep_comment);
if (scalar @dep_errors) {
- warn "[Bug " . $bug->id . "] Failed to create additional moz-project-review bugs:\n" .
- join("\n", @dep_errors);
- $vars->{'message'} = 'moz_project_review_creation_failed';
- }
-
- if (scalar @dep_comment) {
- my $comment = join("\n", @dep_comment);
- if (scalar @dep_errors) {
- $comment .= "\n\nSome errors occurred creating dependent bugs and have been recorded";
- }
- $bug->add_comment($comment);
- $bug->update($bug->creation_ts);
+ $comment
+ .= "\n\nSome errors occurred creating dependent bugs and have been recorded";
}
+ $bug->add_comment($comment);
+ $bug->update($bug->creation_ts);
+ }
- foreach my $bug_id (@send_mail) {
- Bugzilla::BugMail::Send($bug_id, { changer => Bugzilla->user });
- }
+ foreach my $bug_id (@send_mail) {
+ Bugzilla::BugMail::Send($bug_id, {changer => Bugzilla->user});
+ }
}
sub _file_child_bug {
- my ($params) = @_;
- my ($parent_bug, $template_vars, $template_suffix, $bug_data, $dep_comment, $dep_errors, $send_mail)
- = @$params{qw(parent_bug template_vars template_suffix bug_data dep_comment dep_errors send_mail)};
-
- my $old_error_mode = Bugzilla->error_mode;
- Bugzilla->error_mode(ERROR_MODE_DIE);
-
- my $new_bug;
- eval {
- my $comment;
- my $full_template = "bug/create/comment-moz-project-review-$template_suffix.txt.tmpl";
- Bugzilla->template->process($full_template, $template_vars, \$comment)
- || ThrowTemplateError(Bugzilla->template->error());
- $bug_data->{'comment'} = $comment;
- if ($new_bug = Bugzilla::Bug->create($bug_data)) {
- my $set_all = {
- dependson => { add => [ $new_bug->bug_id ] }
- };
- $parent_bug->set_all($set_all);
- $parent_bug->update($parent_bug->creation_ts);
- }
+ my ($params) = @_;
+ my ($parent_bug, $template_vars, $template_suffix, $bug_data, $dep_comment,
+ $dep_errors, $send_mail)
+ = @$params{
+ qw(parent_bug template_vars template_suffix bug_data dep_comment dep_errors send_mail)
};
- if ($@ || !($new_bug && $new_bug->{'bug_id'})) {
- push(@$dep_comment, "Error creating $template_suffix review bug");
- push(@$dep_errors, "$template_suffix : $@") if $@;
- # Since we performed Bugzilla::Bug::create in an eval block, we
- # need to manually rollback the commit as this is not done
- # in Bugzilla::Error automatically for eval'ed code.
- Bugzilla->dbh->bz_rollback_transaction();
- }
- else {
- push(@$send_mail, $new_bug->id);
- push(@$dep_comment, "Bug " . $new_bug->id . " - " . $new_bug->short_desc);
+ my $old_error_mode = Bugzilla->error_mode;
+ Bugzilla->error_mode(ERROR_MODE_DIE);
+
+ my $new_bug;
+ eval {
+ my $comment;
+ my $full_template
+ = "bug/create/comment-moz-project-review-$template_suffix.txt.tmpl";
+ Bugzilla->template->process($full_template, $template_vars, \$comment)
+ || ThrowTemplateError(Bugzilla->template->error());
+ $bug_data->{'comment'} = $comment;
+ if ($new_bug = Bugzilla::Bug->create($bug_data)) {
+ my $set_all = {dependson => {add => [$new_bug->bug_id]}};
+ $parent_bug->set_all($set_all);
+ $parent_bug->update($parent_bug->creation_ts);
}
-
- undef $@;
- Bugzilla->error_mode($old_error_mode);
+ };
+
+ if ($@ || !($new_bug && $new_bug->{'bug_id'})) {
+ push(@$dep_comment, "Error creating $template_suffix review bug");
+ push(@$dep_errors, "$template_suffix : $@") if $@;
+
+ # Since we performed Bugzilla::Bug::create in an eval block, we
+ # need to manually rollback the commit as this is not done
+ # in Bugzilla::Error automatically for eval'ed code.
+ Bugzilla->dbh->bz_rollback_transaction();
+ }
+ else {
+ push(@$send_mail, $new_bug->id);
+ push(@$dep_comment, "Bug " . $new_bug->id . " - " . $new_bug->short_desc);
+ }
+
+ undef $@;
+ Bugzilla->error_mode($old_error_mode);
}
__PACKAGE__->NAME;
diff --git a/extensions/MyDashboard/Extension.pm b/extensions/MyDashboard/Extension.pm
index fc3a689bf..ae7921af7 100644
--- a/extensions/MyDashboard/Extension.pm
+++ b/extensions/MyDashboard/Extension.pm
@@ -29,54 +29,53 @@ our $VERSION = BUGZILLA_VERSION;
################
sub db_schema_abstract_schema {
- my ($self, $args) = @_;
-
- my $schema = $args->{schema};
-
- $schema->{'mydashboard'} = {
- FIELDS => [
- namedquery_id => {TYPE => 'INT3', NOTNULL => 1,
- REFERENCES => {TABLE => 'namedqueries',
- COLUMN => 'id',
- DELETE => 'CASCADE'}},
- user_id => {TYPE => 'INT3', NOTNULL => 1,
- REFERENCES => {TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'CASCADE'}},
- ],
- INDEXES => [
- mydashboard_namedquery_id_idx => {FIELDS => [qw(namedquery_id user_id)],
- TYPE => 'UNIQUE'},
- mydashboard_user_id_idx => ['user_id'],
- ],
- };
-
- $schema->{'bug_interest'} = {
- FIELDS => [
- id => { TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1 },
-
- bug_id => { TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => { TABLE => 'bugs',
- COLUMN => 'bug_id',
- DELETE => 'CASCADE' } },
-
- user_id => { TYPE => 'INT3',
- NOTNOLL => 1,
- REFERENCES => { TABLE => 'profiles',
- COLUMN => 'userid' } },
-
- modification_time => { TYPE => 'DATETIME',
- NOTNULL => 1 }
- ],
- INDEXES => [
- bug_interest_idx => { FIELDS => [qw(bug_id user_id)],
- TYPE => 'UNIQUE' },
- bug_interest_user_id_idx => ['user_id']
- ],
- };
+ my ($self, $args) = @_;
+
+ my $schema = $args->{schema};
+
+ $schema->{'mydashboard'} = {
+ FIELDS => [
+ namedquery_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'namedqueries', COLUMN => 'id', DELETE => 'CASCADE'}
+ },
+ user_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'}
+ },
+ ],
+ INDEXES => [
+ mydashboard_namedquery_id_idx =>
+ {FIELDS => [qw(namedquery_id user_id)], TYPE => 'UNIQUE'},
+ mydashboard_user_id_idx => ['user_id'],
+ ],
+ };
+
+ $schema->{'bug_interest'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1},
+
+ bug_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'bugs', COLUMN => 'bug_id', DELETE => 'CASCADE'}
+ },
+
+ user_id => {
+ TYPE => 'INT3',
+ NOTNOLL => 1,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid'}
+ },
+
+ modification_time => {TYPE => 'DATETIME', NOTNULL => 1}
+ ],
+ INDEXES => [
+ bug_interest_idx => {FIELDS => [qw(bug_id user_id)], TYPE => 'UNIQUE'},
+ bug_interest_user_id_idx => ['user_id']
+ ],
+ };
}
###########
@@ -84,34 +83,35 @@ sub db_schema_abstract_schema {
###########
BEGIN {
- *Bugzilla::Search::Saved::in_mydashboard = \&_in_mydashboard;
- *Bugzilla::Component::watcher_ids = \&_component_watcher_ids;
+ *Bugzilla::Search::Saved::in_mydashboard = \&_in_mydashboard;
+ *Bugzilla::Component::watcher_ids = \&_component_watcher_ids;
}
sub _in_mydashboard {
- my ($self) = @_;
- my $dbh = Bugzilla->dbh;
- return $self->{'in_mydashboard'} if exists $self->{'in_mydashboard'};
- $self->{'in_mydashboard'} = $dbh->selectrow_array("
- SELECT 1 FROM mydashboard WHERE namedquery_id = ? AND user_id = ?",
- undef, $self->id, Bugzilla->user->id);
- return $self->{'in_mydashboard'};
+ my ($self) = @_;
+ my $dbh = Bugzilla->dbh;
+ return $self->{'in_mydashboard'} if exists $self->{'in_mydashboard'};
+ $self->{'in_mydashboard'} = $dbh->selectrow_array("
+ SELECT 1 FROM mydashboard WHERE namedquery_id = ? AND user_id = ?", undef,
+ $self->id, Bugzilla->user->id);
+ return $self->{'in_mydashboard'};
}
sub _component_watcher_ids {
- my ($self) = @_;
- my $dbh = Bugzilla->dbh;
+ my ($self) = @_;
+ my $dbh = Bugzilla->dbh;
- my $query = "SELECT user_id FROM component_watch
+ my $query = "SELECT user_id FROM component_watch
WHERE product_id = ?
AND (component_id = ?
OR component_id IS NULL
OR ? LIKE @{[$dbh->sql_string_concat('component_prefix', q{'%'})]})";
- $self->{watcher_ids} ||= $dbh->selectcol_arrayref($query, undef,
- $self->product_id, $self->id, $self->name);
+ $self->{watcher_ids}
+ ||= $dbh->selectcol_arrayref($query, undef, $self->product_id, $self->id,
+ $self->name);
- return $self->{watcher_ids};
+ return $self->{watcher_ids};
}
#############
@@ -119,16 +119,16 @@ sub _component_watcher_ids {
#############
sub page_before_template {
- my ($self, $args) = @_;
- my $page = $args->{'page_id'};
- my $vars = $args->{'vars'};
+ my ($self, $args) = @_;
+ my $page = $args->{'page_id'};
+ my $vars = $args->{'vars'};
- return if $page ne 'mydashboard.html';
+ return if $page ne 'mydashboard.html';
- # require user to be logged in for this page
- Bugzilla->login(LOGIN_REQUIRED);
+ # require user to be logged in for this page
+ Bugzilla->login(LOGIN_REQUIRED);
- $vars->{queries} = [ QUERY_DEFS ];
+ $vars->{queries} = [QUERY_DEFS];
}
#########
@@ -136,115 +136,122 @@ sub page_before_template {
#########
sub user_preferences {
- my ($self, $args) = @_;
- my $tab = $args->{'current_tab'};
- return unless $tab eq 'saved-searches';
+ my ($self, $args) = @_;
+ my $tab = $args->{'current_tab'};
+ return unless $tab eq 'saved-searches';
- my $save = $args->{'save_changes'};
- my $handled = $args->{'handled'};
- my $vars = $args->{'vars'};
+ my $save = $args->{'save_changes'};
+ my $handled = $args->{'handled'};
+ my $vars = $args->{'vars'};
- my $dbh = Bugzilla->dbh;
- my $user = Bugzilla->user;
- my $params = Bugzilla->input_params;
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->user;
+ my $params = Bugzilla->input_params;
- if ($save) {
- my $sth_insert_fp = $dbh->prepare('INSERT INTO mydashboard
+ if ($save) {
+ my $sth_insert_fp = $dbh->prepare(
+ 'INSERT INTO mydashboard
(namedquery_id, user_id)
- VALUES (?, ?)');
- my $sth_delete_fp = $dbh->prepare('DELETE FROM mydashboard
+ VALUES (?, ?)'
+ );
+ my $sth_delete_fp = $dbh->prepare(
+ 'DELETE FROM mydashboard
WHERE namedquery_id = ?
- AND user_id = ?');
- foreach my $q (@{$user->queries}) {
- if (defined $params->{'in_mydashboard_' . $q->id}) {
- $sth_insert_fp->execute($q->id, $user->id) if !$q->in_mydashboard;
- }
- else {
- $sth_delete_fp->execute($q->id, $user->id) if $q->in_mydashboard;
- }
- }
+ AND user_id = ?'
+ );
+ foreach my $q (@{$user->queries}) {
+ if (defined $params->{'in_mydashboard_' . $q->id}) {
+ $sth_insert_fp->execute($q->id, $user->id) if !$q->in_mydashboard;
+ }
+ else {
+ $sth_delete_fp->execute($q->id, $user->id) if $q->in_mydashboard;
+ }
}
+ }
}
sub webservice {
- my ($self, $args) = @_;
- my $dispatch = $args->{dispatch};
- $dispatch->{MyDashboard} = "Bugzilla::Extension::MyDashboard::WebService";
+ my ($self, $args) = @_;
+ my $dispatch = $args->{dispatch};
+ $dispatch->{MyDashboard} = "Bugzilla::Extension::MyDashboard::WebService";
}
sub bug_end_of_create {
- my ($self, $args) = @_;
- my ($bug, $params, $timestamp) = @$args{qw(bug params timestamp)};
- my $user = Bugzilla->user;
-
- # Anyone added to the CC list of a bug is now interested in that bug.
- foreach my $cc_user (@{ $bug->cc_users }) {
- next if $user->id == $cc_user->id;
- Bugzilla::Extension::MyDashboard::BugInterest->mark($cc_user->id, $bug->id, $timestamp);
- }
-
- # Anyone that is watching a component is interested when a bug is filed into the component.
- foreach my $watcher_id (@{ $bug->component_obj->watcher_ids }) {
- Bugzilla::Extension::MyDashboard::BugInterest->mark($watcher_id, $bug->id, $timestamp);
- }
+ my ($self, $args) = @_;
+ my ($bug, $params, $timestamp) = @$args{qw(bug params timestamp)};
+ my $user = Bugzilla->user;
+
+ # Anyone added to the CC list of a bug is now interested in that bug.
+ foreach my $cc_user (@{$bug->cc_users}) {
+ next if $user->id == $cc_user->id;
+ Bugzilla::Extension::MyDashboard::BugInterest->mark($cc_user->id, $bug->id,
+ $timestamp);
+ }
+
+# Anyone that is watching a component is interested when a bug is filed into the component.
+ foreach my $watcher_id (@{$bug->component_obj->watcher_ids}) {
+ Bugzilla::Extension::MyDashboard::BugInterest->mark($watcher_id, $bug->id,
+ $timestamp);
+ }
}
sub bug_end_of_update {
- my ($self, $args) = @_;
- my ($bug, $old_bug, $changes, $timestamp) = @$args{qw(bug old_bug changes timestamp)};
- my $user = Bugzilla->user;
-
- # Anyone added to the CC list of a bug is now interested in that bug.
- my %old_cc = map { $_->id => $_ } grep { defined } @{ $old_bug->cc_users };
- my @added = grep { not $old_cc{ $_->id } } grep { defined } @{ $bug->cc_users };
- foreach my $cc_user (@added) {
- next if $user->id == $cc_user->id;
- Bugzilla::Extension::MyDashboard::BugInterest->mark($cc_user->id, $bug->id, $timestamp);
+ my ($self, $args) = @_;
+ my ($bug, $old_bug, $changes, $timestamp)
+ = @$args{qw(bug old_bug changes timestamp)};
+ my $user = Bugzilla->user;
+
+ # Anyone added to the CC list of a bug is now interested in that bug.
+ my %old_cc = map { $_->id => $_ } grep {defined} @{$old_bug->cc_users};
+ my @added = grep { not $old_cc{$_->id} } grep {defined} @{$bug->cc_users};
+ foreach my $cc_user (@added) {
+ next if $user->id == $cc_user->id;
+ Bugzilla::Extension::MyDashboard::BugInterest->mark($cc_user->id, $bug->id,
+ $timestamp);
+ }
+
+# Anyone that is watching a component is interested when a bug is filed into the component.
+ if ($changes->{product} or $changes->{component}) {
+
+ # All of the watchers would be interested in this bug update
+ foreach my $watcher_id (@{$bug->component_obj->watcher_ids}) {
+ Bugzilla::Extension::MyDashboard::BugInterest->mark($watcher_id, $bug->id,
+ $timestamp);
}
+ }
- # Anyone that is watching a component is interested when a bug is filed into the component.
- if ($changes->{product} or $changes->{component}) {
- # All of the watchers would be interested in this bug update
- foreach my $watcher_id (@{ $bug->component_obj->watcher_ids }) {
- Bugzilla::Extension::MyDashboard::BugInterest->mark($watcher_id, $bug->id, $timestamp);
- }
- }
+ if ($changes->{bug_status}) {
+ my ($old_status, $new_status) = @{$changes->{bug_status}};
+ if (is_open_state($old_status) && !is_open_state($new_status)) {
+ my @related_bugs = (@{$bug->blocks_obj}, @{$bug->depends_on_obj});
+ my %involved;
- if ($changes->{bug_status}) {
- my ($old_status, $new_status) = @{ $changes->{bug_status} };
- if (is_open_state($old_status) && !is_open_state($new_status)) {
- my @related_bugs = (@{ $bug->blocks_obj }, @{ $bug->depends_on_obj });
- my %involved;
-
- foreach my $related_bug (@related_bugs) {
- my @users = grep { defined } $related_bug->assigned_to,
- $related_bug->reporter,
- $related_bug->qa_contact,
- @{ $related_bug->cc_users };
-
- foreach my $involved_user (@users) {
- $involved{ $involved_user->id }{ $related_bug->id } = 1;
- }
- }
- foreach my $involved_user_id (keys %involved) {
- foreach my $related_bug_id (keys %{$involved{$involved_user_id}}) {
- Bugzilla::Extension::MyDashboard::BugInterest->mark($involved_user_id,
- $related_bug_id,
- $timestamp);
- }
- }
+ foreach my $related_bug (@related_bugs) {
+ my @users = grep {defined} $related_bug->assigned_to, $related_bug->reporter,
+ $related_bug->qa_contact, @{$related_bug->cc_users};
+
+ foreach my $involved_user (@users) {
+ $involved{$involved_user->id}{$related_bug->id} = 1;
+ }
+ }
+ foreach my $involved_user_id (keys %involved) {
+ foreach my $related_bug_id (keys %{$involved{$involved_user_id}}) {
+ Bugzilla::Extension::MyDashboard::BugInterest->mark($involved_user_id,
+ $related_bug_id, $timestamp);
}
+ }
}
+ }
}
sub merge_users_before {
- my ($self, $args) = @_;
- my $old_id = $args->{old_id};
- my $dbh = Bugzilla->dbh;
+ my ($self, $args) = @_;
+ my $old_id = $args->{old_id};
+ my $dbh = Bugzilla->dbh;
- # If the bug_interest table has both the source user
- # and destination user, then we remove the old user entry.
- $dbh->do("DELETE FROM bug_interest WHERE user_id = ?", undef, $old_id);
+ # If the bug_interest table has both the source user
+ # and destination user, then we remove the old user entry.
+ $dbh->do("DELETE FROM bug_interest WHERE user_id = ?", undef, $old_id);
}
__PACKAGE__->NAME;
diff --git a/extensions/MyDashboard/lib/BugInterest.pm b/extensions/MyDashboard/lib/BugInterest.pm
index cf33900c5..2e427d612 100644
--- a/extensions/MyDashboard/lib/BugInterest.pm
+++ b/extensions/MyDashboard/lib/BugInterest.pm
@@ -25,47 +25,45 @@ use constant LIST_ORDER => 'id';
use constant NAME_FIELD => 'id';
# turn off auditing and exclude these objects from memcached
-use constant { AUDIT_CREATES => 0,
- AUDIT_UPDATES => 0,
- AUDIT_REMOVES => 0,
- USE_MEMCACHED => 0 };
+use constant {
+ AUDIT_CREATES => 0,
+ AUDIT_UPDATES => 0,
+ AUDIT_REMOVES => 0,
+ USE_MEMCACHED => 0
+};
#####################################################################
# Provide accessors for our columns
#####################################################################
-sub id { return $_[0]->{id} }
-sub bug_id { return $_[0]->{bug_id} }
-sub user_id { return $_[0]->{user_id} }
+sub id { return $_[0]->{id} }
+sub bug_id { return $_[0]->{bug_id} }
+sub user_id { return $_[0]->{user_id} }
sub modification_time { return $_[0]->{modification_time} }
sub mark {
- my ($class, $user_id, $bug_id, $timestamp) = @_;
+ my ($class, $user_id, $bug_id, $timestamp) = @_;
- my ($interest) = @{ $class->match({ user_id => $user_id,
- bug_id => $bug_id }) };
- if ($interest) {
- $interest->set(modification_time => $timestamp);
- $interest->update();
- return $interest;
- }
- else {
- return $class->create({
- user_id => $user_id,
- bug_id => $bug_id,
- modification_time => $timestamp,
- });
- }
+ my ($interest) = @{$class->match({user_id => $user_id, bug_id => $bug_id})};
+ if ($interest) {
+ $interest->set(modification_time => $timestamp);
+ $interest->update();
+ return $interest;
+ }
+ else {
+ return $class->create({
+ user_id => $user_id, bug_id => $bug_id, modification_time => $timestamp,
+ });
+ }
}
sub unmark {
- my ($class, $user_id, $bug_id) = @_;
+ my ($class, $user_id, $bug_id) = @_;
- my ($interest) = @{ $class->match({ user_id => $user_id,
- bug_id => $bug_id }) };
- if ($interest) {
- $interest->remove_from_db();
- }
+ my ($interest) = @{$class->match({user_id => $user_id, bug_id => $bug_id})};
+ if ($interest) {
+ $interest->remove_from_db();
+ }
}
1;
diff --git a/extensions/MyDashboard/lib/Queries.pm b/extensions/MyDashboard/lib/Queries.pm
index d77be7da4..59cb4f14e 100644
--- a/extensions/MyDashboard/lib/Queries.pm
+++ b/extensions/MyDashboard/lib/Queries.pm
@@ -25,11 +25,11 @@ use DateTime;
use base qw(Exporter);
our @EXPORT = qw(
- QUERY_ORDER
- SELECT_COLUMNS
- QUERY_DEFS
- query_bugs
- query_flags
+ QUERY_ORDER
+ SELECT_COLUMNS
+ QUERY_DEFS
+ query_bugs
+ query_flags
);
# Default sort order
@@ -38,294 +38,300 @@ use constant QUERY_ORDER => ("changeddate desc", "bug_id");
# List of columns that we will be selecting. In the future this should be configurable
# Share with buglist.cgi?
use constant SELECT_COLUMNS => qw(
- bug_id
- bug_status
- short_desc
- changeddate
+ bug_id
+ bug_status
+ short_desc
+ changeddate
);
sub QUERY_DEFS {
- my $user = Bugzilla->user;
-
- my @query_defs = (
- {
- name => 'assignedbugs',
- heading => 'Assigned to You',
- description => 'The bug has been assigned to you, and it is not resolved or closed.',
- params => {
- 'bug_status' => ['__open__'],
- 'emailassigned_to1' => 1,
- 'emailtype1' => 'exact',
- 'email1' => $user->login
- }
- },
- {
- name => 'newbugs',
- heading => 'New Reported by You',
- description => 'You reported the bug; it\'s unconfirmed or new. No one has assigned themselves to fix it yet.',
- params => {
- 'bug_status' => ['UNCONFIRMED', 'NEW'],
- 'emailreporter1' => 1,
- 'emailtype1' => 'exact',
- 'email1' => $user->login
- }
- },
- {
- name => 'inprogressbugs',
- heading => "In Progress Reported by You",
- description => 'A developer accepted your bug and is working on it. (It has someone in the "Assigned to" field.)',
- params => {
- 'bug_status' => [ map { $_->name } grep($_->name ne 'UNCONFIRMED' && $_->name ne 'NEW', open_states()) ],
- 'emailreporter1' => 1,
- 'emailtype1' => 'exact',
- 'email1' => $user->login
- }
- },
- {
- name => 'openccbugs',
- heading => "You Are CC'd On",
- description => 'You are in the CC list of the bug, so you are watching it.',
- params => {
- 'bug_status' => ['__open__'],
- 'emailcc1' => 1,
- 'emailtype1' => 'exact',
- 'email1' => $user->login
- }
- },
- {
- name => 'mentorbugs',
- heading => "You Are a Mentor",
- description => 'You are one of the mentors for the bug.',
- params => {
- 'bug_status' => ['__open__'],
- 'emailbug_mentor1' => 1,
- 'emailtype1' => 'exact',
- 'email1' => $user->login
- }
- },
- {
- name => 'lastvisitedbugs',
- heading => 'Updated Since Last Visit',
- description => 'Bugs updated since last visited',
- mark_read => 'Mark Visited',
- params => {
- o1 => 'lessthan',
- v1 => '%last_changed%',
- f1 => 'last_visit_ts',
- },
- },
- {
- name => 'interestingbugs',
- heading => 'Interesting Bugs',
- description => 'Bugs that you may find interesting',
- mark_read => 'Remove Interest',
- params => {
- j_top => 'OR',
- f1 => 'bug_interest_ts',
- o1 => 'isnotempty',
-
- f2 => 'last_visit_ts',
- o2 => 'lessthan',
- v2 => '%last_changed%',
- }
- },
- {
- name => 'nevervisitbugs',
- heading => 'Involved with and Never Visited',
- description => "Bugs you've never visited, but are involved with",
- mark_read => 'Mark Visited',
- params => {
- query_format => "advanced",
- bug_status => ['__open__'],,
- o1 => "isempty",
- f1 => "last_visit_ts",
- j2 => "OR",
- f2 => "OP",
- f3 => "assigned_to",
- o3 => "equals",
- v3 => $user->login,
- o4 => "equals",
- f4 => "reporter",
- v4 => $user->login,
- v5 => $user->login,
- f5 => "qa_contact",
- o5 => "equals",
- o6 => "equals",
- f6 => "cc",
- v6 => $user->login,
- f7 => "bug_mentor",
- o7 => "equals",
- v7 => $user->login,
- f9 => "CP",
- },
- },
+ my $user = Bugzilla->user;
+
+ my @query_defs = (
+ {
+ name => 'assignedbugs',
+ heading => 'Assigned to You',
+ description =>
+ 'The bug has been assigned to you, and it is not resolved or closed.',
+ params => {
+ 'bug_status' => ['__open__'],
+ 'emailassigned_to1' => 1,
+ 'emailtype1' => 'exact',
+ 'email1' => $user->login
+ }
+ },
+ {
+ name => 'newbugs',
+ heading => 'New Reported by You',
+ description =>
+ 'You reported the bug; it\'s unconfirmed or new. No one has assigned themselves to fix it yet.',
+ params => {
+ 'bug_status' => ['UNCONFIRMED', 'NEW'],
+ 'emailreporter1' => 1,
+ 'emailtype1' => 'exact',
+ 'email1' => $user->login
+ }
+ },
+ {
+ name => 'inprogressbugs',
+ heading => "In Progress Reported by You",
+ description =>
+ 'A developer accepted your bug and is working on it. (It has someone in the "Assigned to" field.)',
+ params => {
+ 'bug_status' => [
+ map { $_->name }
+ grep($_->name ne 'UNCONFIRMED' && $_->name ne 'NEW', open_states())
+ ],
+ 'emailreporter1' => 1,
+ 'emailtype1' => 'exact',
+ 'email1' => $user->login
+ }
+ },
+ {
+ name => 'openccbugs',
+ heading => "You Are CC'd On",
+ description => 'You are in the CC list of the bug, so you are watching it.',
+ params => {
+ 'bug_status' => ['__open__'],
+ 'emailcc1' => 1,
+ 'emailtype1' => 'exact',
+ 'email1' => $user->login
+ }
+ },
+ {
+ name => 'mentorbugs',
+ heading => "You Are a Mentor",
+ description => 'You are one of the mentors for the bug.',
+ params => {
+ 'bug_status' => ['__open__'],
+ 'emailbug_mentor1' => 1,
+ 'emailtype1' => 'exact',
+ 'email1' => $user->login
+ }
+ },
+ {
+ name => 'lastvisitedbugs',
+ heading => 'Updated Since Last Visit',
+ description => 'Bugs updated since last visited',
+ mark_read => 'Mark Visited',
+ params => {o1 => 'lessthan', v1 => '%last_changed%', f1 => 'last_visit_ts',},
+ },
+ {
+ name => 'interestingbugs',
+ heading => 'Interesting Bugs',
+ description => 'Bugs that you may find interesting',
+ mark_read => 'Remove Interest',
+ params => {
+ j_top => 'OR',
+ f1 => 'bug_interest_ts',
+ o1 => 'isnotempty',
+
+ f2 => 'last_visit_ts',
+ o2 => 'lessthan',
+ v2 => '%last_changed%',
+ }
+ },
+ {
+ name => 'nevervisitbugs',
+ heading => 'Involved with and Never Visited',
+ description => "Bugs you've never visited, but are involved with",
+ mark_read => 'Mark Visited',
+ params => {
+ query_format => "advanced",
+ bug_status => ['__open__'],
+ ,
+ o1 => "isempty",
+ f1 => "last_visit_ts",
+ j2 => "OR",
+ f2 => "OP",
+ f3 => "assigned_to",
+ o3 => "equals",
+ v3 => $user->login,
+ o4 => "equals",
+ f4 => "reporter",
+ v4 => $user->login,
+ v5 => $user->login,
+ f5 => "qa_contact",
+ o5 => "equals",
+ o6 => "equals",
+ f6 => "cc",
+ v6 => $user->login,
+ f7 => "bug_mentor",
+ o7 => "equals",
+ v7 => $user->login,
+ f9 => "CP",
+ },
+ },
+ );
+
+ if (Bugzilla->params->{'useqacontact'}) {
+ push(
+ @query_defs,
+ {
+ name => 'qacontactbugs',
+ heading => 'You Are QA Contact',
+ description =>
+ 'You are the qa contact on this bug, and it is not resolved or closed.',
+ params => {
+ 'bug_status' => ['__open__'],
+ 'emailqa_contact1' => 1,
+ 'emailtype1' => 'exact',
+ 'email1' => $user->login
+ }
+ }
);
-
- if (Bugzilla->params->{'useqacontact'}) {
- push(@query_defs, {
- name => 'qacontactbugs',
- heading => 'You Are QA Contact',
- description => 'You are the qa contact on this bug, and it is not resolved or closed.',
- params => {
- 'bug_status' => ['__open__'],
- 'emailqa_contact1' => 1,
- 'emailtype1' => 'exact',
- 'email1' => $user->login
- }
- });
- }
-
- if ($user->showmybugslink) {
- my $query = Bugzilla->params->{mybugstemplate};
- my $login = $user->login;
- $query =~ s/%userid%/$login/;
- $query =~ s/^buglist.cgi\?//;
- push(@query_defs, {
- name => 'mybugs',
- heading => "My Bugs",
- saved => 1,
- params => $query,
- });
- }
-
- foreach my $q (@{$user->queries}) {
- next if !$q->in_mydashboard;
- push(@query_defs, { name => $q->name,
- saved => 1,
- params => $q->url });
- }
-
- return @query_defs;
+ }
+
+ if ($user->showmybugslink) {
+ my $query = Bugzilla->params->{mybugstemplate};
+ my $login = $user->login;
+ $query =~ s/%userid%/$login/;
+ $query =~ s/^buglist.cgi\?//;
+ push(@query_defs,
+ {name => 'mybugs', heading => "My Bugs", saved => 1, params => $query,});
+ }
+
+ foreach my $q (@{$user->queries}) {
+ next if !$q->in_mydashboard;
+ push(@query_defs, {name => $q->name, saved => 1, params => $q->url});
+ }
+
+ return @query_defs;
}
sub query_bugs {
- my $qdef = shift;
- my $dbh = Bugzilla->dbh;
- my $user = Bugzilla->user;
- my $datetime_now = DateTime->now(time_zone => $user->timezone);
-
- ## HACK to remove POST
- delete $ENV{REQUEST_METHOD};
-
- my $params = new Bugzilla::CGI($qdef->{params});
-
- my $search = new Bugzilla::Search( fields => [ SELECT_COLUMNS ],
- params => scalar $params->Vars,
- order => [ QUERY_ORDER ]);
- my $data = $search->data;
-
- my @bugs;
- foreach my $row (@$data) {
- my $bug = {};
- foreach my $column (SELECT_COLUMNS) {
- $bug->{$column} = shift @$row;
- if ($column eq 'changeddate') {
- my $datetime = datetime_from($bug->{$column});
- $datetime->set_time_zone($user->timezone);
- $bug->{$column} = $datetime->strftime('%Y-%m-%d %T %Z');
- $bug->{'changeddate_fancy'} = time_ago($datetime, $datetime_now);
-
- # Provide a version for use by Bug.history and also for looking up last comment.
- # We have to set to server's timezone and also subtract one second.
- $datetime->set_time_zone(Bugzilla->local_timezone);
- $datetime->subtract(seconds => 1);
- $bug->{changeddate_api} = $datetime->strftime('%Y-%m-%d %T');
- }
- }
- push(@bugs, $bug);
+ my $qdef = shift;
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->user;
+ my $datetime_now = DateTime->now(time_zone => $user->timezone);
+
+ ## HACK to remove POST
+ delete $ENV{REQUEST_METHOD};
+
+ my $params = new Bugzilla::CGI($qdef->{params});
+
+ my $search = new Bugzilla::Search(
+ fields => [SELECT_COLUMNS],
+ params => scalar $params->Vars,
+ order => [QUERY_ORDER]
+ );
+ my $data = $search->data;
+
+ my @bugs;
+ foreach my $row (@$data) {
+ my $bug = {};
+ foreach my $column (SELECT_COLUMNS) {
+ $bug->{$column} = shift @$row;
+ if ($column eq 'changeddate') {
+ my $datetime = datetime_from($bug->{$column});
+ $datetime->set_time_zone($user->timezone);
+ $bug->{$column} = $datetime->strftime('%Y-%m-%d %T %Z');
+ $bug->{'changeddate_fancy'} = time_ago($datetime, $datetime_now);
+
+ # Provide a version for use by Bug.history and also for looking up last comment.
+ # We have to set to server's timezone and also subtract one second.
+ $datetime->set_time_zone(Bugzilla->local_timezone);
+ $datetime->subtract(seconds => 1);
+ $bug->{changeddate_api} = $datetime->strftime('%Y-%m-%d %T');
+ }
}
+ push(@bugs, $bug);
+ }
- return (\@bugs, $params->canonicalise_query());
+ return (\@bugs, $params->canonicalise_query());
}
sub query_flags {
- my ($type) = @_;
- my $user = Bugzilla->user;
- my $dbh = Bugzilla->dbh;
- my $datetime_now = DateTime->now(time_zone => $user->timezone);
-
- ($type ne 'requestee' || $type ne 'requester')
- || ThrowCodeError('param_required', { param => 'type' });
-
- my $match_params = { status => '?' };
-
- if ($type eq 'requestee') {
- $match_params->{'requestee_id'} = $user->id;
- }
- else {
- $match_params->{'setter_id'} = $user->id;
- }
-
- my $matched = Bugzilla::Flag->match($match_params);
-
- return [] if !@$matched;
-
- my @unfiltered_flags;
- my %all_bugs; # Use hash to filter out duplicates
- foreach my $flag (@$matched) {
- next if ($flag->attach_id && $flag->attachment->isprivate && !$user->is_insider);
-
- my $data = {
- id => $flag->id,
- type => $flag->type->name,
- status => $flag->status,
- attach_id => $flag->attach_id,
- is_patch => $flag->attach_id ? $flag->attachment->ispatch : 0,
- bug_id => $flag->bug_id,
- requester => $flag->setter->login,
- requestee => $flag->requestee ? $flag->requestee->login : '',
- updated => $flag->modification_date,
- };
- push(@unfiltered_flags, $data);
-
- # Record bug id for later retrieval of status/summary
- $all_bugs{$flag->{'bug_id'}}++;
- }
-
- # Filter the bug list based on permission to see the bug
- my %visible_bugs = map { $_ => 1 } @{ $user->visible_bugs([ keys %all_bugs ]) };
-
- return [] if !scalar keys %visible_bugs;
-
- # Get all bug statuses and summaries in one query instead of loading
- # many separate bug objects
- my $bug_rows = $dbh->selectall_arrayref("SELECT bug_id, bug_status, short_desc
+ my ($type) = @_;
+ my $user = Bugzilla->user;
+ my $dbh = Bugzilla->dbh;
+ my $datetime_now = DateTime->now(time_zone => $user->timezone);
+
+ ($type ne 'requestee' || $type ne 'requester')
+ || ThrowCodeError('param_required', {param => 'type'});
+
+ my $match_params = {status => '?'};
+
+ if ($type eq 'requestee') {
+ $match_params->{'requestee_id'} = $user->id;
+ }
+ else {
+ $match_params->{'setter_id'} = $user->id;
+ }
+
+ my $matched = Bugzilla::Flag->match($match_params);
+
+ return [] if !@$matched;
+
+ my @unfiltered_flags;
+ my %all_bugs; # Use hash to filter out duplicates
+ foreach my $flag (@$matched) {
+ next
+ if ($flag->attach_id && $flag->attachment->isprivate && !$user->is_insider);
+
+ my $data = {
+ id => $flag->id,
+ type => $flag->type->name,
+ status => $flag->status,
+ attach_id => $flag->attach_id,
+ is_patch => $flag->attach_id ? $flag->attachment->ispatch : 0,
+ bug_id => $flag->bug_id,
+ requester => $flag->setter->login,
+ requestee => $flag->requestee ? $flag->requestee->login : '',
+ updated => $flag->modification_date,
+ };
+ push(@unfiltered_flags, $data);
+
+ # Record bug id for later retrieval of status/summary
+ $all_bugs{$flag->{'bug_id'}}++;
+ }
+
+ # Filter the bug list based on permission to see the bug
+ my %visible_bugs = map { $_ => 1 } @{$user->visible_bugs([keys %all_bugs])};
+
+ return [] if !scalar keys %visible_bugs;
+
+ # Get all bug statuses and summaries in one query instead of loading
+ # many separate bug objects
+ my $bug_rows = $dbh->selectall_arrayref(
+ "SELECT bug_id, bug_status, short_desc
FROM bugs
- WHERE " . $dbh->sql_in('bug_id', [ keys %visible_bugs ]),
- { Slice => {} });
- foreach my $row (@$bug_rows) {
- $visible_bugs{$row->{'bug_id'}} = {
- bug_status => $row->{'bug_status'},
- short_desc => $row->{'short_desc'}
- };
- }
-
- # Now drop out any flags for bugs the user cannot see
- # or if the user did not want to see closed bugs
- my @filtered_flags;
- foreach my $flag (@unfiltered_flags) {
- # Skip this flag if the bug is not visible to the user
- next if !$visible_bugs{$flag->{'bug_id'}};
-
- # Include bug status and summary with each flag
- $flag->{'bug_status'} = $visible_bugs{$flag->{'bug_id'}}->{'bug_status'};
- $flag->{'bug_summary'} = $visible_bugs{$flag->{'bug_id'}}->{'short_desc'};
-
- # Format the updated date specific to the user's timezone
- # and add the fancy human readable version
- my $datetime = datetime_from($flag->{'updated'});
- $datetime->set_time_zone($user->timezone);
- $flag->{'updated'} = $datetime->strftime('%Y-%m-%d %T %Z');
- $flag->{'updated_epoch'} = $datetime->epoch;
- $flag->{'updated_fancy'} = time_ago($datetime, $datetime_now);
-
- push(@filtered_flags, $flag);
- }
-
- return [] if !@filtered_flags;
-
- # Sort by most recently updated
- return [ sort { $b->{'updated_epoch'} <=> $a->{'updated_epoch'} } @filtered_flags ];
+ WHERE "
+ . $dbh->sql_in('bug_id', [keys %visible_bugs]), {Slice => {}}
+ );
+ foreach my $row (@$bug_rows) {
+ $visible_bugs{$row->{'bug_id'}}
+ = {bug_status => $row->{'bug_status'}, short_desc => $row->{'short_desc'}};
+ }
+
+ # Now drop out any flags for bugs the user cannot see
+ # or if the user did not want to see closed bugs
+ my @filtered_flags;
+ foreach my $flag (@unfiltered_flags) {
+
+ # Skip this flag if the bug is not visible to the user
+ next if !$visible_bugs{$flag->{'bug_id'}};
+
+ # Include bug status and summary with each flag
+ $flag->{'bug_status'} = $visible_bugs{$flag->{'bug_id'}}->{'bug_status'};
+ $flag->{'bug_summary'} = $visible_bugs{$flag->{'bug_id'}}->{'short_desc'};
+
+ # Format the updated date specific to the user's timezone
+ # and add the fancy human readable version
+ my $datetime = datetime_from($flag->{'updated'});
+ $datetime->set_time_zone($user->timezone);
+ $flag->{'updated'} = $datetime->strftime('%Y-%m-%d %T %Z');
+ $flag->{'updated_epoch'} = $datetime->epoch;
+ $flag->{'updated_fancy'} = time_ago($datetime, $datetime_now);
+
+ push(@filtered_flags, $flag);
+ }
+
+ return [] if !@filtered_flags;
+
+ # Sort by most recently updated
+ return [sort { $b->{'updated_epoch'} <=> $a->{'updated_epoch'} }
+ @filtered_flags];
}
1;
diff --git a/extensions/MyDashboard/lib/Util.pm b/extensions/MyDashboard/lib/Util.pm
index 77d9505cb..f2e734b63 100644
--- a/extensions/MyDashboard/lib/Util.pm
+++ b/extensions/MyDashboard/lib/Util.pm
@@ -17,36 +17,40 @@ use Bugzilla::Status;
use base qw(Exporter);
@Bugzilla::Extension::MyDashboard::Util::EXPORT = qw(
- open_states
- closed_states
- quoted_open_states
- quoted_closed_states
+ open_states
+ closed_states
+ quoted_open_states
+ quoted_closed_states
);
our $_open_states;
+
sub open_states {
- $_open_states ||= Bugzilla::Status->match({ is_open => 1, isactive => 1 });
- return wantarray ? @$_open_states : $_open_states;
+ $_open_states ||= Bugzilla::Status->match({is_open => 1, isactive => 1});
+ return wantarray ? @$_open_states : $_open_states;
}
our $_quoted_open_states;
+
sub quoted_open_states {
- my $dbh = Bugzilla->dbh;
- $_quoted_open_states ||= [ map { $dbh->quote($_->name) } open_states() ];
- return wantarray ? @$_quoted_open_states : $_quoted_open_states;
+ my $dbh = Bugzilla->dbh;
+ $_quoted_open_states ||= [map { $dbh->quote($_->name) } open_states()];
+ return wantarray ? @$_quoted_open_states : $_quoted_open_states;
}
our $_closed_states;
+
sub closed_states {
- $_closed_states ||= Bugzilla::Status->match({ is_open => 0, isactive => 1 });
- return wantarray ? @$_closed_states : $_closed_states;
+ $_closed_states ||= Bugzilla::Status->match({is_open => 0, isactive => 1});
+ return wantarray ? @$_closed_states : $_closed_states;
}
our $_quoted_closed_states;
+
sub quoted_closed_states {
- my $dbh = Bugzilla->dbh;
- $_quoted_closed_states ||= [ map { $dbh->quote($_->name) } closed_states() ];
- return wantarray ? @$_quoted_closed_states : $_quoted_closed_states;
+ my $dbh = Bugzilla->dbh;
+ $_quoted_closed_states ||= [map { $dbh->quote($_->name) } closed_states()];
+ return wantarray ? @$_quoted_closed_states : $_quoted_closed_states;
}
1;
diff --git a/extensions/MyDashboard/lib/WebService.pm b/extensions/MyDashboard/lib/WebService.pm
index 5407c1d0b..6638bacf2 100644
--- a/extensions/MyDashboard/lib/WebService.pm
+++ b/extensions/MyDashboard/lib/WebService.pm
@@ -17,149 +17,149 @@ use Bugzilla::Error;
use Bugzilla::Util qw(detaint_natural trick_taint template_var datetime_from);
use Bugzilla::WebService::Util qw(validate);
-use Bugzilla::Extension::MyDashboard::Queries qw(QUERY_DEFS query_bugs query_flags);
+use Bugzilla::Extension::MyDashboard::Queries
+ qw(QUERY_DEFS query_bugs query_flags);
use Bugzilla::Extension::MyDashboard::BugInterest;
use constant READ_ONLY => qw(
- run_bug_query
- run_flag_query
+ run_bug_query
+ run_flag_query
);
use constant PUBLIC_METHODS => qw(
- bug_interest_unmark
- run_bug_query
- run_flag_query
- run_last_changes
+ bug_interest_unmark
+ run_bug_query
+ run_flag_query
+ run_last_changes
);
sub run_last_changes {
- my ($self, $params) = @_;
+ my ($self, $params) = @_;
- my $dbh = Bugzilla->dbh;
- my $user = Bugzilla->login(LOGIN_REQUIRED);
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
- trick_taint($params->{changeddate_api});
- trick_taint($params->{bug_id});
+ trick_taint($params->{changeddate_api});
+ trick_taint($params->{bug_id});
- my $last_comment_sql = "
+ my $last_comment_sql = "
SELECT comment_id
FROM longdescs
WHERE bug_id = ? AND bug_when > ?";
- if (!$user->is_insider) {
- $last_comment_sql .= " AND isprivate = 0";
+ if (!$user->is_insider) {
+ $last_comment_sql .= " AND isprivate = 0";
+ }
+ $last_comment_sql .= " LIMIT 1";
+ my $last_comment_sth = $dbh->prepare($last_comment_sql);
+
+ my $last_changes = {};
+ my $activity
+ = $self->history({
+ ids => [$params->{bug_id}], new_since => $params->{changeddate_api}
+ });
+ if (@{$activity->{bugs}[0]{history}}) {
+ my $change_set = $activity->{bugs}[0]{history}[0];
+ $last_changes->{activity} = $change_set->{changes};
+ foreach my $change (@{$last_changes->{activity}}) {
+ $change->{field_desc} = template_var('field_descs')->{$change->{field_name}}
+ || $change->{field_name};
}
- $last_comment_sql .= " LIMIT 1";
- my $last_comment_sth = $dbh->prepare($last_comment_sql);
-
- my $last_changes = {};
- my $activity = $self->history({ ids => [ $params->{bug_id} ],
- new_since => $params->{changeddate_api} });
- if (@{$activity->{bugs}[0]{history}}) {
- my $change_set = $activity->{bugs}[0]{history}[0];
- $last_changes->{activity} = $change_set->{changes};
- foreach my $change (@{ $last_changes->{activity} }) {
- $change->{field_desc}
- = template_var('field_descs')->{$change->{field_name}} || $change->{field_name};
- }
- $last_changes->{email} = $change_set->{who};
- my $datetime = datetime_from($change_set->{when});
- $datetime->set_time_zone($user->timezone);
- $last_changes->{when} = $datetime->strftime('%Y-%m-%d %T %Z');
- }
- my $last_comment_id = $dbh->selectrow_array(
- $last_comment_sth, undef, $params->{bug_id}, $params->{changeddate_api});
- if ($last_comment_id) {
- my $comments = $self->comments({ comment_ids => [ $last_comment_id ] });
- my $comment = $comments->{comments}{$last_comment_id};
- $last_changes->{comment} = $comment->{text};
- $last_changes->{email} = $comment->{creator} if !$last_changes->{email};
- my $datetime = datetime_from($comment->{creation_time});
- $datetime->set_time_zone($user->timezone);
- $last_changes->{when} = $datetime->strftime('%Y-%m-%d %T %Z');
- }
-
- return { results => [ {last_changes => $last_changes } ] };
+ $last_changes->{email} = $change_set->{who};
+ my $datetime = datetime_from($change_set->{when});
+ $datetime->set_time_zone($user->timezone);
+ $last_changes->{when} = $datetime->strftime('%Y-%m-%d %T %Z');
+ }
+ my $last_comment_id
+ = $dbh->selectrow_array($last_comment_sth, undef, $params->{bug_id},
+ $params->{changeddate_api});
+ if ($last_comment_id) {
+ my $comments = $self->comments({comment_ids => [$last_comment_id]});
+ my $comment = $comments->{comments}{$last_comment_id};
+ $last_changes->{comment} = $comment->{text};
+ $last_changes->{email} = $comment->{creator} if !$last_changes->{email};
+ my $datetime = datetime_from($comment->{creation_time});
+ $datetime->set_time_zone($user->timezone);
+ $last_changes->{when} = $datetime->strftime('%Y-%m-%d %T %Z');
+ }
+
+ return {results => [{last_changes => $last_changes}]};
}
sub run_bug_query {
- my($self, $params) = @_;
- my $dbh = Bugzilla->dbh;
- my $user = Bugzilla->login(LOGIN_REQUIRED);
-
- defined $params->{query}
- || ThrowCodeError('param_required',
- { function => 'MyDashboard.run_bug_query',
- param => 'query' });
-
- my $result;
- foreach my $qdef (QUERY_DEFS) {
- next if $qdef->{name} ne $params->{query};
- my ($bugs, $query_string) = query_bugs($qdef);
-
- # Add last changes to each bug
- foreach my $b (@$bugs) {
- # Set the data type properly for webservice clients
- # for non-string values.
- $b->{bug_id} = $self->type('int', $b->{bug_id});
- }
-
- $query_string =~ s/^POSTDATA=&//;
- $qdef->{bugs} = $bugs;
- $qdef->{buffer} = $query_string;
- $result = $qdef;
- last;
+ my ($self, $params) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
+
+ defined $params->{query}
+ || ThrowCodeError('param_required',
+ {function => 'MyDashboard.run_bug_query', param => 'query'});
+
+ my $result;
+ foreach my $qdef (QUERY_DEFS) {
+ next if $qdef->{name} ne $params->{query};
+ my ($bugs, $query_string) = query_bugs($qdef);
+
+ # Add last changes to each bug
+ foreach my $b (@$bugs) {
+
+ # Set the data type properly for webservice clients
+ # for non-string values.
+ $b->{bug_id} = $self->type('int', $b->{bug_id});
}
- return { result => $result };
+ $query_string =~ s/^POSTDATA=&//;
+ $qdef->{bugs} = $bugs;
+ $qdef->{buffer} = $query_string;
+ $result = $qdef;
+ last;
+ }
+
+ return {result => $result};
}
sub run_flag_query {
- my ($self, $params) =@_;
- my $user = Bugzilla->login(LOGIN_REQUIRED);
-
- my $type = $params->{type};
- $type || ThrowCodeError('param_required',
- { function => 'MyDashboard.run_flag_query',
- param => 'type' });
-
- my $results = query_flags($type);
-
- # Set the data type properly for webservice clients
- # for non-string values.
- foreach my $flag (@$results) {
- $flag->{id} = $self->type('int', $flag->{id});
- $flag->{attach_id} = $self->type('int', $flag->{attach_id});
- $flag->{bug_id} = $self->type('int', $flag->{bug_id});
- $flag->{is_patch} = $self->type('boolean', $flag->{is_patch});
- }
-
- return { result => { $type => $results }};
+ my ($self, $params) = @_;
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
+
+ my $type = $params->{type};
+ $type
+ || ThrowCodeError('param_required',
+ {function => 'MyDashboard.run_flag_query', param => 'type'});
+
+ my $results = query_flags($type);
+
+ # Set the data type properly for webservice clients
+ # for non-string values.
+ foreach my $flag (@$results) {
+ $flag->{id} = $self->type('int', $flag->{id});
+ $flag->{attach_id} = $self->type('int', $flag->{attach_id});
+ $flag->{bug_id} = $self->type('int', $flag->{bug_id});
+ $flag->{is_patch} = $self->type('boolean', $flag->{is_patch});
+ }
+
+ return {result => {$type => $results}};
}
sub bug_interest_unmark {
- my ($self, $params) = @_;
- my $user = Bugzilla->login(LOGIN_REQUIRED);
+ my ($self, $params) = @_;
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
- ThrowCodeError('param_required', { function => 'MyDashboard.bug_interest_unmark', param => 'bug_ids' })
- unless $params->{bug_ids};
+ ThrowCodeError('param_required',
+ {function => 'MyDashboard.bug_interest_unmark', param => 'bug_ids'})
+ unless $params->{bug_ids};
- my @bug_ids = ref($params->{bug_ids}) ? @{$params->{bug_ids}} : ( $params->{bug_ids} );
+ my @bug_ids
+ = ref($params->{bug_ids}) ? @{$params->{bug_ids}} : ($params->{bug_ids});
- Bugzilla->dbh->bz_start_transaction();
- foreach my $bug_id (@bug_ids) {
- Bugzilla::Extension::MyDashboard::BugInterest->unmark($user->id, $bug_id);
- }
- Bugzilla->dbh->bz_commit_transaction();
+ Bugzilla->dbh->bz_start_transaction();
+ foreach my $bug_id (@bug_ids) {
+ Bugzilla::Extension::MyDashboard::BugInterest->unmark($user->id, $bug_id);
+ }
+ Bugzilla->dbh->bz_commit_transaction();
}
sub rest_resources {
- return [
- qr{^/bug_interest_unmark$}, {
- PUT => {
- method => 'bug_interest_unmark'
- }
- }
- ];
+ return [qr{^/bug_interest_unmark$}, {PUT => {method => 'bug_interest_unmark'}}];
}
1;
diff --git a/extensions/Needinfo/Config.pm b/extensions/Needinfo/Config.pm
index d523d9d78..c930134d4 100644
--- a/extensions/Needinfo/Config.pm
+++ b/extensions/Needinfo/Config.pm
@@ -12,10 +12,8 @@ use warnings;
use constant NAME => 'Needinfo';
-use constant REQUIRED_MODULES => [
-];
+use constant REQUIRED_MODULES => [];
-use constant OPTIONAL_MODULES => [
-];
+use constant OPTIONAL_MODULES => [];
__PACKAGE__->NAME;
diff --git a/extensions/Needinfo/Extension.pm b/extensions/Needinfo/Extension.pm
index f3f32439e..f9702a55f 100644
--- a/extensions/Needinfo/Extension.pm
+++ b/extensions/Needinfo/Extension.pm
@@ -21,256 +21,265 @@ use Bugzilla::User::Setting;
our $VERSION = '0.01';
BEGIN {
- *Bugzilla::User::needinfo_blocked = \&_user_needinfo_blocked;
+ *Bugzilla::User::needinfo_blocked = \&_user_needinfo_blocked;
}
sub _user_needinfo_blocked {
- return $_[0]->settings->{block_needinfo}->{value} eq 'on';
+ return $_[0]->settings->{block_needinfo}->{value} eq 'on';
}
sub install_update_db {
- my ($self, $args) = @_;
- my $dbh = Bugzilla->dbh;
-
- if (@{ Bugzilla::FlagType::match({ name => 'needinfo' }) }) {
- return;
- }
-
- print "Creating needinfo flag ... " .
- "enable the Needinfo feature by editing the flag's properties.\n";
-
- # inclusions 0:0 maps to __ANY__ : __ANY__ in the UI,
- # meaning needinfo is enabled for all products and components by default
- my $flagtype = Bugzilla::FlagType->create({
- name => 'needinfo',
- description => "Set this flag when the bug is in need of additional information",
- target_type => 'bug',
- cc_list => '',
- sortkey => 1,
- is_active => 1,
- is_requestable => 1,
- is_requesteeble => 1,
- is_multiplicable => 0,
- request_group => '',
- grant_group => '',
- inclusions => ['0:0'],
- exclusions => [],
- });
+ my ($self, $args) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ if (@{Bugzilla::FlagType::match({name => 'needinfo'})}) {
+ return;
+ }
+
+ print "Creating needinfo flag ... "
+ . "enable the Needinfo feature by editing the flag's properties.\n";
+
+ # inclusions 0:0 maps to __ANY__ : __ANY__ in the UI,
+ # meaning needinfo is enabled for all products and components by default
+ my $flagtype = Bugzilla::FlagType->create({
+ name => 'needinfo',
+ description =>
+ "Set this flag when the bug is in need of additional information",
+ target_type => 'bug',
+ cc_list => '',
+ sortkey => 1,
+ is_active => 1,
+ is_requestable => 1,
+ is_requesteeble => 1,
+ is_multiplicable => 0,
+ request_group => '',
+ grant_group => '',
+ inclusions => ['0:0'],
+ exclusions => [],
+ });
}
sub install_before_final_checks {
- my ($self, $args) = @_;
- add_setting({
- name => 'block_needinfo',
- options => ['on', 'off'],
- default => 'off',
- category => 'Reviews and Needinfo'
- });
+ my ($self, $args) = @_;
+ add_setting({
+ name => 'block_needinfo',
+ options => ['on', 'off'],
+ default => 'off',
+ category => 'Reviews and Needinfo'
+ });
}
# Clear the needinfo? flag if comment is being given by
# requestee or someone used the override flag.
sub bug_start_of_update {
- my ($self, $args) = @_;
- my $bug = $args->{bug};
- my $old_bug = $args->{old_bug};
-
- my $user = Bugzilla->user;
- my $cgi = Bugzilla->cgi;
- my $params = Bugzilla->input_params;
-
- if ($params->{needinfo}) {
- # do a match if applicable
- Bugzilla::User::match_field({
- 'needinfo_from' => { 'type' => 'multi' }
- });
+ my ($self, $args) = @_;
+ my $bug = $args->{bug};
+ my $old_bug = $args->{old_bug};
+
+ my $user = Bugzilla->user;
+ my $cgi = Bugzilla->cgi;
+ my $params = Bugzilla->input_params;
+
+ if ($params->{needinfo}) {
+
+ # do a match if applicable
+ Bugzilla::User::match_field({'needinfo_from' => {'type' => 'multi'}});
+ }
+
+ # Set needinfo_done param to true so as to not loop back here
+ return if $params->{needinfo_done};
+ $params->{needinfo_done} = 1;
+ Bugzilla->input_params($params);
+
+ my $add_needinfo = delete $params->{needinfo};
+ my $needinfo_type = delete $params->{needinfo_type} // '';
+ my $needinfo_from = delete $params->{needinfo_from};
+ my $needinfo_role = delete $params->{needinfo_role};
+ my $is_redirect = $needinfo_type eq 'redirect_to' ? 1 : 0;
+ my $is_private = $params->{'comment_is_private'};
+
+ my @needinfo_overrides;
+ foreach my $key (grep(/^needinfo_override_/, keys %$params)) {
+ my ($id) = $key =~ /(\d+)$/;
+
+ # Should always be true if key exists (checkbox) but better to be sure
+ push(@needinfo_overrides, $id) if $id && $params->{$key};
+ }
+
+ # Set the needinfo flag if user is requesting more information
+ my @new_flags;
+ my $needinfo_requestee;
+
+ if ($add_needinfo) {
+ foreach my $type (@{$bug->flag_types}) {
+ next if $type->name ne 'needinfo';
+ my %requestees;
+
+ # Allow anyone to be the requestee
+ if (!$needinfo_role) {
+ $requestees{'anyone'} = 1;
+ }
+
+ # Use assigned_to as requestee
+ elsif ($needinfo_role eq 'assigned_to') {
+ $requestees{$bug->assigned_to->login} = 1;
+ }
+
+ # Use reporter as requestee
+ elsif ($needinfo_role eq 'reporter') {
+ $requestees{$bug->reporter->login} = 1;
+ }
+
+ # Use qa_contact as requestee
+ elsif ($needinfo_role eq 'qa_contact') {
+ $requestees{$bug->qa_contact->login} = 1;
+ }
+
+ # Use current user as requestee
+ elsif ($needinfo_role eq 'user') {
+ $requestees{$user->login} = 1;
+ }
+ elsif ($needinfo_role eq 'triage_owner') {
+ if ($bug->component_obj->triage_owner_id) {
+ $requestees{$bug->component_obj->triage_owner->login} = 1;
+ }
+ }
+
+ # Use user specified requestee
+ elsif ($needinfo_role eq 'other' && $needinfo_from) {
+ my @needinfo_from_list
+ = ref $needinfo_from ? @$needinfo_from : ($needinfo_from);
+ foreach my $requestee (@needinfo_from_list) {
+ my $requestee_obj = Bugzilla::User->check($requestee);
+ $requestees{$requestee_obj->login} = 1;
+ }
+ }
+
+ # Requestee is a mentor
+ elsif ($needinfo_role
+ && Bugzilla::User->check({name => $needinfo_role, cache => 1}))
+ {
+ $requestees{$needinfo_role} = 1;
+ }
+
+ # Find out if the requestee has already been used and skip if so
+ my $requestee_found;
+ foreach my $flag (@{$type->{flags}}) {
+ if (!$flag->requestee && $requestees{'anyone'}) {
+ delete $requestees{'anyone'};
+ }
+ if ($flag->requestee && $requestees{$flag->requestee->login}) {
+ delete $requestees{$flag->requestee->login};
+ }
+ }
+
+ foreach my $requestee (keys %requestees) {
+ my $needinfo_flag = {type_id => $type->id, status => '?'};
+ if ($requestee ne 'anyone') {
+ _check_requestee($requestee);
+ $needinfo_flag->{requestee} = $requestee;
+ my $requestee_obj = Bugzilla::User->check($requestee);
+ if (!$requestee_obj->can_see_bug($bug->id)) {
+ $bug->add_cc($requestee_obj);
+ }
+ }
+ push(@new_flags, $needinfo_flag);
+ }
}
+ }
- # Set needinfo_done param to true so as to not loop back here
- return if $params->{needinfo_done};
- $params->{needinfo_done} = 1;
- Bugzilla->input_params($params);
-
- my $add_needinfo = delete $params->{needinfo};
- my $needinfo_type = delete $params->{needinfo_type} // '';
- my $needinfo_from = delete $params->{needinfo_from};
- my $needinfo_role = delete $params->{needinfo_role};
- my $is_redirect = $needinfo_type eq 'redirect_to' ? 1 : 0;
- my $is_private = $params->{'comment_is_private'};
-
- my @needinfo_overrides;
- foreach my $key (grep(/^needinfo_override_/, keys %$params)) {
- my ($id) = $key =~ /(\d+)$/;
- # Should always be true if key exists (checkbox) but better to be sure
- push(@needinfo_overrides, $id) if $id && $params->{$key};
- }
+ my @flags;
+ foreach my $flag (@{$bug->flags}) {
+ next if $flag->type->name ne 'needinfo';
- # Set the needinfo flag if user is requesting more information
- my @new_flags;
- my $needinfo_requestee;
-
- if ($add_needinfo) {
- foreach my $type (@{ $bug->flag_types }) {
- next if $type->name ne 'needinfo';
- my %requestees;
-
- # Allow anyone to be the requestee
- if (!$needinfo_role) {
- $requestees{'anyone'} = 1;
- }
- # Use assigned_to as requestee
- elsif ($needinfo_role eq 'assigned_to') {
- $requestees{$bug->assigned_to->login} = 1;
- }
- # Use reporter as requestee
- elsif ($needinfo_role eq 'reporter') {
- $requestees{$bug->reporter->login} = 1;
- }
- # Use qa_contact as requestee
- elsif ($needinfo_role eq 'qa_contact') {
- $requestees{$bug->qa_contact->login} = 1;
- }
- # Use current user as requestee
- elsif ($needinfo_role eq 'user') {
- $requestees{$user->login} = 1;
- }
- elsif ($needinfo_role eq 'triage_owner') {
- if ($bug->component_obj->triage_owner_id) {
- $requestees{$bug->component_obj->triage_owner->login} = 1;
- }
- }
- # Use user specified requestee
- elsif ($needinfo_role eq 'other' && $needinfo_from) {
- my @needinfo_from_list = ref $needinfo_from
- ? @$needinfo_from :
- ($needinfo_from);
- foreach my $requestee (@needinfo_from_list) {
- my $requestee_obj = Bugzilla::User->check($requestee);
- $requestees{$requestee_obj->login} = 1;
- }
- }
- # Requestee is a mentor
- elsif ($needinfo_role
- && Bugzilla::User->check({ name => $needinfo_role, cache => 1 }))
- {
- $requestees{$needinfo_role} = 1;
- }
-
- # Find out if the requestee has already been used and skip if so
- my $requestee_found;
- foreach my $flag (@{ $type->{flags} }) {
- if (!$flag->requestee && $requestees{'anyone'}) {
- delete $requestees{'anyone'};
- }
- if ($flag->requestee && $requestees{$flag->requestee->login}) {
- delete $requestees{$flag->requestee->login};
- }
- }
-
- foreach my $requestee (keys %requestees) {
- my $needinfo_flag = { type_id => $type->id, status => '?' };
- if ($requestee ne 'anyone') {
- _check_requestee($requestee);
- $needinfo_flag->{requestee} = $requestee;
- my $requestee_obj = Bugzilla::User->check($requestee);
- if (!$requestee_obj->can_see_bug($bug->id)) {
- $bug->add_cc($requestee_obj);
- }
- }
- push(@new_flags, $needinfo_flag);
- }
- }
+ # Clear if somehow the flag has been set to +/-
+ # or if the "clear needinfo" override checkbox is selected
+ if ($flag->status ne '?' or grep { $_ == $flag->id } @needinfo_overrides) {
+ push(@flags, {id => $flag->id, status => 'X'});
}
+ }
- my @flags;
- foreach my $flag (@{ $bug->flags }) {
- next if $flag->type->name ne 'needinfo';
- # Clear if somehow the flag has been set to +/-
- # or if the "clear needinfo" override checkbox is selected
- if ($flag->status ne '?'
- or grep { $_ == $flag->id } @needinfo_overrides)
- {
- push(@flags, { id => $flag->id, status => 'X' });
- }
- }
+ if ($is_redirect && scalar(@new_flags) == 1) {
- if ($is_redirect && scalar(@new_flags) == 1) {
- # Find the current user's needinfo request
- foreach my $flag (@{ $bug->flags }) {
- next unless $flag->type->name eq 'needinfo'
- && $flag->requestee
- && $flag->requestee->id == $user->id;
- # Setting the id on new_flag updates the existing flag instead of
- # creating a new one.
- $new_flags[0]->{id} = $flag->id;
- last;
- }
- }
+ # Find the current user's needinfo request
+ foreach my $flag (@{$bug->flags}) {
+ next
+ unless $flag->type->name eq 'needinfo'
+ && $flag->requestee
+ && $flag->requestee->id == $user->id;
- if (@flags || @new_flags) {
- $bug->set_flags(\@flags, \@new_flags);
+ # Setting the id on new_flag updates the existing flag instead of
+ # creating a new one.
+ $new_flags[0]->{id} = $flag->id;
+ last;
}
+ }
+
+ if (@flags || @new_flags) {
+ $bug->set_flags(\@flags, \@new_flags);
+ }
}
sub _check_requestee {
- my ($requestee) = @_;
- my $user = ref($requestee)
- ? $requestee
- : Bugzilla::User->new({ name => $requestee, cache => 1 });
- if ($user->needinfo_blocked) {
- ThrowUserError('needinfo_blocked', { requestee => $user });
- }
+ my ($requestee) = @_;
+ my $user
+ = ref($requestee)
+ ? $requestee
+ : Bugzilla::User->new({name => $requestee, cache => 1});
+ if ($user->needinfo_blocked) {
+ ThrowUserError('needinfo_blocked', {requestee => $user});
+ }
}
sub object_end_of_create {
- my ($self, $args) = @_;
- my $object = $args->{object};
- return unless $object->isa('Bugzilla::Flag')
- && $object->type->name eq 'needinfo'
- && $object->requestee;
- _check_requestee($object->requestee);
+ my ($self, $args) = @_;
+ my $object = $args->{object};
+ return
+ unless $object->isa('Bugzilla::Flag')
+ && $object->type->name eq 'needinfo'
+ && $object->requestee;
+ _check_requestee($object->requestee);
}
sub object_end_of_update {
- my ($self, $args) = @_;
- my $object = $args->{object};
- return unless exists $args->{changes}->{requestee_id}
- && $object->isa('Bugzilla::Flag')
- && $object->type->name eq 'needinfo'
- && $object->requestee;
- _check_requestee($object->requestee);
+ my ($self, $args) = @_;
+ my $object = $args->{object};
+ return
+ unless exists $args->{changes}->{requestee_id}
+ && $object->isa('Bugzilla::Flag')
+ && $object->type->name eq 'needinfo'
+ && $object->requestee;
+ _check_requestee($object->requestee);
}
sub object_before_delete {
- my ($self, $args) = @_;
- my $object = $args->{object};
- return unless $object->isa('Bugzilla::Flag')
- && $object->type->name eq 'needinfo';
- my $user = Bugzilla->user;
-
- # Require canconfirm to clear requests targetted at someone else
- if ($object->setter_id != $user->id
- && $object->requestee
- && $object->requestee->id != $user->id
- && !$user->in_group('canconfirm'))
- {
- ThrowUserError('needinfo_illegal_change');
- }
+ my ($self, $args) = @_;
+ my $object = $args->{object};
+ return
+ unless $object->isa('Bugzilla::Flag') && $object->type->name eq 'needinfo';
+ my $user = Bugzilla->user;
+
+ # Require canconfirm to clear requests targetted at someone else
+ if ( $object->setter_id != $user->id
+ && $object->requestee
+ && $object->requestee->id != $user->id
+ && !$user->in_group('canconfirm'))
+ {
+ ThrowUserError('needinfo_illegal_change');
+ }
}
sub user_preferences {
- my ($self, $args) = @_;
- return unless
- $args->{current_tab} eq 'account'
- && $args->{save_changes};
-
- my $input = Bugzilla->input_params;
- my $settings = Bugzilla->user->settings;
-
- my $value = $input->{block_needinfo} ? 'on' : 'off';
- $settings->{block_needinfo}->validate_value($value);
- $settings->{block_needinfo}->set($value);
- clear_settings_cache(Bugzilla->user->id);
+ my ($self, $args) = @_;
+ return unless $args->{current_tab} eq 'account' && $args->{save_changes};
+
+ my $input = Bugzilla->input_params;
+ my $settings = Bugzilla->user->settings;
+
+ my $value = $input->{block_needinfo} ? 'on' : 'off';
+ $settings->{block_needinfo}->validate_value($value);
+ $settings->{block_needinfo}->set($value);
+ clear_settings_cache(Bugzilla->user->id);
}
__PACKAGE__->NAME;
diff --git a/extensions/OldBugMove/Extension.pm b/extensions/OldBugMove/Extension.pm
index 1fa96c1ef..aa3d1aab0 100644
--- a/extensions/OldBugMove/Extension.pm
+++ b/extensions/OldBugMove/Extension.pm
@@ -39,172 +39,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->{VARIABLES}{constants};
- $constants->{CMT_MOVED_TO} = CMT_MOVED_TO;
+ my $constants = $config->{VARIABLES}{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 a8617e347..cea3fecf6 100644
--- a/extensions/OldBugMove/lib/Params.pm
+++ b/extensions/OldBugMove/lib/Params.pm
@@ -38,23 +38,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/OpenGraph/Config.pm b/extensions/OpenGraph/Config.pm
index 92d0a89bc..419d5d9d3 100644
--- a/extensions/OpenGraph/Config.pm
+++ b/extensions/OpenGraph/Config.pm
@@ -11,8 +11,8 @@ use 5.10.1;
use strict;
use warnings;
-use constant NAME => 'OpenGraph';
-use constant REQUIRED_MODULES => [ ];
-use constant OPTIONAL_MODULES => [ ];
+use constant NAME => 'OpenGraph';
+use constant REQUIRED_MODULES => [];
+use constant OPTIONAL_MODULES => [];
__PACKAGE__->NAME;
diff --git a/extensions/OrangeFactor/Extension.pm b/extensions/OrangeFactor/Extension.pm
index 56dd5dc6e..14d9da6df 100644
--- a/extensions/OrangeFactor/Extension.pm
+++ b/extensions/OrangeFactor/Extension.pm
@@ -22,37 +22,40 @@ use DateTime;
our $VERSION = '1.0';
sub template_before_process {
- my ($self, $args) = @_;
- my $file = $args->{'file'};
- my $vars = $args->{'vars'};
-
- my $user = Bugzilla->user;
-
- return unless ($file eq 'bug/show-header.html.tmpl'
- || $file eq 'bug/edit.html.tmpl'
- || $file eq 'bug_modal/header.html.tmpl'
- || $file eq 'bug_modal/edit.html.tmpl');
- return unless ($user->id
- && $user->settings->{'orange_factor'}->{'value'} eq 'on');
-
- # in the header we just need to set the var,
- # to ensure the css and javascript get included
- my $bug = exists $vars->{'bugs'} ? $vars->{'bugs'}[0] : $vars->{'bug'};
- if ($bug && grep($_->name eq 'intermittent-failure', @{ $bug->keyword_objects })) {
- $vars->{'orange_factor'} = 1;
- $vars->{'date_start'} = ( DateTime->now() - DateTime::Duration->new( days => 7 ) )->ymd();
- $vars->{'date_end'} = DateTime->now->ymd();
- }
+ my ($self, $args) = @_;
+ my $file = $args->{'file'};
+ my $vars = $args->{'vars'};
+
+ my $user = Bugzilla->user;
+
+ return
+ unless ($file eq 'bug/show-header.html.tmpl'
+ || $file eq 'bug/edit.html.tmpl'
+ || $file eq 'bug_modal/header.html.tmpl'
+ || $file eq 'bug_modal/edit.html.tmpl');
+ return
+ unless ($user->id && $user->settings->{'orange_factor'}->{'value'} eq 'on');
+
+ # in the header we just need to set the var,
+ # to ensure the css and javascript get included
+ my $bug = exists $vars->{'bugs'} ? $vars->{'bugs'}[0] : $vars->{'bug'};
+ if ($bug && grep($_->name eq 'intermittent-failure', @{$bug->keyword_objects}))
+ {
+ $vars->{'orange_factor'} = 1;
+ $vars->{'date_start'}
+ = (DateTime->now() - DateTime::Duration->new(days => 7))->ymd();
+ $vars->{'date_end'} = DateTime->now->ymd();
+ }
}
sub install_before_final_checks {
- my ($self, $args) = @_;
- add_setting({
- name => 'orange_factor',
- options => ['on', 'off'],
- default => 'off',
- category => 'User Interface'
- });
+ my ($self, $args) = @_;
+ add_setting({
+ name => 'orange_factor',
+ options => ['on', 'off'],
+ default => 'off',
+ category => 'User Interface'
+ });
}
__PACKAGE__->NAME;
diff --git a/extensions/PhabBugz/Extension.pm b/extensions/PhabBugz/Extension.pm
index c857c60ab..5622f5050 100644
--- a/extensions/PhabBugz/Extension.pm
+++ b/extensions/PhabBugz/Extension.pm
@@ -19,29 +19,31 @@ use Bugzilla::Extension::PhabBugz::Feed;
our $VERSION = '0.01';
sub config_add_panels {
- my ($self, $args) = @_;
- my $modules = $args->{panel_modules};
- $modules->{PhabBugz} = "Bugzilla::Extension::PhabBugz::Config";
+ my ($self, $args) = @_;
+ my $modules = $args->{panel_modules};
+ $modules->{PhabBugz} = "Bugzilla::Extension::PhabBugz::Config";
}
sub auth_delegation_confirm {
- my ($self, $args) = @_;
- my $phab_enabled = Bugzilla->params->{phabricator_enabled};
- my $phab_callback_url = Bugzilla->params->{phabricator_auth_callback_url};
- my $phab_app_id = Bugzilla->params->{phabricator_app_id};
-
- return unless $phab_enabled;
- return unless $phab_callback_url;
- return unless $phab_app_id;
-
- if (index($args->{callback}, $phab_callback_url) == 0 && $args->{app_id} eq $phab_app_id) {
- ${$args->{skip_confirmation}} = 1;
- }
+ my ($self, $args) = @_;
+ my $phab_enabled = Bugzilla->params->{phabricator_enabled};
+ my $phab_callback_url = Bugzilla->params->{phabricator_auth_callback_url};
+ my $phab_app_id = Bugzilla->params->{phabricator_app_id};
+
+ return unless $phab_enabled;
+ return unless $phab_callback_url;
+ return unless $phab_app_id;
+
+ if (index($args->{callback}, $phab_callback_url) == 0
+ && $args->{app_id} eq $phab_app_id)
+ {
+ ${$args->{skip_confirmation}} = 1;
+ }
}
sub webservice {
- my ($self, $args) = @_;
- $args->{dispatch}->{PhabBugz} = "Bugzilla::Extension::PhabBugz::WebService";
+ my ($self, $args) = @_;
+ $args->{dispatch}->{PhabBugz} = "Bugzilla::Extension::PhabBugz::WebService";
}
#
@@ -49,42 +51,25 @@ sub webservice {
#
sub db_schema_abstract_schema {
- my ($self, $args) = @_;
- $args->{'schema'}->{'phabbugz'} = {
- FIELDS => [
- id => {
- TYPE => 'INTSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- name => {
- TYPE => 'VARCHAR(255)',
- NOTNULL => 1,
- },
- value => {
- TYPE => 'MEDIUMTEXT',
- NOTNULL => 1
- }
- ],
- INDEXES => [
- phabbugz_idx => {
- FIELDS => ['name'],
- TYPE => 'UNIQUE',
- },
- ],
- };
+ my ($self, $args) = @_;
+ $args->{'schema'}->{'phabbugz'} = {
+ FIELDS => [
+ id => {TYPE => 'INTSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ name => {TYPE => 'VARCHAR(255)', NOTNULL => 1,},
+ value => {TYPE => 'MEDIUMTEXT', NOTNULL => 1}
+ ],
+ INDEXES => [phabbugz_idx => {FIELDS => ['name'], TYPE => 'UNIQUE',},],
+ };
}
sub install_filesystem {
- my ($self, $args) = @_;
- my $files = $args->{'files'};
+ my ($self, $args) = @_;
+ my $files = $args->{'files'};
- my $extensionsdir = bz_locations()->{'extensionsdir'};
- my $scriptname = $extensionsdir . "/PhabBugz/bin/phabbugz_feed.pl";
+ my $extensionsdir = bz_locations()->{'extensionsdir'};
+ my $scriptname = $extensionsdir . "/PhabBugz/bin/phabbugz_feed.pl";
- $files->{$scriptname} = {
- perms => Bugzilla::Install::Filesystem::WS_EXECUTE
- };
+ $files->{$scriptname} = {perms => Bugzilla::Install::Filesystem::WS_EXECUTE};
}
__PACKAGE__->NAME;
diff --git a/extensions/PhabBugz/bin/phabbugz_feed.pl b/extensions/PhabBugz/bin/phabbugz_feed.pl
index 9db491bd0..2720c9104 100755
--- a/extensions/PhabBugz/bin/phabbugz_feed.pl
+++ b/extensions/PhabBugz/bin/phabbugz_feed.pl
@@ -14,8 +14,8 @@ use warnings;
use lib qw(. lib local/lib/perl5);
BEGIN {
- use Bugzilla;
- Bugzilla->extensions;
+ use Bugzilla;
+ Bugzilla->extensions;
}
use Bugzilla::Extension::PhabBugz::Daemon;
diff --git a/extensions/PhabBugz/lib/Config.pm b/extensions/PhabBugz/lib/Config.pm
index d4b71430b..d808b607a 100644
--- a/extensions/PhabBugz/lib/Config.pm
+++ b/extensions/PhabBugz/lib/Config.pm
@@ -16,48 +16,40 @@ use Bugzilla::Config::Common;
our $sortkey = 1300;
sub get_param_list {
- my ($class) = @_;
-
- my @params = (
- {
- name => 'phabricator_enabled',
- type => 'b',
- default => 0
- },
- {
- name => 'phabricator_base_uri',
- type => 't',
- default => '',
- checker => \&check_urlbase
- },
- {
- name => 'phabricator_api_key',
- type => 't',
- default => '',
- },
- {
- name => 'phabricator_auth_callback_url',
- type => 't',
- default => '',
- checker => sub {
- my ($url) = (@_);
- return 'must be an HTTP/HTTPS absolute URL' unless $url =~ m{^https?://};
- return '';
- }
- },
- {
- name => 'phabricator_app_id',
- type => 't',
- default => '',
- checker => sub {
- my ($app_id) = (@_);
- return 'must be a hex number' unless $app_id =~ /^[[:xdigit:]]+$/;
- return '';
- }
- }
- );
-
- return @params;
+ my ($class) = @_;
+
+ my @params = (
+ {name => 'phabricator_enabled', type => 'b', default => 0},
+ {
+ name => 'phabricator_base_uri',
+ type => 't',
+ default => '',
+ checker => \&check_urlbase
+ },
+ {name => 'phabricator_api_key', type => 't', default => '',},
+ {
+ name => 'phabricator_auth_callback_url',
+ type => 't',
+ default => '',
+ checker => sub {
+ my ($url) = (@_);
+ return 'must be an HTTP/HTTPS absolute URL' unless $url =~ m{^https?://};
+ return '';
+ }
+ },
+ {
+ name => 'phabricator_app_id',
+ type => 't',
+ default => '',
+ checker => sub {
+ my ($app_id) = (@_);
+ return 'must be a hex number' unless $app_id =~ /^[[:xdigit:]]+$/;
+ return '';
+ }
+ }
+ );
+
+ return @params;
}
1;
diff --git a/extensions/PhabBugz/lib/Constants.pm b/extensions/PhabBugz/lib/Constants.pm
index 19987de25..642c1962b 100644
--- a/extensions/PhabBugz/lib/Constants.pm
+++ b/extensions/PhabBugz/lib/Constants.pm
@@ -13,13 +13,13 @@ use warnings;
use base qw(Exporter);
our @EXPORT = qw(
- PHAB_AUTOMATION_USER
- PHAB_ATTACHMENT_PATTERN
- PHAB_CONTENT_TYPE
- PHAB_FEED_POLL_SECONDS
- PHAB_USER_POLL_SECONDS
- PHAB_GROUP_POLL_SECONDS
- PHAB_TIMEOUT
+ PHAB_AUTOMATION_USER
+ PHAB_ATTACHMENT_PATTERN
+ PHAB_CONTENT_TYPE
+ PHAB_FEED_POLL_SECONDS
+ PHAB_USER_POLL_SECONDS
+ PHAB_GROUP_POLL_SECONDS
+ PHAB_TIMEOUT
);
use constant PHAB_ATTACHMENT_PATTERN => qr/^phabricator-D(\d+)/;
diff --git a/extensions/PhabBugz/lib/Daemon.pm b/extensions/PhabBugz/lib/Daemon.pm
index ef4a00534..9f995553f 100644
--- a/extensions/PhabBugz/lib/Daemon.pm
+++ b/extensions/PhabBugz/lib/Daemon.pm
@@ -21,7 +21,7 @@ use File::Spec;
use Pod::Usage;
sub start {
- newdaemon();
+ newdaemon();
}
#
@@ -29,69 +29,72 @@ sub start {
#
sub gd_preconfig {
- my $self = shift;
- my $pidfile = $self->{gd_args}{pidfile};
- if (!$pidfile) {
- $pidfile = File::Spec->catfile(bz_locations()->{datadir}, $self->{gd_progname} . ".pid");
- }
- return (pidfile => $pidfile);
+ my $self = shift;
+ my $pidfile = $self->{gd_args}{pidfile};
+ if (!$pidfile) {
+ $pidfile = File::Spec->catfile(bz_locations()->{datadir},
+ $self->{gd_progname} . ".pid");
+ }
+ return (pidfile => $pidfile);
}
sub gd_getopt {
- my $self = shift;
- $self->SUPER::gd_getopt();
- if ($self->{gd_args}{progname}) {
- $self->{gd_progname} = $self->{gd_args}{progname};
- } else {
- $self->{gd_progname} = basename($0);
- }
- $self->{_original_zero} = $0;
- $0 = $self->{gd_progname};
+ my $self = shift;
+ $self->SUPER::gd_getopt();
+ if ($self->{gd_args}{progname}) {
+ $self->{gd_progname} = $self->{gd_args}{progname};
+ }
+ else {
+ $self->{gd_progname} = basename($0);
+ }
+ $self->{_original_zero} = $0;
+ $0 = $self->{gd_progname};
}
sub gd_postconfig {
- my $self = shift;
- $0 = delete $self->{_original_zero};
+ my $self = shift;
+ $0 = delete $self->{_original_zero};
}
sub gd_more_opt {
- my $self = shift;
- return (
- 'pidfile=s' => \$self->{gd_args}{pidfile},
- 'n=s' => \$self->{gd_args}{progname},
- );
+ my $self = shift;
+ return (
+ 'pidfile=s' => \$self->{gd_args}{pidfile},
+ 'n=s' => \$self->{gd_args}{progname},
+ );
}
sub gd_usage {
- pod2usage({ -verbose => 0, -exitval => 'NOEXIT' });
- return 0;
-};
+ pod2usage({-verbose => 0, -exitval => 'NOEXIT'});
+ return 0;
+}
sub gd_redirect_output {
- my $self = shift;
-
- my $filename = File::Spec->catfile(bz_locations()->{datadir}, $self->{gd_progname} . ".log");
+ my $self = shift;
+
+ my $filename = File::Spec->catfile(bz_locations()->{datadir},
+ $self->{gd_progname} . ".log");
+ open(STDERR, ">>", $filename) or (print "could not open stderr: $!" && exit(1));
+ close(STDOUT);
+ open(STDOUT, ">&", STDERR) or die "redirect STDOUT -> STDERR: $!";
+ $SIG{HUP} = sub {
+ close(STDERR);
open(STDERR, ">>", $filename) or (print "could not open stderr: $!" && exit(1));
- close(STDOUT);
- open(STDOUT, ">&", STDERR) or die "redirect STDOUT -> STDERR: $!";
- $SIG{HUP} = sub {
- close(STDERR);
- open(STDERR, ">>", $filename) or (print "could not open stderr: $!" && exit(1));
- };
+ };
}
sub gd_setup_signals {
- my $self = shift;
- $self->SUPER::gd_setup_signals();
- $SIG{TERM} = sub { $self->gd_quit_event(); }
+ my $self = shift;
+ $self->SUPER::gd_setup_signals();
+ $SIG{TERM} = sub { $self->gd_quit_event(); }
}
sub gd_run {
- my $self = shift;
- $SIG{__DIE__} = \&Carp::confess if $self->{debug};
- my $phabbugz = Bugzilla::Extension::PhabBugz::Feed->new();
- $phabbugz->is_daemon(1);
- $phabbugz->start();
+ my $self = shift;
+ $SIG{__DIE__} = \&Carp::confess if $self->{debug};
+ my $phabbugz = Bugzilla::Extension::PhabBugz::Feed->new();
+ $phabbugz->is_daemon(1);
+ $phabbugz->start();
}
1;
diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm
index 71e6aa827..f199a96aa 100644
--- a/extensions/PhabBugz/lib/Feed.pm
+++ b/extensions/PhabBugz/lib/Feed.pm
@@ -26,7 +26,8 @@ use Bugzilla::Field;
use Bugzilla::Logging;
use Bugzilla::Mailer;
use Bugzilla::Search;
-use Bugzilla::Util qw(diff_arrays format_time with_writable_database with_readonly_database);
+use Bugzilla::Util
+ qw(diff_arrays format_time with_writable_database with_readonly_database);
use Bugzilla::Types qw(:types);
use Bugzilla::Extension::PhabBugz::Types qw(:types);
use Bugzilla::Extension::PhabBugz::Constants;
@@ -34,663 +35,661 @@ use Bugzilla::Extension::PhabBugz::Policy;
use Bugzilla::Extension::PhabBugz::Revision;
use Bugzilla::Extension::PhabBugz::User;
use Bugzilla::Extension::PhabBugz::Util qw(
- create_revision_attachment
- get_bug_role_phids
- is_attachment_phab_revision
- request
- set_phab_user
+ create_revision_attachment
+ get_bug_role_phids
+ is_attachment_phab_revision
+ request
+ set_phab_user
);
-has 'is_daemon' => ( is => 'rw', default => 0 );
+has 'is_daemon' => (is => 'rw', default => 0);
-my $Invocant = class_type { class => __PACKAGE__ };
+my $Invocant = class_type {class => __PACKAGE__};
sub start {
- my ($self) = @_;
-
- my $sig_alarm = IO::Async::Signal->new(
- name => 'ALRM',
- on_receipt => sub {
- FATAL("Timeout reached");
- exit;
- },
- );
- # Query for new revisions or changes
- my $feed_timer = IO::Async::Timer::Periodic->new(
- first_interval => 0,
- interval => PHAB_FEED_POLL_SECONDS,
- reschedule => 'drift',
- on_tick => sub {
- try {
- with_writable_database {
- alarm(PHAB_TIMEOUT);
- $self->feed_query();
- };
- }
- catch {
- FATAL($_);
- }
- finally {
- alarm(0);
- Bugzilla->_cleanup();
- };
- },
- );
-
- # Query for new users
- my $user_timer = IO::Async::Timer::Periodic->new(
- first_interval => 0,
- interval => PHAB_USER_POLL_SECONDS,
- reschedule => 'drift',
- on_tick => sub {
- try {
- with_writable_database {
- alarm(PHAB_TIMEOUT);
- $self->user_query();
- };
- }
- catch {
- FATAL($_);
- }
- finally {
- alarm(0);
- Bugzilla->_cleanup();
- };
- },
- );
-
- # Update project membership in Phabricator based on Bugzilla groups
- my $group_timer = IO::Async::Timer::Periodic->new(
- first_interval => 0,
- interval => PHAB_GROUP_POLL_SECONDS,
- reschedule => 'drift',
- on_tick => sub {
- try {
- with_writable_database {
- alarm(PHAB_TIMEOUT);
- $self->group_query();
- };
- }
- catch {
- FATAL($_);
- }
- finally {
- alarm(0);
- Bugzilla->_cleanup();
- };
-
- },
- );
-
- my $loop = IO::Async::Loop->new;
- $loop->add($feed_timer);
- $loop->add($user_timer);
- $loop->add($group_timer);
- $loop->add($sig_alarm);
- $feed_timer->start;
- $user_timer->start;
- $group_timer->start;
- $loop->run;
+ my ($self) = @_;
+
+ my $sig_alarm = IO::Async::Signal->new(
+ name => 'ALRM',
+ on_receipt => sub {
+ FATAL("Timeout reached");
+ exit;
+ },
+ );
+
+ # Query for new revisions or changes
+ my $feed_timer = IO::Async::Timer::Periodic->new(
+ first_interval => 0,
+ interval => PHAB_FEED_POLL_SECONDS,
+ reschedule => 'drift',
+ on_tick => sub {
+ try {
+ with_writable_database {
+ alarm(PHAB_TIMEOUT);
+ $self->feed_query();
+ };
+ }
+ catch {
+ FATAL($_);
+ }
+ finally {
+ alarm(0);
+ Bugzilla->_cleanup();
+ };
+ },
+ );
+
+ # Query for new users
+ my $user_timer = IO::Async::Timer::Periodic->new(
+ first_interval => 0,
+ interval => PHAB_USER_POLL_SECONDS,
+ reschedule => 'drift',
+ on_tick => sub {
+ try {
+ with_writable_database {
+ alarm(PHAB_TIMEOUT);
+ $self->user_query();
+ };
+ }
+ catch {
+ FATAL($_);
+ }
+ finally {
+ alarm(0);
+ Bugzilla->_cleanup();
+ };
+ },
+ );
+
+ # Update project membership in Phabricator based on Bugzilla groups
+ my $group_timer = IO::Async::Timer::Periodic->new(
+ first_interval => 0,
+ interval => PHAB_GROUP_POLL_SECONDS,
+ reschedule => 'drift',
+ on_tick => sub {
+ try {
+ with_writable_database {
+ alarm(PHAB_TIMEOUT);
+ $self->group_query();
+ };
+ }
+ catch {
+ FATAL($_);
+ }
+ finally {
+ alarm(0);
+ Bugzilla->_cleanup();
+ };
+
+ },
+ );
+
+ my $loop = IO::Async::Loop->new;
+ $loop->add($feed_timer);
+ $loop->add($user_timer);
+ $loop->add($group_timer);
+ $loop->add($sig_alarm);
+ $feed_timer->start;
+ $user_timer->start;
+ $group_timer->start;
+ $loop->run;
}
sub feed_query {
- my ($self) = @_;
+ my ($self) = @_;
+
+ local Bugzilla::Logging->fields->{type} = 'FEED';
+
+ # Ensure Phabricator syncing is enabled
+ if (!Bugzilla->params->{phabricator_enabled}) {
+ WARN("PHABRICATOR SYNC DISABLED");
+ return;
+ }
+
+ # PROCESS NEW FEED TRANSACTIONS
+
+ INFO("Fetching new stories");
+
+ my $story_last_id = $self->get_last_id('feed');
+
+ # Check for new transctions (stories)
+ my $new_stories = $self->new_stories($story_last_id);
+ INFO("No new stories") unless @$new_stories;
+
+ # Process each story
+ foreach my $story_data (@$new_stories) {
+ my $story_id = $story_data->{id};
+ my $story_phid = $story_data->{phid};
+ my $author_phid = $story_data->{authorPHID};
+ my $object_phid = $story_data->{objectPHID};
+ my $story_text = $story_data->{text};
- local Bugzilla::Logging->fields->{type} = 'FEED';
+ TRACE("STORY ID: $story_id");
+ TRACE("STORY PHID: $story_phid");
+ TRACE("AUTHOR PHID: $author_phid");
+ TRACE("OBJECT PHID: $object_phid");
+ INFO("STORY TEXT: $story_text");
- # Ensure Phabricator syncing is enabled
- if (!Bugzilla->params->{phabricator_enabled}) {
- WARN("PHABRICATOR SYNC DISABLED");
- return;
+ # Only interested in changes to revisions for now.
+ if ($object_phid !~ /^PHID-DREV/) {
+ INFO("SKIPPING: Not a revision change");
+ $self->save_last_id($story_id, 'feed');
+ next;
}
- # PROCESS NEW FEED TRANSACTIONS
-
- INFO("Fetching new stories");
-
- my $story_last_id = $self->get_last_id('feed');
-
- # Check for new transctions (stories)
- my $new_stories = $self->new_stories($story_last_id);
- INFO("No new stories") unless @$new_stories;
-
- # Process each story
- foreach my $story_data (@$new_stories) {
- my $story_id = $story_data->{id};
- my $story_phid = $story_data->{phid};
- my $author_phid = $story_data->{authorPHID};
- my $object_phid = $story_data->{objectPHID};
- my $story_text = $story_data->{text};
-
- TRACE("STORY ID: $story_id");
- TRACE("STORY PHID: $story_phid");
- TRACE("AUTHOR PHID: $author_phid");
- TRACE("OBJECT PHID: $object_phid");
- INFO("STORY TEXT: $story_text");
-
- # Only interested in changes to revisions for now.
- if ($object_phid !~ /^PHID-DREV/) {
- INFO("SKIPPING: Not a revision change");
- $self->save_last_id($story_id, 'feed');
- next;
- }
-
- # Skip changes done by phab-bot user
- # If changer does not exist in bugzilla database
- # we use the phab-bot account as the changer
- my $author = Bugzilla::Extension::PhabBugz::User->new_from_query(
- { phids => [ $author_phid ] }
- );
-
- if ($author && $author->bugzilla_id) {
- if ($author->bugzilla_user->login eq PHAB_AUTOMATION_USER) {
- INFO("SKIPPING: Change made by phabricator user");
- $self->save_last_id($story_id, 'feed');
- next;
- }
- }
- else {
- my $phab_user = Bugzilla::User->new( { name => PHAB_AUTOMATION_USER } );
- $author = Bugzilla::Extension::PhabBugz::User->new_from_query(
- {
- ids => [ $phab_user->id ]
- }
- );
- }
- # Load the revision from Phabricator
- my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query({ phids => [ $object_phid ] });
- $self->process_revision_change($revision, $author, $story_text);
+ # Skip changes done by phab-bot user
+ # If changer does not exist in bugzilla database
+ # we use the phab-bot account as the changer
+ my $author = Bugzilla::Extension::PhabBugz::User->new_from_query(
+ {phids => [$author_phid]});
+
+ if ($author && $author->bugzilla_id) {
+ if ($author->bugzilla_user->login eq PHAB_AUTOMATION_USER) {
+ INFO("SKIPPING: Change made by phabricator user");
$self->save_last_id($story_id, 'feed');
+ next;
+ }
+ }
+ else {
+ my $phab_user = Bugzilla::User->new({name => PHAB_AUTOMATION_USER});
+ $author
+ = Bugzilla::Extension::PhabBugz::User->new_from_query({ids => [$phab_user->id]
+ });
}
- # Process any build targets as well.
- my $dbh = Bugzilla->dbh;
+ # Load the revision from Phabricator
+ my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query(
+ {phids => [$object_phid]});
+ $self->process_revision_change($revision, $author, $story_text);
+ $self->save_last_id($story_id, 'feed');
+ }
- INFO("Checking for revisions in draft mode");
- my $build_targets = $dbh->selectall_arrayref(
- "SELECT name, value FROM phabbugz WHERE name LIKE 'build_target_%'",
- { Slice => {} }
- );
+ # Process any build targets as well.
+ my $dbh = Bugzilla->dbh;
- my $delete_build_target = $dbh->prepare(
- "DELETE FROM phabbugz WHERE name = ? AND VALUE = ?"
- );
+ INFO("Checking for revisions in draft mode");
+ my $build_targets
+ = $dbh->selectall_arrayref(
+ "SELECT name, value FROM phabbugz WHERE name LIKE 'build_target_%'",
+ {Slice => {}});
- foreach my $target (@$build_targets) {
- my ($revision_id) = ($target->{name} =~ /^build_target_(\d+)$/);
- my $build_target = $target->{value};
+ my $delete_build_target
+ = $dbh->prepare("DELETE FROM phabbugz WHERE name = ? AND VALUE = ?");
- next unless $revision_id && $build_target;
+ foreach my $target (@$build_targets) {
+ my ($revision_id) = ($target->{name} =~ /^build_target_(\d+)$/);
+ my $build_target = $target->{value};
- INFO("Processing revision $revision_id with build target $build_target");
+ next unless $revision_id && $build_target;
- my $revision =
- Bugzilla::Extension::PhabBugz::Revision->new_from_query(
- {
- ids => [ int($revision_id) ]
- }
- );
+ INFO("Processing revision $revision_id with build target $build_target");
- $self->process_revision_change( $revision, $revision->author, " created D" . $revision->id );
+ my $revision
+ = Bugzilla::Extension::PhabBugz::Revision->new_from_query({
+ ids => [int($revision_id)]
+ });
- # Set the build target to a passing status to
- # allow the revision to exit draft state
- request( 'harbormaster.sendmessage', {
- buildTargetPHID => $build_target,
- type => 'pass'
- } );
+ $self->process_revision_change($revision, $revision->author,
+ " created D" . $revision->id);
- $delete_build_target->execute($target->{name}, $target->{value});
- }
+ # Set the build target to a passing status to
+ # allow the revision to exit draft state
+ request('harbormaster.sendmessage',
+ {buildTargetPHID => $build_target, type => 'pass'});
- if (Bugzilla->datadog) {
- my $dd = Bugzilla->datadog();
- $dd->increment('bugzilla.phabbugz.feed_query_count');
- }
+ $delete_build_target->execute($target->{name}, $target->{value});
+ }
+
+ if (Bugzilla->datadog) {
+ my $dd = Bugzilla->datadog();
+ $dd->increment('bugzilla.phabbugz.feed_query_count');
+ }
}
sub user_query {
- my ( $self ) = @_;
+ my ($self) = @_;
- local Bugzilla::Logging->fields->{type} = 'USERS';
+ local Bugzilla::Logging->fields->{type} = 'USERS';
- # Ensure Phabricator syncing is enabled
- if (!Bugzilla->params->{phabricator_enabled}) {
- WARN("PHABRICATOR SYNC DISABLED");
- return;
- }
+ # Ensure Phabricator syncing is enabled
+ if (!Bugzilla->params->{phabricator_enabled}) {
+ WARN("PHABRICATOR SYNC DISABLED");
+ return;
+ }
- # PROCESS NEW USERS
+ # PROCESS NEW USERS
- INFO("Fetching new users");
+ INFO("Fetching new users");
- my $user_last_id = $self->get_last_id('user');
+ my $user_last_id = $self->get_last_id('user');
- # Check for new users
- my $new_users = $self->new_users($user_last_id);
- INFO("No new users") unless @$new_users;
+ # Check for new users
+ my $new_users = $self->new_users($user_last_id);
+ INFO("No new users") unless @$new_users;
- # Process each new user
- foreach my $user_data (@$new_users) {
- my $user_id = $user_data->{id};
- my $user_login = $user_data->{fields}{username};
- my $user_realname = $user_data->{fields}{realName};
- my $object_phid = $user_data->{phid};
+ # Process each new user
+ foreach my $user_data (@$new_users) {
+ my $user_id = $user_data->{id};
+ my $user_login = $user_data->{fields}{username};
+ my $user_realname = $user_data->{fields}{realName};
+ my $object_phid = $user_data->{phid};
- TRACE("ID: $user_id");
- TRACE("LOGIN: $user_login");
- TRACE("REALNAME: $user_realname");
- TRACE("OBJECT PHID: $object_phid");
+ TRACE("ID: $user_id");
+ TRACE("LOGIN: $user_login");
+ TRACE("REALNAME: $user_realname");
+ TRACE("OBJECT PHID: $object_phid");
- with_readonly_database {
- $self->process_new_user($user_data);
- };
- $self->save_last_id($user_id, 'user');
- }
+ with_readonly_database {
+ $self->process_new_user($user_data);
+ };
+ $self->save_last_id($user_id, 'user');
+ }
- if (Bugzilla->datadog) {
- my $dd = Bugzilla->datadog();
- $dd->increment('bugzilla.phabbugz.user_query_count');
- }
+ if (Bugzilla->datadog) {
+ my $dd = Bugzilla->datadog();
+ $dd->increment('bugzilla.phabbugz.user_query_count');
+ }
}
sub group_query {
- my ($self) = @_;
+ my ($self) = @_;
- local Bugzilla::Logging->fields->{type} = 'GROUPS';
+ local Bugzilla::Logging->fields->{type} = 'GROUPS';
- # Ensure Phabricator syncing is enabled
- if ( !Bugzilla->params->{phabricator_enabled} ) {
- WARN("PHABRICATOR SYNC DISABLED");
- return;
- }
+ # Ensure Phabricator syncing is enabled
+ if (!Bugzilla->params->{phabricator_enabled}) {
+ WARN("PHABRICATOR SYNC DISABLED");
+ return;
+ }
- # PROCESS SECURITY GROUPS
-
- INFO("Updating group memberships");
-
- # Loop through each group and perform the following:
- #
- # 1. Load flattened list of group members
- # 2. Check to see if Phab project exists for 'bmo-<group_name>'
- # 3. Create if does not exist with locked down policy.
- # 4. Set project members to exact list including phab-bot user
- # 5. Profit
-
- my $sync_groups = Bugzilla::Group->match( { isactive => 1, isbuggroup => 1 } );
-
- # Load phab-bot Phabricator user to add as a member of each project group later
- my $phab_bmo_user = Bugzilla::User->new( { name => PHAB_AUTOMATION_USER, cache => 1 } );
- my $phab_user =
- Bugzilla::Extension::PhabBugz::User->new_from_query(
- {
- ids => [ $phab_bmo_user->id ]
- }
- );
-
- # secure-revision project that will be used for bmo group projects
- my $secure_revision =
- Bugzilla::Extension::PhabBugz::Project->new_from_query(
- {
- name => 'secure-revision'
- }
- );
-
- foreach my $group (@$sync_groups) {
- # Create group project if one does not yet exist
- my $phab_project_name = 'bmo-' . $group->name;
- my $project =
- Bugzilla::Extension::PhabBugz::Project->new_from_query(
- {
- name => $phab_project_name
- }
- );
-
- if ( !$project ) {
- INFO("Project $phab_project_name not found. Creating.");
- $project = Bugzilla::Extension::PhabBugz::Project->create(
- {
- name => $phab_project_name,
- description => 'BMO Security Group for ' . $group->name,
- view_policy => $secure_revision->phid,
- edit_policy => $secure_revision->phid,
- join_policy => $secure_revision->phid
- }
- );
- }
- else {
- # Make sure that the group project permissions are set properly
- INFO("Updating permissions on $phab_project_name");
- $project->set_policy( 'view', $secure_revision->phid );
- $project->set_policy( 'edit', $secure_revision->phid );
- $project->set_policy( 'join', $secure_revision->phid );
- }
-
- # Make sure phab-bot also a member of the new project group so that it can
- # make policy changes to the private revisions
- INFO( "Checking project members for " . $project->name );
- my $set_members = $self->get_group_members($group);
- my @set_member_phids = uniq map { $_->phid } ( @$set_members, $phab_user );
- my @current_member_phids = uniq map { $_->phid } @{ $project->members };
- my ( $removed, $added ) = diff_arrays( \@current_member_phids, \@set_member_phids );
-
- if (@$added) {
- INFO( 'Adding project members: ' . join( ',', @$added ) );
- $project->add_member($_) foreach @$added;
- }
-
- if (@$removed) {
- INFO( 'Removing project members: ' . join( ',', @$removed ) );
- $project->remove_member($_) foreach @$removed;
- }
-
- if (@$added || @$removed) {
- my $result = $project->update();
- local Bugzilla::Logging->fields->{api_result} = $result;
- INFO( "Project " . $project->name . " updated" );
- }
- }
+ # PROCESS SECURITY GROUPS
+
+ INFO("Updating group memberships");
+
+ # Loop through each group and perform the following:
+ #
+ # 1. Load flattened list of group members
+ # 2. Check to see if Phab project exists for 'bmo-<group_name>'
+ # 3. Create if does not exist with locked down policy.
+ # 4. Set project members to exact list including phab-bot user
+ # 5. Profit
+
+ my $sync_groups = Bugzilla::Group->match({isactive => 1, isbuggroup => 1});
- if (Bugzilla->datadog) {
- my $dd = Bugzilla->datadog();
- $dd->increment('bugzilla.phabbugz.group_query_count');
+ # Load phab-bot Phabricator user to add as a member of each project group later
+ my $phab_bmo_user
+ = Bugzilla::User->new({name => PHAB_AUTOMATION_USER, cache => 1});
+ my $phab_user
+ = Bugzilla::Extension::PhabBugz::User->new_from_query({
+ ids => [$phab_bmo_user->id]
+ });
+
+ # secure-revision project that will be used for bmo group projects
+ my $secure_revision
+ = Bugzilla::Extension::PhabBugz::Project->new_from_query({
+ name => 'secure-revision'
+ });
+
+ foreach my $group (@$sync_groups) {
+
+ # Create group project if one does not yet exist
+ my $phab_project_name = 'bmo-' . $group->name;
+ my $project
+ = Bugzilla::Extension::PhabBugz::Project->new_from_query({
+ name => $phab_project_name
+ });
+
+ if (!$project) {
+ INFO("Project $phab_project_name not found. Creating.");
+ $project = Bugzilla::Extension::PhabBugz::Project->create({
+ name => $phab_project_name,
+ description => 'BMO Security Group for ' . $group->name,
+ view_policy => $secure_revision->phid,
+ edit_policy => $secure_revision->phid,
+ join_policy => $secure_revision->phid
+ });
+ }
+ else {
+ # Make sure that the group project permissions are set properly
+ INFO("Updating permissions on $phab_project_name");
+ $project->set_policy('view', $secure_revision->phid);
+ $project->set_policy('edit', $secure_revision->phid);
+ $project->set_policy('join', $secure_revision->phid);
}
-}
-sub process_revision_change {
- state $check = compile($Invocant, Revision, LinkedPhabUser, Str);
- my ($self, $revision, $changer, $story_text) = $check->(@_);
-
- # NO BUG ID
- if (!$revision->bug_id) {
- if ($story_text =~ /\s+created\s+D\d+/) {
- # If new revision and bug id was omitted, make revision public
- INFO("No bug associated with new revision. Marking public.");
- $revision->make_public();
- $revision->update();
- INFO("SUCCESS");
- return;
- }
- else {
- INFO("SKIPPING: No bug associated with revision change");
- return;
- }
+ # Make sure phab-bot also a member of the new project group so that it can
+ # make policy changes to the private revisions
+ INFO("Checking project members for " . $project->name);
+ my $set_members = $self->get_group_members($group);
+ my @set_member_phids = uniq map { $_->phid } (@$set_members, $phab_user);
+ my @current_member_phids = uniq map { $_->phid } @{$project->members};
+ my ($removed, $added) = diff_arrays(\@current_member_phids, \@set_member_phids);
+
+ if (@$added) {
+ INFO('Adding project members: ' . join(',', @$added));
+ $project->add_member($_) foreach @$added;
}
+ if (@$removed) {
+ INFO('Removing project members: ' . join(',', @$removed));
+ $project->remove_member($_) foreach @$removed;
+ }
- my $log_message = sprintf(
- "REVISION CHANGE FOUND: D%d: %s | bug: %d | %s | %s",
- $revision->id,
- $revision->title,
- $revision->bug_id,
- $changer->name,
- $story_text);
- INFO($log_message);
-
- # change to the phabricator user, which returns a guard that restores the previous user.
- my $restore_prev_user = set_phab_user();
- my $bug = $revision->bug;
-
- # Check to make sure bug id is valid and author can see it
- if ($bug->{error}
- ||!$revision->author->bugzilla_user->can_see_bug($revision->bug_id))
- {
- if ($story_text =~ /\s+created\s+D\d+/) {
- INFO('Invalid bug ID or author does not have access to the bug. ' .
- 'Waiting til next revision update to notify author.');
- return;
- }
-
- INFO('Invalid bug ID or author does not have access to the bug');
- my $phab_error_message = "";
- Bugzilla->template->process('revision/comments.html.tmpl',
- { message => 'invalid_bug_id' },
- \$phab_error_message);
- $revision->add_comment($phab_error_message);
- $revision->update();
- return;
+ if (@$added || @$removed) {
+ my $result = $project->update();
+ local Bugzilla::Logging->fields->{api_result} = $result;
+ INFO("Project " . $project->name . " updated");
}
+ }
- # REVISION SECURITY POLICY
+ if (Bugzilla->datadog) {
+ my $dd = Bugzilla->datadog();
+ $dd->increment('bugzilla.phabbugz.group_query_count');
+ }
+}
- # If bug is public then remove privacy policy
- if (!@{ $bug->groups_in }) {
- INFO('Bug is public so setting view/edit public');
- $revision->make_public();
+sub process_revision_change {
+ state $check = compile($Invocant, Revision, LinkedPhabUser, Str);
+ my ($self, $revision, $changer, $story_text) = $check->(@_);
+
+ # NO BUG ID
+ if (!$revision->bug_id) {
+ if ($story_text =~ /\s+created\s+D\d+/) {
+
+ # If new revision and bug id was omitted, make revision public
+ INFO("No bug associated with new revision. Marking public.");
+ $revision->make_public();
+ $revision->update();
+ INFO("SUCCESS");
+ return;
}
- # else bug is private.
else {
- # Here we create a new custom policy containing the project
- # groups that are mapped to bugzilla groups.
- my $set_project_names = [ map { "bmo-" . $_->name } @{ $bug->groups_in } ];
-
- # If current policy projects matches what we want to set, then
- # we leave the current policy alone.
- my $current_policy;
- if ($revision->view_policy =~ /^PHID-PLCY/) {
- INFO("Loading current policy: " . $revision->view_policy);
- $current_policy
- = Bugzilla::Extension::PhabBugz::Policy->new_from_query({ phids => [ $revision->view_policy ]});
- my $current_project_names = [ map { $_->name } @{ $current_policy->rule_projects } ];
- INFO("Current policy projects: " . join(", ", @$current_project_names));
- my ($added, $removed) = diff_arrays($current_project_names, $set_project_names);
- if (@$added || @$removed) {
- INFO('Project groups do not match. Need new custom policy');
- $current_policy = undef;
- }
- else {
- INFO('Project groups match. Leaving current policy as-is');
- }
- }
-
- if (!$current_policy) {
- INFO("Creating new custom policy: " . join(", ", @$set_project_names));
- $revision->make_private($set_project_names);
- }
-
- # Subscriber list of the private revision should always match
- # the bug roles such as assignee, qa contact, and cc members.
- my $subscribers = get_bug_role_phids($bug);
- $revision->set_subscribers($subscribers);
+ INFO("SKIPPING: No bug associated with revision change");
+ return;
+ }
+ }
+
+
+ my $log_message = sprintf(
+ "REVISION CHANGE FOUND: D%d: %s | bug: %d | %s | %s",
+ $revision->id, $revision->title, $revision->bug_id,
+ $changer->name, $story_text
+ );
+ INFO($log_message);
+
+# change to the phabricator user, which returns a guard that restores the previous user.
+ my $restore_prev_user = set_phab_user();
+ my $bug = $revision->bug;
+
+ # Check to make sure bug id is valid and author can see it
+ if ($bug->{error}
+ || !$revision->author->bugzilla_user->can_see_bug($revision->bug_id))
+ {
+ if ($story_text =~ /\s+created\s+D\d+/) {
+ INFO( 'Invalid bug ID or author does not have access to the bug. '
+ . 'Waiting til next revision update to notify author.');
+ return;
}
- my ($timestamp) = Bugzilla->dbh->selectrow_array("SELECT NOW()");
-
- INFO('Checking for revision attachment');
- my $rev_attachment = create_revision_attachment($bug, $revision, $timestamp, $revision->author->bugzilla_user);
- INFO('Attachment ' . $rev_attachment->id . ' created or already exists.');
-
- # ATTACHMENT OBSOLETES
+ INFO('Invalid bug ID or author does not have access to the bug');
+ my $phab_error_message = "";
+ Bugzilla->template->process('revision/comments.html.tmpl',
+ {message => 'invalid_bug_id'},
+ \$phab_error_message);
+ $revision->add_comment($phab_error_message);
+ $revision->update();
+ return;
+ }
+
+ # REVISION SECURITY POLICY
+
+ # If bug is public then remove privacy policy
+ if (!@{$bug->groups_in}) {
+ INFO('Bug is public so setting view/edit public');
+ $revision->make_public();
+ }
+
+ # else bug is private.
+ else {
+ # Here we create a new custom policy containing the project
+ # groups that are mapped to bugzilla groups.
+ my $set_project_names = [map { "bmo-" . $_->name } @{$bug->groups_in}];
+
+ # If current policy projects matches what we want to set, then
+ # we leave the current policy alone.
+ my $current_policy;
+ if ($revision->view_policy =~ /^PHID-PLCY/) {
+ INFO("Loading current policy: " . $revision->view_policy);
+ $current_policy = Bugzilla::Extension::PhabBugz::Policy->new_from_query(
+ {phids => [$revision->view_policy]});
+ my $current_project_names
+ = [map { $_->name } @{$current_policy->rule_projects}];
+ INFO("Current policy projects: " . join(", ", @$current_project_names));
+ my ($added, $removed) = diff_arrays($current_project_names, $set_project_names);
+ if (@$added || @$removed) {
+ INFO('Project groups do not match. Need new custom policy');
+ $current_policy = undef;
+ }
+ else {
+ INFO('Project groups match. Leaving current policy as-is');
+ }
+ }
- # fixup attachments on current bug
- my @attachments =
- grep { is_attachment_phab_revision($_) } @{ $bug->attachments() };
+ if (!$current_policy) {
+ INFO("Creating new custom policy: " . join(", ", @$set_project_names));
+ $revision->make_private($set_project_names);
+ }
- foreach my $attachment (@attachments) {
- my ($attach_revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN);
- next if $attach_revision_id != $revision->id;
+ # Subscriber list of the private revision should always match
+ # the bug roles such as assignee, qa contact, and cc members.
+ my $subscribers = get_bug_role_phids($bug);
+ $revision->set_subscribers($subscribers);
+ }
- my $make_obsolete = $revision->status eq 'abandoned' ? 1 : 0;
- INFO('Updating obsolete status on attachmment ' . $attachment->id);
- $attachment->set_is_obsolete($make_obsolete);
+ my ($timestamp) = Bugzilla->dbh->selectrow_array("SELECT NOW()");
- if ($revision->title ne $attachment->description) {
- INFO('Updating description on attachment ' . $attachment->id);
- $attachment->set_description($revision->title);
- }
+ INFO('Checking for revision attachment');
+ my $rev_attachment = create_revision_attachment($bug, $revision, $timestamp,
+ $revision->author->bugzilla_user);
+ INFO('Attachment ' . $rev_attachment->id . ' created or already exists.');
- $attachment->update($timestamp);
- }
+ # ATTACHMENT OBSOLETES
- # fixup attachments with same revision id but on different bugs
- my %other_bugs;
- my $other_attachments = Bugzilla::Attachment->match({
- mimetype => PHAB_CONTENT_TYPE,
- filename => 'phabricator-D' . $revision->id . '-url.txt',
- WHERE => { 'bug_id != ? AND NOT isobsolete' => $bug->id }
- });
- foreach my $attachment (@$other_attachments) {
- $other_bugs{$attachment->bug_id}++;
- INFO('Updating obsolete status on attachment ' .
- $attachment->id . " for bug " . $attachment->bug_id);
- $attachment->set_is_obsolete(1);
- $attachment->update($timestamp);
- }
+ # fixup attachments on current bug
+ my @attachments
+ = grep { is_attachment_phab_revision($_) } @{$bug->attachments()};
- # FINISH UP
+ foreach my $attachment (@attachments) {
+ my ($attach_revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN);
+ next if $attach_revision_id != $revision->id;
- $bug->update($timestamp);
- $revision->update();
+ my $make_obsolete = $revision->status eq 'abandoned' ? 1 : 0;
+ INFO('Updating obsolete status on attachmment ' . $attachment->id);
+ $attachment->set_is_obsolete($make_obsolete);
- # Email changes for this revisions bug and also for any other
- # bugs that previously had these revision attachments
- foreach my $bug_id ($revision->bug_id, keys %other_bugs) {
- Bugzilla::BugMail::Send($bug_id, { changer => $changer->bugzilla_user });
+ if ($revision->title ne $attachment->description) {
+ INFO('Updating description on attachment ' . $attachment->id);
+ $attachment->set_description($revision->title);
}
- INFO('SUCCESS: Revision D' . $revision->id . ' processed');
+ $attachment->update($timestamp);
+ }
+
+ # fixup attachments with same revision id but on different bugs
+ my %other_bugs;
+ my $other_attachments = Bugzilla::Attachment->match({
+ mimetype => PHAB_CONTENT_TYPE,
+ filename => 'phabricator-D' . $revision->id . '-url.txt',
+ WHERE => {'bug_id != ? AND NOT isobsolete' => $bug->id}
+ });
+ foreach my $attachment (@$other_attachments) {
+ $other_bugs{$attachment->bug_id}++;
+ INFO( 'Updating obsolete status on attachment '
+ . $attachment->id
+ . " for bug "
+ . $attachment->bug_id);
+ $attachment->set_is_obsolete(1);
+ $attachment->update($timestamp);
+ }
+
+ # FINISH UP
+
+ $bug->update($timestamp);
+ $revision->update();
+
+ # Email changes for this revisions bug and also for any other
+ # bugs that previously had these revision attachments
+ foreach my $bug_id ($revision->bug_id, keys %other_bugs) {
+ Bugzilla::BugMail::Send($bug_id, {changer => $changer->bugzilla_user});
+ }
+
+ INFO('SUCCESS: Revision D' . $revision->id . ' processed');
}
sub process_new_user {
- state $check = compile($Invocant, HashRef);
- my ( $self, $user_data ) = $check->(@_);
+ state $check = compile($Invocant, HashRef);
+ my ($self, $user_data) = $check->(@_);
- # Load the user data into a proper object
- my $phab_user = Bugzilla::Extension::PhabBugz::User->new($user_data);
+ # Load the user data into a proper object
+ my $phab_user = Bugzilla::Extension::PhabBugz::User->new($user_data);
- if (!$phab_user->bugzilla_id) {
- WARN("SKIPPING: No bugzilla id associated with user");
- return;
- }
+ if (!$phab_user->bugzilla_id) {
+ WARN("SKIPPING: No bugzilla id associated with user");
+ return;
+ }
- my $bug_user = $phab_user->bugzilla_user;
+ my $bug_user = $phab_user->bugzilla_user;
- # Pre setup before querying DB
- my $restore_prev_user = set_phab_user();
+ # Pre setup before querying DB
+ my $restore_prev_user = set_phab_user();
- # CHECK AND WARN FOR POSSIBLE USERNAME SQUATTING
- INFO("Checking for username squatters");
- my $dbh = Bugzilla->dbh;
- my $regexp = $dbh->quote( ":?:" . quotemeta($phab_user->name) . "[[:>:]]" );
- my $results = $dbh->selectall_arrayref( "
+ # CHECK AND WARN FOR POSSIBLE USERNAME SQUATTING
+ INFO("Checking for username squatters");
+ my $dbh = Bugzilla->dbh;
+ my $regexp = $dbh->quote(":?:" . quotemeta($phab_user->name) . "[[:>:]]");
+ my $results = $dbh->selectall_arrayref("
SELECT userid, login_name, realname
FROM profiles
- WHERE userid != ? AND " . $dbh->sql_regexp( 'realname', $regexp ),
- { Slice => {} },
- $bug_user->id );
- if (@$results) {
- # The email client will display the Date: header in the desired timezone,
- # so we can always use UTC here.
- my $timestamp = Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
- $timestamp = format_time($timestamp, '%a, %d %b %Y %T %z', 'UTC');
-
- foreach my $row (@$results) {
- WARN(
- 'Possible username squatter: ',
- 'phab user login: ' . $phab_user->name,
- ' phab user realname: ' . $phab_user->realname,
- ' bugzilla user id: ' . $row->{userid},
- ' bugzilla login: ' . $row->{login_name},
- ' bugzilla realname: ' . $row->{realname}
- );
-
- my $vars = {
- date => $timestamp,
- phab_user_login => $phab_user->name,
- phab_user_realname => $phab_user->realname,
- bugzilla_userid => $bug_user->id,
- bugzilla_login => $bug_user->login,
- bugzilla_realname => $bug_user->name,
- squat_userid => $row->{userid},
- squat_login => $row->{login_name},
- squat_realname => $row->{realname}
- };
-
- my $message;
- my $template = Bugzilla->template;
- $template->process("admin/email/squatter-alert.txt.tmpl", $vars, \$message)
- || ThrowTemplateError($template->error());
-
- MessageToMTA($message);
- }
+ WHERE userid != ? AND " . $dbh->sql_regexp('realname', $regexp),
+ {Slice => {}}, $bug_user->id);
+ if (@$results) {
+
+ # The email client will display the Date: header in the desired timezone,
+ # so we can always use UTC here.
+ my $timestamp = Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
+ $timestamp = format_time($timestamp, '%a, %d %b %Y %T %z', 'UTC');
+
+ foreach my $row (@$results) {
+ WARN(
+ 'Possible username squatter: ',
+ 'phab user login: ' . $phab_user->name,
+ ' phab user realname: ' . $phab_user->realname,
+ ' bugzilla user id: ' . $row->{userid},
+ ' bugzilla login: ' . $row->{login_name},
+ ' bugzilla realname: ' . $row->{realname}
+ );
+
+ my $vars = {
+ date => $timestamp,
+ phab_user_login => $phab_user->name,
+ phab_user_realname => $phab_user->realname,
+ bugzilla_userid => $bug_user->id,
+ bugzilla_login => $bug_user->login,
+ bugzilla_realname => $bug_user->name,
+ squat_userid => $row->{userid},
+ squat_login => $row->{login_name},
+ squat_realname => $row->{realname}
+ };
+
+ my $message;
+ my $template = Bugzilla->template;
+ $template->process("admin/email/squatter-alert.txt.tmpl", $vars, \$message)
+ || ThrowTemplateError($template->error());
+
+ MessageToMTA($message);
}
+ }
- # ADD SUBSCRIBERS TO REVSISIONS FOR CURRENT PRIVATE BUGS
+ # ADD SUBSCRIBERS TO REVSISIONS FOR CURRENT PRIVATE BUGS
- my $params = {
- f3 => 'OP',
- j3 => 'OR',
+ my $params = {
+ f3 => 'OP',
+ j3 => 'OR',
- # User must be either reporter, assignee, qa_contact
- # or on the cc list of the bug
- f4 => 'cc',
- o4 => 'equals',
- v4 => $bug_user->login,
+ # User must be either reporter, assignee, qa_contact
+ # or on the cc list of the bug
+ f4 => 'cc',
+ o4 => 'equals',
+ v4 => $bug_user->login,
- f5 => 'assigned_to',
- o5 => 'equals',
- v5 => $bug_user->login,
+ f5 => 'assigned_to',
+ o5 => 'equals',
+ v5 => $bug_user->login,
- f6 => 'qa_contact',
- o6 => 'equals',
- v6 => $bug_user->login,
+ f6 => 'qa_contact',
+ o6 => 'equals',
+ v6 => $bug_user->login,
- f7 => 'reporter',
- o7 => 'equals',
- v7 => $bug_user->login,
+ f7 => 'reporter',
+ o7 => 'equals',
+ v7 => $bug_user->login,
- f9 => 'CP',
+ f9 => 'CP',
- # The bug needs to be private
- f10 => 'bug_group',
- o10 => 'isnotempty',
+ # The bug needs to be private
+ f10 => 'bug_group',
+ o10 => 'isnotempty',
- # And the bug must have one or more attachments
- # that are connected to revisions
- f11 => 'attachments.filename',
- o11 => 'regexp',
- v11 => '^phabricator-D[[:digit:]]+-url.txt$',
- };
+ # And the bug must have one or more attachments
+ # that are connected to revisions
+ f11 => 'attachments.filename',
+ o11 => 'regexp',
+ v11 => '^phabricator-D[[:digit:]]+-url.txt$',
+ };
- my $search = Bugzilla::Search->new( fields => [ 'bug_id' ],
- params => $params,
- order => [ 'bug_id' ] );
- my $data = $search->data;
+ my $search = Bugzilla::Search->new(
+ fields => ['bug_id'],
+ params => $params,
+ order => ['bug_id']
+ );
+ my $data = $search->data;
- # the first value of each row should be the bug id
- my @bug_ids = map { shift @$_ } @$data;
+ # the first value of each row should be the bug id
+ my @bug_ids = map { shift @$_ } @$data;
- INFO("Updating subscriber values for old private bugs");
+ INFO("Updating subscriber values for old private bugs");
- foreach my $bug_id (@bug_ids) {
- INFO("Processing bug $bug_id");
+ foreach my $bug_id (@bug_ids) {
+ INFO("Processing bug $bug_id");
- my $bug = Bugzilla::Bug->new({ id => $bug_id, cache => 1 });
+ my $bug = Bugzilla::Bug->new({id => $bug_id, cache => 1});
- my @attachments =
- grep { is_attachment_phab_revision($_) } @{ $bug->attachments() };
+ my @attachments
+ = grep { is_attachment_phab_revision($_) } @{$bug->attachments()};
- foreach my $attachment (@attachments) {
- my ($revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN);
+ foreach my $attachment (@attachments) {
+ my ($revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN);
- if (!$revision_id) {
- WARN("Skipping " . $attachment->filename . " on bug $bug_id. Filename should be fixed.");
- next;
- }
+ if (!$revision_id) {
+ WARN( "Skipping "
+ . $attachment->filename
+ . " on bug $bug_id. Filename should be fixed.");
+ next;
+ }
- INFO("Processing revision D$revision_id");
+ INFO("Processing revision D$revision_id");
- my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query(
- { ids => [ int($revision_id) ] });
+ my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query(
+ {ids => [int($revision_id)]});
- $revision->add_subscriber($phab_user->phid);
- $revision->update();
+ $revision->add_subscriber($phab_user->phid);
+ $revision->update();
- INFO("Revision $revision_id updated");
- }
+ INFO("Revision $revision_id updated");
}
+ }
- INFO('SUCCESS: User ' . $phab_user->id . ' processed');
+ INFO('SUCCESS: User ' . $phab_user->id . ' processed');
}
##################
@@ -698,87 +697,74 @@ sub process_new_user {
##################
sub new_stories {
- my ( $self, $after ) = @_;
- my $data = { view => 'text' };
- $data->{after} = $after if $after;
+ my ($self, $after) = @_;
+ my $data = {view => 'text'};
+ $data->{after} = $after if $after;
- my $result = request( 'feed.query_id', $data );
+ my $result = request('feed.query_id', $data);
- unless ( ref $result->{result}{data} eq 'ARRAY'
- && @{ $result->{result}{data} } )
- {
- return [];
- }
+ unless (ref $result->{result}{data} eq 'ARRAY' && @{$result->{result}{data}}) {
+ return [];
+ }
- # Guarantee that the data is in ascending ID order
- return [ sort { $a->{id} <=> $b->{id} } @{ $result->{result}{data} } ];
+ # Guarantee that the data is in ascending ID order
+ return [sort { $a->{id} <=> $b->{id} } @{$result->{result}{data}}];
}
sub new_users {
- my ( $self, $after ) = @_;
- my $data = {
- order => [ "id" ],
- attachments => {
- 'external-accounts' => 1
- }
- };
- $data->{before} = $after if $after;
+ my ($self, $after) = @_;
+ my $data = {order => ["id"], attachments => {'external-accounts' => 1}};
+ $data->{before} = $after if $after;
- my $result = request( 'user.search', $data );
+ my $result = request('user.search', $data);
- unless ( ref $result->{result}{data} eq 'ARRAY'
- && @{ $result->{result}{data} } )
- {
- return [];
- }
+ unless (ref $result->{result}{data} eq 'ARRAY' && @{$result->{result}{data}}) {
+ return [];
+ }
- # Guarantee that the data is in ascending ID order
- return [ sort { $a->{id} <=> $b->{id} } @{ $result->{result}{data} } ];
+ # Guarantee that the data is in ascending ID order
+ return [sort { $a->{id} <=> $b->{id} } @{$result->{result}{data}}];
}
sub get_last_id {
- my ( $self, $type ) = @_;
- my $type_full = $type . "_last_id";
- my $last_id = Bugzilla->dbh->selectrow_array( "
- SELECT value FROM phabbugz WHERE name = ?", undef, $type_full );
- $last_id ||= 0;
- TRACE(uc($type_full) . ": $last_id" );
- return $last_id;
+ my ($self, $type) = @_;
+ my $type_full = $type . "_last_id";
+ my $last_id = Bugzilla->dbh->selectrow_array("
+ SELECT value FROM phabbugz WHERE name = ?", undef, $type_full);
+ $last_id ||= 0;
+ TRACE(uc($type_full) . ": $last_id");
+ return $last_id;
}
sub save_last_id {
- my ( $self, $last_id, $type ) = @_;
+ my ($self, $last_id, $type) = @_;
- # Store the largest last key so we can start from there in the next session
- my $type_full = $type . "_last_id";
- TRACE("UPDATING " . uc($type_full) . ": $last_id" );
- Bugzilla->dbh->do( "REPLACE INTO phabbugz (name, value) VALUES (?, ?)",
- undef, $type_full, $last_id );
+ # Store the largest last key so we can start from there in the next session
+ my $type_full = $type . "_last_id";
+ TRACE("UPDATING " . uc($type_full) . ": $last_id");
+ Bugzilla->dbh->do("REPLACE INTO phabbugz (name, value) VALUES (?, ?)",
+ undef, $type_full, $last_id);
}
sub get_group_members {
- state $check = compile( $Invocant, Group | Str );
- my ( $self, $group ) = $check->(@_);
- my $group_obj =
- ref $group ? $group : Bugzilla::Group->check( { name => $group, cache => 1 } );
+ state $check = compile($Invocant, Group | Str);
+ my ($self, $group) = $check->(@_);
+ my $group_obj
+ = ref $group ? $group : Bugzilla::Group->check({name => $group, cache => 1});
- my $flat_list = join(',',
- @{ Bugzilla::Group->flatten_group_membership( $group_obj->id ) } );
+ my $flat_list
+ = join(',', @{Bugzilla::Group->flatten_group_membership($group_obj->id)});
- my $user_query = "
+ my $user_query = "
SELECT DISTINCT profiles.userid
FROM profiles, user_group_map AS ugm
WHERE ugm.user_id = profiles.userid
AND ugm.isbless = 0
AND ugm.group_id IN($flat_list)";
- my $user_ids = Bugzilla->dbh->selectcol_arrayref($user_query);
+ my $user_ids = Bugzilla->dbh->selectcol_arrayref($user_query);
- # Return matching users in Phabricator
- return Bugzilla::Extension::PhabBugz::User->match(
- {
- ids => $user_ids
- }
- );
+ # Return matching users in Phabricator
+ return Bugzilla::Extension::PhabBugz::User->match({ids => $user_ids});
}
1;
diff --git a/extensions/PhabBugz/lib/Policy.pm b/extensions/PhabBugz/lib/Policy.pm
index 415ea20fb..658d0ddde 100644
--- a/extensions/PhabBugz/lib/Policy.pm
+++ b/extensions/PhabBugz/lib/Policy.pm
@@ -21,30 +21,22 @@ use Types::Standard -all;
use Type::Utils;
use Type::Params qw( compile );
-has 'phid' => ( is => 'ro', isa => Str );
-has 'type' => ( is => 'ro', isa => Str );
-has 'name' => ( is => 'ro', isa => Str );
-has 'shortName' => ( is => 'ro', isa => Str );
-has 'fullName' => ( is => 'ro', isa => Str );
-has 'href' => ( is => 'ro', isa => Maybe[Str] );
-has 'workflow' => ( is => 'ro', isa => Maybe[Str] );
-has 'icon' => ( is => 'ro', isa => Str );
-has 'default' => ( is => 'ro', isa => Str );
+has 'phid' => (is => 'ro', isa => Str);
+has 'type' => (is => 'ro', isa => Str);
+has 'name' => (is => 'ro', isa => Str);
+has 'shortName' => (is => 'ro', isa => Str);
+has 'fullName' => (is => 'ro', isa => Str);
+has 'href' => (is => 'ro', isa => Maybe [Str]);
+has 'workflow' => (is => 'ro', isa => Maybe [Str]);
+has 'icon' => (is => 'ro', isa => Str);
+has 'default' => (is => 'ro', isa => Str);
has 'rules' => (
- is => 'ro',
- isa => ArrayRef[
- Dict[
- action => Str,
- rule => Str,
- value => Maybe[ArrayRef[Str]]
- ]
- ]
+ is => 'ro',
+ isa =>
+ ArrayRef [Dict [action => Str, rule => Str, value => Maybe [ArrayRef [Str]]]]
);
-has 'rule_projects' => (
- is => 'lazy',
- isa => ArrayRef[Project],
-);
+has 'rule_projects' => (is => 'lazy', isa => ArrayRef [Project],);
# {
# "data": [
@@ -81,64 +73,61 @@ has 'rule_projects' => (
# }
# }
-my $Invocant = class_type { class => __PACKAGE__ };
+my $Invocant = class_type {class => __PACKAGE__};
sub new_from_query {
- state $check = compile($Invocant | ClassName, Dict[phids => ArrayRef[Str]]);
- my ($class, $params) = $check->(@_);
- my $result = request('policy.query', $params);
- if (exists $result->{result}{data} && @{ $result->{result}{data} }) {
- return $class->new($result->{result}->{data}->[0]);
- }
+ state $check = compile($Invocant | ClassName, Dict [phids => ArrayRef [Str]]);
+ my ($class, $params) = $check->(@_);
+ my $result = request('policy.query', $params);
+ if (exists $result->{result}{data} && @{$result->{result}{data}}) {
+ return $class->new($result->{result}->{data}->[0]);
+ }
}
sub create {
- state $check = compile($Invocant | ClassName, ArrayRef[Project]);
- my ($class, $projects) = $check->(@_);
-
- my $data = {
- objectType => 'DREV',
- default => 'deny',
- policy => [
- {
- action => 'allow',
- rule => 'PhabricatorSubscriptionsSubscribersPolicyRule',
- },
- {
- action => 'allow',
- rule => 'PhabricatorDifferentialReviewersPolicyRule'
- }
- ]
- };
-
- if (@$projects) {
- push @{ $data->{policy} }, {
- action => 'allow',
- rule => 'PhabricatorProjectsAllPolicyRule',
- value => [ map { $_->phid } @$projects ],
- };
- }
- else {
- my $secure_revision = Bugzilla::Extension::PhabBugz::Project->new_from_query({
- name => 'secure-revision'
- });
- push @{ $data->{policy} }, { action => 'allow', value => $secure_revision->phid };
- }
-
- my $result = request('policy.create', $data);
- return $class->new_from_query({ phids => [ $result->{result}{phid} ] });
+ state $check = compile($Invocant | ClassName, ArrayRef [Project]);
+ my ($class, $projects) = $check->(@_);
+
+ my $data = {
+ objectType => 'DREV',
+ default => 'deny',
+ policy => [
+ {action => 'allow', rule => 'PhabricatorSubscriptionsSubscribersPolicyRule',},
+ {action => 'allow', rule => 'PhabricatorDifferentialReviewersPolicyRule'}
+ ]
+ };
+
+ if (@$projects) {
+ push @{$data->{policy}},
+ {
+ action => 'allow',
+ rule => 'PhabricatorProjectsAllPolicyRule',
+ value => [map { $_->phid } @$projects],
+ };
+ }
+ else {
+ my $secure_revision
+ = Bugzilla::Extension::PhabBugz::Project->new_from_query({
+ name => 'secure-revision'
+ });
+ push @{$data->{policy}}, {action => 'allow', value => $secure_revision->phid};
+ }
+
+ my $result = request('policy.create', $data);
+ return $class->new_from_query({phids => [$result->{result}{phid}]});
}
sub _build_rule_projects {
- my ($self) = @_;
-
- return [] unless $self->rules;
- my $rule = first { $_->{rule} =~ /PhabricatorProjects(?:All)?PolicyRule/ } @{ $self->rules };
- return [] unless $rule;
- return [
- map { Bugzilla::Extension::PhabBugz::Project->new_from_query( { phids => [$_] } ) }
- @{ $rule->{value} }
- ];
+ my ($self) = @_;
+
+ return [] unless $self->rules;
+ my $rule = first { $_->{rule} =~ /PhabricatorProjects(?:All)?PolicyRule/ }
+ @{$self->rules};
+ return [] unless $rule;
+ return [
+ map { Bugzilla::Extension::PhabBugz::Project->new_from_query({phids => [$_]}) }
+ @{$rule->{value}}
+ ];
}
-1; \ No newline at end of file
+1;
diff --git a/extensions/PhabBugz/lib/Project.pm b/extensions/PhabBugz/lib/Project.pm
index c18708887..8af01f74e 100644
--- a/extensions/PhabBugz/lib/Project.pm
+++ b/extensions/PhabBugz/lib/Project.pm
@@ -24,63 +24,61 @@ use Bugzilla::Extension::PhabBugz::Util qw(request);
# Initialization #
#########################
-has id => ( is => 'ro', isa => Int );
-has phid => ( is => 'ro', isa => Str );
-has type => ( is => 'ro', isa => Str );
-has name => ( is => 'ro', isa => Str );
-has description => ( is => 'ro', isa => Maybe[Str] );
-has creation_ts => ( is => 'ro', isa => Str );
-has modification_ts => ( is => 'ro', isa => Str );
-has view_policy => ( is => 'ro', isa => Str );
-has edit_policy => ( is => 'ro', isa => Str );
-has join_policy => ( is => 'ro', isa => Str );
-has members_raw => ( is => 'ro', isa => ArrayRef [ Dict [ phid => Str ] ] );
-has members => ( is => 'lazy', isa => ArrayRef[PhabUser] );
-
-my $Invocant = class_type { class => __PACKAGE__ };
+has id => (is => 'ro', isa => Int);
+has phid => (is => 'ro', isa => Str);
+has type => (is => 'ro', isa => Str);
+has name => (is => 'ro', isa => Str);
+has description => (is => 'ro', isa => Maybe [Str]);
+has creation_ts => (is => 'ro', isa => Str);
+has modification_ts => (is => 'ro', isa => Str);
+has view_policy => (is => 'ro', isa => Str);
+has edit_policy => (is => 'ro', isa => Str);
+has join_policy => (is => 'ro', isa => Str);
+has members_raw => (is => 'ro', isa => ArrayRef [Dict [phid => Str]]);
+has members => (is => 'lazy', isa => ArrayRef [PhabUser]);
+
+my $Invocant = class_type {class => __PACKAGE__};
sub new_from_query {
- my ( $class, $params ) = @_;
-
- my $data = {
- queryKey => 'all',
- attachments => { members => 1 },
- constraints => $params
- };
-
- my $result = request( 'project.search', $data );
- if ( exists $result->{result}{data} && @{ $result->{result}{data} } ) {
- # If name is used as a query param, we need to loop through and look
- # for exact match as Conduit will tokenize the name instead of doing
- # exact string match :( If name is not used, then return first one.
- if ( exists $params->{name} ) {
- foreach my $item ( @{ $result->{result}{data} } ) {
- next if $item->{fields}{name} ne $params->{name};
- return $class->new($item);
- }
- }
- else {
- return $class->new( $result->{result}{data}[0] );
- }
+ my ($class, $params) = @_;
+
+ my $data
+ = {queryKey => 'all', attachments => {members => 1}, constraints => $params};
+
+ my $result = request('project.search', $data);
+ if (exists $result->{result}{data} && @{$result->{result}{data}}) {
+
+ # If name is used as a query param, we need to loop through and look
+ # for exact match as Conduit will tokenize the name instead of doing
+ # exact string match :( If name is not used, then return first one.
+ if (exists $params->{name}) {
+ foreach my $item (@{$result->{result}{data}}) {
+ next if $item->{fields}{name} ne $params->{name};
+ return $class->new($item);
+ }
+ }
+ else {
+ return $class->new($result->{result}{data}[0]);
}
+ }
}
sub BUILDARGS {
- my ( $class, $params ) = @_;
+ my ($class, $params) = @_;
- $params->{name} = $params->{fields}->{name};
- $params->{description} = $params->{fields}->{description};
- $params->{creation_ts} = $params->{fields}->{dateCreated};
- $params->{modification_ts} = $params->{fields}->{dateModified};
- $params->{view_policy} = $params->{fields}->{policy}->{view};
- $params->{edit_policy} = $params->{fields}->{policy}->{edit};
- $params->{join_policy} = $params->{fields}->{policy}->{join};
- $params->{members_raw} = $params->{attachments}->{members}->{members};
+ $params->{name} = $params->{fields}->{name};
+ $params->{description} = $params->{fields}->{description};
+ $params->{creation_ts} = $params->{fields}->{dateCreated};
+ $params->{modification_ts} = $params->{fields}->{dateModified};
+ $params->{view_policy} = $params->{fields}->{policy}->{view};
+ $params->{edit_policy} = $params->{fields}->{policy}->{edit};
+ $params->{join_policy} = $params->{fields}->{policy}->{join};
+ $params->{members_raw} = $params->{attachments}->{members}->{members};
- delete $params->{fields};
- delete $params->{attachments};
+ delete $params->{fields};
+ delete $params->{attachments};
- return $params;
+ return $params;
}
# {
@@ -146,131 +144,106 @@ sub BUILDARGS {
#########################
sub create {
- state $check = compile(
- $Invocant | ClassName,
- Dict[
- name => Str,
- description => Str,
- view_policy => Str,
- edit_policy => Str,
- join_policy => Str,
- ]
- );
- my ( $class, $params ) = $check->(@_);
-
- my $name = trim($params->{name});
- my $description = $params->{description};
- my $view_policy = $params->{view_policy};
- my $edit_policy = $params->{edit_policy};
- my $join_policy = $params->{join_policy};
-
- my $data = {
- transactions => [
- { type => 'name', value => $name },
- { type => 'description', value => $description },
- { type => 'edit', value => $edit_policy },
- { type => 'join', value => $join_policy },
- { type => 'view', value => $view_policy },
- { type => 'icon', value => 'group' },
- { type => 'color', value => 'red' }
- ]
- };
-
- my $result = request( 'project.edit', $data );
-
- return $class->new_from_query(
- { phids => [ $result->{result}{object}{phid} ] } );
+ state $check = compile(
+ $Invocant | ClassName,
+ Dict [
+ name => Str,
+ description => Str,
+ view_policy => Str,
+ edit_policy => Str,
+ join_policy => Str,
+ ]
+ );
+ my ($class, $params) = $check->(@_);
+
+ my $name = trim($params->{name});
+ my $description = $params->{description};
+ my $view_policy = $params->{view_policy};
+ my $edit_policy = $params->{edit_policy};
+ my $join_policy = $params->{join_policy};
+
+ my $data = {
+ transactions => [
+ {type => 'name', value => $name},
+ {type => 'description', value => $description},
+ {type => 'edit', value => $edit_policy},
+ {type => 'join', value => $join_policy},
+ {type => 'view', value => $view_policy},
+ {type => 'icon', value => 'group'},
+ {type => 'color', value => 'red'}
+ ]
+ };
+
+ my $result = request('project.edit', $data);
+
+ return $class->new_from_query({phids => [$result->{result}{object}{phid}]});
}
sub update {
- my ($self) = @_;
-
- my $data = {
- objectIdentifier => $self->phid,
- transactions => []
- };
-
- if ( $self->{set_name} ) {
- push(
- @{ $data->{transactions} },
- {
- type => 'name',
- value => $self->{set_name}
- }
- );
- }
+ my ($self) = @_;
- if ( $self->{set_description} ) {
- push(
- @{ $data->{transactions} },
- {
- type => 'description',
- value => $self->{set_description}
- }
- );
- }
+ my $data = {objectIdentifier => $self->phid, transactions => []};
- if ( $self->{set_members} ) {
- push(
- @{ $data->{transactions} },
- {
- type => 'members.set',
- value => $self->{set_members}
- }
- );
- }
- else {
- if ( $self->{add_members} ) {
- push(
- @{ $data->{transactions} },
- {
- type => 'members.add',
- value => $self->{add_members}
- }
- );
- }
-
- if ( $self->{remove_members} ) {
- push(
- @{ $data->{transactions} },
- {
- type => 'members.remove',
- value => $self->{remove_members}
- }
- );
- }
- }
+ if ($self->{set_name}) {
+ push(@{$data->{transactions}}, {type => 'name', value => $self->{set_name}});
+ }
- if ( $self->{set_policy} ) {
- foreach my $name ( "view", "edit" ) {
- next unless $self->{set_policy}->{$name};
- push(
- @{ $data->{transactions} },
- {
- type => $name,
- value => $self->{set_policy}->{$name}
- }
- );
- }
- }
+ if ($self->{set_description}) {
+ push(
+ @{$data->{transactions}},
+ {type => 'description', value => $self->{set_description}}
+ );
+ }
- if ($self->{add_projects}) {
- push(@{ $data->{transactions} }, {
- type => 'projects.add',
- value => $self->{add_projects}
- });
+ if ($self->{set_members}) {
+ push(
+ @{$data->{transactions}},
+ {type => 'members.set', value => $self->{set_members}}
+ );
+ }
+ else {
+ if ($self->{add_members}) {
+ push(
+ @{$data->{transactions}},
+ {type => 'members.add', value => $self->{add_members}}
+ );
}
- if ($self->{remove_projects}) {
- push(@{ $data->{transactions} }, {
- type => 'projects.remove',
- value => $self->{remove_projects}
- });
+ if ($self->{remove_members}) {
+ push(
+ @{$data->{transactions}},
+ {type => 'members.remove', value => $self->{remove_members}}
+ );
}
+ }
+
+ if ($self->{set_policy}) {
+ foreach my $name ("view", "edit") {
+ next unless $self->{set_policy}->{$name};
+ push(
+ @{$data->{transactions}},
+ {type => $name, value => $self->{set_policy}->{$name}}
+ );
+ }
+ }
- my $result = request( 'project.edit', $data );
+ if ($self->{add_projects}) {
+ push(
+ @{$data->{transactions}},
+ {type => 'projects.add', value => $self->{add_projects}}
+ );
+ }
- return $result;
+ if ($self->{remove_projects}) {
+ push(
+ @{$data->{transactions}},
+ {type => 'projects.remove', value => $self->{remove_projects}}
+ );
+ }
+
+ my $result = request('project.edit', $data);
+
+ return $result;
}
#########################
@@ -278,40 +251,40 @@ sub update {
#########################
sub set_name {
- my ( $self, $name ) = @_;
- $name = trim($name);
- $self->{set_name} = $name;
+ my ($self, $name) = @_;
+ $name = trim($name);
+ $self->{set_name} = $name;
}
sub set_description {
- my ( $self, $description ) = @_;
- $description = trim($description);
- $self->{set_description} = $description;
+ my ($self, $description) = @_;
+ $description = trim($description);
+ $self->{set_description} = $description;
}
sub add_member {
- my ( $self, $member ) = @_;
- $self->{add_members} ||= [];
- my $member_phid = blessed $member ? $member->phid : $member;
- push( @{ $self->{add_members} }, $member_phid );
+ my ($self, $member) = @_;
+ $self->{add_members} ||= [];
+ my $member_phid = blessed $member ? $member->phid : $member;
+ push(@{$self->{add_members}}, $member_phid);
}
sub remove_member {
- my ( $self, $member ) = @_;
- $self->{remove_members} ||= [];
- my $member_phid = blessed $member ? $member->phid : $member;
- push( @{ $self->{remove_members} }, $member_phid );
+ my ($self, $member) = @_;
+ $self->{remove_members} ||= [];
+ my $member_phid = blessed $member ? $member->phid : $member;
+ push(@{$self->{remove_members}}, $member_phid);
}
sub set_members {
- my ( $self, $members ) = @_;
- $self->{set_members} = [ map { blessed $_ ? $_->phid : $_ } @$members ];
+ my ($self, $members) = @_;
+ $self->{set_members} = [map { blessed $_ ? $_->phid : $_ } @$members];
}
sub set_policy {
- my ( $self, $name, $policy ) = @_;
- $self->{set_policy} ||= {};
- $self->{set_policy}->{$name} = $policy;
+ my ($self, $name, $policy) = @_;
+ $self->{set_policy} ||= {};
+ $self->{set_policy}->{$name} = $policy;
}
############
@@ -319,21 +292,17 @@ sub set_policy {
############
sub _build_members {
- my ( $self ) = @_;
- return [] unless $self->members_raw;
+ my ($self) = @_;
+ return [] unless $self->members_raw;
- my @phids;
- foreach my $member ( @{ $self->members_raw } ) {
- push( @phids, $member->{phid} );
- }
+ my @phids;
+ foreach my $member (@{$self->members_raw}) {
+ push(@phids, $member->{phid});
+ }
- return [] if !@phids;
+ return [] if !@phids;
- return Bugzilla::Extension::PhabBugz::User->match(
- {
- phids => \@phids
- }
- );
+ return Bugzilla::Extension::PhabBugz::User->match({phids => \@phids});
}
-1; \ No newline at end of file
+1;
diff --git a/extensions/PhabBugz/lib/Revision.pm b/extensions/PhabBugz/lib/Revision.pm
index 6ad906829..d529c581d 100644
--- a/extensions/PhabBugz/lib/Revision.pm
+++ b/extensions/PhabBugz/lib/Revision.pm
@@ -27,100 +27,93 @@ use Bugzilla::Extension::PhabBugz::Util qw(request);
# Initialization #
#########################
-has id => ( is => 'ro', isa => Int );
-has phid => ( is => 'ro', isa => Str );
-has title => ( is => 'ro', isa => Str );
-has summary => ( is => 'ro', isa => Str );
-has status => ( is => 'ro', isa => Str );
-has creation_ts => ( is => 'ro', isa => Str );
-has modification_ts => ( is => 'ro', isa => Str );
-has author_phid => ( is => 'ro', isa => Str );
-has bug_id => ( is => 'ro', isa => Str );
-has view_policy => ( is => 'ro', isa => Str );
-has edit_policy => ( is => 'ro', isa => Str );
-has subscriber_count => ( is => 'ro', isa => Int );
-has bug => ( is => 'lazy', isa => Object );
-has author => ( is => 'lazy', isa => Object );
-has reviews => ( is => 'lazy', isa => ArrayRef [ Dict [ user => PhabUser, status => Str ] ] );
-has subscribers => ( is => 'lazy', isa => ArrayRef [PhabUser] );
-has projects => ( is => 'lazy', isa => ArrayRef [Project] );
+has id => (is => 'ro', isa => Int);
+has phid => (is => 'ro', isa => Str);
+has title => (is => 'ro', isa => Str);
+has summary => (is => 'ro', isa => Str);
+has status => (is => 'ro', isa => Str);
+has creation_ts => (is => 'ro', isa => Str);
+has modification_ts => (is => 'ro', isa => Str);
+has author_phid => (is => 'ro', isa => Str);
+has bug_id => (is => 'ro', isa => Str);
+has view_policy => (is => 'ro', isa => Str);
+has edit_policy => (is => 'ro', isa => Str);
+has subscriber_count => (is => 'ro', isa => Int);
+has bug => (is => 'lazy', isa => Object);
+has author => (is => 'lazy', isa => Object);
+has reviews =>
+ (is => 'lazy', isa => ArrayRef [Dict [user => PhabUser, status => Str]]);
+has subscribers => (is => 'lazy', isa => ArrayRef [PhabUser]);
+has projects => (is => 'lazy', isa => ArrayRef [Project]);
has reviewers_raw => (
- is => 'ro',
- isa => ArrayRef [
- Dict [
- reviewerPHID => Str,
- status => Str,
- isBlocking => Bool | JSONBool,
- actorPHID => Maybe [Str],
- ],
- ]
+ is => 'ro',
+ isa => ArrayRef [
+ Dict [
+ reviewerPHID => Str,
+ status => Str,
+ isBlocking => Bool | JSONBool,
+ actorPHID => Maybe [Str],
+ ],
+ ]
);
has subscribers_raw => (
- is => 'ro',
- isa => Dict [
- subscriberPHIDs => ArrayRef [Str],
- subscriberCount => Int,
- viewerIsSubscribed => Bool | JSONBool,
- ]
-);
-has projects_raw => (
- is => 'ro',
- isa => Dict [
- projectPHIDs => ArrayRef [Str]
- ]
+ is => 'ro',
+ isa => Dict [
+ subscriberPHIDs => ArrayRef [Str],
+ subscriberCount => Int,
+ viewerIsSubscribed => Bool | JSONBool,
+ ]
);
+has projects_raw => (is => 'ro', isa => Dict [projectPHIDs => ArrayRef [Str]]);
sub new_from_query {
- my ( $class, $params ) = @_;
-
- my $data = {
- queryKey => 'all',
- attachments => {
- projects => 1,
- reviewers => 1,
- subscribers => 1
- },
- constraints => $params
- };
-
- my $result = request( 'differential.revision.search', $data );
- if ( exists $result->{result}{data} && @{ $result->{result}{data} } ) {
- $result = $result->{result}{data}[0];
-
- # Some values in Phabricator for bug ids may have been saved
- # white whitespace so we remove any here just in case.
- $result->{fields}->{'bugzilla.bug-id'} =
- $result->{fields}->{'bugzilla.bug-id'}
- ? trim( $result->{fields}->{'bugzilla.bug-id'} )
- : "";
- return $class->new($result);
- }
-
- return undef;
+ my ($class, $params) = @_;
+
+ my $data = {
+ queryKey => 'all',
+ attachments => {projects => 1, reviewers => 1, subscribers => 1},
+ constraints => $params
+ };
+
+ my $result = request('differential.revision.search', $data);
+ if (exists $result->{result}{data} && @{$result->{result}{data}}) {
+ $result = $result->{result}{data}[0];
+
+ # Some values in Phabricator for bug ids may have been saved
+ # white whitespace so we remove any here just in case.
+ $result->{fields}->{'bugzilla.bug-id'}
+ = $result->{fields}->{'bugzilla.bug-id'}
+ ? trim($result->{fields}->{'bugzilla.bug-id'})
+ : "";
+ return $class->new($result);
+ }
+
+ return undef;
}
sub BUILDARGS {
- my ( $class, $params ) = @_;
-
- $params->{title} = $params->{fields}->{title};
- $params->{summary} = $params->{fields}->{summary};
- $params->{status} = $params->{fields}->{status}->{value};
- $params->{creation_ts} = $params->{fields}->{dateCreated};
- $params->{modification_ts} = $params->{fields}->{dateModified};
- $params->{author_phid} = $params->{fields}->{authorPHID};
- $params->{bug_id} = $params->{fields}->{'bugzilla.bug-id'};
- $params->{view_policy} = $params->{fields}->{policy}->{view};
- $params->{edit_policy} = $params->{fields}->{policy}->{edit};
- $params->{reviewers_raw} = $params->{attachments}->{reviewers}->{reviewers} // [];
- $params->{subscribers_raw} = $params->{attachments}->{subscribers};
- $params->{projects_raw} = $params->{attachments}->{projects};
- $params->{subscriber_count} =
- $params->{attachments}->{subscribers}->{subscriberCount};
-
- delete $params->{fields};
- delete $params->{attachments};
-
- return $params;
+ my ($class, $params) = @_;
+
+ $params->{title} = $params->{fields}->{title};
+ $params->{summary} = $params->{fields}->{summary};
+ $params->{status} = $params->{fields}->{status}->{value};
+ $params->{creation_ts} = $params->{fields}->{dateCreated};
+ $params->{modification_ts} = $params->{fields}->{dateModified};
+ $params->{author_phid} = $params->{fields}->{authorPHID};
+ $params->{bug_id} = $params->{fields}->{'bugzilla.bug-id'};
+ $params->{view_policy} = $params->{fields}->{policy}->{view};
+ $params->{edit_policy} = $params->{fields}->{policy}->{edit};
+ $params->{reviewers_raw} = $params->{attachments}->{reviewers}->{reviewers}
+ // [];
+ $params->{subscribers_raw} = $params->{attachments}->{subscribers};
+ $params->{projects_raw} = $params->{attachments}->{projects};
+ $params->{subscriber_count}
+ = $params->{attachments}->{subscribers}->{subscriberCount};
+
+ delete $params->{fields};
+ delete $params->{attachments};
+
+ return $params;
}
# {
@@ -185,99 +178,71 @@ sub BUILDARGS {
#########################
sub update {
- my ($self) = @_;
-
- my $data = {
- objectIdentifier => $self->phid,
- transactions => []
- };
-
- if ( $self->{added_comments} ) {
- foreach my $comment ( @{ $self->{added_comments} } ) {
- push @{ $data->{transactions} },
- {
- type => 'comment',
- value => $comment
- };
- }
- }
-
- if ( $self->{set_subscribers} ) {
- push @{ $data->{transactions} },
- {
- type => 'subscribers.set',
- value => $self->{set_subscribers}
- };
- }
-
- if ( $self->{add_subscribers} ) {
- push @{ $data->{transactions} },
- {
- type => 'subscribers.add',
- value => $self->{add_subscribers}
- };
- }
+ my ($self) = @_;
- if ( $self->{remove_subscribers} ) {
- push @{ $data->{transactions} },
- {
- type => 'subscribers.remove',
- value => $self->{remove_subscribers}
- };
- }
-
- if ( $self->{set_reviewers} ) {
- push @{ $data->{transactions} },
- {
- type => 'reviewers.set',
- value => $self->{set_reviewers}
- };
- }
-
- if ( $self->{add_reviewers} ) {
- push @{ $data->{transactions} },
- {
- type => 'reviewers.add',
- value => $self->{add_reviewers}
- };
- }
+ my $data = {objectIdentifier => $self->phid, transactions => []};
- if ( $self->{remove_reviewers} ) {
- push @{ $data->{transactions} },
- {
- type => 'reviewers.remove',
- value => $self->{remove_reviewers}
- };
+ if ($self->{added_comments}) {
+ foreach my $comment (@{$self->{added_comments}}) {
+ push @{$data->{transactions}}, {type => 'comment', value => $comment};
}
-
- if ( $self->{set_policy} ) {
- foreach my $name ( "view", "edit" ) {
- next unless $self->{set_policy}->{$name};
- push @{ $data->{transactions} },
- {
- type => $name,
- value => $self->{set_policy}->{$name}
- };
- }
+ }
+
+ if ($self->{set_subscribers}) {
+ push @{$data->{transactions}},
+ {type => 'subscribers.set', value => $self->{set_subscribers}};
+ }
+
+ if ($self->{add_subscribers}) {
+ push @{$data->{transactions}},
+ {type => 'subscribers.add', value => $self->{add_subscribers}};
+ }
+
+ if ($self->{remove_subscribers}) {
+ push @{$data->{transactions}},
+ {type => 'subscribers.remove', value => $self->{remove_subscribers}};
+ }
+
+ if ($self->{set_reviewers}) {
+ push @{$data->{transactions}},
+ {type => 'reviewers.set', value => $self->{set_reviewers}};
+ }
+
+ if ($self->{add_reviewers}) {
+ push @{$data->{transactions}},
+ {type => 'reviewers.add', value => $self->{add_reviewers}};
+ }
+
+ if ($self->{remove_reviewers}) {
+ push @{$data->{transactions}},
+ {type => 'reviewers.remove', value => $self->{remove_reviewers}};
+ }
+
+ if ($self->{set_policy}) {
+ foreach my $name ("view", "edit") {
+ next unless $self->{set_policy}->{$name};
+ push @{$data->{transactions}},
+ {type => $name, value => $self->{set_policy}->{$name}};
}
+ }
- if ($self->{add_projects}) {
- push(@{ $data->{transactions} }, {
- type => 'projects.add',
- value => $self->{add_projects}
- });
- }
+ if ($self->{add_projects}) {
+ push(
+ @{$data->{transactions}},
+ {type => 'projects.add', value => $self->{add_projects}}
+ );
+ }
- if ($self->{remove_projects}) {
- push(@{ $data->{transactions} }, {
- type => 'projects.remove',
- value => $self->{remove_projects}
- });
- }
+ if ($self->{remove_projects}) {
+ push(
+ @{$data->{transactions}},
+ {type => 'projects.remove', value => $self->{remove_projects}}
+ );
+ }
- my $result = request( 'differential.revision.edit', $data );
+ my $result = request('differential.revision.edit', $data);
- return $result;
+ return $result;
}
#########################
@@ -285,80 +250,61 @@ sub update {
#########################
sub _build_bug {
- my ($self) = @_;
- return $self->{bug} ||=
- Bugzilla::Bug->new( { id => $self->bug_id, cache => 1 } );
+ my ($self) = @_;
+ return $self->{bug} ||= Bugzilla::Bug->new({id => $self->bug_id, cache => 1});
}
sub _build_author {
- my ($self) = @_;
- return $self->{author} if $self->{author};
- my $phab_user = Bugzilla::Extension::PhabBugz::User->new_from_query(
- {
- phids => [ $self->author_phid ]
- }
- );
- if ($phab_user) {
- return $self->{author} = $phab_user;
- }
+ my ($self) = @_;
+ return $self->{author} if $self->{author};
+ my $phab_user
+ = Bugzilla::Extension::PhabBugz::User->new_from_query({
+ phids => [$self->author_phid]
+ });
+ if ($phab_user) {
+ return $self->{author} = $phab_user;
+ }
}
sub _build_reviews {
- my ($self) = @_;
+ my ($self) = @_;
- my %by_phid = map { $_->{reviewerPHID} => $_ } @{ $self->reviewers_raw };
- my $users = Bugzilla::Extension::PhabBugz::User->match(
- {
- phids => [keys %by_phid]
- }
- );
+ my %by_phid = map { $_->{reviewerPHID} => $_ } @{$self->reviewers_raw};
+ my $users
+ = Bugzilla::Extension::PhabBugz::User->match({phids => [keys %by_phid]});
- return [
- map {
- {
- user => $_,
- status => $by_phid{ $_->phid }{status},
- }
- } @$users
- ];
+ return [map { {user => $_, status => $by_phid{$_->phid}{status},} } @$users];
}
sub _build_subscribers {
- my ($self) = @_;
+ my ($self) = @_;
- return $self->{subscribers} if $self->{subscribers};
- return [] unless $self->subscribers_raw->{subscriberPHIDs};
+ return $self->{subscribers} if $self->{subscribers};
+ return [] unless $self->subscribers_raw->{subscriberPHIDs};
- my @phids;
- foreach my $phid ( @{ $self->subscribers_raw->{subscriberPHIDs} } ) {
- push @phids, $phid;
- }
+ my @phids;
+ foreach my $phid (@{$self->subscribers_raw->{subscriberPHIDs}}) {
+ push @phids, $phid;
+ }
- my $users = Bugzilla::Extension::PhabBugz::User->match(
- {
- phids => \@phids
- }
- );
+ my $users = Bugzilla::Extension::PhabBugz::User->match({phids => \@phids});
- return $self->{subscribers} = $users;
+ return $self->{subscribers} = $users;
}
sub _build_projects {
- my ($self) = @_;
-
- return $self->{projects} if $self->{projects};
- return [] unless $self->projects_raw->{projectPHIDs};
-
- my @projects;
- foreach my $phid ( @{ $self->projects_raw->{projectPHIDs} } ) {
- push @projects, Bugzilla::Extension::PhabBugz::Project->new_from_query(
- {
- phids => [ $phid ]
- }
- );
- }
+ my ($self) = @_;
- return $self->{projects} = \@projects;
+ return $self->{projects} if $self->{projects};
+ return [] unless $self->projects_raw->{projectPHIDs};
+
+ my @projects;
+ foreach my $phid (@{$self->projects_raw->{projectPHIDs}}) {
+ push @projects,
+ Bugzilla::Extension::PhabBugz::Project->new_from_query({phids => [$phid]});
+ }
+
+ return $self->{projects} = \@projects;
}
#########################
@@ -366,124 +312,116 @@ sub _build_projects {
#########################
sub add_comment {
- my ( $self, $comment ) = @_;
- $comment = trim($comment);
- $self->{added_comments} ||= [];
- push @{ $self->{added_comments} }, $comment;
+ my ($self, $comment) = @_;
+ $comment = trim($comment);
+ $self->{added_comments} ||= [];
+ push @{$self->{added_comments}}, $comment;
}
sub add_reviewer {
- my ( $self, $reviewer ) = @_;
- $self->{add_reviewers} ||= [];
- my $reviewer_phid = blessed $reviewer ? $reviewer->phid : $reviewer;
- push @{ $self->{add_reviewers} }, $reviewer_phid;
+ my ($self, $reviewer) = @_;
+ $self->{add_reviewers} ||= [];
+ my $reviewer_phid = blessed $reviewer ? $reviewer->phid : $reviewer;
+ push @{$self->{add_reviewers}}, $reviewer_phid;
}
sub remove_reviewer {
- my ( $self, $reviewer ) = @_;
- $self->{remove_reviewers} ||= [];
- my $reviewer_phid = blessed $reviewer ? $reviewer->phid : $reviewer;
- push @{ $self->{remove_reviewers} }, $reviewer_phid;
+ my ($self, $reviewer) = @_;
+ $self->{remove_reviewers} ||= [];
+ my $reviewer_phid = blessed $reviewer ? $reviewer->phid : $reviewer;
+ push @{$self->{remove_reviewers}}, $reviewer_phid;
}
sub set_reviewers {
- my ( $self, $reviewers ) = @_;
- $self->{set_reviewers} = [ map { $_->phid } @$reviewers ];
+ my ($self, $reviewers) = @_;
+ $self->{set_reviewers} = [map { $_->phid } @$reviewers];
}
sub add_subscriber {
- my ( $self, $subscriber ) = @_;
- $self->{add_subscribers} ||= [];
- my $subscriber_phid =
- blessed $subscriber ? $subscriber->phid : $subscriber;
- push @{ $self->{add_subscribers} }, $subscriber_phid;
+ my ($self, $subscriber) = @_;
+ $self->{add_subscribers} ||= [];
+ my $subscriber_phid = blessed $subscriber ? $subscriber->phid : $subscriber;
+ push @{$self->{add_subscribers}}, $subscriber_phid;
}
sub remove_subscriber {
- my ( $self, $subscriber ) = @_;
- $self->{remove_subscribers} ||= [];
- my $subscriber_phid =
- blessed $subscriber ? $subscriber->phid : $subscriber;
- push @{ $self->{remove_subscribers} }, $subscriber_phid;
+ my ($self, $subscriber) = @_;
+ $self->{remove_subscribers} ||= [];
+ my $subscriber_phid = blessed $subscriber ? $subscriber->phid : $subscriber;
+ push @{$self->{remove_subscribers}}, $subscriber_phid;
}
sub set_subscribers {
- my ( $self, $subscribers ) = @_;
- $self->{set_subscribers} = $subscribers;
+ my ($self, $subscribers) = @_;
+ $self->{set_subscribers} = $subscribers;
}
sub set_policy {
- my ( $self, $name, $policy ) = @_;
- $self->{set_policy} ||= {};
- $self->{set_policy}->{$name} = $policy;
+ my ($self, $name, $policy) = @_;
+ $self->{set_policy} ||= {};
+ $self->{set_policy}->{$name} = $policy;
}
sub add_project {
- my ( $self, $project ) = @_;
- $self->{add_projects} ||= [];
- my $project_phid = blessed $project ? $project->phid : $project;
- return undef unless $project_phid;
- push @{ $self->{add_projects} }, $project_phid;
+ my ($self, $project) = @_;
+ $self->{add_projects} ||= [];
+ my $project_phid = blessed $project ? $project->phid : $project;
+ return undef unless $project_phid;
+ push @{$self->{add_projects}}, $project_phid;
}
sub remove_project {
- my ( $self, $project ) = @_;
- $self->{remove_projects} ||= [];
- my $project_phid = blessed $project ? $project->phid : $project;
- return undef unless $project_phid;
- push @{ $self->{remove_projects} }, $project_phid;
+ my ($self, $project) = @_;
+ $self->{remove_projects} ||= [];
+ my $project_phid = blessed $project ? $project->phid : $project;
+ return undef unless $project_phid;
+ push @{$self->{remove_projects}}, $project_phid;
}
sub make_private {
- my ( $self, $project_names ) = @_;
-
- my $secure_revision_project =
- Bugzilla::Extension::PhabBugz::Project->new_from_query(
- {
- name => 'secure-revision'
- }
- );
-
- my @set_projects;
- foreach my $name (@$project_names) {
- my $set_project =
- Bugzilla::Extension::PhabBugz::Project->new_from_query(
- {
- name => $name
- }
- );
- push @set_projects, $set_project;
- }
+ my ($self, $project_names) = @_;
- my $new_policy = Bugzilla::Extension::PhabBugz::Policy->create(\@set_projects);
- $self->set_policy('view', $new_policy->phid);
- $self->set_policy('edit', $new_policy->phid);
+ my $secure_revision_project
+ = Bugzilla::Extension::PhabBugz::Project->new_from_query({
+ name => 'secure-revision'
+ });
- foreach my $project ($secure_revision_project, @set_projects) {
- $self->add_project($project->phid);
- }
+ my @set_projects;
+ foreach my $name (@$project_names) {
+ my $set_project
+ = Bugzilla::Extension::PhabBugz::Project->new_from_query({name => $name});
+ push @set_projects, $set_project;
+ }
- return $self;
+ my $new_policy = Bugzilla::Extension::PhabBugz::Policy->create(\@set_projects);
+ $self->set_policy('view', $new_policy->phid);
+ $self->set_policy('edit', $new_policy->phid);
+
+ foreach my $project ($secure_revision_project, @set_projects) {
+ $self->add_project($project->phid);
+ }
+
+ return $self;
}
sub make_public {
- my ( $self ) = @_;
+ my ($self) = @_;
- my $editbugs = Bugzilla::Extension::PhabBugz::Project->new_from_query(
- {
- name => 'bmo-editbugs-team'
- }
- );
+ my $editbugs
+ = Bugzilla::Extension::PhabBugz::Project->new_from_query({
+ name => 'bmo-editbugs-team'
+ });
- $self->set_policy( 'view', 'public' );
- $self->set_policy( 'edit', ( $editbugs ? $editbugs->phid : 'users' ) );
+ $self->set_policy('view', 'public');
+ $self->set_policy('edit', ($editbugs ? $editbugs->phid : 'users'));
- my @current_group_projects = grep { $_->name =~ /^(bmo-.*|secure-revision)$/ } @{ $self->projects };
- foreach my $project (@current_group_projects) {
- $self->remove_project($project->phid);
- }
+ my @current_group_projects
+ = grep { $_->name =~ /^(bmo-.*|secure-revision)$/ } @{$self->projects};
+ foreach my $project (@current_group_projects) {
+ $self->remove_project($project->phid);
+ }
- return $self;
+ return $self;
}
1;
diff --git a/extensions/PhabBugz/lib/Types.pm b/extensions/PhabBugz/lib/Types.pm
index 493e97fbc..267b8c26a 100644
--- a/extensions/PhabBugz/lib/Types.pm
+++ b/extensions/PhabBugz/lib/Types.pm
@@ -11,18 +11,15 @@ use 5.10.1;
use strict;
use warnings;
-use Type::Library
- -base,
- -declare => qw( Revision LinkedPhabUser PhabUser Policy Project );
+use Type::Library -base,
+ -declare => qw( Revision LinkedPhabUser PhabUser Policy Project );
use Type::Utils -all;
use Types::Standard -all;
-class_type Revision, { class => 'Bugzilla::Extension::PhabBugz::Revision' };
-class_type Policy, { class => 'Bugzilla::Extension::PhabBugz::Policy' };
-class_type Project, { class => 'Bugzilla::Extension::PhabBugz::Project' };
-class_type PhabUser, { class => 'Bugzilla::Extension::PhabBugz::User' };
-declare LinkedPhabUser,
- as PhabUser,
- where { is_Int($_->bugzilla_id) };
+class_type Revision, {class => 'Bugzilla::Extension::PhabBugz::Revision'};
+class_type Policy, {class => 'Bugzilla::Extension::PhabBugz::Policy'};
+class_type Project, {class => 'Bugzilla::Extension::PhabBugz::Project'};
+class_type PhabUser, {class => 'Bugzilla::Extension::PhabBugz::User'};
+declare LinkedPhabUser, as PhabUser, where { is_Int($_->bugzilla_id) };
1;
diff --git a/extensions/PhabBugz/lib/User.pm b/extensions/PhabBugz/lib/User.pm
index 209425bdf..2bc2ed7dc 100644
--- a/extensions/PhabBugz/lib/User.pm
+++ b/extensions/PhabBugz/lib/User.pm
@@ -23,44 +23,44 @@ use Type::Params qw(compile);
# Initialization #
#########################
-has 'id' => ( is => 'ro', isa => Int );
-has 'type' => ( is => 'ro', isa => Str );
-has 'phid' => ( is => 'ro', isa => Str );
-has 'name' => ( is => 'ro', isa => Str );
-has 'realname' => ( is => 'ro', isa => Str );
-has 'creation_ts' => ( is => 'ro', isa => Int );
-has 'modification_ts' => ( is => 'ro', isa => Int );
-has 'roles' => ( is => 'ro', isa => ArrayRef [Str] );
-has 'view_policy' => ( is => 'ro', isa => Str );
-has 'edit_policy' => ( is => 'ro', isa => Str );
-has 'bugzilla_id' => ( is => 'ro', isa => Maybe [Int] );
-has 'bugzilla_user' => ( is => 'lazy', isa => Maybe [User] );
-
-my $Invocant = class_type { class => __PACKAGE__ };
+has 'id' => (is => 'ro', isa => Int);
+has 'type' => (is => 'ro', isa => Str);
+has 'phid' => (is => 'ro', isa => Str);
+has 'name' => (is => 'ro', isa => Str);
+has 'realname' => (is => 'ro', isa => Str);
+has 'creation_ts' => (is => 'ro', isa => Int);
+has 'modification_ts' => (is => 'ro', isa => Int);
+has 'roles' => (is => 'ro', isa => ArrayRef [Str]);
+has 'view_policy' => (is => 'ro', isa => Str);
+has 'edit_policy' => (is => 'ro', isa => Str);
+has 'bugzilla_id' => (is => 'ro', isa => Maybe [Int]);
+has 'bugzilla_user' => (is => 'lazy', isa => Maybe [User]);
+
+my $Invocant = class_type {class => __PACKAGE__};
sub BUILDARGS {
- my ( $class, $params ) = @_;
-
- $params->{name} = $params->{fields}->{username};
- $params->{realname} = $params->{fields}->{realName};
- $params->{creation_ts} = $params->{fields}->{dateCreated};
- $params->{modification_ts} = $params->{fields}->{dateModified};
- $params->{roles} = $params->{fields}->{roles};
- $params->{view_policy} = $params->{fields}->{policy}->{view};
- $params->{edit_policy} = $params->{fields}->{policy}->{edit};
-
- delete $params->{fields};
-
- my $external_accounts =
- $params->{attachments}{'external-accounts'}{'external-accounts'};
- if ($external_accounts) {
- my $bug_user = first { $_->{type} eq 'bmo' } @$external_accounts;
- $params->{bugzilla_id} = $bug_user->{id};
- }
+ my ($class, $params) = @_;
+
+ $params->{name} = $params->{fields}->{username};
+ $params->{realname} = $params->{fields}->{realName};
+ $params->{creation_ts} = $params->{fields}->{dateCreated};
+ $params->{modification_ts} = $params->{fields}->{dateModified};
+ $params->{roles} = $params->{fields}->{roles};
+ $params->{view_policy} = $params->{fields}->{policy}->{view};
+ $params->{edit_policy} = $params->{fields}->{policy}->{edit};
+
+ delete $params->{fields};
- delete $params->{attachments};
+ my $external_accounts
+ = $params->{attachments}{'external-accounts'}{'external-accounts'};
+ if ($external_accounts) {
+ my $bug_user = first { $_->{type} eq 'bmo' } @$external_accounts;
+ $params->{bugzilla_id} = $bug_user->{id};
+ }
- return $params;
+ delete $params->{attachments};
+
+ return $params;
}
# {
@@ -110,45 +110,45 @@ sub BUILDARGS {
# }
sub new_from_query {
- my ( $class, $params ) = @_;
- my $matches = $class->match($params);
- return $matches->[0];
+ my ($class, $params) = @_;
+ my $matches = $class->match($params);
+ return $matches->[0];
}
sub match {
- state $check = compile( $Invocant | ClassName, Dict[ ids => ArrayRef[Int] ] | Dict[ phids => ArrayRef[Str] ] );
- my ( $class, $params ) = $check->(@_);
-
- # BMO id search takes precedence if bugzilla_ids is used.
- my $bugzilla_ids = delete $params->{ids};
- if ($bugzilla_ids) {
- my $bugzilla_data =
- $class->get_phab_bugzilla_ids( { ids => $bugzilla_ids } );
- $params->{phids} = [ map { $_->{phid} } @$bugzilla_data ];
+ state $check = compile($Invocant | ClassName,
+ Dict [ids => ArrayRef [Int]] | Dict [phids => ArrayRef [Str]]);
+ my ($class, $params) = $check->(@_);
+
+ # BMO id search takes precedence if bugzilla_ids is used.
+ my $bugzilla_ids = delete $params->{ids};
+ if ($bugzilla_ids) {
+ my $bugzilla_data = $class->get_phab_bugzilla_ids({ids => $bugzilla_ids});
+ $params->{phids} = [map { $_->{phid} } @$bugzilla_data];
+ }
+
+ return [] if !@{$params->{phids}};
+
+ # Look for BMO external user id in external-accounts attachment
+ my $data = {
+ constraints => {phids => $params->{phids}},
+ attachments => {'external-accounts' => 1}
+ };
+
+ # We can only fetch 100 users at a time so we need to do this in lumps
+ my $phab_users = [];
+ my $result;
+ do {
+ $result = request('user.search', $data)->{result};
+ if (exists $result->{data} && @{$result->{data}}) {
+ foreach my $user (@{$result->{data}}) {
+ push @$phab_users, $class->new($user);
+ }
}
+ $data->{after} = $result->{cursor}->{after};
+ } while ($result->{cursor}->{after});
- return [] if !@{ $params->{phids} };
-
- # Look for BMO external user id in external-accounts attachment
- my $data = {
- constraints => { phids => $params->{phids} },
- attachments => { 'external-accounts' => 1 }
- };
-
- # We can only fetch 100 users at a time so we need to do this in lumps
- my $phab_users = [];
- my $result;
- do {
- $result = request( 'user.search', $data )->{result};
- if ( exists $result->{data} && @{ $result->{data} } ) {
- foreach my $user ( @{ $result->{data} } ) {
- push @$phab_users, $class->new($user);
- }
- }
- $data->{after} = $result->{cursor}->{after};
- } while ($result->{cursor}->{after});
-
- return $phab_users;
+ return $phab_users;
}
#################
@@ -156,48 +156,44 @@ sub match {
#################
sub _build_bugzilla_user {
- my ($self) = @_;
- return undef unless $self->bugzilla_id;
- return Bugzilla::User->new( { id => $self->bugzilla_id, cache => 1 } );
+ my ($self) = @_;
+ return undef unless $self->bugzilla_id;
+ return Bugzilla::User->new({id => $self->bugzilla_id, cache => 1});
}
sub get_phab_bugzilla_ids {
- state $check = compile($Invocant | ClassName, Dict[ids => ArrayRef[Int]]);
- my ( $class, $params ) = $check->(@_);
-
- my $memcache = Bugzilla->memcached;
-
- # Try to find the values in memcache first
- my @results;
- my %bugzilla_ids = map { $_ => 1 } @{ $params->{ids} };
- foreach my $bugzilla_id ( keys %bugzilla_ids ) {
- my $phid =
- $memcache->get( { key => "phab_user_bugzilla_id_" . $bugzilla_id } );
- if ($phid) {
- push @results, { id => $bugzilla_id, phid => $phid };
- delete $bugzilla_ids{$bugzilla_id};
- }
+ state $check = compile($Invocant | ClassName, Dict [ids => ArrayRef [Int]]);
+ my ($class, $params) = $check->(@_);
+
+ my $memcache = Bugzilla->memcached;
+
+ # Try to find the values in memcache first
+ my @results;
+ my %bugzilla_ids = map { $_ => 1 } @{$params->{ids}};
+ foreach my $bugzilla_id (keys %bugzilla_ids) {
+ my $phid = $memcache->get({key => "phab_user_bugzilla_id_" . $bugzilla_id});
+ if ($phid) {
+ push @results, {id => $bugzilla_id, phid => $phid};
+ delete $bugzilla_ids{$bugzilla_id};
}
+ }
+
+ if (%bugzilla_ids) {
+ $params->{ids} = [keys %bugzilla_ids];
+
+ my $result = request('bugzilla.account.search', $params);
- if (%bugzilla_ids) {
- $params->{ids} = [ keys %bugzilla_ids ];
-
- my $result = request( 'bugzilla.account.search', $params );
-
- # Store new values in memcache for later retrieval
- foreach my $user ( @{ $result->{result} } ) {
- next if !$user->{phid};
- $memcache->set(
- {
- key => "phab_user_bugzilla_id_" . $user->{id},
- value => $user->{phid}
- }
- );
- push @results, $user;
- }
+ # Store new values in memcache for later retrieval
+ foreach my $user (@{$result->{result}}) {
+ next if !$user->{phid};
+ $memcache->set({
+ key => "phab_user_bugzilla_id_" . $user->{id}, value => $user->{phid}
+ });
+ push @results, $user;
}
+ }
- return \@results;
+ return \@results;
}
1;
diff --git a/extensions/PhabBugz/lib/Util.pm b/extensions/PhabBugz/lib/Util.pm
index 32f860413..613fd3466 100644
--- a/extensions/PhabBugz/lib/Util.pm
+++ b/extensions/PhabBugz/lib/Util.pm
@@ -32,167 +32,166 @@ use Mojo::JSON qw(encode_json);
use base qw(Exporter);
our @EXPORT = qw(
- create_revision_attachment
- get_attachment_revisions
- get_bug_role_phids
- intersect
- is_attachment_phab_revision
- request
- set_phab_user
+ create_revision_attachment
+ get_attachment_revisions
+ get_bug_role_phids
+ intersect
+ is_attachment_phab_revision
+ request
+ set_phab_user
);
sub create_revision_attachment {
- state $check = compile(Bug, Revision, Str, User);
- my ( $bug, $revision, $timestamp, $submitter ) = $check->(@_);
-
- my $phab_base_uri = Bugzilla->params->{phabricator_base_uri};
- ThrowUserError('invalid_phabricator_uri') unless $phab_base_uri;
-
- my $revision_uri = $phab_base_uri . "D" . $revision->id;
-
- # Check for previous attachment with same revision id.
- # If one matches then return it instead. This is fine as
- # BMO does not contain actual diff content.
- my @review_attachments = grep { is_attachment_phab_revision($_) } @{ $bug->attachments };
- my $review_attachment = first { trim($_->data) eq $revision_uri } @review_attachments;
- return $review_attachment if defined $review_attachment;
-
- # No attachment is present, so we can now create new one
-
- if (!$timestamp) {
- ($timestamp) = Bugzilla->dbh->selectrow_array("SELECT NOW()");
- }
-
- # If submitter, then switch to that user when creating attachment
- local $submitter->{groups} = [ Bugzilla::Group->get_all ]; # We need to always be able to add attachment
- my $restore_prev_user = Bugzilla->set_user($submitter, scope_guard => 1);
-
- my $attachment = Bugzilla::Attachment->create(
- {
- bug => $bug,
- creation_ts => $timestamp,
- data => $revision_uri,
- description => $revision->title,
- filename => 'phabricator-D' . $revision->id . '-url.txt',
- ispatch => 0,
- isprivate => 0,
- mimetype => PHAB_CONTENT_TYPE,
- }
- );
-
- # Insert a comment about the new attachment into the database.
- $bug->add_comment($revision->summary, { type => CMT_ATTACHMENT_CREATED,
- extra_data => $attachment->id });
-
- delete $bug->{attachments};
-
- return $attachment;
+ state $check = compile(Bug, Revision, Str, User);
+ my ($bug, $revision, $timestamp, $submitter) = $check->(@_);
+
+ my $phab_base_uri = Bugzilla->params->{phabricator_base_uri};
+ ThrowUserError('invalid_phabricator_uri') unless $phab_base_uri;
+
+ my $revision_uri = $phab_base_uri . "D" . $revision->id;
+
+ # Check for previous attachment with same revision id.
+ # If one matches then return it instead. This is fine as
+ # BMO does not contain actual diff content.
+ my @review_attachments
+ = grep { is_attachment_phab_revision($_) } @{$bug->attachments};
+ my $review_attachment
+ = first { trim($_->data) eq $revision_uri } @review_attachments;
+ return $review_attachment if defined $review_attachment;
+
+ # No attachment is present, so we can now create new one
+
+ if (!$timestamp) {
+ ($timestamp) = Bugzilla->dbh->selectrow_array("SELECT NOW()");
+ }
+
+ # If submitter, then switch to that user when creating attachment
+ local $submitter->{groups} = [Bugzilla::Group->get_all]; # We need to always be able to add attachment
+ my $restore_prev_user = Bugzilla->set_user($submitter, scope_guard => 1);
+
+ my $attachment = Bugzilla::Attachment->create({
+ bug => $bug,
+ creation_ts => $timestamp,
+ data => $revision_uri,
+ description => $revision->title,
+ filename => 'phabricator-D' . $revision->id . '-url.txt',
+ ispatch => 0,
+ isprivate => 0,
+ mimetype => PHAB_CONTENT_TYPE,
+ });
+
+ # Insert a comment about the new attachment into the database.
+ $bug->add_comment($revision->summary,
+ {type => CMT_ATTACHMENT_CREATED, extra_data => $attachment->id});
+
+ delete $bug->{attachments};
+
+ return $attachment;
}
sub intersect {
- my ($list1, $list2) = @_;
- my %e = map { $_ => undef } @{$list1};
- return grep { exists( $e{$_} ) } @{$list2};
+ my ($list1, $list2) = @_;
+ my %e = map { $_ => undef } @{$list1};
+ return grep { exists($e{$_}) } @{$list2};
}
sub get_bug_role_phids {
- state $check = compile(Bug);
- my ($bug) = $check->(@_);
-
- my @bug_users = ( $bug->reporter );
- push(@bug_users, $bug->assigned_to)
- if $bug->assigned_to->email != Bugzilla->params->{'nobody_user'};
- push(@bug_users, $bug->qa_contact) if $bug->qa_contact;
- push(@bug_users, @{ $bug->cc_users }) if @{ $bug->cc_users };
-
- my $phab_users =
- Bugzilla::Extension::PhabBugz::User->match(
- {
- ids => [ map { $_->id } @bug_users ]
- }
- );
-
- return [ map { $_->phid } @{ $phab_users } ];
+ state $check = compile(Bug);
+ my ($bug) = $check->(@_);
+
+ my @bug_users = ($bug->reporter);
+ push(@bug_users, $bug->assigned_to)
+ if $bug->assigned_to->email != Bugzilla->params->{'nobody_user'};
+ push(@bug_users, $bug->qa_contact) if $bug->qa_contact;
+ push(@bug_users, @{$bug->cc_users}) if @{$bug->cc_users};
+
+ my $phab_users
+ = Bugzilla::Extension::PhabBugz::User->match({
+ ids => [map { $_->id } @bug_users]
+ });
+
+ return [map { $_->phid } @{$phab_users}];
}
sub is_attachment_phab_revision {
- state $check = compile(Attachment);
- my ($attachment) = $check->(@_);
- return $attachment->contenttype eq PHAB_CONTENT_TYPE;
+ state $check = compile(Attachment);
+ my ($attachment) = $check->(@_);
+ return $attachment->contenttype eq PHAB_CONTENT_TYPE;
}
sub get_attachment_revisions {
- state $check = compile(Bug);
- my ($bug) = $check->(@_);
+ state $check = compile(Bug);
+ my ($bug) = $check->(@_);
- my @attachments =
- grep { is_attachment_phab_revision($_) } @{ $bug->attachments() };
+ my @attachments
+ = grep { is_attachment_phab_revision($_) } @{$bug->attachments()};
- return unless @attachments;
+ return unless @attachments;
- my @revision_ids;
- foreach my $attachment (@attachments) {
- my ($revision_id) =
- ( $attachment->filename =~ PHAB_ATTACHMENT_PATTERN );
- next if !$revision_id;
- push( @revision_ids, int($revision_id) );
- }
+ my @revision_ids;
+ foreach my $attachment (@attachments) {
+ my ($revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN);
+ next if !$revision_id;
+ push(@revision_ids, int($revision_id));
+ }
- return unless @revision_ids;
+ return unless @revision_ids;
- my @revisions;
- foreach my $revision_id (@revision_ids) {
- my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query({
- ids => [ $revision_id ]
- });
- push @revisions, $revision if $revision;
- }
+ my @revisions;
+ foreach my $revision_id (@revision_ids) {
+ my $revision
+ = Bugzilla::Extension::PhabBugz::Revision->new_from_query({
+ ids => [$revision_id]
+ });
+ push @revisions, $revision if $revision;
+ }
- return \@revisions;
+ return \@revisions;
}
sub request {
- state $check = compile(Str, HashRef);
- my ($method, $data) = $check->(@_);
- my $request_cache = Bugzilla->request_cache;
- my $params = Bugzilla->params;
-
- my $ua = $request_cache->{phabricator_ua};
- unless ($ua) {
- $ua = $request_cache->{phabricator_ua} = Mojo::UserAgent->new;
- if ($params->{proxy_url}) {
- $ua->proxy($params->{proxy_url});
- }
+ state $check = compile(Str, HashRef);
+ my ($method, $data) = $check->(@_);
+ my $request_cache = Bugzilla->request_cache;
+ my $params = Bugzilla->params;
+
+ my $ua = $request_cache->{phabricator_ua};
+ unless ($ua) {
+ $ua = $request_cache->{phabricator_ua} = Mojo::UserAgent->new;
+ if ($params->{proxy_url}) {
+ $ua->proxy($params->{proxy_url});
}
+ }
- my $phab_api_key = $params->{phabricator_api_key};
- ThrowUserError('invalid_phabricator_api_key') unless $phab_api_key;
- my $phab_base_uri = $params->{phabricator_base_uri};
- ThrowUserError('invalid_phabricator_uri') unless $phab_base_uri;
+ my $phab_api_key = $params->{phabricator_api_key};
+ ThrowUserError('invalid_phabricator_api_key') unless $phab_api_key;
+ my $phab_base_uri = $params->{phabricator_base_uri};
+ ThrowUserError('invalid_phabricator_uri') unless $phab_base_uri;
- my $full_uri = $phab_base_uri . '/api/' . $method;
+ my $full_uri = $phab_base_uri . '/api/' . $method;
- $data->{__conduit__} = { token => $phab_api_key };
+ $data->{__conduit__} = {token => $phab_api_key};
- my $response = $ua->post($full_uri => form => { params => encode_json($data) })->result;
- ThrowCodeError('phabricator_api_error', { reason => $response->message })
- if $response->is_error;
+ my $response
+ = $ua->post($full_uri => form => {params => encode_json($data)})->result;
+ ThrowCodeError('phabricator_api_error', {reason => $response->message})
+ if $response->is_error;
- my $result = $response->json;
- ThrowCodeError('phabricator_api_error',
- { reason => 'JSON decode failure' }) if !defined($result);
- ThrowCodeError('phabricator_api_error',
- { code => $result->{error_code},
- reason => $result->{error_info} }) if $result->{error_code};
+ my $result = $response->json;
+ ThrowCodeError('phabricator_api_error', {reason => 'JSON decode failure'})
+ if !defined($result);
+ ThrowCodeError('phabricator_api_error',
+ {code => $result->{error_code}, reason => $result->{error_info}})
+ if $result->{error_code};
- return $result;
+ return $result;
}
sub set_phab_user {
- my $user = Bugzilla::User->new( { name => PHAB_AUTOMATION_USER } );
- $user->{groups} = [ Bugzilla::Group->get_all ];
+ my $user = Bugzilla::User->new({name => PHAB_AUTOMATION_USER});
+ $user->{groups} = [Bugzilla::Group->get_all];
- return Bugzilla->set_user($user, scope_guard => 1);
+ return Bugzilla->set_user($user, scope_guard => 1);
}
1;
diff --git a/extensions/PhabBugz/lib/WebService.pm b/extensions/PhabBugz/lib/WebService.pm
index 19a758a70..a9115263a 100644
--- a/extensions/PhabBugz/lib/WebService.pm
+++ b/extensions/PhabBugz/lib/WebService.pm
@@ -26,143 +26,140 @@ use List::MoreUtils qw(any);
use MIME::Base64 qw(decode_base64);
use constant READ_ONLY => qw(
- check_user_enter_bug_permission
- check_user_permission_for_bug
+ check_user_enter_bug_permission
+ check_user_permission_for_bug
);
use constant PUBLIC_METHODS => qw(
- check_user_enter_bug_permission
- check_user_permission_for_bug
- set_build_target
+ check_user_enter_bug_permission
+ check_user_permission_for_bug
+ set_build_target
);
sub _check_phabricator {
- # Ensure PhabBugz is on
- ThrowUserError('phabricator_not_enabled')
- unless Bugzilla->params->{phabricator_enabled};
+
+ # Ensure PhabBugz is on
+ ThrowUserError('phabricator_not_enabled')
+ unless Bugzilla->params->{phabricator_enabled};
}
sub _validate_phab_user {
- my ($self, $user) = @_;
+ my ($self, $user) = @_;
- $self->_check_phabricator();
+ $self->_check_phabricator();
- # Validate that the requesting user's email matches phab-bot
- ThrowUserError('phabricator_unauthorized_user')
- unless $user->login eq PHAB_AUTOMATION_USER;
+ # Validate that the requesting user's email matches phab-bot
+ ThrowUserError('phabricator_unauthorized_user')
+ unless $user->login eq PHAB_AUTOMATION_USER;
}
sub check_user_permission_for_bug {
- my ($self, $params) = @_;
+ my ($self, $params) = @_;
- my $user = Bugzilla->login(LOGIN_REQUIRED);
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
- $self->_validate_phab_user($user);
+ $self->_validate_phab_user($user);
- # Validate that a bug id and user id are provided
- ThrowUserError('phabricator_invalid_request_params')
- unless ($params->{bug_id} && $params->{user_id});
+ # Validate that a bug id and user id are provided
+ ThrowUserError('phabricator_invalid_request_params')
+ unless ($params->{bug_id} && $params->{user_id});
- # Validate that the user exists
- my $target_user = Bugzilla::User->check({ id => $params->{user_id}, cache => 1 });
+ # Validate that the user exists
+ my $target_user = Bugzilla::User->check({id => $params->{user_id}, cache => 1});
- # Send back an object which says { "result": 1|0 }
- return {
- result => $target_user->can_see_bug($params->{bug_id})
- };
+ # Send back an object which says { "result": 1|0 }
+ return {result => $target_user->can_see_bug($params->{bug_id})};
}
sub check_user_enter_bug_permission {
- my ($self, $params) = @_;
+ my ($self, $params) = @_;
- my $user = Bugzilla->login(LOGIN_REQUIRED);
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
- $self->_validate_phab_user($user);
+ $self->_validate_phab_user($user);
- # Validate that a product name and user id are provided
- ThrowUserError('phabricator_invalid_request_params')
- unless ($params->{product} && $params->{user_id});
+ # Validate that a product name and user id are provided
+ ThrowUserError('phabricator_invalid_request_params')
+ unless ($params->{product} && $params->{user_id});
- # Validate that the user exists
- my $target_user = Bugzilla::User->check({ id => $params->{user_id}, cache => 1 });
+ # Validate that the user exists
+ my $target_user = Bugzilla::User->check({id => $params->{user_id}, cache => 1});
- # Send back an object with the attribute "result" set to 1 if the user
- # can enter bugs into the given product, or 0 if not.
- return {
- result => $target_user->can_enter_product($params->{product}) ? 1 : 0
- };
+ # Send back an object with the attribute "result" set to 1 if the user
+ # can enter bugs into the given product, or 0 if not.
+ return {result => $target_user->can_enter_product($params->{product}) ? 1 : 0};
}
sub set_build_target {
- my ( $self, $params ) = @_;
+ my ($self, $params) = @_;
- # Phabricator only supports sending credentials via HTTP Basic Auth
- # so we exploit that function to pass in an API key as the password
- # of basic auth. BMO does not support basic auth but does support
- # use of API keys.
- my $http_auth = Bugzilla->cgi->http('Authorization');
- $http_auth =~ s/^Basic\s+//;
- $http_auth = decode_base64($http_auth);
- my ($login, $api_key) = split(':', $http_auth);
- $params->{'Bugzilla_login'} = $login;
- $params->{'Bugzilla_api_key'} = $api_key;
+ # Phabricator only supports sending credentials via HTTP Basic Auth
+ # so we exploit that function to pass in an API key as the password
+ # of basic auth. BMO does not support basic auth but does support
+ # use of API keys.
+ my $http_auth = Bugzilla->cgi->http('Authorization');
+ $http_auth =~ s/^Basic\s+//;
+ $http_auth = decode_base64($http_auth);
+ my ($login, $api_key) = split(':', $http_auth);
+ $params->{'Bugzilla_login'} = $login;
+ $params->{'Bugzilla_api_key'} = $api_key;
- my $user = Bugzilla->login(LOGIN_REQUIRED);
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
- $self->_validate_phab_user($user);
+ $self->_validate_phab_user($user);
- my $revision_id = $params->{revision_id};
- my $build_target = $params->{build_target};
+ my $revision_id = $params->{revision_id};
+ my $build_target = $params->{build_target};
- ThrowUserError('invalid_phabricator_revision_id')
- unless detaint_natural($revision_id);
+ ThrowUserError('invalid_phabricator_revision_id')
+ unless detaint_natural($revision_id);
- ThrowUserError('invalid_phabricator_build_target')
- unless $build_target =~ /^PHID-HMBT-[a-zA-Z0-9]+$/;
- trick_taint($build_target);
+ ThrowUserError('invalid_phabricator_build_target')
+ unless $build_target =~ /^PHID-HMBT-[a-zA-Z0-9]+$/;
+ trick_taint($build_target);
- Bugzilla->dbh->do(
- "INSERT INTO phabbugz (name, value) VALUES (?, ?)",
- undef,
- 'build_target_' . $revision_id,
- $build_target
- );
+ Bugzilla->dbh->do(
+ "INSERT INTO phabbugz (name, value) VALUES (?, ?)",
+ undef, 'build_target_' . $revision_id,
+ $build_target
+ );
- return { result => 1 };
+ return {result => 1};
}
sub rest_resources {
- return [
- # Set build target in Phabricator
- qr{^/phabbugz/build_target/(\d+)/(PHID-HMBT-.*)$}, {
- POST => {
- method => 'set_build_target',
- params => sub {
- return {
- revision_id => $_[0],
- build_target => $_[1]
- };
- }
- }
- },
- # Bug permission checks
- qr{^/phabbugz/check_bug/(\d+)/(\d+)$}, {
- GET => {
- method => 'check_user_permission_for_bug',
- params => sub {
- return { bug_id => $_[0], user_id => $_[1] };
- }
- }
- },
- qr{^/phabbugz/check_enter_bug/([^/]+)/(\d+)$}, {
- GET => {
- method => 'check_user_enter_bug_permission',
- params => sub {
- return { product => $_[0], user_id => $_[1] };
- },
- },
+ return [
+ # Set build target in Phabricator
+ qr{^/phabbugz/build_target/(\d+)/(PHID-HMBT-.*)$},
+ {
+ POST => {
+ method => 'set_build_target',
+ params => sub {
+ return {revision_id => $_[0], build_target => $_[1]};
+ }
+ }
+ },
+
+ # Bug permission checks
+ qr{^/phabbugz/check_bug/(\d+)/(\d+)$},
+ {
+ GET => {
+ method => 'check_user_permission_for_bug',
+ params => sub {
+ return {bug_id => $_[0], user_id => $_[1]};
+ }
+ }
+ },
+ qr{^/phabbugz/check_enter_bug/([^/]+)/(\d+)$},
+ {
+ GET => {
+ method => 'check_user_enter_bug_permission',
+ params => sub {
+ return {product => $_[0], user_id => $_[1]};
},
- ];
+ },
+ },
+ ];
}
1;
diff --git a/extensions/PhabBugz/t/basic.t b/extensions/PhabBugz/t/basic.t
index af92dc64f..d0083c275 100644
--- a/extensions/PhabBugz/t/basic.t
+++ b/extensions/PhabBugz/t/basic.t
@@ -11,7 +11,7 @@ use 5.10.1;
use lib qw( . lib local/lib/perl5 );
use Bugzilla;
-BEGIN { Bugzilla->extensions };
+BEGIN { Bugzilla->extensions }
use Test::More;
use Test2::Tools::Mock;
@@ -30,68 +30,65 @@ our @project_members;
my $User = mock 'Bugzilla::Extension::PhabBugz::User' => (
- add_constructor => [
- 'fake_new' => 'hash',
- ],
- override => [
- 'match' => sub { [ mock() ] },
- ],
+ add_constructor => ['fake_new' => 'hash',],
+ override => ['match' => sub { [mock()] },],
);
my $Feed = mock 'Bugzilla::Extension::PhabBugz::Feed' => (
- override => [
- get_group_members => sub {
- return [ map { Bugzilla::Extension::PhabBugz::User->fake_new(%$_) } @group_members ];
- }
- ]
+ override => [
+ get_group_members => sub {
+ return [map { Bugzilla::Extension::PhabBugz::User->fake_new(%$_) }
+ @group_members];
+ }
+ ]
);
my $Project = mock 'Bugzilla::Extension::PhabBugz::Project' => (
- override_constructor => [
- new_from_query => 'ref_copy',
- ],
- override => [
- 'members' => sub {
- return [ map { Bugzilla::Extension::PhabBugz::User->fake_new(%$_) } @project_members ];
- }
- ]
+ override_constructor => [new_from_query => 'ref_copy',],
+ override => [
+ 'members' => sub {
+ return [map { Bugzilla::Extension::PhabBugz::User->fake_new(%$_) }
+ @project_members];
+ }
+ ]
);
-local Bugzilla->params->{phabricator_enabled} = 1;
-local Bugzilla->params->{phabricator_api_key} = 'FAKE-API-KEY';
+local Bugzilla->params->{phabricator_enabled} = 1;
+local Bugzilla->params->{phabricator_api_key} = 'FAKE-API-KEY';
local Bugzilla->params->{phabricator_base_uri} = 'http://fake.fabricator.tld';
my $Bugzilla = mock 'Bugzilla' => (
- override => [
- 'dbh' => sub { mock() },
- 'user' => sub { Bugzilla::User->new({ name => 'phab-bot@bmo.tld' }) },
- ],
+ override => [
+ 'dbh' => sub { mock() },
+ 'user' => sub { Bugzilla::User->new({name => 'phab-bot@bmo.tld'}) },
+ ],
);
my $BugzillaGroup = mock 'Bugzilla::Group' => (
- add_constructor => [
- 'fake_new' => 'hash',
- ],
- override => [
- 'match' => sub { [ Bugzilla::Group->fake_new(id => 1, name => 'firefox-security' ) ] },
- ],
+ add_constructor => ['fake_new' => 'hash',],
+ override => [
+ 'match' =>
+ sub { [Bugzilla::Group->fake_new(id => 1, name => 'firefox-security')] },
+ ],
);
my $BugzillaUser = mock 'Bugzilla::User' => (
- add_constructor => [
- 'fake_new' => 'hash',
- ],
- override => [
- 'new' => sub {
- my ($class, $hash) = @_;
- if ($hash->{name} eq 'phab-bot@bmo.tld') {
- return $class->fake_new( id => 8_675_309, login_name => 'phab-bot@bmo.tld', realname => 'Fake PhabBot' );
- }
- else {
- }
- },
- 'match' => sub { [ mock() ] },
- ],
+ add_constructor => ['fake_new' => 'hash',],
+ override => [
+ 'new' => sub {
+ my ($class, $hash) = @_;
+ if ($hash->{name} eq 'phab-bot@bmo.tld') {
+ return $class->fake_new(
+ id => 8_675_309,
+ login_name => 'phab-bot@bmo.tld',
+ realname => 'Fake PhabBot'
+ );
+ }
+ else {
+ }
+ },
+ 'match' => sub { [mock()] },
+ ],
);
@@ -99,78 +96,66 @@ my $feed = Bugzilla::Extension::PhabBugz::Feed->new;
# Same members in both
do {
- my $UserAgent = mock 'Mojo::UserAgent' => (
- override => [
- 'post' => sub {
- my ($self, $url, undef, $params) = @_;
- my $data = decode_json($params->{params});
- is_deeply($data->{transactions}, [], 'no-op');
- return mock_useragent_tx('{}');
- },
- ],
- );
- local @group_members = (
- { phid => 'foo' },
- );
- local @project_members = (
- { phid => 'foo' },
- );
- $feed->group_query;
+ my $UserAgent = mock 'Mojo::UserAgent' => (
+ override => [
+ 'post' => sub {
+ my ($self, $url, undef, $params) = @_;
+ my $data = decode_json($params->{params});
+ is_deeply($data->{transactions}, [], 'no-op');
+ return mock_useragent_tx('{}');
+ },
+ ],
+ );
+ local @group_members = ({phid => 'foo'},);
+ local @project_members = ({phid => 'foo'},);
+ $feed->group_query;
};
# Project has members not in group
do {
- my $UserAgent = mock 'Mojo::UserAgent' => (
- override => [
- 'post' => sub {
- my ($self, $url, undef, $params) = @_;
- my $data = decode_json($params->{params});
- my $expected = [ { type => 'members.remove', value => ['foo'] } ];
- is_deeply($data->{transactions}, $expected, 'remove foo');
- return mock_useragent_tx('{}');
- },
- ]
- );
- local @group_members = ();
- local @project_members = (
- { phid => 'foo' },
- );
- $feed->group_query;
+ my $UserAgent = mock 'Mojo::UserAgent' => (
+ override => [
+ 'post' => sub {
+ my ($self, $url, undef, $params) = @_;
+ my $data = decode_json($params->{params});
+ my $expected = [{type => 'members.remove', value => ['foo']}];
+ is_deeply($data->{transactions}, $expected, 'remove foo');
+ return mock_useragent_tx('{}');
+ },
+ ]
+ );
+ local @group_members = ();
+ local @project_members = ({phid => 'foo'},);
+ $feed->group_query;
};
# Group has members not in project
do {
- my $UserAgent = mock 'Mojo::UserAgent' => (
- override => [
- 'post' => sub {
- my ($self, $url, undef, $params) = @_;
- my $data = decode_json($params->{params});
- my $expected = [ { type => 'members.add', value => ['foo'] } ];
- is_deeply($data->{transactions}, $expected, 'add foo');
- return mock_useragent_tx('{}');
- },
- ]
- );
- local @group_members = (
- { phid => 'foo' },
- );
- local @project_members = (
- );
- $feed->group_query;
+ my $UserAgent = mock 'Mojo::UserAgent' => (
+ override => [
+ 'post' => sub {
+ my ($self, $url, undef, $params) = @_;
+ my $data = decode_json($params->{params});
+ my $expected = [{type => 'members.add', value => ['foo']}];
+ is_deeply($data->{transactions}, $expected, 'add foo');
+ return mock_useragent_tx('{}');
+ },
+ ]
+ );
+ local @group_members = ({phid => 'foo'},);
+ local @project_members = ();
+ $feed->group_query;
};
do {
- my $Revision = mock 'Bugzilla::Extension::PhabBugz::Revision' => (
- override => [
- 'update' => sub { 1 },
- ],
- );
- my $UserAgent = mock 'Mojo::UserAgent' => (
- override => [
- 'post' => sub {
- my ($self, $url, undef, $params) = @_;
- if ($url =~ /differential\.revision\.search/) {
- my $content = <<JSON;
+ my $Revision = mock 'Bugzilla::Extension::PhabBugz::Revision' =>
+ (override => ['update' => sub {1},],);
+ my $UserAgent = mock 'Mojo::UserAgent' => (
+ override => [
+ 'post' => sub {
+ my ($self, $url, undef, $params) = @_;
+ if ($url =~ /differential\.revision\.search/) {
+ my $content = <<JSON;
{
"error_info": null,
"error_code": null,
@@ -216,35 +201,32 @@ do {
}
}
JSON
- return mock_useragent_tx($content);
- }
- else {
- return mock_useragent_tx("bad request");
- }
- },
- ],
- );
- my $Attachment = mock 'Bugzilla::Attachment' => (
- add_constructor => [ fake_new => 'hash' ],
- );
- my $Bug = mock 'Bugzilla::Bug' => (
- add_constructor => [ fake_new => 'hash' ],
- );
- my $bug = Bugzilla::Bug->fake_new(
- bug_id => 23,
- attachments => [
- Bugzilla::Attachment->fake_new(
- mimetype => 'text/x-phabricator-request',
- filename => 'phabricator-D9999-url.txt',
- ),
- ]
- );
+ return mock_useragent_tx($content);
+ }
+ else {
+ return mock_useragent_tx("bad request");
+ }
+ },
+ ],
+ );
+ my $Attachment
+ = mock 'Bugzilla::Attachment' => (add_constructor => [fake_new => 'hash'],);
+ my $Bug = mock 'Bugzilla::Bug' => (add_constructor => [fake_new => 'hash'],);
+ my $bug = Bugzilla::Bug->fake_new(
+ bug_id => 23,
+ attachments => [
+ Bugzilla::Attachment->fake_new(
+ mimetype => 'text/x-phabricator-request',
+ filename => 'phabricator-D9999-url.txt',
+ ),
+ ]
+ );
- my $revisions = get_attachment_revisions($bug);
- is(ref($revisions), 'ARRAY', 'it is an array ref');
- isa_ok($revisions->[0], 'Bugzilla::Extension::PhabBugz::Revision');
- is($revisions->[0]->bug_id, 23, 'Bugzila ID is 23');
- ok( try { $revisions->[0]->update }, 'update revision');
+ my $revisions = get_attachment_revisions($bug);
+ is(ref($revisions), 'ARRAY', 'it is an array ref');
+ isa_ok($revisions->[0], 'Bugzilla::Extension::PhabBugz::Revision');
+ is($revisions->[0]->bug_id, 23, 'Bugzila ID is 23');
+ ok(try { $revisions->[0]->update }, 'update revision');
};
diff --git a/extensions/PhabBugz/t/feed-daemon-guts.t b/extensions/PhabBugz/t/feed-daemon-guts.t
index 0c508be98..44d65eab4 100644
--- a/extensions/PhabBugz/t/feed-daemon-guts.t
+++ b/extensions/PhabBugz/t/feed-daemon-guts.t
@@ -24,8 +24,11 @@ use Digest::SHA qw(sha1_hex);
use ok 'Bugzilla::Extension::PhabBugz::Feed';
use ok 'Bugzilla::Extension::PhabBugz::Constants', 'PHAB_AUTOMATION_USER';
-use ok 'Bugzilla::Config', 'SetParam';
-can_ok('Bugzilla::Extension::PhabBugz::Feed', qw( group_query feed_query user_query ));
+use ok 'Bugzilla::Config', 'SetParam';
+can_ok(
+ 'Bugzilla::Extension::PhabBugz::Feed',
+ qw( group_query feed_query user_query )
+);
Bugzilla->error_mode(ERROR_MODE_TEST);
@@ -34,127 +37,117 @@ my $phab_bot = create_user(PHAB_AUTOMATION_USER, '*');
my $UserAgent = mock 'Mojo::UserAgent' => ();
{
- SetParam('phabricator_enabled', 0);
- my $feed = Bugzilla::Extension::PhabBugz::Feed->new;
- my $Feed = mock 'Bugzilla::Extension::PhabBugz::Feed' => (
- override => [
- get_last_id => sub { die "get_last_id" },
- ],
- );
-
- foreach my $method (qw( feed_query user_query group_query )) {
- try {
- $feed->$method;
- pass "disabling the phabricator sync: $method";
- }
- catch {
- fail "disabling the phabricator sync: $method";
- }
+ SetParam('phabricator_enabled', 0);
+ my $feed = Bugzilla::Extension::PhabBugz::Feed->new;
+ my $Feed = mock 'Bugzilla::Extension::PhabBugz::Feed' =>
+ (override => [get_last_id => sub { die "get_last_id" },],);
+
+ foreach my $method (qw( feed_query user_query group_query )) {
+ try {
+ $feed->$method;
+ pass "disabling the phabricator sync: $method";
+ }
+ catch {
+ fail "disabling the phabricator sync: $method";
}
+ }
}
my @bad_response = (
- ['http error', mock_useragent_tx("doesn't matter", sub { $_->code(500) }) ],
- ['invalid json', mock_useragent_tx('<xml>foo</xml>') ],
- ['json containing error code', mock_useragent_tx(encode_json({error_code => 1234 }))],
+ ['http error', mock_useragent_tx("doesn't matter", sub { $_->code(500) })],
+ ['invalid json', mock_useragent_tx('<xml>foo</xml>')],
+ [
+ 'json containing error code',
+ mock_useragent_tx(encode_json({error_code => 1234}))
+ ],
);
-SetParam(phabricator_enabled => 1);
-SetParam(phabricator_api_key => 'FAKE-API-KEY');
+SetParam(phabricator_enabled => 1);
+SetParam(phabricator_api_key => 'FAKE-API-KEY');
SetParam(phabricator_base_uri => 'http://fake.fabricator.tld/');
foreach my $bad_response (@bad_response) {
- my $feed = Bugzilla::Extension::PhabBugz::Feed->new;
- $UserAgent->override(
- post => sub {
- my ( $self, $url, undef, $params ) = @_;
- return $bad_response->[1];
- }
- );
-
- foreach my $method (qw( feed_query user_query group_query )) {
- try {
- # This is a hack to get reasonable exception objects.
- local $Bugzilla::Template::is_processing = 1;
- $feed->$method;
- fail "$method - $bad_response->[0]";
- }
- catch {
- is( $_->type, 'bugzilla.code.phabricator_api_error', "$method - $bad_response->[0]" );
- };
+ my $feed = Bugzilla::Extension::PhabBugz::Feed->new;
+ $UserAgent->override(
+ post => sub {
+ my ($self, $url, undef, $params) = @_;
+ return $bad_response->[1];
}
- $UserAgent->reset('post');
+ );
+
+ foreach my $method (qw( feed_query user_query group_query )) {
+ try {
+ # This is a hack to get reasonable exception objects.
+ local $Bugzilla::Template::is_processing = 1;
+ $feed->$method;
+ fail "$method - $bad_response->[0]";
+ }
+ catch {
+ is(
+ $_->type,
+ 'bugzilla.code.phabricator_api_error',
+ "$method - $bad_response->[0]"
+ );
+ };
+ }
+ $UserAgent->reset('post');
}
-my $feed = Bugzilla::Extension::PhabBugz::Feed->new;
-my $json = JSON::MaybeXS->new( canonical => 1, pretty => 1 );
-my $dylan = create_user( 'dylan@mozilla.com', '*', realname => 'Dylan Hardison :dylan' );
-my $evildylan = create_user( 'dylan@gmail.com', '*', realname => 'Evil Dylan :dylan' );
-my $myk = create_user( 'myk@mozilla.com', '*', realname => 'Myk Melez :myk' );
+my $feed = Bugzilla::Extension::PhabBugz::Feed->new;
+my $json = JSON::MaybeXS->new(canonical => 1, pretty => 1);
+my $dylan
+ = create_user('dylan@mozilla.com', '*', realname => 'Dylan Hardison :dylan');
+my $evildylan
+ = create_user('dylan@gmail.com', '*', realname => 'Evil Dylan :dylan');
+my $myk = create_user('myk@mozilla.com', '*', realname => 'Myk Melez :myk');
my $phab_bot_phid = next_phid('PHID-USER');
done_testing;
sub user_search {
- my (%conf) = @_;
-
- return {
- error_info => undef,
- error_code => undef,
- result => {
- cursor => {
- after => $conf{after},
- order => undef,
- limit => 100,
- before => undef
+ my (%conf) = @_;
+
+ return {
+ error_info => undef,
+ error_code => undef,
+ result => {
+ cursor =>
+ {after => $conf{after}, order => undef, limit => 100, before => undef},
+ query => {queryKey => undef},
+ maps => {},
+ data => [
+ map {
+ +{
+ attachments => {
+ $_->{bmo_id}
+ ? ("external-accounts" =>
+ {"external-accounts" => [{type => 'bmo', id => $_->{bmo_id},}]})
+ : (),
},
- query => {
- queryKey => undef
+ fields => {
+ roles => ["verified", "approved", "activated"],
+ realName => $_->{realname},
+ dateModified => time,
+ policy => {view => "public", edit => "no-one"},
+ dateCreated => time,
+ username => $_->{username},
},
- maps => {},
- data => [
- map {
- +{
- attachments => {
- $_->{bmo_id}
- ? ( "external-accounts" => {
- "external-accounts" => [
- {
- type => 'bmo',
- id => $_->{bmo_id},
- }
- ]
- }
- )
- : (),
- },
- fields => {
- roles => [ "verified", "approved", "activated" ],
- realName => $_->{realname},
- dateModified => time,
- policy => {
- view => "public",
- edit => "no-one"
- },
- dateCreated => time,
- username => $_->{username},
- },
- phid => next_phid("PHID-USER"),
- type => "USER",
- id => $_->{phab_id},
- },
- } @{ $conf{users} },
- ]
- }
- };
+ phid => next_phid("PHID-USER"),
+ type => "USER",
+ id => $_->{phab_id},
+ },
+ } @{$conf{users}},
+ ]
+ }
+ };
}
sub next_phid {
- my ($prefix) = @_;
- state $number = 'a' x 20;
- return $prefix . '-' . ($number++);
+ my ($prefix) = @_;
+ state $number = 'a' x 20;
+ return $prefix . '-' . ($number++);
}
diff --git a/extensions/PhabBugz/t/review-flags.t b/extensions/PhabBugz/t/review-flags.t
index 610c46dca..b23a55eec 100644
--- a/extensions/PhabBugz/t/review-flags.t
+++ b/extensions/PhabBugz/t/review-flags.t
@@ -15,11 +15,11 @@ use Test2::V0;
our @EMAILS;
BEGIN {
- require Bugzilla::Mailer;
- no warnings 'redefine';
- *Bugzilla::Mailer::MessageToMTA = sub {
- push @EMAILS, [@_];
- };
+ require Bugzilla::Mailer;
+ no warnings 'redefine';
+ *Bugzilla::Mailer::MessageToMTA = sub {
+ push @EMAILS, [@_];
+ };
}
use Bugzilla::Test::MockDB;
use Bugzilla::Test::MockParams;
@@ -35,138 +35,121 @@ use Data::Dumper;
use ok 'Bugzilla::Extension::PhabBugz::Feed';
use ok 'Bugzilla::Extension::PhabBugz::Constants', 'PHAB_AUTOMATION_USER';
-use ok 'Bugzilla::Config', 'SetParam';
-can_ok('Bugzilla::Extension::PhabBugz::Feed', qw( group_query feed_query user_query ));
+use ok 'Bugzilla::Config', 'SetParam';
+can_ok(
+ 'Bugzilla::Extension::PhabBugz::Feed',
+ qw( group_query feed_query user_query )
+);
SetParam(phabricator_base_uri => 'http://fake.phabricator.tld/');
-SetParam(mailfrom => 'bugzilla-daemon');
+SetParam(mailfrom => 'bugzilla-daemon');
Bugzilla->error_mode(ERROR_MODE_TEST);
-my $nobody = create_user('nobody@mozilla.org', '*');
+my $nobody = create_user('nobody@mozilla.org', '*');
my $phab_bot = create_user(PHAB_AUTOMATION_USER, '*');
# Steve Rogers is the revision author
-my $steve = create_user('steverogers@avengers.org', '*', realname => 'Steve Rogers :steve');
+my $steve = create_user('steverogers@avengers.org', '*',
+ realname => 'Steve Rogers :steve');
# Bucky Barns is the reviewer
-my $bucky = create_user('bucky@avengers.org', '*', realname => 'Bucky Barns :bucky');
+my $bucky
+ = create_user('bucky@avengers.org', '*', realname => 'Bucky Barns :bucky');
my $firefox = Bugzilla::Product->create(
- {
- name => 'Firefox',
- description => 'Fake firefox product',
- version => 'Unspecified',
- },
+ {
+ name => 'Firefox',
+ description => 'Fake firefox product',
+ version => 'Unspecified',
+ },
);
-my $general = Bugzilla::Component->create(
- {
- product =>$firefox,
- name => 'General',
- description => 'The most general description',
- initialowner => { id => $nobody->id },
- }
-);
+my $general = Bugzilla::Component->create({
+ product => $firefox,
+ name => 'General',
+ description => 'The most general description',
+ initialowner => {id => $nobody->id},
+});
Bugzilla->set_user($steve);
-my $bug = Bugzilla::Bug->create(
- {
- short_desc => 'test bug',
- product => $firefox,
- component => $general->name,
- bug_severity => 'normal',
- op_sys => 'Unspecified',
- rep_platform => 'Unspecified',
- version => 'Unspecified',
- comment => 'first post',
- priority => 'P1',
- }
-);
-
-my $recipients = { changer => $steve };
+my $bug = Bugzilla::Bug->create({
+ short_desc => 'test bug',
+ product => $firefox,
+ component => $general->name,
+ bug_severity => 'normal',
+ op_sys => 'Unspecified',
+ rep_platform => 'Unspecified',
+ version => 'Unspecified',
+ comment => 'first post',
+ priority => 'P1',
+});
+
+my $recipients = {changer => $steve};
Bugzilla::BugMail::Send($bug->bug_id, $recipients);
@EMAILS = ();
-my $revision = Bugzilla::Extension::PhabBugz::Revision->new(
- {
- id => 1,
- phid => 'PHID-DREV-uozm3ggfp7e7uoqegmc3',
- type => 'DREV',
- fields => {
- title => "title",
- summary => "the summary of the revision",
- status => { value => "not sure" },
- dateCreated => time() - (60 * 60),
- dateModified => time() - (60 * 5),
- authorPHID => 'authorPHID',
- policy => {
- view => 'policy.view',
- edit => 'policy.edit',
- },
- 'bugzilla.bug-id' => $bug->id,
- },
- attachments => {
- projects => { projectPHIDs => [] },
- reviewers => {
- reviewers => [ ],
- },
- subscribers => {
- subscriberPHIDs => [],
- subscriberCount => 1,
- viewerIsSubscribed => 1,
- }
- },
- reviews => [
- {
- user => new_phab_user($bucky),
- status => 'accepted',
- }
- ]
- }
-);
-my $PhabRevisionMock = mock 'Bugzilla::Extension::PhabBugz::Revision' => (
- override => [
- make_public => sub { },
- update => sub { },
- ]
-);
+my $revision = Bugzilla::Extension::PhabBugz::Revision->new({
+ id => 1,
+ phid => 'PHID-DREV-uozm3ggfp7e7uoqegmc3',
+ type => 'DREV',
+ fields => {
+ title => "title",
+ summary => "the summary of the revision",
+ status => {value => "not sure"},
+ dateCreated => time() - (60 * 60),
+ dateModified => time() - (60 * 5),
+ authorPHID => 'authorPHID',
+ policy => {view => 'policy.view', edit => 'policy.edit',},
+ 'bugzilla.bug-id' => $bug->id,
+ },
+ attachments => {
+ projects => {projectPHIDs => []},
+ reviewers => {reviewers => [],},
+ subscribers =>
+ {subscriberPHIDs => [], subscriberCount => 1, viewerIsSubscribed => 1,}
+ },
+ reviews => [{user => new_phab_user($bucky), status => 'accepted',}]
+});
+my $PhabRevisionMock = mock 'Bugzilla::Extension::PhabBugz::Revision' =>
+ (override => [make_public => sub { }, update => sub { },]);
my $PhabUserMock = mock 'Bugzilla::Extension::PhabBugz::User' => (
- override => [
- match => sub {
- my ($class, $query) = @_;
- if ($query && $query->{phids} && $query->{phids}[0]) {
- my $phid = $query->{phids}[0];
- if ($phid eq 'authorPHID') {
- return [ new_phab_user($steve, $phid) ];
- }
- }
- },
- ]
+ override => [
+ match => sub {
+ my ($class, $query) = @_;
+ if ($query && $query->{phids} && $query->{phids}[0]) {
+ my $phid = $query->{phids}[0];
+ if ($phid eq 'authorPHID') {
+ return [new_phab_user($steve, $phid)];
+ }
+ }
+ },
+ ]
);
my $feed = Bugzilla::Extension::PhabBugz::Feed->new;
my $changer = new_phab_user($bucky);
@EMAILS = ();
-$feed->process_revision_change(
- $revision, $changer, "story text"
-);
+$feed->process_revision_change($revision, $changer, "story text");
# The first comment, and the comment made when the attachment is attached
# are made by Steve.
# The review comment is made by Bucky.
-my $sth = Bugzilla->dbh->prepare("select profiles.login_name, thetext from longdescs join profiles on who = userid");
+my $sth
+ = Bugzilla->dbh->prepare(
+ "select profiles.login_name, thetext from longdescs join profiles on who = userid"
+ );
$sth->execute;
while (my $row = $sth->fetchrow_hashref) {
- if ($row->{thetext} =~ /first post/i) {
- is($row->{login_name}, $steve->login, 'first post author');
- }
- elsif ($row->{thetext} =~ /the summary of the revision/i) {
- is($row->{login_name}, $steve->login, 'the first attachment comment');
- }
- elsif ($row->{thetext} =~ /has approved the revision/i) {
- is($row->{login_name}, $bucky->login);
- }
+ if ($row->{thetext} =~ /first post/i) {
+ is($row->{login_name}, $steve->login, 'first post author');
+ }
+ elsif ($row->{thetext} =~ /the summary of the revision/i) {
+ is($row->{login_name}, $steve->login, 'the first attachment comment');
+ }
+ elsif ($row->{thetext} =~ /has approved the revision/i) {
+ is($row->{login_name}, $bucky->login);
+ }
}
diag Dumper(\@EMAILS);
@@ -174,36 +157,25 @@ diag Dumper(\@EMAILS);
done_testing;
sub new_phab_user {
- my ($bug_user, $phid) = @_;
-
- return Bugzilla::Extension::PhabBugz::User->new(
- {
- id => $bug_user->id * 1000,
- type => "USER",
- phid => $phid // "PHID-USER-" . ( $bug_user->id * 1000 ),
- fields => {
- username => $bug_user->nick,
- realName => $bug_user->name,
- dateCreated => time() - 60 * 60 * 24,
- dateModified => time(),
- roles => [],
- policy => {
- view => 'view',
- edit => 'edit',
- },
- },
- attachments => {
- 'external-accounts' => {
- 'external-accounts' => [
- {
- type => 'bmo',
- id => $bug_user->id,
- }
- ]
- }
- }
- }
- );
+ my ($bug_user, $phid) = @_;
+
+ return Bugzilla::Extension::PhabBugz::User->new({
+ id => $bug_user->id * 1000,
+ type => "USER",
+ phid => $phid // "PHID-USER-" . ($bug_user->id * 1000),
+ fields => {
+ username => $bug_user->nick,
+ realName => $bug_user->name,
+ dateCreated => time() - 60 * 60 * 24,
+ dateModified => time(),
+ roles => [],
+ policy => {view => 'view', edit => 'edit',},
+ },
+ attachments => {
+ 'external-accounts' =>
+ {'external-accounts' => [{type => 'bmo', id => $bug_user->id,}]}
+ }
+ });
-} \ No newline at end of file
+}
diff --git a/extensions/ProdCompSearch/Config.pm b/extensions/ProdCompSearch/Config.pm
index 9631de570..240530f00 100644
--- a/extensions/ProdCompSearch/Config.pm
+++ b/extensions/ProdCompSearch/Config.pm
@@ -11,7 +11,7 @@ use 5.10.1;
use strict;
use warnings;
-use constant NAME => 'ProdCompSearch';
+use constant NAME => 'ProdCompSearch';
use constant REQUIRED_MODULES => [];
use constant OPTIONAL_MODULES => [];
diff --git a/extensions/ProdCompSearch/Extension.pm b/extensions/ProdCompSearch/Extension.pm
index ae507a7d6..6647eb08d 100644
--- a/extensions/ProdCompSearch/Extension.pm
+++ b/extensions/ProdCompSearch/Extension.pm
@@ -16,9 +16,9 @@ use base qw(Bugzilla::Extension);
our $VERSION = '1';
sub webservice {
- my ($self, $args) = @_;
- my $dispatch = $args->{dispatch};
- $dispatch->{PCS} = "Bugzilla::Extension::ProdCompSearch::WebService";
+ my ($self, $args) = @_;
+ my $dispatch = $args->{dispatch};
+ $dispatch->{PCS} = "Bugzilla::Extension::ProdCompSearch::WebService";
}
diff --git a/extensions/ProdCompSearch/lib/WebService.pm b/extensions/ProdCompSearch/lib/WebService.pm
index b173137dd..b47b4a402 100644
--- a/extensions/ProdCompSearch/lib/WebService.pm
+++ b/extensions/ProdCompSearch/lib/WebService.pm
@@ -21,20 +21,21 @@ use Bugzilla::Util qw(detaint_natural trick_taint trim);
#############
use constant PUBLIC_METHODS => qw(
- prod_comp_search
+ prod_comp_search
);
sub rest_resources {
- return [
- qr{^/prod_comp_search/(.*)$}, {
- GET => {
- method => 'prod_comp_search',
- params => sub {
- return { search => $_[0] }
- }
- }
+ return [
+ qr{^/prod_comp_search/(.*)$},
+ {
+ GET => {
+ method => 'prod_comp_search',
+ params => sub {
+ return {search => $_[0]};
}
- ]
+ }
+ }
+ ];
}
##################
@@ -42,79 +43,91 @@ sub rest_resources {
##################
sub prod_comp_search {
- my ($self, $params) = @_;
- my $user = Bugzilla->user;
- my $dbh = Bugzilla->switch_to_shadow_db();
-
- my $search = trim($params->{'search'} || '');
- $search || ThrowCodeError('param_required',
- { function => 'PCS.prod_comp_search', param => 'search' });
-
- my $limit = detaint_natural($params->{'limit'})
- ? $dbh->sql_limit($params->{'limit'})
- : '';
-
- # We do this in the DB directly as we want it to be fast and
- # not have the overhead of loading full product objects
-
- # All products which the user has "Entry" access to.
- my $enterable_ids = $dbh->selectcol_arrayref(
- 'SELECT products.id FROM products
+ my ($self, $params) = @_;
+ my $user = Bugzilla->user;
+ my $dbh = Bugzilla->switch_to_shadow_db();
+
+ my $search = trim($params->{'search'} || '');
+ $search
+ || ThrowCodeError('param_required',
+ {function => 'PCS.prod_comp_search', param => 'search'});
+
+ my $limit
+ = detaint_natural($params->{'limit'})
+ ? $dbh->sql_limit($params->{'limit'})
+ : '';
+
+ # We do this in the DB directly as we want it to be fast and
+ # not have the overhead of loading full product objects
+
+ # All products which the user has "Entry" access to.
+ my $enterable_ids = $dbh->selectcol_arrayref(
+ 'SELECT products.id FROM products
LEFT JOIN group_control_map
ON group_control_map.product_id = products.id
AND group_control_map.entry != 0
AND group_id NOT IN (' . $user->groups_as_string . ')
WHERE group_id IS NULL
- AND products.isactive = 1');
-
- if (scalar @$enterable_ids) {
- # And all of these products must have at least one component
- # and one version.
- $enterable_ids = $dbh->selectcol_arrayref(
- 'SELECT DISTINCT products.id FROM products
- WHERE ' . $dbh->sql_in('products.id', $enterable_ids) .
- ' AND products.id IN (SELECT DISTINCT components.product_id
+ AND products.isactive = 1'
+ );
+
+ if (scalar @$enterable_ids) {
+
+ # And all of these products must have at least one component
+ # and one version.
+ $enterable_ids = $dbh->selectcol_arrayref(
+ 'SELECT DISTINCT products.id FROM products
+ WHERE '
+ . $dbh->sql_in('products.id', $enterable_ids)
+ . ' AND products.id IN (SELECT DISTINCT components.product_id
FROM components
WHERE components.isactive = 1)
AND products.id IN (SELECT DISTINCT versions.product_id
FROM versions
- WHERE versions.isactive = 1)');
- }
-
- return { products => [] } if !scalar @$enterable_ids;
-
- trick_taint($search);
- my @terms;
- my @order;
-
- if ($search =~ /^(.*?)::(.*)$/) {
- my ($product, $component) = (trim($1), trim($2));
- push @terms, _build_terms($product, 1, 0);
- push @terms, _build_terms($component, 0, 1);
- push @order, "products.name != " . $dbh->quote($product) if $product ne '';
- push @order, "components.name != " . $dbh->quote($component) if $component ne '';
- push @order, _build_like_order($product . ' ' . $component);
- push @order, "products.name";
- push @order, "components.name";
- } else {
- push @terms, _build_terms($search, 1, 1);
- push @order, "products.name != " . $dbh->quote($search);
- push @order, "components.name != " . $dbh->quote($search);
- push @order, _build_like_order($search);
- push @order, "products.name";
- push @order, "components.name";
- }
- return { products => [] } if !scalar @terms;
-
- # To help mozilla staff file bmo administration bugs into the right
- # component, sort bmo first when searching for 'bugzilla'
- if ($search =~ /bugzilla/i && $search !~ /^bugzilla\s*::/i
- && ($user->in_group('mozilla-corporation') || $user->in_group('mozilla-foundation')))
- {
- unshift @order, "products.name != 'bugzilla.mozilla.org'";
- }
-
- my $components = $dbh->selectall_arrayref("
+ WHERE versions.isactive = 1)'
+ );
+ }
+
+ return {products => []} if !scalar @$enterable_ids;
+
+ trick_taint($search);
+ my @terms;
+ my @order;
+
+ if ($search =~ /^(.*?)::(.*)$/) {
+ my ($product, $component) = (trim($1), trim($2));
+ push @terms, _build_terms($product, 1, 0);
+ push @terms, _build_terms($component, 0, 1);
+ push @order, "products.name != " . $dbh->quote($product) if $product ne '';
+ push @order, "components.name != " . $dbh->quote($component)
+ if $component ne '';
+ push @order, _build_like_order($product . ' ' . $component);
+ push @order, "products.name";
+ push @order, "components.name";
+ }
+ else {
+ push @terms, _build_terms($search, 1, 1);
+ push @order, "products.name != " . $dbh->quote($search);
+ push @order, "components.name != " . $dbh->quote($search);
+ push @order, _build_like_order($search);
+ push @order, "products.name";
+ push @order, "components.name";
+ }
+ return {products => []} if !scalar @terms;
+
+ # To help mozilla staff file bmo administration bugs into the right
+ # component, sort bmo first when searching for 'bugzilla'
+ if (
+ $search =~ /bugzilla/i
+ && $search !~ /^bugzilla\s*::/i
+ && ( $user->in_group('mozilla-corporation')
+ || $user->in_group('mozilla-foundation'))
+ )
+ {
+ unshift @order, "products.name != 'bugzilla.mozilla.org'";
+ }
+
+ my $components = $dbh->selectall_arrayref("
SELECT products.name AS product,
components.name AS component
FROM products
@@ -122,19 +135,18 @@ sub prod_comp_search {
WHERE (" . join(" AND ", @terms) . ")
AND products.id IN (" . join(",", @$enterable_ids) . ")
AND components.isactive = 1
- ORDER BY " . join(", ", @order) . " $limit",
- { Slice => {} });
-
- my $products = [];
- my $current_product;
- foreach my $component (@$components) {
- if (!$current_product || $component->{product} ne $current_product) {
- $current_product = $component->{product};
- push @$products, { product => $current_product };
- }
- push @$products, $component;
+ ORDER BY " . join(", ", @order) . " $limit", {Slice => {}});
+
+ my $products = [];
+ my $current_product;
+ foreach my $component (@$components) {
+ if (!$current_product || $component->{product} ne $current_product) {
+ $current_product = $component->{product};
+ push @$products, {product => $current_product};
}
- return { products => $products };
+ push @$products, $component;
+ }
+ return {products => $products};
}
###################
@@ -142,34 +154,37 @@ sub prod_comp_search {
###################
sub _build_terms {
- my ($query, $product, $component) = @_;
- my $dbh = Bugzilla->dbh();
-
- my @fields;
- push @fields, 'products.name', 'products.description' if $product;
- push @fields, 'components.name', 'components.description' if $component;
- # note: CONCAT_WS is MySQL specific
- my $field = "CONCAT_WS(' ', ". join(',', @fields) . ")";
-
- my @terms;
- foreach my $word (split(/[\s,]+/, $query)) {
- push(@terms, $dbh->sql_iposition($dbh->quote($word), $field) . " > 0")
- if $word ne '';
- }
- return @terms;
+ my ($query, $product, $component) = @_;
+ my $dbh = Bugzilla->dbh();
+
+ my @fields;
+ push @fields, 'products.name', 'products.description' if $product;
+ push @fields, 'components.name', 'components.description' if $component;
+
+ # note: CONCAT_WS is MySQL specific
+ my $field = "CONCAT_WS(' ', " . join(',', @fields) . ")";
+
+ my @terms;
+ foreach my $word (split(/[\s,]+/, $query)) {
+ push(@terms, $dbh->sql_iposition($dbh->quote($word), $field) . " > 0")
+ if $word ne '';
+ }
+ return @terms;
}
sub _build_like_order {
- my ($query) = @_;
- my $dbh = Bugzilla->dbh;
-
- my @terms;
- foreach my $word (split(/[\s,]+/, $query)) {
- push @terms, "CONCAT(products.name, components.name) LIKE " . $dbh->quote('%' . $word . '%')
- if $word ne '';
- }
-
- return 'NOT(' . join(' AND ', @terms) . ')';
+ my ($query) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ my @terms;
+ foreach my $word (split(/[\s,]+/, $query)) {
+ push @terms,
+ "CONCAT(products.name, components.name) LIKE "
+ . $dbh->quote('%' . $word . '%')
+ if $word ne '';
+ }
+
+ return 'NOT(' . join(' AND ', @terms) . ')';
}
1;
diff --git a/extensions/Profanivore/Config.pm b/extensions/Profanivore/Config.pm
index 311400d16..4acaf4fa2 100644
--- a/extensions/Profanivore/Config.pm
+++ b/extensions/Profanivore/Config.pm
@@ -28,16 +28,8 @@ use warnings;
use constant NAME => 'Profanivore';
use constant REQUIRED_MODULES => [
- {
- package => 'Regexp-Common',
- module => 'Regexp::Common',
- version => 0
- },
- {
- package => 'HTML-Tree',
- module => 'HTML::Tree',
- version => 0,
- }
+ {package => 'Regexp-Common', module => 'Regexp::Common', version => 0},
+ {package => 'HTML-Tree', module => 'HTML::Tree', version => 0,}
];
__PACKAGE__->NAME;
diff --git a/extensions/Profanivore/Extension.pm b/extensions/Profanivore/Extension.pm
index 013f92fee..4682b3d1d 100644
--- a/extensions/Profanivore/Extension.pm
+++ b/extensions/Profanivore/Extension.pm
@@ -35,153 +35,150 @@ use Bugzilla::Util qw(is_7bit_clean);
our $VERSION = '0.01';
sub bug_format_comment {
- my ($self, $args) = @_;
- my $regexes = $args->{'regexes'};
- my $comment = $args->{'comment'};
-
- # Censor profanities if the comment author is not reasonably trusted.
- # However, allow people to see their own profanities, which might stop
- # them immediately noticing and trying to go around the filter. (I.e.
- # it tries to stop an arms race starting.)
- if ($comment &&
- !$comment->author->in_group('editbugs') &&
- $comment->author->id != Bugzilla->user->id)
- {
- push (@$regexes, {
- match => RE_profanity('-i'),
- replace => \&_replace_profanity
- });
- }
+ my ($self, $args) = @_;
+ my $regexes = $args->{'regexes'};
+ my $comment = $args->{'comment'};
+
+ # Censor profanities if the comment author is not reasonably trusted.
+ # However, allow people to see their own profanities, which might stop
+ # them immediately noticing and trying to go around the filter. (I.e.
+ # it tries to stop an arms race starting.)
+ if ( $comment
+ && !$comment->author->in_group('editbugs')
+ && $comment->author->id != Bugzilla->user->id)
+ {
+ push(@$regexes, {match => RE_profanity('-i'), replace => \&_replace_profanity});
+ }
}
sub _replace_profanity {
- # We don't have access to the actual profanity.
- return "****";
+
+ # We don't have access to the actual profanity.
+ return "****";
}
sub mailer_before_send {
- my ($self, $args) = @_;
- my $email = $args->{'email'};
-
- my $author = $email->header("X-Bugzilla-Who");
- my $recipient = $email->header("To");
-
- if ($author && $recipient && lc($author) ne lc($recipient)) {
- my $email_suffix = Bugzilla->params->{'emailsuffix'};
- if ($email_suffix ne '') {
- $recipient =~ s/\Q$email_suffix\E$//;
- $author =~ s/\Q$email_suffix\E$//;
- }
-
- $author = new Bugzilla::User({ name => $author });
-
- if ($author &&
- $author->id &&
- !$author->in_group('editbugs'))
- {
- # Multipart emails
- if (scalar $email->parts > 1) {
- $email->walk_parts(sub {
- my ($part) = @_;
- return if $part->parts > 1; # Top-level
- # do not filter attachments such as patches, etc.
- if ($part->header('Content-Disposition')
- && $part->header('Content-Disposition') =~ /attachment/)
- {
- return;
- }
- _fix_encoding($part);
- my $body = $part->body_str;
- my $new_body;
- if ($part->content_type =~ /^text\/html/) {
- $new_body = _filter_html($body);
- if ($new_body ne $body) {
- # HTML::Tree removes unnecessary whitespace,
- # resulting in very long lines. We need to use
- # quoted-printable encoding to avoid exceeding
- # email's maximum line length.
- $part->encoding_set('quoted-printable');
- }
- }
- elsif ($part->content_type =~ /^text\/plain/) {
- $new_body = _filter_text($body);
- }
- if ($new_body && $new_body ne $body) {
- $part->body_str_set($new_body);
- }
- });
- }
- # Single part email
- else {
- _fix_encoding($email);
- $email->body_str_set(_filter_text($email->body_str));
+ my ($self, $args) = @_;
+ my $email = $args->{'email'};
+
+ my $author = $email->header("X-Bugzilla-Who");
+ my $recipient = $email->header("To");
+
+ if ($author && $recipient && lc($author) ne lc($recipient)) {
+ my $email_suffix = Bugzilla->params->{'emailsuffix'};
+ if ($email_suffix ne '') {
+ $recipient =~ s/\Q$email_suffix\E$//;
+ $author =~ s/\Q$email_suffix\E$//;
+ }
+
+ $author = new Bugzilla::User({name => $author});
+
+ if ($author && $author->id && !$author->in_group('editbugs')) {
+
+ # Multipart emails
+ if (scalar $email->parts > 1) {
+ $email->walk_parts(sub {
+ my ($part) = @_;
+ return if $part->parts > 1; # Top-level
+ # do not filter attachments such as patches, etc.
+ if ( $part->header('Content-Disposition')
+ && $part->header('Content-Disposition') =~ /attachment/)
+ {
+ return;
+ }
+ _fix_encoding($part);
+ my $body = $part->body_str;
+ my $new_body;
+ if ($part->content_type =~ /^text\/html/) {
+ $new_body = _filter_html($body);
+ if ($new_body ne $body) {
+
+ # HTML::Tree removes unnecessary whitespace,
+ # resulting in very long lines. We need to use
+ # quoted-printable encoding to avoid exceeding
+ # email's maximum line length.
+ $part->encoding_set('quoted-printable');
}
- }
+ }
+ elsif ($part->content_type =~ /^text\/plain/) {
+ $new_body = _filter_text($body);
+ }
+ if ($new_body && $new_body ne $body) {
+ $part->body_str_set($new_body);
+ }
+ });
+ }
+
+ # Single part email
+ else {
+ _fix_encoding($email);
+ $email->body_str_set(_filter_text($email->body_str));
+ }
}
+ }
}
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_text {
- my $text = shift;
- my $offensive = RE_profanity('-i');
- $text =~ s/$offensive/****/g;
- return $text;
+ my $text = shift;
+ my $offensive = RE_profanity('-i');
+ $text =~ s/$offensive/****/g;
+ return $text;
}
sub _filter_html {
- my $html = shift;
- my $tree = HTML::Tree->new->parse_content($html);
- my $comments_div = $tree->look_down( _tag => 'div', id => 'comments' );
- return $html if !$comments_div;
- my @comments = $comments_div->look_down( _tag => 'pre' );
- my $dirty = 0;
- foreach my $comment (@comments) {
- _filter_html_node($comment, \$dirty);
- }
- if ($dirty) {
- $html = $tree->as_HTML;
- $tree->delete;
- }
- return $html;
+ my $html = shift;
+ my $tree = HTML::Tree->new->parse_content($html);
+ my $comments_div = $tree->look_down(_tag => 'div', id => 'comments');
+ return $html if !$comments_div;
+ my @comments = $comments_div->look_down(_tag => 'pre');
+ my $dirty = 0;
+ foreach my $comment (@comments) {
+ _filter_html_node($comment, \$dirty);
+ }
+ if ($dirty) {
+ $html = $tree->as_HTML;
+ $tree->delete;
+ }
+ return $html;
}
sub _filter_html_node {
- my ($node, $dirty) = @_;
- my $content = [ $node->content_list ];
- foreach my $item_r ($node->content_refs_list) {
- if (ref $$item_r) {
- _filter_html_node($$item_r);
- } else {
- my $new_text = _filter_text($$item_r);
- if ($new_text ne $$item_r) {
- $$item_r = $new_text;
- $$dirty = 1;
- }
- }
+ my ($node, $dirty) = @_;
+ my $content = [$node->content_list];
+ foreach my $item_r ($node->content_refs_list) {
+ if (ref $$item_r) {
+ _filter_html_node($$item_r);
+ }
+ else {
+ my $new_text = _filter_text($$item_r);
+ if ($new_text ne $$item_r) {
+ $$item_r = $new_text;
+ $$dirty = 1;
+ }
}
+ }
}
__PACKAGE__->NAME;
diff --git a/extensions/Push/Config.pm b/extensions/Push/Config.pm
index 59b78d5a2..9ca73815a 100644
--- a/extensions/Push/Config.pm
+++ b/extensions/Push/Config.pm
@@ -14,39 +14,14 @@ use warnings;
use constant NAME => 'Push';
use constant REQUIRED_MODULES => [
- {
- package => 'Daemon-Generic',
- module => 'Daemon::Generic',
- version => '0'
- },
- {
- package => 'JSON-XS',
- module => 'JSON::XS',
- version => '2.0'
- },
- {
- package => 'Crypt-CBC',
- module => 'Crypt::CBC',
- version => '0'
- },
- {
- package => 'Crypt-DES',
- module => 'Crypt::DES',
- version => '0'
- },
- {
- package => 'Crypt-DES_EDE3',
- module => 'Crypt::DES_EDE3',
- version => '0'
- },
+ {package => 'Daemon-Generic', module => 'Daemon::Generic', version => '0'},
+ {package => 'JSON-XS', module => 'JSON::XS', version => '2.0'},
+ {package => 'Crypt-CBC', module => 'Crypt::CBC', version => '0'},
+ {package => 'Crypt-DES', module => 'Crypt::DES', version => '0'},
+ {package => 'Crypt-DES_EDE3', module => 'Crypt::DES_EDE3', version => '0'},
];
-use constant OPTIONAL_MODULES => [
- {
- package => 'XML-Simple',
- module => 'XML::Simple',
- version => '0'
- },
-];
+use constant OPTIONAL_MODULES =>
+ [{package => 'XML-Simple', module => 'XML::Simple', version => '0'},];
__PACKAGE__->NAME;
diff --git a/extensions/Push/Extension.pm b/extensions/Push/Extension.pm
index f682dea35..4b60dcb73 100644
--- a/extensions/Push/Extension.pm
+++ b/extensions/Push/Extension.pm
@@ -38,18 +38,18 @@ $Carp::CarpInternal{'CGI::Carp'} = 1;
#
BEGIN {
- *Bugzilla::push_ext = \&_get_instance;
+ *Bugzilla::push_ext = \&_get_instance;
}
sub _get_instance {
- my $cache = Bugzilla->request_cache;
- if (!$cache->{'push.instance'}) {
- my $instance = Bugzilla::Extension::Push::Push->new();
- $cache->{'push.instance'} = $instance;
- $instance->logger(Bugzilla::Extension::Push::Logger->new());
- $instance->connectors(Bugzilla::Extension::Push::Connectors->new());
- }
- return $cache->{'push.instance'};
+ my $cache = Bugzilla->request_cache;
+ if (!$cache->{'push.instance'}) {
+ my $instance = Bugzilla::Extension::Push::Push->new();
+ $cache->{'push.instance'} = $instance;
+ $instance->logger(Bugzilla::Extension::Push::Logger->new());
+ $instance->connectors(Bugzilla::Extension::Push::Connectors->new());
+ }
+ return $cache->{'push.instance'};
}
#
@@ -57,22 +57,23 @@ sub _get_instance {
#
sub _enabled {
- my ($self) = @_;
- if (!exists $self->{'enabled'}) {
- my $push = Bugzilla->push_ext;
- $self->{'enabled'} = $push->config->{enabled} eq 'Enabled';
- if ($self->{'enabled'}) {
- # if no connectors are enabled, no need to push anything
- $self->{'enabled'} = 0;
- foreach my $connector (Bugzilla->push_ext->connectors->list) {
- if ($connector->enabled) {
- $self->{'enabled'} = 1;
- last;
- }
- }
+ my ($self) = @_;
+ if (!exists $self->{'enabled'}) {
+ my $push = Bugzilla->push_ext;
+ $self->{'enabled'} = $push->config->{enabled} eq 'Enabled';
+ if ($self->{'enabled'}) {
+
+ # if no connectors are enabled, no need to push anything
+ $self->{'enabled'} = 0;
+ foreach my $connector (Bugzilla->push_ext->connectors->list) {
+ if ($connector->enabled) {
+ $self->{'enabled'} = 1;
+ last;
}
+ }
}
- return $self->{'enabled'};
+ }
+ return $self->{'enabled'};
}
#
@@ -80,191 +81,186 @@ sub _enabled {
#
sub _object_created {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my $object = _get_object_from_args($args);
- return unless $object;
- return unless _should_push($object);
+ my $object = _get_object_from_args($args);
+ return unless $object;
+ return unless _should_push($object);
- $self->_push_object('create', $object, change_set_id(), { timestamp => $args->{'timestamp'} });
+ $self->_push_object('create', $object, change_set_id(),
+ {timestamp => $args->{'timestamp'}});
}
sub _object_modified {
- my ($self, $args) = @_;
-
- my $object = _get_object_from_args($args);
- return unless $object;
- return unless _should_push($object);
-
- my $changes = $args->{'changes'} || {};
- return unless scalar keys %$changes;
-
- my $change_set = change_set_id();
-
- # detect when a bug changes from public to private (or back), so connectors
- # can remove now-private bugs if required.
- if ($object->isa('Bugzilla::Bug')) {
- # we can't use user->can_see_bug(old_bug) as that works on IDs, and the
- # bug has already been updated, so for now assume that a bug without
- # groups is public.
- my $old_bug = $args->{'old_bug'};
- my $is_public = is_public($object);
- my $was_public = $old_bug ? !@{$old_bug->groups_in} : $is_public;
-
- if (!$is_public && $was_public) {
- # bug is changing from public to private
- # push a fake update with the just is_private change
- my $private_changes = {
- timestamp => $args->{'timestamp'},
- changes => [
- {
- field => 'is_private',
- removed => '0',
- added => '1',
- },
- ],
- };
- # note we're sending the old bug object so we don't leak any
- # security sensitive information.
- $self->_push_object('modify', $old_bug, $change_set, $private_changes);
- } elsif ($is_public && !$was_public) {
- # bug is changing from private to public
- # push a fake update with the just is_private change
- my $private_changes = {
- timestamp => $args->{'timestamp'},
- changes => [
- {
- field => 'is_private',
- removed => '1',
- added => '0',
- },
- ],
- };
- # it's ok to send the new bug state here
- $self->_push_object('modify', $object, $change_set, $private_changes);
- }
- }
+ my ($self, $args) = @_;
- # make flagtypes changes easier to process
- if (exists $changes->{'flagtypes.name'}) {
- _split_flagtypes($changes);
- }
+ my $object = _get_object_from_args($args);
+ return unless $object;
+ return unless _should_push($object);
+
+ my $changes = $args->{'changes'} || {};
+ return unless scalar keys %$changes;
- # TODO split group changes?
+ my $change_set = change_set_id();
- # restructure the changes hash
- my $changes_data = {
+ # detect when a bug changes from public to private (or back), so connectors
+ # can remove now-private bugs if required.
+ if ($object->isa('Bugzilla::Bug')) {
+
+ # we can't use user->can_see_bug(old_bug) as that works on IDs, and the
+ # bug has already been updated, so for now assume that a bug without
+ # groups is public.
+ my $old_bug = $args->{'old_bug'};
+ my $is_public = is_public($object);
+ my $was_public = $old_bug ? !@{$old_bug->groups_in} : $is_public;
+
+ if (!$is_public && $was_public) {
+
+ # bug is changing from public to private
+ # push a fake update with the just is_private change
+ my $private_changes = {
timestamp => $args->{'timestamp'},
- changes => [],
- };
- foreach my $field_name (sort keys %$changes) {
- my $new_field_name = $field_name;
- $new_field_name =~ s/isprivate/is_private/;
-
- push @{$changes_data->{'changes'}}, {
- field => $new_field_name,
- removed => $changes->{$field_name}[0],
- added => $changes->{$field_name}[1],
- };
+ changes => [{field => 'is_private', removed => '0', added => '1',},],
+ };
+
+ # note we're sending the old bug object so we don't leak any
+ # security sensitive information.
+ $self->_push_object('modify', $old_bug, $change_set, $private_changes);
}
+ elsif ($is_public && !$was_public) {
+
+ # bug is changing from private to public
+ # push a fake update with the just is_private change
+ my $private_changes = {
+ timestamp => $args->{'timestamp'},
+ changes => [{field => 'is_private', removed => '1', added => '0',},],
+ };
- $self->_push_object('modify', $object, $change_set, $changes_data);
+ # it's ok to send the new bug state here
+ $self->_push_object('modify', $object, $change_set, $private_changes);
+ }
+ }
+
+ # make flagtypes changes easier to process
+ if (exists $changes->{'flagtypes.name'}) {
+ _split_flagtypes($changes);
+ }
+
+ # TODO split group changes?
+
+ # restructure the changes hash
+ my $changes_data = {timestamp => $args->{'timestamp'}, changes => [],};
+ foreach my $field_name (sort keys %$changes) {
+ my $new_field_name = $field_name;
+ $new_field_name =~ s/isprivate/is_private/;
+
+ push @{$changes_data->{'changes'}},
+ {
+ field => $new_field_name,
+ removed => $changes->{$field_name}[0],
+ added => $changes->{$field_name}[1],
+ };
+ }
+
+ $self->_push_object('modify', $object, $change_set, $changes_data);
}
sub _get_object_from_args {
- my ($args) = @_;
- return get_first_value($args, qw(object bug flag group));
+ my ($args) = @_;
+ return get_first_value($args, qw(object bug flag group));
}
sub _should_push {
- my ($object_or_class) = @_;
- my $class = blessed($object_or_class) || $object_or_class;
- return grep { $_ eq $class } qw(Bugzilla::Bug Bugzilla::Attachment Bugzilla::Comment);
+ my ($object_or_class) = @_;
+ my $class = blessed($object_or_class) || $object_or_class;
+ return
+ grep { $_ eq $class }
+ qw(Bugzilla::Bug Bugzilla::Attachment Bugzilla::Comment);
}
# changes to bug flags are presented in a single field 'flagtypes.name' split
# into individual fields
sub _split_flagtypes {
- my ($changes) = @_;
-
- my @removed = _split_flagtype($changes->{'flagtypes.name'}->[0]);
- my @added = _split_flagtype($changes->{'flagtypes.name'}->[1]);
- delete $changes->{'flagtypes.name'};
-
- foreach my $ra (@removed, @added) {
- $changes->{$ra->[0]} = ['', ''];
- }
- foreach my $ra (@removed) {
- my ($name, $value) = @$ra;
- $changes->{$name}->[0] = $value;
- }
- foreach my $ra (@added) {
- my ($name, $value) = @$ra;
- $changes->{$name}->[1] = $value;
- }
+ my ($changes) = @_;
+
+ my @removed = _split_flagtype($changes->{'flagtypes.name'}->[0]);
+ my @added = _split_flagtype($changes->{'flagtypes.name'}->[1]);
+ delete $changes->{'flagtypes.name'};
+
+ foreach my $ra (@removed, @added) {
+ $changes->{$ra->[0]} = ['', ''];
+ }
+ foreach my $ra (@removed) {
+ my ($name, $value) = @$ra;
+ $changes->{$name}->[0] = $value;
+ }
+ foreach my $ra (@added) {
+ my ($name, $value) = @$ra;
+ $changes->{$name}->[1] = $value;
+ }
}
sub _split_flagtype {
- my ($value) = @_;
- my @result;
- foreach my $change (split(/, /, $value)) {
- my $requestee = '';
- if ($change =~ s/\(([^\)]+)\)$//) {
- $requestee = $1;
- }
- my ($name, $value) = $change =~ /^(.+)(.)$/;
- $value .= " ($requestee)" if $requestee;
- push @result, [ "flag.$name", $value ];
+ my ($value) = @_;
+ my @result;
+ foreach my $change (split(/, /, $value)) {
+ my $requestee = '';
+ if ($change =~ s/\(([^\)]+)\)$//) {
+ $requestee = $1;
}
- return @result;
+ my ($name, $value) = $change =~ /^(.+)(.)$/;
+ $value .= " ($requestee)" if $requestee;
+ push @result, ["flag.$name", $value];
+ }
+ return @result;
}
# changes to attachment flags come in via flag_end_of_update which has a
# completely different structure for reporting changes than
# object_end_of_update. this morphs flag to object updates.
sub _morph_flag_updates {
- my ($args) = @_;
-
- my @removed = _morph_flag_update($args->{'old_flags'});
- my @added = _morph_flag_update($args->{'new_flags'});
-
- my $changes = {};
- foreach my $ra (@removed, @added) {
- $changes->{$ra->[0]} = ['', ''];
- }
- foreach my $ra (@removed) {
- my ($name, $value) = @$ra;
- $changes->{$name}->[0] = $value;
- }
- foreach my $ra (@added) {
- my ($name, $value) = @$ra;
- $changes->{$name}->[1] = $value;
- }
-
- foreach my $flag (keys %$changes) {
- if ($changes->{$flag}->[0] eq $changes->{$flag}->[1]) {
- delete $changes->{$flag};
- }
+ my ($args) = @_;
+
+ my @removed = _morph_flag_update($args->{'old_flags'});
+ my @added = _morph_flag_update($args->{'new_flags'});
+
+ my $changes = {};
+ foreach my $ra (@removed, @added) {
+ $changes->{$ra->[0]} = ['', ''];
+ }
+ foreach my $ra (@removed) {
+ my ($name, $value) = @$ra;
+ $changes->{$name}->[0] = $value;
+ }
+ foreach my $ra (@added) {
+ my ($name, $value) = @$ra;
+ $changes->{$name}->[1] = $value;
+ }
+
+ foreach my $flag (keys %$changes) {
+ if ($changes->{$flag}->[0] eq $changes->{$flag}->[1]) {
+ delete $changes->{$flag};
}
+ }
- $args->{'changes'} = $changes;
+ $args->{'changes'} = $changes;
}
sub _morph_flag_update {
- my ($values) = @_;
- my @result;
- foreach my $orig_change (@$values) {
- my $change = $orig_change; # work on a copy
- $change =~ s/^[^:]+://;
- my $requestee = '';
- if ($change =~ s/\(([^\)]+)\)$//) {
- $requestee = $1;
- }
- my ($name, $value) = $change =~ /^(.+)(.)$/;
- $value .= " ($requestee)" if $requestee;
- push @result, [ "flag.$name", $value ];
+ my ($values) = @_;
+ my @result;
+ foreach my $orig_change (@$values) {
+ my $change = $orig_change; # work on a copy
+ $change =~ s/^[^:]+://;
+ my $requestee = '';
+ if ($change =~ s/\(([^\)]+)\)$//) {
+ $requestee = $1;
}
- return @result;
+ my ($name, $value) = $change =~ /^(.+)(.)$/;
+ $value .= " ($requestee)" if $requestee;
+ push @result, ["flag.$name", $value];
+ }
+ return @result;
}
#
@@ -272,49 +268,52 @@ sub _morph_flag_update {
#
sub _push_object {
- my ($self, $message_type, $object, $change_set, $changes) = @_;
- my $rh;
-
- # serialise the object
- my ($rh_object, $name) = Bugzilla::Extension::Push::Serialise->instance->object_to_hash($object);
-
- if (!$rh_object) {
- warn "empty hash from serialiser ($message_type $object)\n";
- return;
- }
- $rh->{$name} = $rh_object;
-
- # add in the events hash
- my $rh_event = Bugzilla::Extension::Push::Serialise->instance->changes_to_event($changes);
- return unless $rh_event;
- $rh_event->{'action'} = $message_type;
- $rh_event->{'target'} = $name;
- $rh_event->{'change_set'} = $change_set;
- $rh_event->{'routing_key'} = "$name.$message_type";
- if (exists $rh_event->{'changes'}) {
- $rh_event->{'routing_key'} .= ':' . join(',', map { $_->{'field'} } @{$rh_event->{'changes'}});
- }
- $rh->{'event'} = $rh_event;
-
- # create message object
- my $message = Bugzilla::Extension::Push::Message->new_transient({
- payload => to_json($rh),
- change_set => $change_set,
- routing_key => $rh_event->{'routing_key'},
- });
-
- # don't hit the database unless there are interested connectors
- my $should_push = 0;
- foreach my $connector (Bugzilla->push_ext->connectors->list) {
- next unless $connector->enabled;
- next unless $connector->should_send($message);
- $should_push = 1;
- last;
- }
- return unless $should_push;
-
- # insert into push table
- $message->create_from_transient();
+ my ($self, $message_type, $object, $change_set, $changes) = @_;
+ my $rh;
+
+ # serialise the object
+ my ($rh_object, $name)
+ = Bugzilla::Extension::Push::Serialise->instance->object_to_hash($object);
+
+ if (!$rh_object) {
+ warn "empty hash from serialiser ($message_type $object)\n";
+ return;
+ }
+ $rh->{$name} = $rh_object;
+
+ # add in the events hash
+ my $rh_event
+ = Bugzilla::Extension::Push::Serialise->instance->changes_to_event($changes);
+ return unless $rh_event;
+ $rh_event->{'action'} = $message_type;
+ $rh_event->{'target'} = $name;
+ $rh_event->{'change_set'} = $change_set;
+ $rh_event->{'routing_key'} = "$name.$message_type";
+ if (exists $rh_event->{'changes'}) {
+ $rh_event->{'routing_key'}
+ .= ':' . join(',', map { $_->{'field'} } @{$rh_event->{'changes'}});
+ }
+ $rh->{'event'} = $rh_event;
+
+ # create message object
+ my $message = Bugzilla::Extension::Push::Message->new_transient({
+ payload => to_json($rh),
+ change_set => $change_set,
+ routing_key => $rh_event->{'routing_key'},
+ });
+
+ # don't hit the database unless there are interested connectors
+ my $should_push = 0;
+ foreach my $connector (Bugzilla->push_ext->connectors->list) {
+ next unless $connector->enabled;
+ next unless $connector->should_send($message);
+ $should_push = 1;
+ last;
+ }
+ return unless $should_push;
+
+ # insert into push table
+ $message->create_from_transient();
}
#
@@ -322,104 +321,113 @@ sub _push_object {
#
sub object_end_of_create {
- my ($self, $args) = @_;
- return unless $self->_enabled;
-
- # it's better to process objects from a non-generic end_of_create where
- # possible; don't process them here to avoid duplicate messages
- my $object = _get_object_from_args($args);
- return if !$object ||
- $object->isa('Bugzilla::Bug') ||
- blessed($object) =~ /^Bugzilla::Extension/;
-
- $self->_object_created($args);
+ my ($self, $args) = @_;
+ return unless $self->_enabled;
+
+ # it's better to process objects from a non-generic end_of_create where
+ # possible; don't process them here to avoid duplicate messages
+ my $object = _get_object_from_args($args);
+ return
+ if !$object
+ || $object->isa('Bugzilla::Bug')
+ || blessed($object) =~ /^Bugzilla::Extension/;
+
+ $self->_object_created($args);
}
sub object_end_of_update {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- # User objects are updated with every page load (to touch the session
- # token). Because we ignore user objects, there's no need to create an
- # instance of Push to check if we're enabled.
- my $object = _get_object_from_args($args);
- return if !$object || $object->isa('Bugzilla::User');
+ # User objects are updated with every page load (to touch the session
+ # token). Because we ignore user objects, there's no need to create an
+ # instance of Push to check if we're enabled.
+ my $object = _get_object_from_args($args);
+ return if !$object || $object->isa('Bugzilla::User');
- return unless $self->_enabled;
+ return unless $self->_enabled;
- # it's better to process objects from a non-generic end_of_update where
- # possible; don't process them here to avoid duplicate messages
- return if $object->isa('Bugzilla::Bug') ||
- $object->isa('Bugzilla::Flag') ||
- blessed($object) =~ /^Bugzilla::Extension/;
+ # it's better to process objects from a non-generic end_of_update where
+ # possible; don't process them here to avoid duplicate messages
+ return
+ if $object->isa('Bugzilla::Bug')
+ || $object->isa('Bugzilla::Flag')
+ || blessed($object) =~ /^Bugzilla::Extension/;
- $self->_object_modified($args);
+ $self->_object_modified($args);
}
# process bugs once they are fully formed
# object_end_of_update is triggered while a bug is being created
sub bug_end_of_create {
- my ($self, $args) = @_;
- return unless $self->_enabled;
- $self->_object_created($args);
+ my ($self, $args) = @_;
+ return unless $self->_enabled;
+ $self->_object_created($args);
}
sub bug_end_of_update {
- my ($self, $args) = @_;
- return unless $self->_enabled;
- $self->_object_modified($args);
+ my ($self, $args) = @_;
+ return unless $self->_enabled;
+ $self->_object_modified($args);
}
sub flag_end_of_update {
- my ($self, $args) = @_;
- return unless $self->_enabled;
- _morph_flag_updates($args);
- $self->_object_modified($args);
- delete $args->{changes};
+ my ($self, $args) = @_;
+ return unless $self->_enabled;
+ _morph_flag_updates($args);
+ $self->_object_modified($args);
+ delete $args->{changes};
}
# comments in bugzilla 4.0 doesn't aren't included in the bug_end_of_* hooks,
# this code uses custom hooks to trigger
sub bug_comment_create {
- my ($self, $args) = @_;
- return unless $self->_enabled;
+ my ($self, $args) = @_;
+ return unless $self->_enabled;
- return unless _should_push('Bugzilla::Comment');
- my $bug = $args->{'bug'} or return;
- my $timestamp = $args->{'timestamp'} or return;
+ return unless _should_push('Bugzilla::Comment');
+ my $bug = $args->{'bug'} or return;
+ my $timestamp = $args->{'timestamp'} or return;
- my $comments = Bugzilla::Comment->match({ bug_id => $bug->id, bug_when => $timestamp });
+ my $comments
+ = Bugzilla::Comment->match({bug_id => $bug->id, bug_when => $timestamp});
- foreach my $comment (@$comments) {
- if ($comment->body ne '') {
- $self->_push_object('create', $comment, change_set_id(), { timestamp => $timestamp });
- }
+ foreach my $comment (@$comments) {
+ if ($comment->body ne '') {
+ $self->_push_object('create', $comment, change_set_id(),
+ {timestamp => $timestamp});
}
+ }
}
sub bug_comment_update {
- my ($self, $args) = @_;
- return unless $self->_enabled;
-
- return unless _should_push('Bugzilla::Comment');
- my $bug = $args->{'bug'} or return;
- my $timestamp = $args->{'timestamp'} or return;
-
- my $comment_id = $args->{'comment_id'};
- if ($comment_id) {
- # XXX this should set changes. only is_private changes will trigger this event
- my $comment = Bugzilla::Comment->new($comment_id);
- $self->_push_object('update', $comment, change_set_id(), { timestamp => $timestamp });
-
- } else {
- # when a bug is created, an update is also triggered; we don't want to sent
- # update messages for the initial comment, or for empty comments
- my $comments = Bugzilla::Comment->match({ bug_id => $bug->id, bug_when => $timestamp });
- foreach my $comment (@$comments) {
- if ($comment->body ne '' && $comment->count) {
- $self->_push_object('create', $comment, change_set_id(), { timestamp => $timestamp });
- }
- }
+ my ($self, $args) = @_;
+ return unless $self->_enabled;
+
+ return unless _should_push('Bugzilla::Comment');
+ my $bug = $args->{'bug'} or return;
+ my $timestamp = $args->{'timestamp'} or return;
+
+ my $comment_id = $args->{'comment_id'};
+ if ($comment_id) {
+
+ # XXX this should set changes. only is_private changes will trigger this event
+ my $comment = Bugzilla::Comment->new($comment_id);
+ $self->_push_object('update', $comment, change_set_id(),
+ {timestamp => $timestamp});
+
+ }
+ else {
+ # when a bug is created, an update is also triggered; we don't want to sent
+ # update messages for the initial comment, or for empty comments
+ my $comments
+ = Bugzilla::Comment->match({bug_id => $bug->id, bug_when => $timestamp});
+ foreach my $comment (@$comments) {
+ if ($comment->body ne '' && $comment->count) {
+ $self->_push_object('create', $comment, change_set_id(),
+ {timestamp => $timestamp});
+ }
}
+ }
}
#
@@ -427,36 +435,30 @@ sub bug_comment_update {
#
sub page_before_template {
- my ($self, $args) = @_;
- my $page = $args->{'page_id'};
- my $vars = $args->{'vars'};
-
- if ($page eq 'push_config.html') {
- Bugzilla->user->in_group('admin')
- || ThrowUserError('auth_failure',
- { group => 'admin',
- action => 'access',
- object => 'administrative_pages' });
- admin_config($vars);
-
- } elsif ($page eq 'push_queues.html'
- || $page eq 'push_queues_view.html'
- ) {
- Bugzilla->user->in_group('admin')
- || ThrowUserError('auth_failure',
- { group => 'admin',
- action => 'access',
- object => 'administrative_pages' });
- admin_queues($vars, $page);
-
- } elsif ($page eq 'push_log.html') {
- Bugzilla->user->in_group('admin')
- || ThrowUserError('auth_failure',
- { group => 'admin',
- action => 'access',
- object => 'administrative_pages' });
- admin_log($vars);
- }
+ my ($self, $args) = @_;
+ my $page = $args->{'page_id'};
+ my $vars = $args->{'vars'};
+
+ if ($page eq 'push_config.html') {
+ Bugzilla->user->in_group('admin')
+ || ThrowUserError('auth_failure',
+ {group => 'admin', action => 'access', object => 'administrative_pages'});
+ admin_config($vars);
+
+ }
+ elsif ($page eq 'push_queues.html' || $page eq 'push_queues_view.html') {
+ Bugzilla->user->in_group('admin')
+ || ThrowUserError('auth_failure',
+ {group => 'admin', action => 'access', object => 'administrative_pages'});
+ admin_queues($vars, $page);
+
+ }
+ elsif ($page eq 'push_log.html') {
+ Bugzilla->user->in_group('admin')
+ || ThrowUserError('auth_failure',
+ {group => 'admin', action => 'access', object => 'administrative_pages'});
+ admin_log($vars);
+ }
}
#
@@ -464,196 +466,86 @@ sub page_before_template {
#
sub db_schema_abstract_schema {
- my ($self, $args) = @_;
- $args->{'schema'}->{'push'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- push_ts => {
- TYPE => 'DATETIME',
- NOTNULL => 1,
- },
- payload => {
- TYPE => 'LONGTEXT',
- NOTNULL => 1,
- },
- change_set => {
- TYPE => 'VARCHAR(32)',
- NOTNULL => 1,
- },
- routing_key => {
- TYPE => 'VARCHAR(64)',
- NOTNULL => 1,
- },
- ],
- };
- $args->{'schema'}->{'push_backlog'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- message_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- },
- push_ts => {
- TYPE => 'DATETIME',
- NOTNULL => 1,
- },
- payload => {
- TYPE => 'LONGTEXT',
- NOTNULL => 1,
- },
- change_set => {
- TYPE => 'VARCHAR(32)',
- NOTNULL => 1,
- },
- routing_key => {
- TYPE => 'VARCHAR(64)',
- NOTNULL => 1,
- },
- connector => {
- TYPE => 'VARCHAR(32)',
- NOTNULL => 1,
- },
- attempt_ts => {
- TYPE => 'DATETIME',
- },
- attempts => {
- TYPE => 'INT2',
- NOTNULL => 1,
- },
- last_error => {
- TYPE => 'MEDIUMTEXT',
- },
- ],
- INDEXES => [
- push_backlog_idx => {
- FIELDS => ['message_id', 'connector'],
- TYPE => 'UNIQUE',
- },
- ],
- };
- $args->{'schema'}->{'push_backoff'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- connector => {
- TYPE => 'VARCHAR(32)',
- NOTNULL => 1,
- },
- next_attempt_ts => {
- TYPE => 'DATETIME',
- },
- attempts => {
- TYPE => 'INT2',
- NOTNULL => 1,
- },
- ],
- INDEXES => [
- push_backoff_idx => {
- FIELDS => ['connector'],
- TYPE => 'UNIQUE',
- },
- ],
- };
- $args->{'schema'}->{'push_options'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- connector => {
- TYPE => 'VARCHAR(32)',
- NOTNULL => 1,
- },
- option_name => {
- TYPE => 'VARCHAR(32)',
- NOTNULL => 1,
- },
- option_value => {
- TYPE => 'VARCHAR(255)',
- NOTNULL => 1,
- },
- ],
- INDEXES => [
- push_options_idx => {
- FIELDS => ['connector', 'option_name'],
- TYPE => 'UNIQUE',
- },
- ],
- };
- $args->{'schema'}->{'push_log'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- message_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- },
- change_set => {
- TYPE => 'VARCHAR(32)',
- NOTNULL => 1,
- },
- routing_key => {
- TYPE => 'VARCHAR(64)',
- NOTNULL => 1,
- },
- connector => {
- TYPE => 'VARCHAR(32)',
- NOTNULL => 1,
- },
- push_ts => {
- TYPE => 'DATETIME',
- NOTNULL => 1,
- },
- processed_ts => {
- TYPE => 'DATETIME',
- NOTNULL => 1,
- },
- result => {
- TYPE => 'INT1',
- NOTNULL => 1,
- },
- data => {
- TYPE => 'MEDIUMTEXT',
- },
- ],
- };
+ my ($self, $args) = @_;
+ $args->{'schema'}->{'push'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ push_ts => {TYPE => 'DATETIME', NOTNULL => 1,},
+ payload => {TYPE => 'LONGTEXT', NOTNULL => 1,},
+ change_set => {TYPE => 'VARCHAR(32)', NOTNULL => 1,},
+ routing_key => {TYPE => 'VARCHAR(64)', NOTNULL => 1,},
+ ],
+ };
+ $args->{'schema'}->{'push_backlog'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ message_id => {TYPE => 'INT3', NOTNULL => 1,},
+ push_ts => {TYPE => 'DATETIME', NOTNULL => 1,},
+ payload => {TYPE => 'LONGTEXT', NOTNULL => 1,},
+ change_set => {TYPE => 'VARCHAR(32)', NOTNULL => 1,},
+ routing_key => {TYPE => 'VARCHAR(64)', NOTNULL => 1,},
+ connector => {TYPE => 'VARCHAR(32)', NOTNULL => 1,},
+ attempt_ts => {TYPE => 'DATETIME',},
+ attempts => {TYPE => 'INT2', NOTNULL => 1,},
+ last_error => {TYPE => 'MEDIUMTEXT',},
+ ],
+ INDEXES => [
+ push_backlog_idx => {FIELDS => ['message_id', 'connector'], TYPE => 'UNIQUE',},
+ ],
+ };
+ $args->{'schema'}->{'push_backoff'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ connector => {TYPE => 'VARCHAR(32)', NOTNULL => 1,},
+ next_attempt_ts => {TYPE => 'DATETIME',},
+ attempts => {TYPE => 'INT2', NOTNULL => 1,},
+ ],
+ INDEXES => [push_backoff_idx => {FIELDS => ['connector'], TYPE => 'UNIQUE',},],
+ };
+ $args->{'schema'}->{'push_options'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ connector => {TYPE => 'VARCHAR(32)', NOTNULL => 1,},
+ option_name => {TYPE => 'VARCHAR(32)', NOTNULL => 1,},
+ option_value => {TYPE => 'VARCHAR(255)', NOTNULL => 1,},
+ ],
+ INDEXES => [
+ push_options_idx => {FIELDS => ['connector', 'option_name'], TYPE => 'UNIQUE',},
+ ],
+ };
+ $args->{'schema'}->{'push_log'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ message_id => {TYPE => 'INT3', NOTNULL => 1,},
+ change_set => {TYPE => 'VARCHAR(32)', NOTNULL => 1,},
+ routing_key => {TYPE => 'VARCHAR(64)', NOTNULL => 1,},
+ connector => {TYPE => 'VARCHAR(32)', NOTNULL => 1,},
+ push_ts => {TYPE => 'DATETIME', NOTNULL => 1,},
+ processed_ts => {TYPE => 'DATETIME', NOTNULL => 1,},
+ result => {TYPE => 'INT1', NOTNULL => 1,},
+ data => {TYPE => 'MEDIUMTEXT',},
+ ],
+ };
}
sub install_filesystem {
- my ($self, $args) = @_;
- my $files = $args->{'files'};
+ my ($self, $args) = @_;
+ my $files = $args->{'files'};
- my $extensionsdir = bz_locations()->{'extensionsdir'};
- my $scriptname = $extensionsdir . "/Push/bin/bugzilla-pushd.pl";
+ my $extensionsdir = bz_locations()->{'extensionsdir'};
+ my $scriptname = $extensionsdir . "/Push/bin/bugzilla-pushd.pl";
- $files->{$scriptname} = {
- perms => Bugzilla::Install::Filesystem::WS_EXECUTE
- };
+ $files->{$scriptname} = {perms => Bugzilla::Install::Filesystem::WS_EXECUTE};
}
sub db_sanitize {
- my $dbh = Bugzilla->dbh;
- print "Deleting push extension logs and messages...\n";
- $dbh->do("DELETE FROM push");
- $dbh->do("DELETE FROM push_backlog");
- $dbh->do("DELETE FROM push_backoff");
- $dbh->do("DELETE FROM push_log");
- $dbh->do("DELETE FROM push_options");
+ my $dbh = Bugzilla->dbh;
+ print "Deleting push extension logs and messages...\n";
+ $dbh->do("DELETE FROM push");
+ $dbh->do("DELETE FROM push_backlog");
+ $dbh->do("DELETE FROM push_backoff");
+ $dbh->do("DELETE FROM push_log");
+ $dbh->do("DELETE FROM push_options");
}
__PACKAGE__->NAME;
diff --git a/extensions/Push/bin/bugzilla-pushd.pl b/extensions/Push/bin/bugzilla-pushd.pl
index 47c905558..cc509aa45 100755
--- a/extensions/Push/bin/bugzilla-pushd.pl
+++ b/extensions/Push/bin/bugzilla-pushd.pl
@@ -14,8 +14,8 @@ use 5.10.1;
use lib qw(. lib local/lib/perl5);
BEGIN {
- use Bugzilla;
- Bugzilla->extensions;
+ use Bugzilla;
+ Bugzilla->extensions;
}
use Bugzilla::Extension::Push::Daemon;
diff --git a/extensions/Push/bin/nagios_push_checker.pl b/extensions/Push/bin/nagios_push_checker.pl
index b578c33d2..4e6e94167 100755
--- a/extensions/Push/bin/nagios_push_checker.pl
+++ b/extensions/Push/bin/nagios_push_checker.pl
@@ -20,15 +20,14 @@ Bugzilla->usage_mode(USAGE_MODE_CMDLINE);
# Number of jobs required in the queue before we alert
-use constant WARN_COUNT => 500;
-use constant ALARM_COUNT => 750;
+use constant WARN_COUNT => 500;
+use constant ALARM_COUNT => 750;
-use constant NAGIOS_OK => 0;
-use constant NAGIOS_WARNING => 1;
-use constant NAGIOS_CRITICAL => 2;
+use constant NAGIOS_OK => 0;
+use constant NAGIOS_WARNING => 1;
+use constant NAGIOS_CRITICAL => 2;
-my $connector = shift
- || die "Syntax: $0 connector\neg. $0 TCL\n";
+my $connector = shift || die "Syntax: $0 connector\neg. $0 TCL\n";
$connector = uc($connector);
my $sql = <<EOF;
@@ -38,15 +37,17 @@ my $sql = <<EOF;
EOF
my $dbh = Bugzilla->switch_to_shadow_db;
-my ($count) = @{ $dbh->selectcol_arrayref($sql, undef, $connector) };
+my ($count) = @{$dbh->selectcol_arrayref($sql, undef, $connector)};
if ($count < WARN_COUNT) {
- print "push $connector OK: $count messages found.\n";
- exit NAGIOS_OK;
-} elsif ($count < ALARM_COUNT) {
- print "push $connector WARNING: $count messages found.\n";
- exit NAGIOS_WARNING;
-} else {
- print "push $connector CRITICAL: $count messages found.\n";
- exit NAGIOS_CRITICAL;
+ print "push $connector OK: $count messages found.\n";
+ exit NAGIOS_OK;
+}
+elsif ($count < ALARM_COUNT) {
+ print "push $connector WARNING: $count messages found.\n";
+ exit NAGIOS_WARNING;
+}
+else {
+ print "push $connector CRITICAL: $count messages found.\n";
+ exit NAGIOS_CRITICAL;
}
diff --git a/extensions/Push/lib/Admin.pm b/extensions/Push/lib/Admin.pm
index 9df2bddcb..d86d30a62 100644
--- a/extensions/Push/lib/Admin.pm
+++ b/extensions/Push/lib/Admin.pm
@@ -19,110 +19,109 @@ use Bugzilla::Util qw(trim detaint_natural trick_taint);
use base qw(Exporter);
our @EXPORT = qw(
- admin_config
- admin_queues
- admin_log
+ admin_config
+ admin_queues
+ admin_log
);
sub admin_config {
- my ($vars) = @_;
- my $push = Bugzilla->push_ext;
- my $input = Bugzilla->input_params;
-
- if ($input->{save}) {
- my $token = $input->{token};
- check_hash_token($token, ['push_config']);
- my $dbh = Bugzilla->dbh;
- $dbh->bz_start_transaction();
- _update_config_from_form('global', $push->config);
- foreach my $connector ($push->connectors->list) {
- _update_config_from_form($connector->name, $connector->config);
- }
- $push->set_config_last_modified();
- $dbh->bz_commit_transaction();
- $vars->{message} = 'push_config_updated';
+ my ($vars) = @_;
+ my $push = Bugzilla->push_ext;
+ my $input = Bugzilla->input_params;
+
+ if ($input->{save}) {
+ my $token = $input->{token};
+ check_hash_token($token, ['push_config']);
+ my $dbh = Bugzilla->dbh;
+ $dbh->bz_start_transaction();
+ _update_config_from_form('global', $push->config);
+ foreach my $connector ($push->connectors->list) {
+ _update_config_from_form($connector->name, $connector->config);
}
+ $push->set_config_last_modified();
+ $dbh->bz_commit_transaction();
+ $vars->{message} = 'push_config_updated';
+ }
- $vars->{push} = $push;
- $vars->{connectors} = $push->connectors;
+ $vars->{push} = $push;
+ $vars->{connectors} = $push->connectors;
}
sub _update_config_from_form {
- my ($name, $config) = @_;
- my $input = Bugzilla->input_params;
-
- # read values from form
- my $values = {};
- foreach my $option ($config->options) {
- my $option_name = $option->{name};
- $values->{$option_name} = trim($input->{$name . ".$option_name"});
+ my ($name, $config) = @_;
+ my $input = Bugzilla->input_params;
+
+ # read values from form
+ my $values = {};
+ foreach my $option ($config->options) {
+ my $option_name = $option->{name};
+ $values->{$option_name} = trim($input->{$name . ".$option_name"});
+ }
+
+ # validate
+ if ($values->{enabled} eq 'Enabled') {
+ eval { $config->validate($values); };
+ if ($@) {
+ ThrowUserError('push_error', {error_message => clean_error($@)});
}
+ }
+
+ # update
+ foreach my $option ($config->options) {
+ my $option_name = $option->{name};
+ trick_taint($values->{$option_name});
+ $config->{$option_name} = $values->{$option_name};
+ }
+ $config->update();
+}
- # validate
- if ($values->{enabled} eq 'Enabled') {
- eval {
- $config->validate($values);
- };
- if ($@) {
- ThrowUserError('push_error', { error_message => clean_error($@) });
- }
- }
+sub admin_queues {
+ my ($vars, $page) = @_;
+ my $push = Bugzilla->push_ext;
+ my $input = Bugzilla->input_params;
- # update
- foreach my $option ($config->options) {
- my $option_name = $option->{name};
- trick_taint($values->{$option_name});
- $config->{$option_name} = $values->{$option_name};
+ if ($page eq 'push_queues.html') {
+ $vars->{push} = $push;
+
+ }
+ elsif ($page eq 'push_queues_view.html') {
+ my $queue;
+ if ($input->{connector}) {
+ my $connector = $push->connectors->by_name($input->{connector})
+ || ThrowUserError('push_error', {error_message => 'Invalid connector'});
+ $queue = $connector->backlog;
}
- $config->update();
-}
+ else {
+ $queue = $push->queue;
+ }
+ $vars->{queue} = $queue;
-sub admin_queues {
- my ($vars, $page) = @_;
- my $push = Bugzilla->push_ext;
- my $input = Bugzilla->input_params;
-
- if ($page eq 'push_queues.html') {
- $vars->{push} = $push;
-
- } elsif ($page eq 'push_queues_view.html') {
- my $queue;
- if ($input->{connector}) {
- my $connector = $push->connectors->by_name($input->{connector})
- || ThrowUserError('push_error', { error_message => 'Invalid connector' });
- $queue = $connector->backlog;
- } else {
- $queue = $push->queue;
- }
- $vars->{queue} = $queue;
-
- my $id = $input->{message} || 0;
- detaint_natural($id)
- || ThrowUserError('push_error', { error_message => 'Invalid message ID' });
- my $message = $queue->by_id($id)
- || ThrowUserError('push_error', { error_message => 'Invalid message ID' });
-
- if ($input->{delete}) {
- my $token = $input->{token};
- check_hash_token($token, ['deleteMessage']);
- $message->remove_from_db();
- $vars->{message} = 'push_message_deleted';
-
- } else {
- $vars->{message_obj} = $message;
- eval {
- $vars->{json} = to_json($message->payload_decoded, 1);
- };
- }
+ my $id = $input->{message} || 0;
+ detaint_natural($id)
+ || ThrowUserError('push_error', {error_message => 'Invalid message ID'});
+ my $message = $queue->by_id($id)
+ || ThrowUserError('push_error', {error_message => 'Invalid message ID'});
+
+ if ($input->{delete}) {
+ my $token = $input->{token};
+ check_hash_token($token, ['deleteMessage']);
+ $message->remove_from_db();
+ $vars->{message} = 'push_message_deleted';
+
+ }
+ else {
+ $vars->{message_obj} = $message;
+ eval { $vars->{json} = to_json($message->payload_decoded, 1); };
}
+ }
}
sub admin_log {
- my ($vars) = @_;
- my $push = Bugzilla->push_ext;
- my $input = Bugzilla->input_params;
+ my ($vars) = @_;
+ my $push = Bugzilla->push_ext;
+ my $input = Bugzilla->input_params;
- $vars->{push} = $push;
+ $vars->{push} = $push;
}
1;
diff --git a/extensions/Push/lib/BacklogMessage.pm b/extensions/Push/lib/BacklogMessage.pm
index 28b17bae3..942eb77eb 100644
--- a/extensions/Push/lib/BacklogMessage.pm
+++ b/extensions/Push/lib/BacklogMessage.pm
@@ -28,31 +28,31 @@ use Encode;
# initialisation
#
-use constant DB_TABLE => 'push_backlog';
+use constant DB_TABLE => 'push_backlog';
use constant DB_COLUMNS => qw(
- id
- message_id
- push_ts
- payload
- change_set
- routing_key
- connector
- attempt_ts
- attempts
- last_error
+ id
+ message_id
+ push_ts
+ payload
+ change_set
+ routing_key
+ connector
+ attempt_ts
+ attempts
+ last_error
);
use constant UPDATE_COLUMNS => qw(
- attempt_ts
- attempts
- last_error
+ attempt_ts
+ attempts
+ last_error
);
use constant LIST_ORDER => 'push_ts';
use constant VALIDATORS => {
- payload => \&_check_payload,
- change_set => \&_check_change_set,
- routing_key => \&_check_routing_key,
- connector => \&_check_connector,
- attempts => \&_check_attempts,
+ payload => \&_check_payload,
+ change_set => \&_check_change_set,
+ routing_key => \&_check_routing_key,
+ connector => \&_check_connector,
+ attempts => \&_check_attempts,
};
#
@@ -60,46 +60,46 @@ use constant VALIDATORS => {
#
sub create_from_message {
- my ($class, $message, $connector) = @_;
- my $self = $class->create({
- message_id => $message->id,
- push_ts => $message->push_ts,
- payload => $message->payload,
- change_set => $message->change_set,
- routing_key => $message->routing_key,
- connector => $connector->name,
- attempt_ts => undef,
- attempts => 0,
- last_error => undef,
- });
- return $self;
+ my ($class, $message, $connector) = @_;
+ my $self = $class->create({
+ message_id => $message->id,
+ push_ts => $message->push_ts,
+ payload => $message->payload,
+ change_set => $message->change_set,
+ routing_key => $message->routing_key,
+ connector => $connector->name,
+ attempt_ts => undef,
+ attempts => 0,
+ last_error => undef,
+ });
+ return $self;
}
#
# accessors
#
-sub message_id { return $_[0]->{'message_id'} }
-sub push_ts { return $_[0]->{'push_ts'}; }
-sub payload { return $_[0]->{'payload'}; }
-sub change_set { return $_[0]->{'change_set'}; }
+sub message_id { return $_[0]->{'message_id'} }
+sub push_ts { return $_[0]->{'push_ts'}; }
+sub payload { return $_[0]->{'payload'}; }
+sub change_set { return $_[0]->{'change_set'}; }
sub routing_key { return $_[0]->{'routing_key'}; }
-sub connector { return $_[0]->{'connector'}; }
-sub attempt_ts { return $_[0]->{'attempt_ts'}; }
-sub attempts { return $_[0]->{'attempts'}; }
-sub last_error { return $_[0]->{'last_error'}; }
+sub connector { return $_[0]->{'connector'}; }
+sub attempt_ts { return $_[0]->{'attempt_ts'}; }
+sub attempts { return $_[0]->{'attempts'}; }
+sub last_error { return $_[0]->{'last_error'}; }
sub payload_decoded {
- my ($self) = @_;
- return from_json($self->{'payload'});
+ my ($self) = @_;
+ return from_json($self->{'payload'});
}
sub attempt_time {
- my ($self) = @_;
- if (!exists $self->{'attempt_time'}) {
- $self->{'attempt_time'} = datetime_from($self->attempt_ts)->epoch;
- }
- return $self->{'attempt_time'};
+ my ($self) = @_;
+ if (!exists $self->{'attempt_time'}) {
+ $self->{'attempt_time'} = datetime_from($self->attempt_ts)->epoch;
+ }
+ return $self->{'attempt_time'};
}
#
@@ -107,11 +107,11 @@ sub attempt_time {
#
sub inc_attempts {
- my ($self, $error) = @_;
- $self->{attempt_ts} = Bugzilla->dbh->selectrow_array('SELECT NOW()');
- $self->{attempts} = $self->{attempts} + 1;
- $self->{last_error} = $error;
- $self->update;
+ my ($self, $error) = @_;
+ $self->{attempt_ts} = Bugzilla->dbh->selectrow_array('SELECT NOW()');
+ $self->{attempts} = $self->{attempts} + 1;
+ $self->{last_error} = $error;
+ $self->update;
}
#
@@ -119,32 +119,35 @@ sub inc_attempts {
#
sub _check_payload {
- my ($invocant, $value) = @_;
- length($value) || ThrowCodeError('push_invalid_payload');
- return $value;
+ my ($invocant, $value) = @_;
+ length($value) || ThrowCodeError('push_invalid_payload');
+ return $value;
}
sub _check_change_set {
- my ($invocant, $value) = @_;
- (defined($value) && length($value)) || ThrowCodeError('push_invalid_change_set');
- return $value;
+ my ($invocant, $value) = @_;
+ (defined($value) && length($value))
+ || ThrowCodeError('push_invalid_change_set');
+ return $value;
}
sub _check_routing_key {
- my ($invocant, $value) = @_;
- (defined($value) && length($value)) || ThrowCodeError('push_invalid_routing_key');
- return $value;
+ my ($invocant, $value) = @_;
+ (defined($value) && length($value))
+ || ThrowCodeError('push_invalid_routing_key');
+ return $value;
}
sub _check_connector {
- my ($invocant, $value) = @_;
- Bugzilla->push_ext->connectors->exists($value) || ThrowCodeError('push_invalid_connector');
- return $value;
+ my ($invocant, $value) = @_;
+ Bugzilla->push_ext->connectors->exists($value)
+ || ThrowCodeError('push_invalid_connector');
+ return $value;
}
sub _check_attempts {
- my ($invocant, $value) = @_;
- return $value || 0;
+ my ($invocant, $value) = @_;
+ return $value || 0;
}
1;
diff --git a/extensions/Push/lib/BacklogQueue.pm b/extensions/Push/lib/BacklogQueue.pm
index a7200c688..17d0a188f 100644
--- a/extensions/Push/lib/BacklogQueue.pm
+++ b/extensions/Push/lib/BacklogQueue.pm
@@ -15,74 +15,67 @@ use Bugzilla;
use Bugzilla::Extension::Push::BacklogMessage;
sub new {
- my ($class, $connector) = @_;
- my $self = {};
- bless($self, $class);
- $self->{connector} = $connector;
- return $self;
+ my ($class, $connector) = @_;
+ my $self = {};
+ bless($self, $class);
+ $self->{connector} = $connector;
+ return $self;
}
sub count {
- my ($self) = @_;
- my $dbh = Bugzilla->dbh;
- return $dbh->selectrow_array("
+ my ($self) = @_;
+ my $dbh = Bugzilla->dbh;
+ return $dbh->selectrow_array("
SELECT COUNT(*)
FROM push_backlog
- WHERE connector = ?",
- undef,
- $self->{connector});
+ WHERE connector = ?", undef, $self->{connector});
}
sub oldest {
- my ($self) = @_;
- my @messages = $self->list(
- limit => 1,
- filter => 'AND ((next_attempt_ts IS NULL) OR (next_attempt_ts <= NOW()))',
- );
- return scalar(@messages) ? $messages[0] : undef;
+ my ($self) = @_;
+ my @messages = $self->list(
+ limit => 1,
+ filter => 'AND ((next_attempt_ts IS NULL) OR (next_attempt_ts <= NOW()))',
+ );
+ return scalar(@messages) ? $messages[0] : undef;
}
sub by_id {
- my ($self, $id) = @_;
- my @messages = $self->list(
- limit => 1,
- filter => "AND (log.id = $id)",
- );
- return scalar(@messages) ? $messages[0] : undef;
+ my ($self, $id) = @_;
+ my @messages = $self->list(limit => 1, filter => "AND (log.id = $id)",);
+ return scalar(@messages) ? $messages[0] : undef;
}
sub list {
- my ($self, %args) = @_;
- $args{limit} ||= 10;
- $args{filter} ||= '';
- my @result;
- my $dbh = Bugzilla->dbh;
+ my ($self, %args) = @_;
+ $args{limit} ||= 10;
+ $args{filter} ||= '';
+ my @result;
+ my $dbh = Bugzilla->dbh;
- my $filter_sql = $args{filter} || '';
- my $sth = $dbh->prepare("
+ my $filter_sql = $args{filter} || '';
+ my $sth = $dbh->prepare("
SELECT log.id, message_id, push_ts, payload, change_set, routing_key, attempt_ts, log.attempts
FROM push_backlog log
LEFT JOIN push_backoff off ON off.connector = log.connector
- WHERE log.connector = ? ".
- $args{filter} . "
- ORDER BY push_ts " .
- $dbh->sql_limit($args{limit})
- );
- $sth->execute($self->{connector});
- while (my $row = $sth->fetchrow_hashref()) {
- push @result, Bugzilla::Extension::Push::BacklogMessage->new({
- id => $row->{id},
- message_id => $row->{message_id},
- push_ts => $row->{push_ts},
- payload => $row->{payload},
- change_set => $row->{change_set},
- routing_key => $row->{routing_key},
- connector => $self->{connector},
- attempt_ts => $row->{attempt_ts},
- attempts => $row->{attempts},
- });
- }
- return @result;
+ WHERE log.connector = ? " . $args{filter} . "
+ ORDER BY push_ts " . $dbh->sql_limit($args{limit}));
+ $sth->execute($self->{connector});
+ while (my $row = $sth->fetchrow_hashref()) {
+ push @result,
+ Bugzilla::Extension::Push::BacklogMessage->new({
+ id => $row->{id},
+ message_id => $row->{message_id},
+ push_ts => $row->{push_ts},
+ payload => $row->{payload},
+ change_set => $row->{change_set},
+ routing_key => $row->{routing_key},
+ connector => $self->{connector},
+ attempt_ts => $row->{attempt_ts},
+ attempts => $row->{attempts},
+ });
+ }
+ return @result;
}
#
@@ -90,39 +83,40 @@ sub list {
#
sub backoff {
- my ($self) = @_;
- if (!$self->{backoff}) {
- my $ra = Bugzilla::Extension::Push::Backoff->match({
- connector => $self->{connector}
+ my ($self) = @_;
+ if (!$self->{backoff}) {
+ my $ra
+ = Bugzilla::Extension::Push::Backoff->match({connector => $self->{connector}
+ });
+ if (@$ra) {
+ $self->{backoff} = $ra->[0];
+ }
+ else {
+ $self->{backoff}
+ = Bugzilla::Extension::Push::Backoff->create({connector => $self->{connector}
});
- if (@$ra) {
- $self->{backoff} = $ra->[0];
- } else {
- $self->{backoff} = Bugzilla::Extension::Push::Backoff->create({
- connector => $self->{connector}
- });
- }
}
- return $self->{backoff};
+ }
+ return $self->{backoff};
}
sub reset_backoff {
- my ($self) = @_;
- my $backoff = $self->backoff;
- $backoff->reset();
- $backoff->update();
+ my ($self) = @_;
+ my $backoff = $self->backoff;
+ $backoff->reset();
+ $backoff->update();
}
sub inc_backoff {
- my ($self) = @_;
- my $backoff = $self->backoff;
- $backoff->inc();
- $backoff->update();
+ my ($self) = @_;
+ my $backoff = $self->backoff;
+ $backoff->inc();
+ $backoff->update();
}
sub connector {
- my ($self) = @_;
- return $self->{connector};
+ my ($self) = @_;
+ return $self->{connector};
}
1;
diff --git a/extensions/Push/lib/Backoff.pm b/extensions/Push/lib/Backoff.pm
index 0436cdf14..070adfc29 100644
--- a/extensions/Push/lib/Backoff.pm
+++ b/extensions/Push/lib/Backoff.pm
@@ -26,21 +26,21 @@ use Bugzilla::Util;
# initialisation
#
-use constant DB_TABLE => 'push_backoff';
+use constant DB_TABLE => 'push_backoff';
use constant DB_COLUMNS => qw(
- id
- connector
- next_attempt_ts
- attempts
+ id
+ connector
+ next_attempt_ts
+ attempts
);
use constant UPDATE_COLUMNS => qw(
- next_attempt_ts
- attempts
+ next_attempt_ts
+ attempts
);
use constant VALIDATORS => {
- connector => \&_check_connector,
- next_attempt_ts => \&_check_next_attempt_ts,
- attempts => \&_check_attempts,
+ connector => \&_check_connector,
+ next_attempt_ts => \&_check_next_attempt_ts,
+ attempts => \&_check_attempts,
};
use constant LIST_ORDER => 'next_attempt_ts';
@@ -48,16 +48,16 @@ use constant LIST_ORDER => 'next_attempt_ts';
# accessors
#
-sub connector { return $_[0]->{'connector'}; }
+sub connector { return $_[0]->{'connector'}; }
sub next_attempt_ts { return $_[0]->{'next_attempt_ts'}; }
-sub attempts { return $_[0]->{'attempts'}; }
+sub attempts { return $_[0]->{'attempts'}; }
sub next_attempt_time {
- my ($self) = @_;
- if (!exists $self->{'next_attempt_time'}) {
- $self->{'next_attempt_time'} = datetime_from($self->next_attempt_ts)->epoch;
- }
- return $self->{'next_attempt_time'};
+ my ($self) = @_;
+ if (!exists $self->{'next_attempt_time'}) {
+ $self->{'next_attempt_time'} = datetime_from($self->next_attempt_ts)->epoch;
+ }
+ return $self->{'next_attempt_time'};
}
#
@@ -65,25 +65,26 @@ sub next_attempt_time {
#
sub reset {
- my ($self) = @_;
- $self->{next_attempt_ts} = Bugzilla->dbh->selectrow_array('SELECT NOW()');
- $self->{attempts} = 0;
- INFO( sprintf 'resetting backoff for %s', $self->connector );
+ my ($self) = @_;
+ $self->{next_attempt_ts} = Bugzilla->dbh->selectrow_array('SELECT NOW()');
+ $self->{attempts} = 0;
+ INFO(sprintf 'resetting backoff for %s', $self->connector);
}
sub inc {
- my ($self) = @_;
- my $dbh = Bugzilla->dbh;
-
- my $attempts = $self->attempts + 1;
- my $seconds = $attempts <= 4 ? 5 ** $attempts : 15 * 60;
- my ($date) = $dbh->selectrow_array("SELECT " . $dbh->sql_date_math('NOW()', '+', $seconds, 'SECOND'));
-
- $self->{next_attempt_ts} = $date;
- $self->{attempts} = $attempts;
- INFO(
- sprintf 'setting next attempt for %s to %s (attempt %s)', $self->connector, $date, $attempts
- );
+ my ($self) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ my $attempts = $self->attempts + 1;
+ my $seconds = $attempts <= 4 ? 5**$attempts : 15 * 60;
+ my ($date)
+ = $dbh->selectrow_array(
+ "SELECT " . $dbh->sql_date_math('NOW()', '+', $seconds, 'SECOND'));
+
+ $self->{next_attempt_ts} = $date;
+ $self->{attempts} = $attempts;
+ INFO(sprintf 'setting next attempt for %s to %s (attempt %s)',
+ $self->connector, $date, $attempts);
}
#
@@ -91,19 +92,20 @@ sub inc {
#
sub _check_connector {
- my ($invocant, $value) = @_;
- Bugzilla->push_ext->connectors->exists($value) || ThrowCodeError('push_invalid_connector');
- return $value;
+ my ($invocant, $value) = @_;
+ Bugzilla->push_ext->connectors->exists($value)
+ || ThrowCodeError('push_invalid_connector');
+ return $value;
}
sub _check_next_attempt_ts {
- my ($invocant, $value) = @_;
- return $value || Bugzilla->dbh->selectrow_array('SELECT NOW()');
+ my ($invocant, $value) = @_;
+ return $value || Bugzilla->dbh->selectrow_array('SELECT NOW()');
}
sub _check_attempts {
- my ($invocant, $value) = @_;
- return $value || 0;
+ my ($invocant, $value) = @_;
+ return $value || 0;
}
1;
diff --git a/extensions/Push/lib/Config.pm b/extensions/Push/lib/Config.pm
index 2db95b972..bb0d523ad 100644
--- a/extensions/Push/lib/Config.pm
+++ b/extensions/Push/lib/Config.pm
@@ -17,199 +17,201 @@ use Bugzilla::Extension::Push::Option;
use Crypt::CBC;
sub new {
- my ($class, $name, @options) = @_;
- my $self = {
- _name => $name
- };
- bless($self, $class);
-
- $self->{_options} = [@options];
- unshift @{$self->{_options}}, {
- name => 'enabled',
- label => 'Status',
- help => '',
- type => 'select',
- values => [ 'Enabled', 'Disabled' ],
- default => 'Disabled',
+ my ($class, $name, @options) = @_;
+ my $self = {_name => $name};
+ bless($self, $class);
+
+ $self->{_options} = [@options];
+ unshift @{$self->{_options}},
+ {
+ name => 'enabled',
+ label => 'Status',
+ help => '',
+ type => 'select',
+ values => ['Enabled', 'Disabled'],
+ default => 'Disabled',
};
- return $self;
+ return $self;
}
sub options {
- my ($self) = @_;
- return @{$self->{_options}};
+ my ($self) = @_;
+ return @{$self->{_options}};
}
sub option {
- my ($self, $name) = @_;
- foreach my $option ($self->options) {
- return $option if $option->{name} eq $name;
- }
- return undef;
+ my ($self, $name) = @_;
+ foreach my $option ($self->options) {
+ return $option if $option->{name} eq $name;
+ }
+ return undef;
}
sub load {
- my ($self) = @_;
- my $config = {};
-
- # prime $config with defaults
- foreach my $rh ($self->options) {
- $config->{$rh->{name}} = $rh->{default};
+ my ($self) = @_;
+ my $config = {};
+
+ # prime $config with defaults
+ foreach my $rh ($self->options) {
+ $config->{$rh->{name}} = $rh->{default};
+ }
+
+ # override defaults with values from database
+ my $options
+ = Bugzilla::Extension::Push::Option->match({connector => $self->{_name},});
+ foreach my $option (@$options) {
+ my $option_config = $self->option($option->name) || next;
+ if ($option_config->{type} eq 'password') {
+ $config->{$option->name} = $self->_decrypt($option->value);
}
-
- # override defaults with values from database
- my $options = Bugzilla::Extension::Push::Option->match({
- connector => $self->{_name},
- });
- foreach my $option (@$options) {
- my $option_config = $self->option($option->name)
- || next;
- if ($option_config->{type} eq 'password') {
- $config->{$option->name} = $self->_decrypt($option->value);
- } else {
- $config->{$option->name} = $option->value;
- }
+ else {
+ $config->{$option->name} = $option->value;
}
+ }
- # validate when running from the daemon
- if (Bugzilla->push_ext->is_daemon) {
- $self->_validate_config($config);
- }
-
- # done, update self
- foreach my $name (keys %$config) {
- my $value = $self->option($name)->{type} eq 'password' ? '********' : $config->{$name};
- TRACE( sprintf "%s: set %s=%s\n", $self->{_name}, $name, $value || '' );
- $self->{$name} = $config->{$name};
- }
+ # validate when running from the daemon
+ if (Bugzilla->push_ext->is_daemon) {
+ $self->_validate_config($config);
+ }
+
+ # done, update self
+ foreach my $name (keys %$config) {
+ my $value
+ = $self->option($name)->{type} eq 'password' ? '********' : $config->{$name};
+ TRACE(sprintf "%s: set %s=%s\n", $self->{_name}, $name, $value || '');
+ $self->{$name} = $config->{$name};
+ }
}
sub validate {
- my ($self, $config) = @_;
- $self->_validate_mandatory($config);
- $self->_validate_config($config);
+ my ($self, $config) = @_;
+ $self->_validate_mandatory($config);
+ $self->_validate_config($config);
}
sub update {
- my ($self) = @_;
-
- my @valid_options = map { $_->{name} } $self->options;
-
- my %options;
- my $options_list = Bugzilla::Extension::Push::Option->match({
- connector => $self->{_name},
- });
- foreach my $option (@$options_list) {
- $options{$option->name} = $option;
- }
-
- # delete options which are no longer valid
- foreach my $name (keys %options) {
- if (!grep { $_ eq $name } @valid_options) {
- $options{$name}->remove_from_db();
- delete $options{$name};
- }
+ my ($self) = @_;
+
+ my @valid_options = map { $_->{name} } $self->options;
+
+ my %options;
+ my $options_list
+ = Bugzilla::Extension::Push::Option->match({connector => $self->{_name},});
+ foreach my $option (@$options_list) {
+ $options{$option->name} = $option;
+ }
+
+ # delete options which are no longer valid
+ foreach my $name (keys %options) {
+ if (!grep { $_ eq $name } @valid_options) {
+ $options{$name}->remove_from_db();
+ delete $options{$name};
}
+ }
- # update options
- foreach my $name (keys %options) {
- my $option = $options{$name};
- if ($self->option($name)->{type} eq 'password') {
- $option->set_value($self->_encrypt($self->{$name}));
- } else {
- $option->set_value($self->{$name});
- }
- $option->update();
+ # update options
+ foreach my $name (keys %options) {
+ my $option = $options{$name};
+ if ($self->option($name)->{type} eq 'password') {
+ $option->set_value($self->_encrypt($self->{$name}));
}
-
- # add missing options
- foreach my $name (@valid_options) {
- next if exists $options{$name};
- Bugzilla::Extension::Push::Option->create({
- connector => $self->{_name},
- option_name => $name,
- option_value => $self->{$name},
- });
+ else {
+ $option->set_value($self->{$name});
}
+ $option->update();
+ }
+
+ # add missing options
+ foreach my $name (@valid_options) {
+ next if exists $options{$name};
+ Bugzilla::Extension::Push::Option->create({
+ connector => $self->{_name},
+ option_name => $name,
+ option_value => $self->{$name},
+ });
+ }
}
sub _remove_invalid_options {
- my ($self, $config) = @_;
- my @names;
- foreach my $rh ($self->options) {
- push @names, $rh->{name};
- }
- foreach my $name (keys %$config) {
- if ($name =~ /^_/ || !grep { $_ eq $name } @names) {
- delete $config->{$name};
- }
+ my ($self, $config) = @_;
+ my @names;
+ foreach my $rh ($self->options) {
+ push @names, $rh->{name};
+ }
+ foreach my $name (keys %$config) {
+ if ($name =~ /^_/ || !grep { $_ eq $name } @names) {
+ delete $config->{$name};
}
+ }
}
sub _validate_mandatory {
- my ($self, $config) = @_;
- $self->_remove_invalid_options($config);
-
- my @missing;
- foreach my $option ($self->options) {
- next unless $option->{required};
- my $name = $option->{name};
- if (!exists $config->{$name} || !defined($config->{$name}) || $config->{$name} eq '') {
- push @missing, $option;
- }
+ my ($self, $config) = @_;
+ $self->_remove_invalid_options($config);
+
+ my @missing;
+ foreach my $option ($self->options) {
+ next unless $option->{required};
+ my $name = $option->{name};
+ if ( !exists $config->{$name}
+ || !defined($config->{$name})
+ || $config->{$name} eq '')
+ {
+ push @missing, $option;
+ }
+ }
+ if (@missing) {
+ my $connector = $self->{_name};
+ @missing = map { $_->{label} } @missing;
+ if (scalar @missing == 1) {
+ die "The option '$missing[0]' for the connector '$connector' is mandatory\n";
}
- if (@missing) {
- my $connector = $self->{_name};
- @missing = map { $_->{label} } @missing;
- if (scalar @missing == 1) {
- die "The option '$missing[0]' for the connector '$connector' is mandatory\n";
- } else {
- die "The following options for the connector '$connector' are mandatory:\n "
- . join("\n ", @missing) . "\n";
- }
+ else {
+ die "The following options for the connector '$connector' are mandatory:\n "
+ . join("\n ", @missing) . "\n";
}
+ }
}
sub _validate_config {
- my ($self, $config) = @_;
- $self->_remove_invalid_options($config);
-
- my @errors;
- foreach my $option ($self->options) {
- my $name = $option->{name};
- next unless exists $config->{$name} && exists $option->{validate};
- eval {
- $option->{validate}->($config->{$name}, $config);
- };
- push @errors, $@ if $@;
- }
- die join("\n", @errors) if @errors;
-
- if ($self->{_name} ne 'global') {
- my $class = 'Bugzilla::Extension::Push::Connector::' . $self->{_name};
- $class->options_validate($config);
- }
+ my ($self, $config) = @_;
+ $self->_remove_invalid_options($config);
+
+ my @errors;
+ foreach my $option ($self->options) {
+ my $name = $option->{name};
+ next unless exists $config->{$name} && exists $option->{validate};
+ eval { $option->{validate}->($config->{$name}, $config); };
+ push @errors, $@ if $@;
+ }
+ die join("\n", @errors) if @errors;
+
+ if ($self->{_name} ne 'global') {
+ my $class = 'Bugzilla::Extension::Push::Connector::' . $self->{_name};
+ $class->options_validate($config);
+ }
}
sub _cipher {
- my ($self) = @_;
- $self->{_cipher} ||= Crypt::CBC->new(
- -key => Bugzilla->localconfig->{'site_wide_secret'},
- -cipher => 'DES_EDE3');
- return $self->{_cipher};
+ my ($self) = @_;
+ $self->{_cipher} ||= Crypt::CBC->new(
+ -key => Bugzilla->localconfig->{'site_wide_secret'},
+ -cipher => 'DES_EDE3'
+ );
+ return $self->{_cipher};
}
sub _decrypt {
- my ($self, $value) = @_;
- my $result;
- eval { $result = $self->_cipher->decrypt_hex($value) };
- return $@ ? '' : $result;
+ my ($self, $value) = @_;
+ my $result;
+ eval { $result = $self->_cipher->decrypt_hex($value) };
+ return $@ ? '' : $result;
}
sub _encrypt {
- my ($self, $value) = @_;
- return $self->_cipher->encrypt_hex($value);
+ my ($self, $value) = @_;
+ return $self->_cipher->encrypt_hex($value);
}
1;
diff --git a/extensions/Push/lib/Connector.disabled/AMQP.pm b/extensions/Push/lib/Connector.disabled/AMQP.pm
index 1ba365e21..dda73dade 100644
--- a/extensions/Push/lib/Connector.disabled/AMQP.pm
+++ b/extensions/Push/lib/Connector.disabled/AMQP.pm
@@ -20,211 +20,197 @@ use Bugzilla::Util qw(generate_random_password);
use DateTime;
sub init {
- my ($self) = @_;
- $self->{mq} = 0;
- $self->{channel} = 1;
-
- if ($self->config->{queue}) {
- $self->{queue_name} = $self->config->{queue};
- } else {
- my $queue_name = Bugzilla->localconfig->{'urlbase'};
- $queue_name =~ s#^https?://##;
- $queue_name =~ s#/$#|#;
- $queue_name .= generate_random_password(16);
- $self->{queue_name} = $queue_name;
- }
+ my ($self) = @_;
+ $self->{mq} = 0;
+ $self->{channel} = 1;
+
+ if ($self->config->{queue}) {
+ $self->{queue_name} = $self->config->{queue};
+ }
+ else {
+ my $queue_name = Bugzilla->localconfig->{'urlbase'};
+ $queue_name =~ s#^https?://##;
+ $queue_name =~ s#/$#|#;
+ $queue_name .= generate_random_password(16);
+ $self->{queue_name} = $queue_name;
+ }
}
sub options {
- return (
- {
- name => 'host',
- label => 'AMQP Hostname',
- type => 'string',
- default => 'localhost',
- required => 1,
- },
- {
- name => 'port',
- label => 'AMQP Port',
- type => 'string',
- default => '5672',
- required => 1,
- validate => sub {
- $_[0] =~ /\D/ && die "Invalid port (must be numeric)\n";
- },
- },
- {
- name => 'username',
- label => 'Username',
- type => 'string',
- default => 'guest',
- required => 1,
- },
- {
- name => 'password',
- label => 'Password',
- type => 'password',
- default => 'guest',
- required => 1,
- },
- {
- name => 'vhost',
- label => 'Virtual Host',
- type => 'string',
- default => '/',
- required => 1,
- },
- {
- name => 'exchange',
- label => 'Exchange',
- type => 'string',
- default => '',
- required => 1,
- },
- {
- name => 'queue',
- label => 'Queue',
- type => 'string',
- },
- );
+ return (
+ {
+ name => 'host',
+ label => 'AMQP Hostname',
+ type => 'string',
+ default => 'localhost',
+ required => 1,
+ },
+ {
+ name => 'port',
+ label => 'AMQP Port',
+ type => 'string',
+ default => '5672',
+ required => 1,
+ validate => sub {
+ $_[0] =~ /\D/ && die "Invalid port (must be numeric)\n";
+ },
+ },
+ {
+ name => 'username',
+ label => 'Username',
+ type => 'string',
+ default => 'guest',
+ required => 1,
+ },
+ {
+ name => 'password',
+ label => 'Password',
+ type => 'password',
+ default => 'guest',
+ required => 1,
+ },
+ {
+ name => 'vhost',
+ label => 'Virtual Host',
+ type => 'string',
+ default => '/',
+ required => 1,
+ },
+ {
+ name => 'exchange',
+ label => 'Exchange',
+ type => 'string',
+ default => '',
+ required => 1,
+ },
+ {name => 'queue', label => 'Queue', type => 'string',},
+ );
}
sub stop {
- my ($self) = @_;
- if ($self->{mq}) {
- Bugzilla->push_ext->logger->debug('AMQP: disconnecting');
- $self->{mq}->disconnect();
- $self->{mq} = 0;
- }
+ my ($self) = @_;
+ if ($self->{mq}) {
+ Bugzilla->push_ext->logger->debug('AMQP: disconnecting');
+ $self->{mq}->disconnect();
+ $self->{mq} = 0;
+ }
}
sub _connect {
- my ($self) = @_;
- my $logger = Bugzilla->push_ext->logger;
- my $config = $self->config;
-
- $self->stop();
-
- $logger->debug('AMQP: Connecting to RabbitMQ ' . $config->{host} . ':' . $config->{port});
- require Net::RabbitMQ;
- my $mq = Net::RabbitMQ->new();
- $mq->connect(
- $config->{host},
- {
- port => $config->{port},
- user => $config->{username},
- password => $config->{password},
- }
- );
- $self->{mq} = $mq;
-
- $logger->debug('AMQP: Opening channel ' . $self->{channel});
- $self->{mq}->channel_open($self->{channel});
-
- $logger->debug('AMQP: Declaring queue ' . $self->{queue_name});
- $self->{mq}->queue_declare(
- $self->{channel},
- $self->{queue_name},
- {
- passive => 0,
- durable => 1,
- exclusive => 0,
- auto_delete => 0,
- },
- );
+ my ($self) = @_;
+ my $logger = Bugzilla->push_ext->logger;
+ my $config = $self->config;
+
+ $self->stop();
+
+ $logger->debug(
+ 'AMQP: Connecting to RabbitMQ ' . $config->{host} . ':' . $config->{port});
+ require Net::RabbitMQ;
+ my $mq = Net::RabbitMQ->new();
+ $mq->connect(
+ $config->{host},
+ {
+ port => $config->{port},
+ user => $config->{username},
+ password => $config->{password},
+ }
+ );
+ $self->{mq} = $mq;
+
+ $logger->debug('AMQP: Opening channel ' . $self->{channel});
+ $self->{mq}->channel_open($self->{channel});
+
+ $logger->debug('AMQP: Declaring queue ' . $self->{queue_name});
+ $self->{mq}->queue_declare($self->{channel}, $self->{queue_name},
+ {passive => 0, durable => 1, exclusive => 0, auto_delete => 0,},
+ );
}
sub _bind {
- my ($self, $message) = @_;
- my $logger = Bugzilla->push_ext->logger;
- my $config = $self->config;
-
- # bind to queue (also acts to verify the connection is still valid)
- if ($self->{mq}) {
- eval {
- $logger->debug('AMQP: binding queue(' . $self->{queue_name} . ') with exchange(' . $config->{exchange} . ')');
- $self->{mq}->queue_bind(
- $self->{channel},
- $self->{queue_name},
- $config->{exchange},
- $message->routing_key,
- );
- };
- if ($@) {
- $logger->debug('AMQP: ' . clean_error($@));
- $self->{mq} = 0;
- }
+ my ($self, $message) = @_;
+ my $logger = Bugzilla->push_ext->logger;
+ my $config = $self->config;
+
+ # bind to queue (also acts to verify the connection is still valid)
+ if ($self->{mq}) {
+ eval {
+ $logger->debug('AMQP: binding queue('
+ . $self->{queue_name}
+ . ') with exchange('
+ . $config->{exchange}
+ . ')');
+ $self->{mq}->queue_bind(
+ $self->{channel}, $self->{queue_name},
+ $config->{exchange}, $message->routing_key,
+ );
+ };
+ if ($@) {
+ $logger->debug('AMQP: ' . clean_error($@));
+ $self->{mq} = 0;
}
+ }
}
sub should_send {
- my ($self, $message) = @_;
- my $logger = Bugzilla->push_ext->logger;
-
- my $payload = $message->payload_decoded();
- my $target = $payload->{event}->{target};
- my $is_private = $payload->{$target}->{is_private} ? 1 : 0;
- if (!$is_private && exists $payload->{$target}->{bug}) {
- $is_private = $payload->{$target}->{bug}->{is_private} ? 1 : 0;
- }
-
- if ($is_private) {
- # we only want to push the is_private message from the change_set, as
- # this is guaranteed to contain public information only
- if ($message->routing_key !~ /\.modify:is_private$/) {
- $logger->debug('AMQP: Ignoring private message');
- return 0;
- }
- $logger->debug('AMQP: Sending change of message to is_private');
+ my ($self, $message) = @_;
+ my $logger = Bugzilla->push_ext->logger;
+
+ my $payload = $message->payload_decoded();
+ my $target = $payload->{event}->{target};
+ my $is_private = $payload->{$target}->{is_private} ? 1 : 0;
+ if (!$is_private && exists $payload->{$target}->{bug}) {
+ $is_private = $payload->{$target}->{bug}->{is_private} ? 1 : 0;
+ }
+
+ if ($is_private) {
+
+ # we only want to push the is_private message from the change_set, as
+ # this is guaranteed to contain public information only
+ if ($message->routing_key !~ /\.modify:is_private$/) {
+ $logger->debug('AMQP: Ignoring private message');
+ return 0;
}
- return 1;
+ $logger->debug('AMQP: Sending change of message to is_private');
+ }
+ return 1;
}
sub send {
- my ($self, $message) = @_;
- my $logger = Bugzilla->push_ext->logger;
- my $config = $self->config;
-
- # don't push comments to pulse
- if ($message->routing_key =~ /^comment\./) {
- $logger->debug('AMQP: Ignoring comment');
- return PUSH_RESULT_IGNORED;
- }
+ my ($self, $message) = @_;
+ my $logger = Bugzilla->push_ext->logger;
+ my $config = $self->config;
- # don't push private data
- $self->should_push($message)
- || return PUSH_RESULT_IGNORED;
+ # don't push comments to pulse
+ if ($message->routing_key =~ /^comment\./) {
+ $logger->debug('AMQP: Ignoring comment');
+ return PUSH_RESULT_IGNORED;
+ }
- $self->_bind($message);
+ # don't push private data
+ $self->should_push($message) || return PUSH_RESULT_IGNORED;
- eval {
- # reconnect if required
- if (!$self->{mq}) {
- $self->_connect();
- }
-
- # send message
- $logger->debug('AMQP: Publishing message');
- $self->{mq}->publish(
- $self->{channel},
- $message->routing_key,
- $message->payload,
- {
- exchange => $config->{exchange},
- },
- {
- content_type => 'text/plain',
- content_encoding => '8bit',
- },
- );
- };
- if ($@) {
- return (PUSH_RESULT_TRANSIENT, clean_error($@));
+ $self->_bind($message);
+
+ eval {
+ # reconnect if required
+ if (!$self->{mq}) {
+ $self->_connect();
}
- return PUSH_RESULT_OK;
+ # send message
+ $logger->debug('AMQP: Publishing message');
+ $self->{mq}->publish(
+ $self->{channel}, $message->routing_key, $message->payload,
+ {exchange => $config->{exchange},},
+ {content_type => 'text/plain', content_encoding => '8bit',},
+ );
+ };
+ if ($@) {
+ return (PUSH_RESULT_TRANSIENT, clean_error($@));
+ }
+
+ return PUSH_RESULT_OK;
}
1;
diff --git a/extensions/Push/lib/Connector.disabled/ServiceNow.pm b/extensions/Push/lib/Connector.disabled/ServiceNow.pm
index d0ebdcf10..032e47dde 100644
--- a/extensions/Push/lib/Connector.disabled/ServiceNow.pm
+++ b/extensions/Push/lib/Connector.disabled/ServiceNow.pm
@@ -32,403 +32,411 @@ use MIME::Base64;
use Net::LDAP;
use constant SEND_COMPONENTS => (
- {
- product => 'mozilla.org',
- component => 'Server Operations: Desktop Issues',
- },
+ {product => 'mozilla.org', component => 'Server Operations: Desktop Issues',},
);
sub options {
- return (
- {
- name => 'bugzilla_user',
- label => 'Bugzilla Service-Now User',
- type => 'string',
- default => 'service.now@bugzilla.tld',
- required => 1,
- validate => sub {
- Bugzilla::User->new({ name => $_[0] })
- || die "Invalid Bugzilla user ($_[0])\n";
- },
- },
- {
- name => 'ldap_scheme',
- label => 'Mozilla LDAP Scheme',
- type => 'select',
- values => [ 'LDAP', 'LDAPS' ],
- default => 'LDAPS',
- required => 1,
- },
- {
- name => 'ldap_host',
- label => 'Mozilla LDAP Host',
- type => 'string',
- default => '',
- required => 1,
- },
- {
- name => 'ldap_user',
- label => 'Mozilla LDAP Bind Username',
- type => 'string',
- default => '',
- required => 1,
- },
- {
- name => 'ldap_pass',
- label => 'Mozilla LDAP Password',
- type => 'password',
- default => '',
- required => 1,
- },
- {
- name => 'ldap_poll',
- label => 'Mozilla LDAP Poll Frequency',
- type => 'string',
- default => '3',
- required => 1,
- help => 'minutes',
- validate => sub {
- $_[0] =~ /\D/
- && die "LDAP Poll Frequency must be an integer\n";
- $_[0] == 0
- && die "LDAP Poll Frequency cannot be less than one minute\n";
- },
- },
- {
- name => 'service_now_url',
- label => 'Service Now JSON URL',
- type => 'string',
- default => 'https://mozilladev.service-now.com',
- required => 1,
- help => "Must start with https:// and end with ?JSON",
- validate => sub {
- $_[0] =~ m#^https://[^\.\/]+\.service-now\.com\/#
- || die "Invalid Service Now JSON URL\n";
- $_[0] =~ m#\?JSON$#
- || die "Invalid Service Now JSON URL (must end with ?JSON)\n";
- },
- },
- {
- name => 'service_now_user',
- label => 'Service Now JSON Username',
- type => 'string',
- default => '',
- required => 1,
- },
- {
- name => 'service_now_pass',
- label => 'Service Now JSON Password',
- type => 'password',
- default => '',
- required => 1,
- },
- );
+ return (
+ {
+ name => 'bugzilla_user',
+ label => 'Bugzilla Service-Now User',
+ type => 'string',
+ default => 'service.now@bugzilla.tld',
+ required => 1,
+ validate => sub {
+ Bugzilla::User->new({name => $_[0]}) || die "Invalid Bugzilla user ($_[0])\n";
+ },
+ },
+ {
+ name => 'ldap_scheme',
+ label => 'Mozilla LDAP Scheme',
+ type => 'select',
+ values => ['LDAP', 'LDAPS'],
+ default => 'LDAPS',
+ required => 1,
+ },
+ {
+ name => 'ldap_host',
+ label => 'Mozilla LDAP Host',
+ type => 'string',
+ default => '',
+ required => 1,
+ },
+ {
+ name => 'ldap_user',
+ label => 'Mozilla LDAP Bind Username',
+ type => 'string',
+ default => '',
+ required => 1,
+ },
+ {
+ name => 'ldap_pass',
+ label => 'Mozilla LDAP Password',
+ type => 'password',
+ default => '',
+ required => 1,
+ },
+ {
+ name => 'ldap_poll',
+ label => 'Mozilla LDAP Poll Frequency',
+ type => 'string',
+ default => '3',
+ required => 1,
+ help => 'minutes',
+ validate => sub {
+ $_[0] =~ /\D/ && die "LDAP Poll Frequency must be an integer\n";
+ $_[0] == 0 && die "LDAP Poll Frequency cannot be less than one minute\n";
+ },
+ },
+ {
+ name => 'service_now_url',
+ label => 'Service Now JSON URL',
+ type => 'string',
+ default => 'https://mozilladev.service-now.com',
+ required => 1,
+ help => "Must start with https:// and end with ?JSON",
+ validate => sub {
+ $_[0] =~ m#^https://[^\.\/]+\.service-now\.com\/#
+ || die "Invalid Service Now JSON URL\n";
+ $_[0] =~ m#\?JSON$#
+ || die "Invalid Service Now JSON URL (must end with ?JSON)\n";
+ },
+ },
+ {
+ name => 'service_now_user',
+ label => 'Service Now JSON Username',
+ type => 'string',
+ default => '',
+ required => 1,
+ },
+ {
+ name => 'service_now_pass',
+ label => 'Service Now JSON Password',
+ type => 'password',
+ default => '',
+ required => 1,
+ },
+ );
}
sub options_validate {
- my ($self, $config) = @_;
- my $host = $config->{ldap_host};
- trick_taint($host);
- my $scheme = lc($config->{ldap_scheme});
- eval {
- my $ldap = Net::LDAP->new($host, scheme => $scheme, onerror => 'die', timeout => 5)
- or die $!;
- $ldap->bind($config->{ldap_user}, password => $config->{ldap_pass});
- };
- if ($@) {
- die sprintf("Failed to connect to %s://%s/: %s\n", $scheme, $host, $@);
- }
+ my ($self, $config) = @_;
+ my $host = $config->{ldap_host};
+ trick_taint($host);
+ my $scheme = lc($config->{ldap_scheme});
+ eval {
+ my $ldap
+ = Net::LDAP->new($host, scheme => $scheme, onerror => 'die', timeout => 5)
+ or die $!;
+ $ldap->bind($config->{ldap_user}, password => $config->{ldap_pass});
+ };
+ if ($@) {
+ die sprintf("Failed to connect to %s://%s/: %s\n", $scheme, $host, $@);
+ }
}
my $_instance;
sub init {
- my ($self) = @_;
- $_instance = $self;
+ my ($self) = @_;
+ $_instance = $self;
}
sub load_config {
- my ($self) = @_;
- $self->SUPER::load_config(@_);
- $self->{bugzilla_user} ||= Bugzilla::User->new({ name => $self->config->{bugzilla_user} });
+ my ($self) = @_;
+ $self->SUPER::load_config(@_);
+ $self->{bugzilla_user}
+ ||= Bugzilla::User->new({name => $self->config->{bugzilla_user}});
}
sub should_send {
- my ($self, $message) = @_;
-
- my $data = $message->payload_decoded;
- my $bug_data = $self->_get_bug_data($data)
- || return 0;
-
- # we don't want to send the initial comment in a separate message
- # because we inject it into the inital message
- if (exists $data->{comment} && $data->{comment}->{number} == 0) {
- return 0;
- }
-
- my $target = $data->{event}->{target};
- unless ($target eq 'bug' || $target eq 'comment' || $target eq 'attachment') {
- return 0;
- }
-
- # ensure the service-now user can see the bug
- if (!$self->{bugzilla_user} || !$self->{bugzilla_user}->is_enabled) {
- return 0;
- }
- $self->{bugzilla_user}->can_see_bug($bug_data->{id})
- || return 0;
-
- # don't push changes made by the service-now account
- $data->{event}->{user}->{id} == $self->{bugzilla_user}->id
- && return 0;
-
- # filter based on the component
- my $bug = Bugzilla::Bug->new($bug_data->{id});
- my $send = 0;
- foreach my $rh (SEND_COMPONENTS) {
- if ($bug->product eq $rh->{product} && $bug->component eq $rh->{component}) {
- $send = 1;
- last;
- }
+ my ($self, $message) = @_;
+
+ my $data = $message->payload_decoded;
+ my $bug_data = $self->_get_bug_data($data) || return 0;
+
+ # we don't want to send the initial comment in a separate message
+ # because we inject it into the inital message
+ if (exists $data->{comment} && $data->{comment}->{number} == 0) {
+ return 0;
+ }
+
+ my $target = $data->{event}->{target};
+ unless ($target eq 'bug' || $target eq 'comment' || $target eq 'attachment') {
+ return 0;
+ }
+
+ # ensure the service-now user can see the bug
+ if (!$self->{bugzilla_user} || !$self->{bugzilla_user}->is_enabled) {
+ return 0;
+ }
+ $self->{bugzilla_user}->can_see_bug($bug_data->{id}) || return 0;
+
+ # don't push changes made by the service-now account
+ $data->{event}->{user}->{id} == $self->{bugzilla_user}->id && return 0;
+
+ # filter based on the component
+ my $bug = Bugzilla::Bug->new($bug_data->{id});
+ my $send = 0;
+ foreach my $rh (SEND_COMPONENTS) {
+ if ($bug->product eq $rh->{product} && $bug->component eq $rh->{component}) {
+ $send = 1;
+ last;
}
- return $send;
+ }
+ return $send;
}
sub send {
- my ($self, $message) = @_;
- my $logger = Bugzilla->push_ext->logger;
- my $config = $self->config;
-
- # should_send intiailises bugzilla_user; make sure we return a useful error message
- if (!$self->{bugzilla_user}) {
- return (PUSH_RESULT_TRANSIENT, "Invalid bugzilla-user (" . $self->config->{bugzilla_user} . ")");
- }
-
- # load the bug
- my $data = $message->payload_decoded;
- my $bug_data = $self->_get_bug_data($data);
- my $bug = Bugzilla::Bug->new($bug_data->{id});
-
- if ($message->routing_key eq 'bug.create') {
- # inject the comment into the data for new bugs
- my $comment = shift @{ $bug->comments };
- if ($comment->body ne '') {
- $bug_data->{comment} = Bugzilla::Extension::Push::Serialise->instance->object_to_hash($comment, 1);
- }
-
- } elsif ($message->routing_key eq 'attachment.create') {
- # inject the attachment payload
- my $attachment = Bugzilla::Attachment->new($data->{attachment}->{id});
- $data->{attachment}->{data} = encode_base64($attachment->data);
- }
-
- # map bmo login to ldap login and insert into json payload
- $self->_add_ldap_logins($data, {});
-
- # flatten json data
- $self->_flatten($data);
-
- # add sysparm_action
- $data->{sysparm_action} = 'insert';
-
- if ($logger->debugging) {
- $logger->debug(to_json(ref($data) ? $data : from_json($data), 1));
- }
-
- # send to service-now
- my $request = HTTP::Request->new(POST => $self->config->{service_now_url});
- $request->content_type('application/json');
- $request->content(to_json($data));
- $request->authorization_basic($self->config->{service_now_user}, $self->config->{service_now_pass});
-
- $self->{lwp} ||= LWP::UserAgent->new(agent => Bugzilla->localconfig->{urlbase});
- my $result = $self->{lwp}->request($request);
-
- # http level errors
- if (!$result->is_success) {
- # treat these as transient
- return (PUSH_RESULT_TRANSIENT, $result->status_line);
- }
-
- # empty response
- if (length($result->content) == 0) {
- # malformed request, treat as transient to allow code to fix
- # may also be misconfiguration on servicenow, also transient
- return (PUSH_RESULT_TRANSIENT, "Empty response");
+ my ($self, $message) = @_;
+ my $logger = Bugzilla->push_ext->logger;
+ my $config = $self->config;
+
+# should_send intiailises bugzilla_user; make sure we return a useful error message
+ if (!$self->{bugzilla_user}) {
+ return (PUSH_RESULT_TRANSIENT,
+ "Invalid bugzilla-user (" . $self->config->{bugzilla_user} . ")");
+ }
+
+ # load the bug
+ my $data = $message->payload_decoded;
+ my $bug_data = $self->_get_bug_data($data);
+ my $bug = Bugzilla::Bug->new($bug_data->{id});
+
+ if ($message->routing_key eq 'bug.create') {
+
+ # inject the comment into the data for new bugs
+ my $comment = shift @{$bug->comments};
+ if ($comment->body ne '') {
+ $bug_data->{comment}
+ = Bugzilla::Extension::Push::Serialise->instance->object_to_hash($comment, 1);
}
- # json errors
- my $result_data;
- eval {
- $result_data = from_json($result->content);
- };
- if ($@) {
- return (PUSH_RESULT_TRANSIENT, clean_error($@));
- }
- if ($logger->debugging) {
- $logger->debug(to_json($result_data, 1));
- }
- if (exists $result_data->{error}) {
- return (PUSH_RESULT_ERROR, $result_data->{error});
- };
-
- # malformed/unexpected json response
- if (!exists $result_data->{records}
- || ref($result_data->{records}) ne 'ARRAY'
- || scalar(@{$result_data->{records}}) == 0
- ) {
- return (PUSH_RESULT_ERROR, "Malformed JSON response from ServiceNow: missing or empty 'records' array");
- }
-
- my $record = $result_data->{records}->[0];
- if (ref($record) ne 'HASH') {
- return (PUSH_RESULT_ERROR, "Malformed JSON response from ServiceNow: 'records' array does not contain an object");
- }
+ }
+ elsif ($message->routing_key eq 'attachment.create') {
+
+ # inject the attachment payload
+ my $attachment = Bugzilla::Attachment->new($data->{attachment}->{id});
+ $data->{attachment}->{data} = encode_base64($attachment->data);
+ }
+
+ # map bmo login to ldap login and insert into json payload
+ $self->_add_ldap_logins($data, {});
+
+ # flatten json data
+ $self->_flatten($data);
+
+ # add sysparm_action
+ $data->{sysparm_action} = 'insert';
+
+ if ($logger->debugging) {
+ $logger->debug(to_json(ref($data) ? $data : from_json($data), 1));
+ }
+
+ # send to service-now
+ my $request = HTTP::Request->new(POST => $self->config->{service_now_url});
+ $request->content_type('application/json');
+ $request->content(to_json($data));
+ $request->authorization_basic($self->config->{service_now_user},
+ $self->config->{service_now_pass});
+
+ $self->{lwp} ||= LWP::UserAgent->new(agent => Bugzilla->localconfig->{urlbase});
+ my $result = $self->{lwp}->request($request);
+
+ # http level errors
+ if (!$result->is_success) {
+
+ # treat these as transient
+ return (PUSH_RESULT_TRANSIENT, $result->status_line);
+ }
+
+ # empty response
+ if (length($result->content) == 0) {
+
+ # malformed request, treat as transient to allow code to fix
+ # may also be misconfiguration on servicenow, also transient
+ return (PUSH_RESULT_TRANSIENT, "Empty response");
+ }
+
+ # json errors
+ my $result_data;
+ eval { $result_data = from_json($result->content); };
+ if ($@) {
+ return (PUSH_RESULT_TRANSIENT, clean_error($@));
+ }
+ if ($logger->debugging) {
+ $logger->debug(to_json($result_data, 1));
+ }
+ if (exists $result_data->{error}) {
+ return (PUSH_RESULT_ERROR, $result_data->{error});
+ }
+
+ # malformed/unexpected json response
+ if (!exists $result_data->{records}
+ || ref($result_data->{records}) ne 'ARRAY'
+ || scalar(@{$result_data->{records}}) == 0)
+ {
+ return (PUSH_RESULT_ERROR,
+ "Malformed JSON response from ServiceNow: missing or empty 'records' array");
+ }
+
+ my $record = $result_data->{records}->[0];
+ if (ref($record) ne 'HASH') {
+ return (PUSH_RESULT_ERROR,
+ "Malformed JSON response from ServiceNow: 'records' array does not contain an object"
+ );
+ }
- # sys_id is the unique identifier for this action
- if (!exists $record->{sys_id} || $record->{sys_id} eq '') {
- return (PUSH_RESULT_ERROR, "Malformed JSON response from ServiceNow: 'records object' does not contain a valid sys_id");
- }
+ # sys_id is the unique identifier for this action
+ if (!exists $record->{sys_id} || $record->{sys_id} eq '') {
+ return (PUSH_RESULT_ERROR,
+ "Malformed JSON response from ServiceNow: 'records object' does not contain a valid sys_id"
+ );
+ }
- # success
- return (PUSH_RESULT_OK, "sys_id: " . $record->{sys_id});
+ # success
+ return (PUSH_RESULT_OK, "sys_id: " . $record->{sys_id});
}
sub _get_bug_data {
- my ($self, $data) = @_;
- my $target = $data->{event}->{target};
- if ($target eq 'bug') {
- return $data->{bug};
- } elsif (exists $data->{$target}->{bug}) {
- return $data->{$target}->{bug};
- } else {
- return;
- }
+ my ($self, $data) = @_;
+ my $target = $data->{event}->{target};
+ if ($target eq 'bug') {
+ return $data->{bug};
+ }
+ elsif (exists $data->{$target}->{bug}) {
+ return $data->{$target}->{bug};
+ }
+ else {
+ return;
+ }
}
sub _flatten {
- # service-now expects a flat json object
- my ($self, $data) = @_;
- my $target = $data->{event}->{target};
+ # service-now expects a flat json object
+ my ($self, $data) = @_;
- # delete unnecessary deep objects
- if ($target eq 'comment' || $target eq 'attachment') {
- $data->{$target}->{bug_id} = $data->{$target}->{bug}->{id};
- delete $data->{$target}->{bug};
- }
- delete $data->{event}->{changes};
+ my $target = $data->{event}->{target};
- $self->_flatten_hash($data, $data, 'u');
+ # delete unnecessary deep objects
+ if ($target eq 'comment' || $target eq 'attachment') {
+ $data->{$target}->{bug_id} = $data->{$target}->{bug}->{id};
+ delete $data->{$target}->{bug};
+ }
+ delete $data->{event}->{changes};
+
+ $self->_flatten_hash($data, $data, 'u');
}
sub _flatten_hash {
- my ($self, $base_hash, $hash, $prefix) = @_;
- foreach my $key (keys %$hash) {
- if (ref($hash->{$key}) eq 'HASH') {
- $self->_flatten_hash($base_hash, $hash->{$key}, $prefix . "_$key");
- } elsif (ref($hash->{$key}) ne 'ARRAY') {
- $base_hash->{$prefix . "_$key"} = $hash->{$key};
- }
- delete $hash->{$key};
+ my ($self, $base_hash, $hash, $prefix) = @_;
+ foreach my $key (keys %$hash) {
+ if (ref($hash->{$key}) eq 'HASH') {
+ $self->_flatten_hash($base_hash, $hash->{$key}, $prefix . "_$key");
+ }
+ elsif (ref($hash->{$key}) ne 'ARRAY') {
+ $base_hash->{$prefix . "_$key"} = $hash->{$key};
}
+ delete $hash->{$key};
+ }
}
sub _add_ldap_logins {
- my ($self, $rh, $cache) = @_;
- if (exists $rh->{login}) {
- my $login = $rh->{login};
- $cache->{$login} ||= $self->_bmo_to_ldap($login);
- Bugzilla->push_ext->logger->debug("BMO($login) --> LDAP(" . $cache->{$login} . ")");
- $rh->{ldap} = $cache->{$login};
- }
- foreach my $key (keys %$rh) {
- next unless ref($rh->{$key}) eq 'HASH';
- $self->_add_ldap_logins($rh->{$key}, $cache);
- }
+ my ($self, $rh, $cache) = @_;
+ if (exists $rh->{login}) {
+ my $login = $rh->{login};
+ $cache->{$login} ||= $self->_bmo_to_ldap($login);
+ Bugzilla->push_ext->logger->debug(
+ "BMO($login) --> LDAP(" . $cache->{$login} . ")");
+ $rh->{ldap} = $cache->{$login};
+ }
+ foreach my $key (keys %$rh) {
+ next unless ref($rh->{$key}) eq 'HASH';
+ $self->_add_ldap_logins($rh->{$key}, $cache);
+ }
}
sub _bmo_to_ldap {
- my ($self, $login) = @_;
- my $ldap = $self->_ldap_cache();
+ my ($self, $login) = @_;
+ my $ldap = $self->_ldap_cache();
- return '' unless $login =~ /\@mozilla\.(?:com|org)$/;
+ return '' unless $login =~ /\@mozilla\.(?:com|org)$/;
- foreach my $check ($login, canon_email($login)) {
- # check for matching bugmail entry
- foreach my $mail (keys %$ldap) {
- next unless $ldap->{$mail}{bugmail_canon} eq $check;
- return $mail;
- }
+ foreach my $check ($login, canon_email($login)) {
- # check for matching mail
- if (exists $ldap->{$check}) {
- return $check;
- }
+ # check for matching bugmail entry
+ foreach my $mail (keys %$ldap) {
+ next unless $ldap->{$mail}{bugmail_canon} eq $check;
+ return $mail;
+ }
- # check for matching email alias
- foreach my $mail (sort keys %$ldap) {
- next unless grep { $check eq $_ } @{$ldap->{$mail}{aliases}};
- return $mail;
- }
+ # check for matching mail
+ if (exists $ldap->{$check}) {
+ return $check;
+ }
+
+ # check for matching email alias
+ foreach my $mail (sort keys %$ldap) {
+ next unless grep { $check eq $_ } @{$ldap->{$mail}{aliases}};
+ return $mail;
}
+ }
- return '';
+ return '';
}
sub _ldap_cache {
- my ($self) = @_;
- my $logger = Bugzilla->push_ext->logger;
- my $config = $self->config;
-
- # cache of all ldap entries; updated infrequently
- if (!$self->{ldap_cache_time} || (time) - $self->{ldap_cache_time} > $config->{ldap_poll} * 60) {
- $logger->debug('refreshing LDAP cache');
-
- my $cache = {};
-
- my $host = $config->{ldap_host};
- trick_taint($host);
- my $scheme = lc($config->{ldap_scheme});
- my $ldap = Net::LDAP->new($host, scheme => $scheme, onerror => 'die')
- or die $!;
- $ldap->bind($config->{ldap_user}, password => $config->{ldap_pass});
- foreach my $ldap_base ('o=com,dc=mozilla', 'o=org,dc=mozilla') {
- my $result = $ldap->search(
- base => $ldap_base,
- scope => 'sub',
- filter => '(mail=*)',
- attrs => ['mail', 'bugzillaEmail', 'emailAlias', 'cn', 'employeeType'],
- );
- foreach my $entry ($result->entries) {
- my ($name, $bugMail, $mail, $type) =
- map { $entry->get_value($_) || '' }
- qw(cn bugzillaEmail mail employeeType);
- next if $type eq 'DISABLED';
- $mail = lc $mail;
- $bugMail = '' if $bugMail !~ /\@/;
- $bugMail = trim($bugMail);
- if ($bugMail =~ / /) {
- $bugMail = (grep { /\@/ } split / /, $bugMail)[0];
- }
- $name =~ s/\s+/ /g;
- $cache->{$mail}{name} = trim($name);
- $cache->{$mail}{bugmail} = $bugMail;
- $cache->{$mail}{bugmail_canon} = canon_email($bugMail);
- $cache->{$mail}{aliases} = [];
- foreach my $alias (
- @{$entry->get_value('emailAlias', asref => 1) || []}
- ) {
- push @{$cache->{$mail}{aliases}}, canon_email($alias);
- }
- }
- }
+ my ($self) = @_;
+ my $logger = Bugzilla->push_ext->logger;
+ my $config = $self->config;
+
+ # cache of all ldap entries; updated infrequently
+ if (!$self->{ldap_cache_time}
+ || (time) - $self->{ldap_cache_time} > $config->{ldap_poll} * 60)
+ {
+ $logger->debug('refreshing LDAP cache');
- $self->{ldap_cache} = $cache;
- $self->{ldap_cache_time} = (time);
+ my $cache = {};
+
+ my $host = $config->{ldap_host};
+ trick_taint($host);
+ my $scheme = lc($config->{ldap_scheme});
+ my $ldap = Net::LDAP->new($host, scheme => $scheme, onerror => 'die') or die $!;
+ $ldap->bind($config->{ldap_user}, password => $config->{ldap_pass});
+ foreach my $ldap_base ('o=com,dc=mozilla', 'o=org,dc=mozilla') {
+ my $result = $ldap->search(
+ base => $ldap_base,
+ scope => 'sub',
+ filter => '(mail=*)',
+ attrs => ['mail', 'bugzillaEmail', 'emailAlias', 'cn', 'employeeType'],
+ );
+ foreach my $entry ($result->entries) {
+ my ($name, $bugMail, $mail, $type)
+ = map { $entry->get_value($_) || '' } qw(cn bugzillaEmail mail employeeType);
+ next if $type eq 'DISABLED';
+ $mail = lc $mail;
+ $bugMail = '' if $bugMail !~ /\@/;
+ $bugMail = trim($bugMail);
+ if ($bugMail =~ / /) {
+ $bugMail = (grep {/\@/} split / /, $bugMail)[0];
+ }
+ $name =~ s/\s+/ /g;
+ $cache->{$mail}{name} = trim($name);
+ $cache->{$mail}{bugmail} = $bugMail;
+ $cache->{$mail}{bugmail_canon} = canon_email($bugMail);
+ $cache->{$mail}{aliases} = [];
+ foreach my $alias (@{$entry->get_value('emailAlias', asref => 1) || []}) {
+ push @{$cache->{$mail}{aliases}}, canon_email($alias);
+ }
+ }
}
- return $self->{ldap_cache};
+ $self->{ldap_cache} = $cache;
+ $self->{ldap_cache_time} = (time);
+ }
+
+ return $self->{ldap_cache};
}
1;
diff --git a/extensions/Push/lib/Connector/Base.pm b/extensions/Push/lib/Connector/Base.pm
index ee41bd160..bd46fe6b4 100644
--- a/extensions/Push/lib/Connector/Base.pm
+++ b/extensions/Push/lib/Connector/Base.pm
@@ -18,59 +18,65 @@ use Bugzilla::Extension::Push::BacklogQueue;
use Bugzilla::Extension::Push::Backoff;
sub new {
- my ($class) = @_;
- my $self = {};
- bless($self, $class);
- ($self->{name}) = $class =~ /^.+:(.+)$/;
- $self->init();
- return $self;
+ my ($class) = @_;
+ my $self = {};
+ bless($self, $class);
+ ($self->{name}) = $class =~ /^.+:(.+)$/;
+ $self->init();
+ return $self;
}
sub name {
- my $self = shift;
- return $self->{name};
+ my $self = shift;
+ return $self->{name};
}
sub init {
- my ($self) = @_;
- # abstract
- # perform any initialisation here
- # will be run when created by the web pages or by the daemon
- # and also when the configuration needs to be reloaded
+ my ($self) = @_;
+
+ # abstract
+ # perform any initialisation here
+ # will be run when created by the web pages or by the daemon
+ # and also when the configuration needs to be reloaded
}
sub stop {
- my ($self) = @_;
- # abstract
- # run from the daemon only; disconnect from remote hosts, etc
+ my ($self) = @_;
+
+ # abstract
+ # run from the daemon only; disconnect from remote hosts, etc
}
sub should_send {
- my ($self, $message) = @_;
- # abstract
- # return boolean indicating if the connector will be sending the message.
- # this will be called each message, and should be a very quick simple test.
- # the connector can perform a more exhaustive test in the send() method.
- return 0;
+ my ($self, $message) = @_;
+
+ # abstract
+ # return boolean indicating if the connector will be sending the message.
+ # this will be called each message, and should be a very quick simple test.
+ # the connector can perform a more exhaustive test in the send() method.
+ return 0;
}
sub send {
- my ($self, $message) = @_;
- # abstract
- # deliver the message, daemon only
+ my ($self, $message) = @_;
+
+ # abstract
+ # deliver the message, daemon only
}
sub options {
- my ($self) = @_;
- # abstract
- # return an array of configuration variables
- return ();
+ my ($self) = @_;
+
+ # abstract
+ # return an array of configuration variables
+ return ();
}
sub options_validate {
- my ($class, $config) = @_;
- # abstract, static
- # die if a combination of options in $config is invalid
+ my ($class, $config) = @_;
+
+ # abstract, static
+ # die if a combination of options in $config is invalid
}
#
@@ -78,29 +84,30 @@ sub options_validate {
#
sub config {
- my ($self) = @_;
- if (!$self->{config}) {
- $self->load_config();
- }
- return $self->{config};
+ my ($self) = @_;
+ if (!$self->{config}) {
+ $self->load_config();
+ }
+ return $self->{config};
}
sub load_config {
- my ($self) = @_;
- my $config = Bugzilla::Extension::Push::Config->new($self->name, $self->options);
- $config->load();
- $self->{config} = $config;
+ my ($self) = @_;
+ my $config
+ = Bugzilla::Extension::Push::Config->new($self->name, $self->options);
+ $config->load();
+ $self->{config} = $config;
}
sub enabled {
- my ($self) = @_;
- return $self->config->{enabled} eq 'Enabled';
+ my ($self) = @_;
+ return $self->config->{enabled} eq 'Enabled';
}
sub backlog {
- my ($self) = @_;
- $self->{backlog} ||= Bugzilla::Extension::Push::BacklogQueue->new($self->name);
- return $self->{backlog};
+ my ($self) = @_;
+ $self->{backlog} ||= Bugzilla::Extension::Push::BacklogQueue->new($self->name);
+ return $self->{backlog};
}
1;
diff --git a/extensions/Push/lib/Connector/File.pm b/extensions/Push/lib/Connector/File.pm
index ae249ffe5..7d86953f8 100644
--- a/extensions/Push/lib/Connector/File.pm
+++ b/extensions/Push/lib/Connector/File.pm
@@ -20,51 +20,46 @@ use Encode;
use FileHandle;
sub init {
- my ($self) = @_;
+ my ($self) = @_;
}
sub options {
- return (
- {
- name => 'filename',
- label => 'Filename',
- type => 'string',
- default => 'push.log',
- required => 1,
- validate => sub {
- my $filename = shift;
- $filename =~ m#^/#
- && die "Absolute paths are not permitted\n";
- $filename =~ m#\.\.#
- && die "Relative paths are not permitted\n";
- },
- },
- );
+ return (
+ {
+ name => 'filename',
+ label => 'Filename',
+ type => 'string',
+ default => 'push.log',
+ required => 1,
+ validate => sub {
+ my $filename = shift;
+ $filename =~ m#^/# && die "Absolute paths are not permitted\n";
+ $filename =~ m#\.\.# && die "Relative paths are not permitted\n";
+ },
+ },
+ );
}
sub should_send {
- my ($self, $message) = @_;
- return 1;
+ my ($self, $message) = @_;
+ return 1;
}
sub send {
- my ($self, $message) = @_;
+ my ($self, $message) = @_;
- # pretty-format json payload
- my $payload = $message->payload_decoded;
- $payload = to_json($payload, 1);
+ # pretty-format json payload
+ my $payload = $message->payload_decoded;
+ $payload = to_json($payload, 1);
- my $filename = bz_locations()->{'datadir'} . '/' . $self->config->{filename};
- Bugzilla->push_ext->logger->debug("File: Appending to $filename");
- my $fh = FileHandle->new(">>$filename");
- $fh->binmode(':utf8');
- $fh->print(
- "[" . scalar(localtime) . "]\n" .
- $payload . "\n\n"
- );
- $fh->close;
+ my $filename = bz_locations()->{'datadir'} . '/' . $self->config->{filename};
+ Bugzilla->push_ext->logger->debug("File: Appending to $filename");
+ my $fh = FileHandle->new(">>$filename");
+ $fh->binmode(':utf8');
+ $fh->print("[" . scalar(localtime) . "]\n" . $payload . "\n\n");
+ $fh->close;
- return PUSH_RESULT_OK;
+ return PUSH_RESULT_OK;
}
1;
diff --git a/extensions/Push/lib/Connector/Phabricator.pm b/extensions/Push/lib/Connector/Phabricator.pm
index 33e2bb6ad..61a39e32b 100644
--- a/extensions/Push/lib/Connector/Phabricator.pm
+++ b/extensions/Push/lib/Connector/Phabricator.pm
@@ -29,105 +29,96 @@ use Bugzilla::Extension::Push::Constants;
use Bugzilla::Extension::Push::Util qw(is_public);
sub options {
- return (
- {
- name => 'phabricator_url',
- label => 'Phabricator URL',
- type => 'string',
- default => '',
- required => 1,
- }
- );
+ return ({
+ name => 'phabricator_url',
+ label => 'Phabricator URL',
+ type => 'string',
+ default => '',
+ required => 1,
+ });
}
sub should_send {
- my ( $self, $message ) = @_;
+ my ($self, $message) = @_;
- return 0 unless Bugzilla->params->{phabricator_enabled};
+ return 0 unless Bugzilla->params->{phabricator_enabled};
- # We are only interested currently in bug group, assignee, qa-contact, or cc changes.
- return 0
- unless $message->routing_key =~
- /^(?:attachment|bug)\.modify:.*\b(bug_group|assigned_to|qa_contact|cc)\b/;
+# We are only interested currently in bug group, assignee, qa-contact, or cc changes.
+ return 0
+ unless $message->routing_key
+ =~ /^(?:attachment|bug)\.modify:.*\b(bug_group|assigned_to|qa_contact|cc)\b/;
- my $bug = $self->_get_bug_by_data( $message->payload_decoded ) || return 0;
+ my $bug = $self->_get_bug_by_data($message->payload_decoded) || return 0;
- return $bug->has_attachment_with_mimetype(PHAB_CONTENT_TYPE);
+ return $bug->has_attachment_with_mimetype(PHAB_CONTENT_TYPE);
}
sub send {
- my ( $self, $message ) = @_;
-
- my $logger = Bugzilla->push_ext->logger;
-
- my $data = $message->payload_decoded;
-
- my $bug = $self->_get_bug_by_data($data) || return PUSH_RESULT_OK;
-
- my $is_public = is_public($bug);
-
- my $revisions = get_attachment_revisions($bug);
-
- my $group_change =
- ($message->routing_key =~ /^(?:attachment|bug)\.modify:.*\bbug_group\b/)
- ? 1
- : 0;
-
- foreach my $revision (@$revisions) {
- if ( $is_public && $group_change ) {
- Bugzilla->audit(sprintf(
- 'Making revision %s public for bug %s',
- $revision->id,
- $bug->id
- ));
- $revision->make_public();
- }
- elsif ( !$is_public && $group_change ) {
- Bugzilla->audit(sprintf(
- 'Giving revision %s a custom policy for bug %s',
- $revision->id,
- $bug->id
- ));
- my $set_project_names = [ map { "bmo-" . $_->name } @{ $bug->groups_in } ];
- $revision->make_private($set_project_names);
- }
-
- # Subscriber list of the private revision should always match
- # the bug roles such as assignee, qa contact, and cc members.
- if (!$is_public) {
- Bugzilla->audit(sprintf(
- 'Updating subscribers for %s for bug %s',
- $revision->id,
- $bug->id
- ));
- my $subscribers = get_bug_role_phids($bug);
- $revision->set_subscribers($subscribers) if $subscribers;
- }
-
- $revision->update();
+ my ($self, $message) = @_;
+
+ my $logger = Bugzilla->push_ext->logger;
+
+ my $data = $message->payload_decoded;
+
+ my $bug = $self->_get_bug_by_data($data) || return PUSH_RESULT_OK;
+
+ my $is_public = is_public($bug);
+
+ my $revisions = get_attachment_revisions($bug);
+
+ my $group_change
+ = ($message->routing_key =~ /^(?:attachment|bug)\.modify:.*\bbug_group\b/)
+ ? 1
+ : 0;
+
+ foreach my $revision (@$revisions) {
+ if ($is_public && $group_change) {
+ Bugzilla->audit(
+ sprintf('Making revision %s public for bug %s', $revision->id, $bug->id));
+ $revision->make_public();
}
+ elsif (!$is_public && $group_change) {
+ Bugzilla->audit(sprintf(
+ 'Giving revision %s a custom policy for bug %s',
+ $revision->id, $bug->id
+ ));
+ my $set_project_names = [map { "bmo-" . $_->name } @{$bug->groups_in}];
+ $revision->make_private($set_project_names);
+ }
+
+ # Subscriber list of the private revision should always match
+ # the bug roles such as assignee, qa contact, and cc members.
+ if (!$is_public) {
+ Bugzilla->audit(
+ sprintf('Updating subscribers for %s for bug %s', $revision->id, $bug->id));
+ my $subscribers = get_bug_role_phids($bug);
+ $revision->set_subscribers($subscribers) if $subscribers;
+ }
+
+ $revision->update();
+ }
- return PUSH_RESULT_OK;
+ return PUSH_RESULT_OK;
}
sub _get_bug_by_data {
- my ( $self, $data ) = @_;
- my $bug_data = $self->_get_bug_data($data) || return 0;
- my $bug = Bugzilla::Bug->new( { id => $bug_data->{id} } );
+ my ($self, $data) = @_;
+ my $bug_data = $self->_get_bug_data($data) || return 0;
+ my $bug = Bugzilla::Bug->new({id => $bug_data->{id}});
}
sub _get_bug_data {
- my ( $self, $data ) = @_;
- my $target = $data->{event}->{target};
- if ( $target eq 'bug' ) {
- return $data->{bug};
- }
- elsif ( exists $data->{$target}->{bug} ) {
- return $data->{$target}->{bug};
- }
- else {
- return;
- }
+ my ($self, $data) = @_;
+ my $target = $data->{event}->{target};
+ if ($target eq 'bug') {
+ return $data->{bug};
+ }
+ elsif (exists $data->{$target}->{bug}) {
+ return $data->{$target}->{bug};
+ }
+ else {
+ return;
+ }
}
1;
diff --git a/extensions/Push/lib/Connector/Spark.pm b/extensions/Push/lib/Connector/Spark.pm
index e58ddfbe4..1eb6f22c6 100644
--- a/extensions/Push/lib/Connector/Spark.pm
+++ b/extensions/Push/lib/Connector/Spark.pm
@@ -25,150 +25,154 @@ use LWP::UserAgent;
use List::MoreUtils qw(any);
sub options {
- return (
- {
- name => 'spark_endpoint',
- label => 'Spark API Endpoint',
- type => 'string',
- default => 'https://api.ciscospark.com/v1',
- required => 1,
- },
- {
- name => 'spark_room_id',
- label => 'Spark Room ID',
- type => 'string',
- default => 'bugzilla',
- required => 1,
- },
- {
- name => 'spark_api_key',
- label => 'Spark API Key',
- type => 'string',
- default => '',
- required => 1,
- },
- );
+ return (
+ {
+ name => 'spark_endpoint',
+ label => 'Spark API Endpoint',
+ type => 'string',
+ default => 'https://api.ciscospark.com/v1',
+ required => 1,
+ },
+ {
+ name => 'spark_room_id',
+ label => 'Spark Room ID',
+ type => 'string',
+ default => 'bugzilla',
+ required => 1,
+ },
+ {
+ name => 'spark_api_key',
+ label => 'Spark API Key',
+ type => 'string',
+ default => '',
+ required => 1,
+ },
+ );
}
sub stop {
- my ($self) = @_;
+ my ($self) = @_;
}
sub should_send {
- my ($self, $message) = @_;
-
- my $data = $message->payload_decoded;
- my $bug_data = $self->_get_bug_data($data)
- || return 0;
-
- # Send if bug has cisco-spark keyword
- my $bug = Bugzilla::Bug->new({ id => $bug_data->{id}, cache => 1 });
- return 0 unless $bug->has_keyword('cisco-spark');
-
- if ($message->routing_key eq 'bug.create') {
- return 1;
- }
- else {
- foreach my $change (@{ $data->{event}->{changes} }) {
- # send status and resolution updates
- return 1 if $change->{field} eq 'bug_status' || $change->{field} eq 'resolution';
- # also send if the right keyword has been added to this bug
- if ($change->{field} eq 'keywords' && $change->{added}) {
- my @added = split(/, /, $change->{added});
- return 1 if any { $_ eq 'cisco-spark' } @added;
- }
- }
+ my ($self, $message) = @_;
+
+ my $data = $message->payload_decoded;
+ my $bug_data = $self->_get_bug_data($data) || return 0;
+
+ # Send if bug has cisco-spark keyword
+ my $bug = Bugzilla::Bug->new({id => $bug_data->{id}, cache => 1});
+ return 0 unless $bug->has_keyword('cisco-spark');
+
+ if ($message->routing_key eq 'bug.create') {
+ return 1;
+ }
+ else {
+ foreach my $change (@{$data->{event}->{changes}}) {
+
+ # send status and resolution updates
+ return 1
+ if $change->{field} eq 'bug_status' || $change->{field} eq 'resolution';
+
+ # also send if the right keyword has been added to this bug
+ if ($change->{field} eq 'keywords' && $change->{added}) {
+ my @added = split(/, /, $change->{added});
+ return 1 if any { $_ eq 'cisco-spark' } @added;
+ }
}
+ }
- # and nothing else
- return 0;
+ # and nothing else
+ return 0;
}
sub send {
- my ($self, $message) = @_;
+ my ($self, $message) = @_;
- eval {
- my $data = $message->payload_decoded();
- my $bug_data = $self->_get_bug_data($data);
- my $bug = Bugzilla::Bug->new({ id => $bug_data->{id}, cache => 1 });
+ eval {
+ my $data = $message->payload_decoded();
+ my $bug_data = $self->_get_bug_data($data);
+ my $bug = Bugzilla::Bug->new({id => $bug_data->{id}, cache => 1});
- my $text = "Bug " . $bug->id . " - " . $bug->short_desc . "\n";
- if ($message->routing_key eq 'bug.create') {
- $text = "New " . $text;
+ my $text = "Bug " . $bug->id . " - " . $bug->short_desc . "\n";
+ if ($message->routing_key eq 'bug.create') {
+ $text = "New " . $text;
+ }
+ else {
+ foreach my $change (@{$data->{event}->{changes}}) {
+ if ($change->{field} eq 'bug_status') {
+ $text
+ .= "Status changed: " . $change->{removed} . " -> " . $change->{added} . "\n";
}
- else {
- foreach my $change (@{ $data->{event}->{changes} }) {
- if ($change->{field} eq 'bug_status') {
- $text .= "Status changed: " .
- $change->{removed} . " -> " . $change->{added} . "\n";
- }
- if ($change->{field} eq 'resolution') {
- $text .= "Resolution changed: " .
- ($change->{removed} ? $change->{removed} . " -> " : "") . $change->{added} . "\n";
- }
- }
+ if ($change->{field} eq 'resolution') {
+ $text
+ .= "Resolution changed: "
+ . ($change->{removed} ? $change->{removed} . " -> " : "")
+ . $change->{added} . "\n";
}
- $text .= Bugzilla->localconfig->{urlbase} . "show_bug.cgi?id=" . $bug->id;
+ }
+ }
+ $text .= Bugzilla->localconfig->{urlbase} . "show_bug.cgi?id=" . $bug->id;
- my $room_id = $self->config->{spark_room_id};
- my $message_uri = $self->_spark_uri('messages');
+ my $room_id = $self->config->{spark_room_id};
+ my $message_uri = $self->_spark_uri('messages');
- my $json_data = { roomId => $room_id, text => $text };
+ my $json_data = {roomId => $room_id, text => $text};
- my $headers = HTTP::Headers->new(
- Content_Type => 'application/json'
- );
- my $request = HTTP::Request->new('POST', $message_uri, $headers, encode_json($json_data));
- my $resp = $self->_user_agent->request($request);
+ my $headers = HTTP::Headers->new(Content_Type => 'application/json');
+ my $request
+ = HTTP::Request->new('POST', $message_uri, $headers, encode_json($json_data));
+ my $resp = $self->_user_agent->request($request);
- if ($resp->code != 200) {
- die "Expected HTTP 200 response, got " . $resp->code;
- }
- };
- if ($@) {
- return (PUSH_RESULT_TRANSIENT, clean_error($@));
+ if ($resp->code != 200) {
+ die "Expected HTTP 200 response, got " . $resp->code;
}
+ };
+ if ($@) {
+ return (PUSH_RESULT_TRANSIENT, clean_error($@));
+ }
- return PUSH_RESULT_OK;
+ return PUSH_RESULT_OK;
}
# Private methods
sub _get_bug_data {
- my ($self, $data) = @_;
- my $target = $data->{event}->{target};
- if ($target eq 'bug') {
- return $data->{bug};
- } elsif (exists $data->{$target}->{bug}) {
- return $data->{$target}->{bug};
- } else {
- return;
- }
+ my ($self, $data) = @_;
+ my $target = $data->{event}->{target};
+ if ($target eq 'bug') {
+ return $data->{bug};
+ }
+ elsif (exists $data->{$target}->{bug}) {
+ return $data->{$target}->{bug};
+ }
+ else {
+ return;
+ }
}
sub _user_agent {
- my ($self) = @_;
- my $ua = LWP::UserAgent->new(agent => 'Bugzilla');
- $ua->timeout(10);
- $ua->protocols_allowed(['http', 'https']);
-
- if (my $proxy_url = Bugzilla->params->{proxy_url}) {
- $ua->proxy(['http', 'https'], $proxy_url);
- }
- else {
- $ua->env_proxy();
- }
-
- $ua->default_header(
- 'Authorization' => 'Bearer ' . $self->config->{spark_api_key}
- );
-
- return $ua;
+ my ($self) = @_;
+ my $ua = LWP::UserAgent->new(agent => 'Bugzilla');
+ $ua->timeout(10);
+ $ua->protocols_allowed(['http', 'https']);
+
+ if (my $proxy_url = Bugzilla->params->{proxy_url}) {
+ $ua->proxy(['http', 'https'], $proxy_url);
+ }
+ else {
+ $ua->env_proxy();
+ }
+
+ $ua->default_header(
+ 'Authorization' => 'Bearer ' . $self->config->{spark_api_key});
+
+ return $ua;
}
sub _spark_uri {
- my ($self, $path) = @_;
- return URI->new($self->config->{spark_endpoint} . "/" . $path);
+ my ($self, $path) = @_;
+ return URI->new($self->config->{spark_endpoint} . "/" . $path);
}
1;
diff --git a/extensions/Push/lib/Connectors.pm b/extensions/Push/lib/Connectors.pm
index d3c55d3ca..9a3856c02 100644
--- a/extensions/Push/lib/Connectors.pm
+++ b/extensions/Push/lib/Connectors.pm
@@ -19,94 +19,97 @@ use File::Basename;
use Try::Tiny;
sub new {
- my ($class) = @_;
- my $self = {};
- bless($self, $class);
-
- $self->{names} = [];
- $self->{objects} = {};
- $self->{path} = bz_locations->{'extensionsdir'} . '/Push/lib/Connector';
-
- foreach my $file (glob($self->{path} . '/*.pm')) {
- my $name = basename($file);
- $name =~ s/\.pm$//;
- next if $name eq 'Base';
- if (length($name) > 32) {
- WARN("Ignoring connector '$name': Name longer than 32 characters");
- }
- push @{$self->{names}}, $name;
- TRACE("Found connector '$name'");
+ my ($class) = @_;
+ my $self = {};
+ bless($self, $class);
+
+ $self->{names} = [];
+ $self->{objects} = {};
+ $self->{path} = bz_locations->{'extensionsdir'} . '/Push/lib/Connector';
+
+ foreach my $file (glob($self->{path} . '/*.pm')) {
+ my $name = basename($file);
+ $name =~ s/\.pm$//;
+ next if $name eq 'Base';
+ if (length($name) > 32) {
+ WARN("Ignoring connector '$name': Name longer than 32 characters");
}
+ push @{$self->{names}}, $name;
+ TRACE("Found connector '$name'");
+ }
- return $self;
+ return $self;
}
sub _load {
- my ($self) = @_;
- return if scalar keys %{$self->{objects}};
-
- foreach my $name (@{$self->{names}}) {
- next if exists $self->{objects}->{$name};
- my $file = $self->{path} . "/$name.pm";
- trick_taint($file);
- require $file;
- my $package = "Bugzilla::Extension::Push::Connector::$name";
-
- TRACE("Loading connector '$name'");
- my $old_error_mode = Bugzilla->error_mode;
- Bugzilla->error_mode(ERROR_MODE_DIE);
- try {
- my $connector = $package->new();
- $connector->load_config();
- $self->{objects}->{$name} = $connector;
- } catch {
- ERROR("Connector '$name' failed to load: " . clean_error($_));
- };
- Bugzilla->error_mode($old_error_mode);
+ my ($self) = @_;
+ return if scalar keys %{$self->{objects}};
+
+ foreach my $name (@{$self->{names}}) {
+ next if exists $self->{objects}->{$name};
+ my $file = $self->{path} . "/$name.pm";
+ trick_taint($file);
+ require $file;
+ my $package = "Bugzilla::Extension::Push::Connector::$name";
+
+ TRACE("Loading connector '$name'");
+ my $old_error_mode = Bugzilla->error_mode;
+ Bugzilla->error_mode(ERROR_MODE_DIE);
+ try {
+ my $connector = $package->new();
+ $connector->load_config();
+ $self->{objects}->{$name} = $connector;
}
+ catch {
+ ERROR("Connector '$name' failed to load: " . clean_error($_));
+ };
+ Bugzilla->error_mode($old_error_mode);
+ }
}
sub stop {
- my ($self) = @_;
- foreach my $connector ($self->list) {
- next unless $connector->enabled;
- TRACE("Stopping '" . $connector->name . "'");
- try {
- $connector->stop();
- } catch {
- ERROR("Connector '" . $connector->name . "' failed to stop: " . clean_error($_));
- };
+ my ($self) = @_;
+ foreach my $connector ($self->list) {
+ next unless $connector->enabled;
+ TRACE("Stopping '" . $connector->name . "'");
+ try {
+ $connector->stop();
}
+ catch {
+ ERROR(
+ "Connector '" . $connector->name . "' failed to stop: " . clean_error($_));
+ };
+ }
}
sub reload {
- my ($self) = @_;
- $self->stop();
- $self->{objects} = {};
- $self->_load();
+ my ($self) = @_;
+ $self->stop();
+ $self->{objects} = {};
+ $self->_load();
}
sub names {
- my ($self) = @_;
- return @{$self->{names}};
+ my ($self) = @_;
+ return @{$self->{names}};
}
sub list {
- my ($self) = @_;
- $self->_load();
- return sort { $a->name cmp $b->name } values %{$self->{objects}};
+ my ($self) = @_;
+ $self->_load();
+ return sort { $a->name cmp $b->name } values %{$self->{objects}};
}
sub exists {
- my ($self, $name) = @_;
- $self->by_name($name) ? 1 : 0;
+ my ($self, $name) = @_;
+ $self->by_name($name) ? 1 : 0;
}
sub by_name {
- my ($self, $name) = @_;
- $self->_load();
- return unless exists $self->{objects}->{$name};
- return $self->{objects}->{$name};
+ my ($self, $name) = @_;
+ $self->_load();
+ return unless exists $self->{objects}->{$name};
+ return $self->{objects}->{$name};
}
1;
diff --git a/extensions/Push/lib/Constants.pm b/extensions/Push/lib/Constants.pm
index 09c5e464c..c021b32f4 100644
--- a/extensions/Push/lib/Constants.pm
+++ b/extensions/Push/lib/Constants.pm
@@ -14,14 +14,14 @@ use warnings;
use base 'Exporter';
our @EXPORT = qw(
- PUSH_RESULT_OK
- PUSH_RESULT_IGNORED
- PUSH_RESULT_TRANSIENT
- PUSH_RESULT_ERROR
- PUSH_RESULT_UNKNOWN
- push_result_to_string
-
- POLL_INTERVAL_SECONDS
+ PUSH_RESULT_OK
+ PUSH_RESULT_IGNORED
+ PUSH_RESULT_TRANSIENT
+ PUSH_RESULT_ERROR
+ PUSH_RESULT_UNKNOWN
+ push_result_to_string
+
+ POLL_INTERVAL_SECONDS
);
use constant PUSH_RESULT_OK => 1;
@@ -31,12 +31,12 @@ use constant PUSH_RESULT_ERROR => 4;
use constant PUSH_RESULT_UNKNOWN => 5;
sub push_result_to_string {
- my ($result) = @_;
- return 'OK' if $result == PUSH_RESULT_OK;
- return 'OK-IGNORED' if $result == PUSH_RESULT_IGNORED;
- return 'TRANSIENT-ERROR' if $result == PUSH_RESULT_TRANSIENT;
- return 'FATAL-ERROR' if $result == PUSH_RESULT_ERROR;
- return 'UNKNOWN' if $result == PUSH_RESULT_UNKNOWN;
+ my ($result) = @_;
+ return 'OK' if $result == PUSH_RESULT_OK;
+ return 'OK-IGNORED' if $result == PUSH_RESULT_IGNORED;
+ return 'TRANSIENT-ERROR' if $result == PUSH_RESULT_TRANSIENT;
+ return 'FATAL-ERROR' if $result == PUSH_RESULT_ERROR;
+ return 'UNKNOWN' if $result == PUSH_RESULT_UNKNOWN;
}
use constant POLL_INTERVAL_SECONDS => 30;
diff --git a/extensions/Push/lib/Daemon.pm b/extensions/Push/lib/Daemon.pm
index 7f2459a95..7fb5352ca 100644
--- a/extensions/Push/lib/Daemon.pm
+++ b/extensions/Push/lib/Daemon.pm
@@ -20,7 +20,7 @@ use File::Basename;
use Pod::Usage;
sub start {
- newdaemon();
+ newdaemon();
}
#
@@ -28,70 +28,71 @@ sub start {
#
sub gd_preconfig {
- my $self = shift;
- my $pidfile = $self->{gd_args}{pidfile};
- if (!$pidfile) {
- $pidfile = bz_locations()->{datadir} . '/' . $self->{gd_progname} . ".pid";
- }
- return (pidfile => $pidfile);
+ my $self = shift;
+ my $pidfile = $self->{gd_args}{pidfile};
+ if (!$pidfile) {
+ $pidfile = bz_locations()->{datadir} . '/' . $self->{gd_progname} . ".pid";
+ }
+ return (pidfile => $pidfile);
}
sub gd_getopt {
- my $self = shift;
- $self->SUPER::gd_getopt();
- if ($self->{gd_args}{progname}) {
- $self->{gd_progname} = $self->{gd_args}{progname};
- } else {
- $self->{gd_progname} = basename($0);
- }
- $self->{_original_zero} = $0;
- $0 = $self->{gd_progname};
+ my $self = shift;
+ $self->SUPER::gd_getopt();
+ if ($self->{gd_args}{progname}) {
+ $self->{gd_progname} = $self->{gd_args}{progname};
+ }
+ else {
+ $self->{gd_progname} = basename($0);
+ }
+ $self->{_original_zero} = $0;
+ $0 = $self->{gd_progname};
}
sub gd_postconfig {
- my $self = shift;
- $0 = delete $self->{_original_zero};
+ my $self = shift;
+ $0 = delete $self->{_original_zero};
}
sub gd_more_opt {
- my $self = shift;
- return (
- 'pidfile=s' => \$self->{gd_args}{pidfile},
- 'n=s' => \$self->{gd_args}{progname},
- );
+ my $self = shift;
+ return (
+ 'pidfile=s' => \$self->{gd_args}{pidfile},
+ 'n=s' => \$self->{gd_args}{progname},
+ );
}
sub gd_usage {
- pod2usage({ -verbose => 0, -exitval => 'NOEXIT' });
- return 0;
-};
+ pod2usage({-verbose => 0, -exitval => 'NOEXIT'});
+ return 0;
+}
sub gd_redirect_output {
- my $self = shift;
+ my $self = shift;
- my $filename = bz_locations()->{datadir} . '/' . $self->{gd_progname} . ".log";
+ my $filename = bz_locations()->{datadir} . '/' . $self->{gd_progname} . ".log";
+ open(STDERR, ">>", $filename) or (print "could not open stderr: $!" && exit(1));
+ close(STDOUT);
+ open(STDOUT, ">&", STDERR) or die "redirect STDOUT -> STDERR: $!";
+ $SIG{HUP} = sub {
+ close(STDERR);
open(STDERR, ">>", $filename) or (print "could not open stderr: $!" && exit(1));
- close(STDOUT);
- open(STDOUT, ">&", STDERR) or die "redirect STDOUT -> STDERR: $!";
- $SIG{HUP} = sub {
- close(STDERR);
- open(STDERR, ">>", $filename) or (print "could not open stderr: $!" && exit(1));
- };
+ };
}
sub gd_setup_signals {
- my $self = shift;
- $self->SUPER::gd_setup_signals();
- $SIG{TERM} = sub { $self->gd_quit_event(); }
+ my $self = shift;
+ $self->SUPER::gd_setup_signals();
+ $SIG{TERM} = sub { $self->gd_quit_event(); }
}
sub gd_run {
- my $self = shift;
- $::SIG{__DIE__} = \&Carp::confess if $self->{debug};
- my $push = Bugzilla->push_ext;
- $push->logger->{debug} = $self->{debug};
- $push->is_daemon(1);
- $push->start();
+ my $self = shift;
+ $::SIG{__DIE__} = \&Carp::confess if $self->{debug};
+ my $push = Bugzilla->push_ext;
+ $push->logger->{debug} = $self->{debug};
+ $push->is_daemon(1);
+ $push->start();
}
1;
diff --git a/extensions/Push/lib/Log.pm b/extensions/Push/lib/Log.pm
index 8a35a6cf5..7358477ed 100644
--- a/extensions/Push/lib/Log.pm
+++ b/extensions/Push/lib/Log.pm
@@ -15,32 +15,30 @@ use Bugzilla;
use Bugzilla::Extension::Push::Message;
sub new {
- my ($class) = @_;
- my $self = {};
- bless($self, $class);
- return $self;
+ my ($class) = @_;
+ my $self = {};
+ bless($self, $class);
+ return $self;
}
sub count {
- my ($self) = @_;
- my $dbh = Bugzilla->dbh;
- return $dbh->selectrow_array("SELECT COUNT(*) FROM push_log");
+ my ($self) = @_;
+ my $dbh = Bugzilla->dbh;
+ return $dbh->selectrow_array("SELECT COUNT(*) FROM push_log");
}
sub list {
- my ($self, %args) = @_;
- $args{limit} ||= 10;
- $args{filter} ||= '';
- my @result;
- my $dbh = Bugzilla->dbh;
+ my ($self, %args) = @_;
+ $args{limit} ||= 10;
+ $args{filter} ||= '';
+ my @result;
+ my $dbh = Bugzilla->dbh;
- my $ids = $dbh->selectcol_arrayref("
+ my $ids = $dbh->selectcol_arrayref("
SELECT id
FROM push_log
- ORDER BY processed_ts DESC " .
- $dbh->sql_limit(100)
- );
- return Bugzilla::Extension::Push::LogEntry->new_from_list($ids);
+ ORDER BY processed_ts DESC " . $dbh->sql_limit(100));
+ return Bugzilla::Extension::Push::LogEntry->new_from_list($ids);
}
1;
diff --git a/extensions/Push/lib/LogEntry.pm b/extensions/Push/lib/LogEntry.pm
index f4e894b94..0d9770a8a 100644
--- a/extensions/Push/lib/LogEntry.pm
+++ b/extensions/Push/lib/LogEntry.pm
@@ -26,21 +26,19 @@ use Bugzilla::Extension::Push::Constants;
# initialisation
#
-use constant DB_TABLE => 'push_log';
+use constant DB_TABLE => 'push_log';
use constant DB_COLUMNS => qw(
- id
- message_id
- change_set
- routing_key
- connector
- push_ts
- processed_ts
- result
- data
+ id
+ message_id
+ change_set
+ routing_key
+ connector
+ push_ts
+ processed_ts
+ result
+ data
);
-use constant VALIDATORS => {
- data => \&_check_data,
-};
+use constant VALIDATORS => {data => \&_check_data,};
use constant NAME_FIELD => '';
use constant LIST_ORDER => 'processed_ts DESC';
@@ -48,14 +46,14 @@ use constant LIST_ORDER => 'processed_ts DESC';
# accessors
#
-sub message_id { return $_[0]->{'message_id'}; }
-sub change_set { return $_[0]->{'change_set'}; }
-sub routing_key { return $_[0]->{'routing_key'}; }
-sub connector { return $_[0]->{'connector'}; }
-sub push_ts { return $_[0]->{'push_ts'}; }
+sub message_id { return $_[0]->{'message_id'}; }
+sub change_set { return $_[0]->{'change_set'}; }
+sub routing_key { return $_[0]->{'routing_key'}; }
+sub connector { return $_[0]->{'connector'}; }
+sub push_ts { return $_[0]->{'push_ts'}; }
sub processed_ts { return $_[0]->{'processed_ts'}; }
-sub result { return $_[0]->{'result'}; }
-sub data { return $_[0]->{'data'}; }
+sub result { return $_[0]->{'result'}; }
+sub data { return $_[0]->{'data'}; }
sub result_string { return push_result_to_string($_[0]->result) }
@@ -64,8 +62,8 @@ sub result_string { return push_result_to_string($_[0]->result) }
#
sub _check_data {
- my ($invocant, $value) = @_;
- return $value eq '' ? undef : $value;
+ my ($invocant, $value) = @_;
+ return $value eq '' ? undef : $value;
}
1;
diff --git a/extensions/Push/lib/Logger.pm b/extensions/Push/lib/Logger.pm
index 5d92010ee..ec6dbe497 100644
--- a/extensions/Push/lib/Logger.pm
+++ b/extensions/Push/lib/Logger.pm
@@ -20,42 +20,38 @@ use Bugzilla::Extension::Push::LogEntry;
Log::Log4perl->wrapper_register(__PACKAGE__);
sub info {
- my ($this, $message) = @_;
- INFO($message);
+ my ($this, $message) = @_;
+ INFO($message);
}
sub error {
- my ($this, $message) = @_;
- ERROR($message);
+ my ($this, $message) = @_;
+ ERROR($message);
}
sub debug {
- my ($this, $message) = @_;
- DEBUG($message);
+ my ($this, $message) = @_;
+ DEBUG($message);
}
sub result {
- my ($self, $connector, $message, $result, $data) = @_;
- $data ||= '';
-
- my $log_msg = sprintf
- '%s: Message #%s: %s %s',
- $connector->name,
- $message->message_id,
- push_result_to_string($result),
- $data;
- $self->info($log_msg);
-
- Bugzilla::Extension::Push::LogEntry->create({
- message_id => $message->message_id,
- change_set => $message->change_set,
- routing_key => $message->routing_key,
- connector => $connector->name,
- push_ts => $message->push_ts,
- processed_ts => Bugzilla->dbh->selectrow_array('SELECT NOW()'),
- result => $result,
- data => $data,
- });
+ my ($self, $connector, $message, $result, $data) = @_;
+ $data ||= '';
+
+ my $log_msg = sprintf '%s: Message #%s: %s %s', $connector->name,
+ $message->message_id, push_result_to_string($result), $data;
+ $self->info($log_msg);
+
+ Bugzilla::Extension::Push::LogEntry->create({
+ message_id => $message->message_id,
+ change_set => $message->change_set,
+ routing_key => $message->routing_key,
+ connector => $connector->name,
+ push_ts => $message->push_ts,
+ processed_ts => Bugzilla->dbh->selectrow_array('SELECT NOW()'),
+ result => $result,
+ data => $data,
+ });
}
sub _build_logger { Log::Log4perl->get_logger(__PACKAGE__); }
diff --git a/extensions/Push/lib/Message.pm b/extensions/Push/lib/Message.pm
index 1beb18ef0..3587de1d9 100644
--- a/extensions/Push/lib/Message.pm
+++ b/extensions/Push/lib/Message.pm
@@ -27,50 +27,50 @@ use Encode;
# initialisation
#
-use constant DB_TABLE => 'push';
+use constant DB_TABLE => 'push';
use constant DB_COLUMNS => qw(
- id
- push_ts
- payload
- change_set
- routing_key
+ id
+ push_ts
+ payload
+ change_set
+ routing_key
);
use constant LIST_ORDER => 'push_ts';
use constant VALIDATORS => {
- push_ts => \&_check_push_ts,
- payload => \&_check_payload,
- change_set => \&_check_change_set,
- routing_key => \&_check_routing_key,
+ push_ts => \&_check_push_ts,
+ payload => \&_check_payload,
+ change_set => \&_check_change_set,
+ routing_key => \&_check_routing_key,
};
# this creates an object which doesn't exist on the database
sub new_transient {
- my $invocant = shift;
- my $class = ref($invocant) || $invocant;
- my $object = shift;
- bless($object, $class) if $object;
- return $object;
+ my $invocant = shift;
+ my $class = ref($invocant) || $invocant;
+ my $object = shift;
+ bless($object, $class) if $object;
+ return $object;
}
# take a transient object and commit
sub create_from_transient {
- my ($self) = @_;
- return $self->create($self);
+ my ($self) = @_;
+ return $self->create($self);
}
#
# accessors
#
-sub push_ts { return $_[0]->{'push_ts'}; }
-sub payload { return $_[0]->{'payload'}; }
-sub change_set { return $_[0]->{'change_set'}; }
+sub push_ts { return $_[0]->{'push_ts'}; }
+sub payload { return $_[0]->{'payload'}; }
+sub change_set { return $_[0]->{'change_set'}; }
sub routing_key { return $_[0]->{'routing_key'}; }
-sub message_id { return $_[0]->id; }
+sub message_id { return $_[0]->id; }
sub payload_decoded {
- my ($self) = @_;
- return from_json($self->{'payload'});
+ my ($self) = @_;
+ return from_json($self->{'payload'});
}
#
@@ -78,27 +78,29 @@ sub payload_decoded {
#
sub _check_push_ts {
- my ($invocant, $value) = @_;
- $value ||= Bugzilla->dbh->selectrow_array('SELECT NOW()');
- return $value;
+ my ($invocant, $value) = @_;
+ $value ||= Bugzilla->dbh->selectrow_array('SELECT NOW()');
+ return $value;
}
sub _check_payload {
- my ($invocant, $value) = @_;
- length($value) || ThrowCodeError('push_invalid_payload');
- return $value;
+ my ($invocant, $value) = @_;
+ length($value) || ThrowCodeError('push_invalid_payload');
+ return $value;
}
sub _check_change_set {
- my ($invocant, $value) = @_;
- (defined($value) && length($value)) || ThrowCodeError('push_invalid_change_set');
- return $value;
+ my ($invocant, $value) = @_;
+ (defined($value) && length($value))
+ || ThrowCodeError('push_invalid_change_set');
+ return $value;
}
sub _check_routing_key {
- my ($invocant, $value) = @_;
- (defined($value) && length($value)) || ThrowCodeError('push_invalid_routing_key');
- return $value;
+ my ($invocant, $value) = @_;
+ (defined($value) && length($value))
+ || ThrowCodeError('push_invalid_routing_key');
+ return $value;
}
1;
diff --git a/extensions/Push/lib/Option.pm b/extensions/Push/lib/Option.pm
index a08e4c11d..a8e67714c 100644
--- a/extensions/Push/lib/Option.pm
+++ b/extensions/Push/lib/Option.pm
@@ -21,27 +21,25 @@ use Bugzilla::Util;
# initialisation
#
-use constant DB_TABLE => 'push_options';
+use constant DB_TABLE => 'push_options';
use constant DB_COLUMNS => qw(
- id
- connector
- option_name
- option_value
+ id
+ connector
+ option_name
+ option_value
);
use constant UPDATE_COLUMNS => qw(
- option_value
+ option_value
);
-use constant VALIDATORS => {
- connector => \&_check_connector,
-};
+use constant VALIDATORS => {connector => \&_check_connector,};
use constant LIST_ORDER => 'connector';
#
# accessors
#
-sub connector { return $_[0]->{'connector'}; }
-sub name { return $_[0]->{'option_name'}; }
+sub connector { return $_[0]->{'connector'}; }
+sub name { return $_[0]->{'option_name'}; }
sub value { return $_[0]->{'option_value'}; }
#
@@ -55,12 +53,12 @@ sub set_value { $_[0]->{'option_value'} = $_[1]; }
#
sub _check_connector {
- my ($invocant, $value) = @_;
- $value eq '*'
- || $value eq 'global'
- || Bugzilla->push_ext->connectors->exists($value)
- || ThrowCodeError('push_invalid_connector');
- return $value;
+ my ($invocant, $value) = @_;
+ $value eq '*'
+ || $value eq 'global'
+ || Bugzilla->push_ext->connectors->exists($value)
+ || ThrowCodeError('push_invalid_connector');
+ return $value;
}
1;
diff --git a/extensions/Push/lib/Push.pm b/extensions/Push/lib/Push.pm
index ab640da81..97bac942b 100644
--- a/extensions/Push/lib/Push.pm
+++ b/extensions/Push/lib/Push.pm
@@ -24,269 +24,273 @@ use Bugzilla::Extension::Push::Util;
use DateTime;
use Try::Tiny;
-has 'is_daemon' => (
- is => 'rw',
- default => 0,
-);
+has 'is_daemon' => (is => 'rw', default => 0,);
sub start {
- my ($self) = @_;
- my $connectors = $self->connectors;
- $self->{config_last_modified} = $self->get_config_last_modified();
- $self->{config_last_checked} = (time);
-
- foreach my $connector ($connectors->list) {
- $connector->backlog->reset_backoff();
- }
-
- my $pushd_loop = IO::Async::Loop->new;
- my $main_timer = IO::Async::Timer::Periodic->new(
- first_interval => 0,
- interval => POLL_INTERVAL_SECONDS,
- reschedule => 'drift',
- on_tick => sub {
- if ( $self->_dbh_check() ) {
- $self->_reload();
- try {
- $self->push();
- }
- catch {
- FATAL($_);
- };
- }
- },
+ my ($self) = @_;
+ my $connectors = $self->connectors;
+ $self->{config_last_modified} = $self->get_config_last_modified();
+ $self->{config_last_checked} = (time);
+
+ foreach my $connector ($connectors->list) {
+ $connector->backlog->reset_backoff();
+ }
+
+ my $pushd_loop = IO::Async::Loop->new;
+ my $main_timer = IO::Async::Timer::Periodic->new(
+ first_interval => 0,
+ interval => POLL_INTERVAL_SECONDS,
+ reschedule => 'drift',
+ on_tick => sub {
+ if ($self->_dbh_check()) {
+ $self->_reload();
+ try {
+ $self->push();
+ }
+ catch {
+ FATAL($_);
+ };
+ }
+ },
+ );
+ if (Bugzilla->datadog) {
+ my $dog_timer = IO::Async::Timer::Periodic->new(
+ interval => 120,
+ reschedule => 'drift',
+ on_tick => sub { $self->heartbeat },
);
- if ( Bugzilla->datadog ) {
- my $dog_timer = IO::Async::Timer::Periodic->new(
- interval => 120,
- reschedule => 'drift',
- on_tick => sub { $self->heartbeat },
- );
- $pushd_loop->add($dog_timer);
- $dog_timer->start;
- }
+ $pushd_loop->add($dog_timer);
+ $dog_timer->start;
+ }
- $pushd_loop->add($main_timer);
- $main_timer->start;
- $pushd_loop->run;
+ $pushd_loop->add($main_timer);
+ $main_timer->start;
+ $pushd_loop->run;
}
sub heartbeat {
- my ($self) = @_;
- my $dd = Bugzilla->datadog('bugzilla.pushd');
-
- $dd->gauge('scheduled_jobs', Bugzilla->dbh->selectrow_array('SELECT COUNT(*) FROM push'));
-
- foreach my $connector ($self->connectors->list) {
- if ($connector->enabled) {
- my $lcname = lc $connector->name;
- $dd->gauge("${lcname}.backlog", Bugzilla->dbh->selectrow_array('SELECT COUNT(*) FROM push_backlog WHERE connector = ?', undef, $connector->name));
- }
+ my ($self) = @_;
+ my $dd = Bugzilla->datadog('bugzilla.pushd');
+
+ $dd->gauge('scheduled_jobs',
+ Bugzilla->dbh->selectrow_array('SELECT COUNT(*) FROM push'));
+
+ foreach my $connector ($self->connectors->list) {
+ if ($connector->enabled) {
+ my $lcname = lc $connector->name;
+ $dd->gauge(
+ "${lcname}.backlog",
+ Bugzilla->dbh->selectrow_array(
+ 'SELECT COUNT(*) FROM push_backlog WHERE connector = ?', undef,
+ $connector->name
+ )
+ );
}
+ }
}
sub push {
- my ($self) = @_;
- my $logger = $self->logger;
- my $connectors = $self->connectors;
+ my ($self) = @_;
+ my $logger = $self->logger;
+ my $connectors = $self->connectors;
+
+ my $enabled = 0;
+ foreach my $connector ($connectors->list) {
+ if ($connector->enabled) {
+ $enabled = 1;
+ last;
+ }
+ }
+ return unless $enabled;
- my $enabled = 0;
+ $logger->debug("polling");
+
+ # process each message
+ while (my $message = $self->queue->oldest) {
foreach my $connector ($connectors->list) {
- if ($connector->enabled) {
- $enabled = 1;
- last;
+ next unless $connector->enabled;
+ next unless $connector->should_send($message);
+ $logger->debug("pushing to " . $connector->name);
+
+ my $is_backlogged = $connector->backlog->count;
+
+ if (!$is_backlogged) {
+
+ # connector isn't backlogged, immediate send
+ $logger->debug("immediate send");
+ my ($result, $data);
+ eval { ($result, $data) = $connector->send($message); };
+ if ($@) {
+ $result = PUSH_RESULT_TRANSIENT;
+ $data = clean_error($@);
}
- }
- return unless $enabled;
-
- $logger->debug("polling");
-
- # process each message
- while(my $message = $self->queue->oldest) {
- foreach my $connector ($connectors->list) {
- next unless $connector->enabled;
- next unless $connector->should_send($message);
- $logger->debug("pushing to " . $connector->name);
-
- my $is_backlogged = $connector->backlog->count;
-
- if (!$is_backlogged) {
- # connector isn't backlogged, immediate send
- $logger->debug("immediate send");
- my ($result, $data);
- eval {
- ($result, $data) = $connector->send($message);
- };
- if ($@) {
- $result = PUSH_RESULT_TRANSIENT;
- $data = clean_error($@);
- }
- if (!$result) {
- $logger->error($connector->name . " failed to return a result code");
- $result = PUSH_RESULT_UNKNOWN;
- }
- $logger->result($connector, $message, $result, $data);
-
- if ($result == PUSH_RESULT_TRANSIENT) {
- $is_backlogged = 1;
- }
- }
-
- # if the connector is backlogged, push to the backlog queue
- if ($is_backlogged) {
- INFO('connector is backlogged');
- my $backlog = Bugzilla::Extension::Push::BacklogMessage->create_from_message($message, $connector);
- }
+ if (!$result) {
+ $logger->error($connector->name . " failed to return a result code");
+ $result = PUSH_RESULT_UNKNOWN;
}
+ $logger->result($connector, $message, $result, $data);
- # message processed
- $message->remove_from_db();
+ if ($result == PUSH_RESULT_TRANSIENT) {
+ $is_backlogged = 1;
+ }
+ }
+
+ # if the connector is backlogged, push to the backlog queue
+ if ($is_backlogged) {
+ INFO('connector is backlogged');
+ my $backlog
+ = Bugzilla::Extension::Push::BacklogMessage->create_from_message($message,
+ $connector);
+ }
}
- # process backlog
- foreach my $connector ($connectors->list) {
- next unless $connector->enabled;
- my $message = $connector->backlog->oldest();
- next unless $message;
-
- $logger->debug("processing backlog for " . $connector->name);
- while ($message) {
- my ($result, $data);
- eval {
- ($result, $data) = $connector->send($message);
- };
- if ($@) {
- $result = PUSH_RESULT_TRANSIENT;
- $data = $@;
- }
- $message->inc_attempts($result == PUSH_RESULT_OK ? '' : $data);
- if (!$result) {
- $logger->error($connector->name . " failed to return a result code");
- $result = PUSH_RESULT_UNKNOWN;
- }
- $logger->result($connector, $message, $result, $data);
-
- if ($result == PUSH_RESULT_TRANSIENT) {
- # connector is still down, stop trying
- $connector->backlog->inc_backoff();
- last;
- }
-
- # message was processed
- $message->remove_from_db();
-
- $message = $connector->backlog->oldest();
- }
+ # message processed
+ $message->remove_from_db();
+ }
+
+ # process backlog
+ foreach my $connector ($connectors->list) {
+ next unless $connector->enabled;
+ my $message = $connector->backlog->oldest();
+ next unless $message;
+
+ $logger->debug("processing backlog for " . $connector->name);
+ while ($message) {
+ my ($result, $data);
+ eval { ($result, $data) = $connector->send($message); };
+ if ($@) {
+ $result = PUSH_RESULT_TRANSIENT;
+ $data = $@;
+ }
+ $message->inc_attempts($result == PUSH_RESULT_OK ? '' : $data);
+ if (!$result) {
+ $logger->error($connector->name . " failed to return a result code");
+ $result = PUSH_RESULT_UNKNOWN;
+ }
+ $logger->result($connector, $message, $result, $data);
+
+ if ($result == PUSH_RESULT_TRANSIENT) {
+
+ # connector is still down, stop trying
+ $connector->backlog->inc_backoff();
+ last;
+ }
+
+ # message was processed
+ $message->remove_from_db();
+
+ $message = $connector->backlog->oldest();
}
+ }
}
sub _reload {
- my ($self) = @_;
-
- # check for updated config every 60 seconds
- my $now = (time);
- if ($now - $self->{config_last_checked} < 60) {
- return;
- }
- $self->{config_last_checked} = $now;
-
- $self->logger->debug('Checking for updated configuration');
- if ($self->get_config_last_modified eq $self->{config_last_modified}) {
- return;
- }
- $self->{config_last_modified} = $self->get_config_last_modified();
-
- $self->logger->debug('Configuration has been updated');
- $self->connectors->reload();
+ my ($self) = @_;
+
+ # check for updated config every 60 seconds
+ my $now = (time);
+ if ($now - $self->{config_last_checked} < 60) {
+ return;
+ }
+ $self->{config_last_checked} = $now;
+
+ $self->logger->debug('Checking for updated configuration');
+ if ($self->get_config_last_modified eq $self->{config_last_modified}) {
+ return;
+ }
+ $self->{config_last_modified} = $self->get_config_last_modified();
+
+ $self->logger->debug('Configuration has been updated');
+ $self->connectors->reload();
}
sub get_config_last_modified {
- my ($self) = @_;
- my $options_list = Bugzilla::Extension::Push::Option->match({
- connector => '*',
- option_name => 'last-modified',
+ my ($self) = @_;
+ my $options_list
+ = Bugzilla::Extension::Push::Option->match({
+ connector => '*', option_name => 'last-modified',
});
- if (@$options_list) {
- return $options_list->[0]->value;
- } else {
- return $self->set_config_last_modified();
- }
+ if (@$options_list) {
+ return $options_list->[0]->value;
+ }
+ else {
+ return $self->set_config_last_modified();
+ }
}
sub set_config_last_modified {
- my ($self) = @_;
- my $options_list = Bugzilla::Extension::Push::Option->match({
- connector => '*',
- option_name => 'last-modified',
+ my ($self) = @_;
+ my $options_list
+ = Bugzilla::Extension::Push::Option->match({
+ connector => '*', option_name => 'last-modified',
});
- my $now = DateTime->now->datetime();
- if (@$options_list) {
- $options_list->[0]->set_value($now);
- $options_list->[0]->update();
- } else {
- Bugzilla::Extension::Push::Option->create({
- connector => '*',
- option_name => 'last-modified',
- option_value => $now,
- });
- }
- return $now;
+ my $now = DateTime->now->datetime();
+ if (@$options_list) {
+ $options_list->[0]->set_value($now);
+ $options_list->[0]->update();
+ }
+ else {
+ Bugzilla::Extension::Push::Option->create({
+ connector => '*', option_name => 'last-modified', option_value => $now,
+ });
+ }
+ return $now;
}
sub config {
- my ($self) = @_;
- if (!$self->{config}) {
- $self->{config} = Bugzilla::Extension::Push::Config->new(
- 'global',
- {
- name => 'log_purge',
- label => 'Purge logs older than (days)',
- type => 'string',
- default => '7',
- required => '1',
- validate => sub { $_[0] =~ /\D/ && die "Invalid purge duration (must be numeric)\n"; },
- },
- );
- $self->{config}->load();
- }
- return $self->{config};
+ my ($self) = @_;
+ if (!$self->{config}) {
+ $self->{config} = Bugzilla::Extension::Push::Config->new(
+ 'global',
+ {
+ name => 'log_purge',
+ label => 'Purge logs older than (days)',
+ type => 'string',
+ default => '7',
+ required => '1',
+ validate =>
+ sub { $_[0] =~ /\D/ && die "Invalid purge duration (must be numeric)\n"; },
+ },
+ );
+ $self->{config}->load();
+ }
+ return $self->{config};
}
sub logger {
- my ($self, $value) = @_;
- $self->{logger} = $value if $value;
- return $self->{logger};
+ my ($self, $value) = @_;
+ $self->{logger} = $value if $value;
+ return $self->{logger};
}
sub connectors {
- my ($self, $value) = @_;
- $self->{connectors} = $value if $value;
- return $self->{connectors};
+ my ($self, $value) = @_;
+ $self->{connectors} = $value if $value;
+ return $self->{connectors};
}
sub queue {
- my ($self) = @_;
- $self->{queue} ||= Bugzilla::Extension::Push::Queue->new();
- return $self->{queue};
+ my ($self) = @_;
+ $self->{queue} ||= Bugzilla::Extension::Push::Queue->new();
+ return $self->{queue};
}
sub log {
- my ($self) = @_;
- $self->{log} ||= Bugzilla::Extension::Push::Log->new();
- return $self->{log};
+ my ($self) = @_;
+ $self->{log} ||= Bugzilla::Extension::Push::Log->new();
+ return $self->{log};
}
sub _dbh_check {
- my ($self) = @_;
- eval {
- Bugzilla->dbh->selectrow_array("SELECT 1 FROM push");
- };
- if ($@) {
- $self->logger->error(clean_error($@));
- return 0;
- } else {
- return 1;
- }
+ my ($self) = @_;
+ eval { Bugzilla->dbh->selectrow_array("SELECT 1 FROM push"); };
+ if ($@) {
+ $self->logger->error(clean_error($@));
+ return 0;
+ }
+ else {
+ return 1;
+ }
}
1;
diff --git a/extensions/Push/lib/Queue.pm b/extensions/Push/lib/Queue.pm
index 3ee0321d9..f59423e6a 100644
--- a/extensions/Push/lib/Queue.pm
+++ b/extensions/Push/lib/Queue.pm
@@ -15,59 +15,54 @@ use Bugzilla;
use Bugzilla::Extension::Push::Message;
sub new {
- my ($class) = @_;
- my $self = {};
- bless($self, $class);
- return $self;
+ my ($class) = @_;
+ my $self = {};
+ bless($self, $class);
+ return $self;
}
sub count {
- my ($self) = @_;
- my $dbh = Bugzilla->dbh;
- return $dbh->selectrow_array("SELECT COUNT(*) FROM push");
+ my ($self) = @_;
+ my $dbh = Bugzilla->dbh;
+ return $dbh->selectrow_array("SELECT COUNT(*) FROM push");
}
sub oldest {
- my ($self) = @_;
- my @messages = $self->list(limit => 1);
- return scalar(@messages) ? $messages[0] : undef;
+ my ($self) = @_;
+ my @messages = $self->list(limit => 1);
+ return scalar(@messages) ? $messages[0] : undef;
}
sub by_id {
- my ($self, $id) = @_;
- my @messages = $self->list(
- limit => 1,
- filter => "AND (push.id = $id)",
- );
- return scalar(@messages) ? $messages[0] : undef;
+ my ($self, $id) = @_;
+ my @messages = $self->list(limit => 1, filter => "AND (push.id = $id)",);
+ return scalar(@messages) ? $messages[0] : undef;
}
sub list {
- my ($self, %args) = @_;
- $args{limit} ||= 10;
- $args{filter} ||= '';
- my @result;
- my $dbh = Bugzilla->dbh;
+ my ($self, %args) = @_;
+ $args{limit} ||= 10;
+ $args{filter} ||= '';
+ my @result;
+ my $dbh = Bugzilla->dbh;
- my $sth = $dbh->prepare("
+ my $sth = $dbh->prepare("
SELECT id, push_ts, payload, change_set, routing_key
FROM push
- WHERE (1 = 1) " .
- $args{filter} . "
- ORDER BY push_ts " .
- $dbh->sql_limit($args{limit})
- );
- $sth->execute();
- while (my $row = $sth->fetchrow_hashref()) {
- push @result, Bugzilla::Extension::Push::Message->new({
- id => $row->{id},
- push_ts => $row->{push_ts},
- payload => $row->{payload},
- change_set => $row->{change_set},
- routing_key => $row->{routing_key},
- });
- }
- return @result;
+ WHERE (1 = 1) " . $args{filter} . "
+ ORDER BY push_ts " . $dbh->sql_limit($args{limit}));
+ $sth->execute();
+ while (my $row = $sth->fetchrow_hashref()) {
+ push @result,
+ Bugzilla::Extension::Push::Message->new({
+ id => $row->{id},
+ push_ts => $row->{push_ts},
+ payload => $row->{payload},
+ change_set => $row->{change_set},
+ routing_key => $row->{routing_key},
+ });
+ }
+ return @result;
}
1;
diff --git a/extensions/Push/lib/Serialise.pm b/extensions/Push/lib/Serialise.pm
index bb6834c13..c878ff4d9 100644
--- a/extensions/Push/lib/Serialise.pm
+++ b/extensions/Push/lib/Serialise.pm
@@ -19,140 +19,145 @@ use Scalar::Util 'blessed';
use JSON ();
my $_instance;
+
sub instance {
- $_instance ||= Bugzilla::Extension::Push::Serialise->_new();
- return $_instance;
+ $_instance ||= Bugzilla::Extension::Push::Serialise->_new();
+ return $_instance;
}
sub _new {
- my ($class) = @_;
- my $self = {};
- bless($self, $class);
- return $self;
+ my ($class) = @_;
+ my $self = {};
+ bless($self, $class);
+ return $self;
}
# given an object, serliase to a hash
sub object_to_hash {
- my ($self, $object, $is_shallow) = @_;
-
- my $method = lc(blessed($object));
- $method =~ s/::/_/g;
- $method =~ s/^bugzilla//;
- return unless $self->can($method);
- (my $name = $method) =~ s/^_//;
-
- # check for a cached hash
- my $cache = Bugzilla->request_cache;
- my $cache_id = "push." . ($is_shallow ? 'shallow.' : 'deep.') . $object;
- if (exists($cache->{$cache_id})) {
- return wantarray ? ($cache->{$cache_id}, $name) : $cache->{$cache_id};
- }
-
- # call the right method to serialise to a hash
- my $rh = $self->$method($object, $is_shallow);
-
- # store in cache
- if ($cache_id) {
- $cache->{$cache_id} = $rh;
- }
-
- return wantarray ? ($rh, $name) : $rh;
+ my ($self, $object, $is_shallow) = @_;
+
+ my $method = lc(blessed($object));
+ $method =~ s/::/_/g;
+ $method =~ s/^bugzilla//;
+ return unless $self->can($method);
+ (my $name = $method) =~ s/^_//;
+
+ # check for a cached hash
+ my $cache = Bugzilla->request_cache;
+ my $cache_id = "push." . ($is_shallow ? 'shallow.' : 'deep.') . $object;
+ if (exists($cache->{$cache_id})) {
+ return wantarray ? ($cache->{$cache_id}, $name) : $cache->{$cache_id};
+ }
+
+ # call the right method to serialise to a hash
+ my $rh = $self->$method($object, $is_shallow);
+
+ # store in cache
+ if ($cache_id) {
+ $cache->{$cache_id} = $rh;
+ }
+
+ return wantarray ? ($rh, $name) : $rh;
}
# given a changes hash, return an event hash
sub changes_to_event {
- my ($self, $changes) = @_;
-
- my $event = {};
-
- # create common (created and modified) fields
- $event->{'user'} = $self->object_to_hash(Bugzilla->user);
- my $timestamp =
- $changes->{'timestamp'}
- || Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
- $event->{'time'} = datetime_to_timestamp($timestamp);
-
- foreach my $change (@{$changes->{'changes'}}) {
- if (exists $change->{'field'}) {
- # map undef to emtpy
- hash_undef_to_empty($change);
-
- # custom_fields change from undef to empty, ignore these changes
- return if ($change->{'added'} || "") eq "" &&
- ($change->{'removed'} || "") eq "";
-
- # use saner field serialisation
- my $field = $change->{'field'};
- $change->{'field'} = $field;
-
- if ($field eq 'priority' || $field eq 'target_milestone') {
- $change->{'added'} = _select($change->{'added'});
- $change->{'removed'} = _select($change->{'removed'});
-
- } elsif ($field =~ /^cf_/) {
- $change->{'added'} = _custom_field($field, $change->{'added'});
- $change->{'removed'} = _custom_field($field, $change->{'removed'});
- }
-
- $event->{'changes'} = [] unless exists $event->{'changes'};
- push @{$event->{'changes'}}, $change;
- }
+ my ($self, $changes) = @_;
+
+ my $event = {};
+
+ # create common (created and modified) fields
+ $event->{'user'} = $self->object_to_hash(Bugzilla->user);
+ my $timestamp = $changes->{'timestamp'}
+ || Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
+ $event->{'time'} = datetime_to_timestamp($timestamp);
+
+ foreach my $change (@{$changes->{'changes'}}) {
+ if (exists $change->{'field'}) {
+
+ # map undef to emtpy
+ hash_undef_to_empty($change);
+
+ # custom_fields change from undef to empty, ignore these changes
+ return
+ if ($change->{'added'} || "") eq "" && ($change->{'removed'} || "") eq "";
+
+ # use saner field serialisation
+ my $field = $change->{'field'};
+ $change->{'field'} = $field;
+
+ if ($field eq 'priority' || $field eq 'target_milestone') {
+ $change->{'added'} = _select($change->{'added'});
+ $change->{'removed'} = _select($change->{'removed'});
+
+ }
+ elsif ($field =~ /^cf_/) {
+ $change->{'added'} = _custom_field($field, $change->{'added'});
+ $change->{'removed'} = _custom_field($field, $change->{'removed'});
+ }
+
+ $event->{'changes'} = [] unless exists $event->{'changes'};
+ push @{$event->{'changes'}}, $change;
}
+ }
- return $event;
+ return $event;
}
# bugzilla returns '---' or '--' for single-select fields that have no value
# selected. it makes more sense to return an empty string.
sub _select {
- my ($value) = @_;
- return '' if $value eq '---' or $value eq '--';
- return $value;
+ my ($value) = @_;
+ return '' if $value eq '---' or $value eq '--';
+ return $value;
}
# return an object which serialises to a json boolean, but still acts as a perl
# boolean
sub _boolean {
- my ($value) = @_;
- return $value ? JSON::true : JSON::false;
+ my ($value) = @_;
+ return $value ? JSON::true : JSON::false;
}
sub _string {
- my ($value) = @_;
- return defined($value) ? $value : '';
+ my ($value) = @_;
+ return defined($value) ? $value : '';
}
sub _time {
- my ($value) = @_;
- return defined($value) ? datetime_to_timestamp($value) : undef;
+ my ($value) = @_;
+ return defined($value) ? datetime_to_timestamp($value) : undef;
}
sub _integer {
- my ($value) = @_;
- return defined($value) ? $value + 0 : undef;
+ my ($value) = @_;
+ return defined($value) ? $value + 0 : undef;
}
sub _array {
- my ($value) = @_;
- return defined($value) ? $value : [];
+ my ($value) = @_;
+ return defined($value) ? $value : [];
}
sub _custom_field {
- my ($field, $value) = @_;
- $field = Bugzilla::Field->new({ name => $field }) unless blessed $field;
+ my ($field, $value) = @_;
+ $field = Bugzilla::Field->new({name => $field}) unless blessed $field;
- if ($field->type == FIELD_TYPE_DATETIME) {
- return _time($value);
+ if ($field->type == FIELD_TYPE_DATETIME) {
+ return _time($value);
- } elsif ($field->type == FIELD_TYPE_SINGLE_SELECT) {
- return _select($value);
+ }
+ elsif ($field->type == FIELD_TYPE_SINGLE_SELECT) {
+ return _select($value);
- } elsif ($field->type == FIELD_TYPE_MULTI_SELECT) {
- return _array($value);
+ }
+ elsif ($field->type == FIELD_TYPE_MULTI_SELECT) {
+ return _array($value);
- } else {
- return _string($value);
- }
+ }
+ else {
+ return _string($value);
+ }
}
#
@@ -162,158 +167,148 @@ sub _custom_field {
#
sub _bug {
- my ($self, $bug) = @_;
-
- my $version = $bug->can('version_obj')
- ? $bug->version_obj
- : Bugzilla::Version->new({ name => $bug->version, product => $bug->product_obj });
-
- my $milestone;
- if (_select($bug->target_milestone) ne '') {
- $milestone = $bug->can('target_milestone_obj')
- ? $bug->target_milestone_obj
- : Bugzilla::Milestone->new({ name => $bug->target_milestone, product => $bug->product_obj });
- }
-
- my $status = $bug->can('status_obj')
- ? $bug->status_obj
- : Bugzilla::Status->new({ name => $bug->bug_status });
-
- my $rh = {
- id => _integer($bug->bug_id),
- alias => _string($bug->alias),
- assigned_to => $self->_user($bug->assigned_to),
- classification => _string($bug->classification),
- component => $self->_component($bug->component_obj),
- creation_time => _time($bug->creation_ts || $bug->delta_ts),
- flags => (mapr { $self->_flag($_) } $bug->flags),
- is_private => _boolean(!is_public($bug)),
- keywords => (mapr { _string($_->name) } $bug->keyword_objects),
- last_change_time => _time($bug->delta_ts),
- operating_system => _string($bug->op_sys),
- platform => _string($bug->rep_platform),
- priority => _select($bug->priority),
- product => $self->_product($bug->product_obj),
- qa_contact => $self->_user($bug->qa_contact),
- reporter => $self->_user($bug->reporter),
- resolution => _string($bug->resolution),
- severity => _string($bug->bug_severity),
- status => $self->_status($status),
- summary => _string($bug->short_desc),
- target_milestone => $self->_milestone($milestone),
- url => _string($bug->bug_file_loc),
- version => $self->_version($version),
- whiteboard => _string($bug->status_whiteboard),
- };
-
- # add custom fields
- my @custom_fields = Bugzilla->active_custom_fields(
- { product => $bug->product_obj, component => $bug->component_obj });
- foreach my $field (@custom_fields) {
- my $name = $field->name;
- $rh->{$name} = _custom_field($field, $bug->$name);
- }
-
- return $rh;
+ my ($self, $bug) = @_;
+
+ my $version
+ = $bug->can('version_obj')
+ ? $bug->version_obj
+ : Bugzilla::Version->new(
+ {name => $bug->version, product => $bug->product_obj});
+
+ my $milestone;
+ if (_select($bug->target_milestone) ne '') {
+ $milestone
+ = $bug->can('target_milestone_obj')
+ ? $bug->target_milestone_obj
+ : Bugzilla::Milestone->new(
+ {name => $bug->target_milestone, product => $bug->product_obj});
+ }
+
+ my $status
+ = $bug->can('status_obj')
+ ? $bug->status_obj
+ : Bugzilla::Status->new({name => $bug->bug_status});
+
+ my $rh = {
+ id => _integer($bug->bug_id),
+ alias => _string($bug->alias),
+ assigned_to => $self->_user($bug->assigned_to),
+ classification => _string($bug->classification),
+ component => $self->_component($bug->component_obj),
+ creation_time => _time($bug->creation_ts || $bug->delta_ts),
+ flags => (mapr { $self->_flag($_) } $bug->flags),
+ is_private => _boolean(!is_public($bug)),
+ keywords => (mapr { _string($_->name) } $bug->keyword_objects),
+ last_change_time => _time($bug->delta_ts),
+ operating_system => _string($bug->op_sys),
+ platform => _string($bug->rep_platform),
+ priority => _select($bug->priority),
+ product => $self->_product($bug->product_obj),
+ qa_contact => $self->_user($bug->qa_contact),
+ reporter => $self->_user($bug->reporter),
+ resolution => _string($bug->resolution),
+ severity => _string($bug->bug_severity),
+ status => $self->_status($status),
+ summary => _string($bug->short_desc),
+ target_milestone => $self->_milestone($milestone),
+ url => _string($bug->bug_file_loc),
+ version => $self->_version($version),
+ whiteboard => _string($bug->status_whiteboard),
+ };
+
+ # add custom fields
+ my @custom_fields = Bugzilla->active_custom_fields(
+ {product => $bug->product_obj, component => $bug->component_obj});
+ foreach my $field (@custom_fields) {
+ my $name = $field->name;
+ $rh->{$name} = _custom_field($field, $bug->$name);
+ }
+
+ return $rh;
}
sub _user {
- my ($self, $user) = @_;
- return undef unless $user;
- return {
- id => _integer($user->id),
- login => _string($user->login),
- real_name => _string($user->name),
- };
+ my ($self, $user) = @_;
+ return undef unless $user;
+ return {
+ id => _integer($user->id),
+ login => _string($user->login),
+ real_name => _string($user->name),
+ };
}
sub _component {
- my ($self, $component) = @_;
- return {
- id => _integer($component->id),
- name => _string($component->name),
- };
+ my ($self, $component) = @_;
+ return {id => _integer($component->id), name => _string($component->name),};
}
sub _attachment {
- my ($self, $attachment, $is_shallow) = @_;
- my $rh = {
- id => _integer($attachment->id),
- content_type => _string($attachment->contenttype),
- creation_time => _time($attachment->attached),
- description => _string($attachment->description),
- file_name => _string($attachment->filename),
- flags => (mapr { $self->_flag($_) } $attachment->flags),
- is_obsolete => _boolean($attachment->isobsolete),
- is_patch => _boolean($attachment->ispatch),
- is_private => _boolean(!is_public($attachment)),
- last_change_time => _time($attachment->modification_time),
- };
- if (!$is_shallow) {
- $rh->{bug} = $self->_bug($attachment->bug);
- }
- return $rh;
+ my ($self, $attachment, $is_shallow) = @_;
+ my $rh = {
+ id => _integer($attachment->id),
+ content_type => _string($attachment->contenttype),
+ creation_time => _time($attachment->attached),
+ description => _string($attachment->description),
+ file_name => _string($attachment->filename),
+ flags => (mapr { $self->_flag($_) } $attachment->flags),
+ is_obsolete => _boolean($attachment->isobsolete),
+ is_patch => _boolean($attachment->ispatch),
+ is_private => _boolean(!is_public($attachment)),
+ last_change_time => _time($attachment->modification_time),
+ };
+ if (!$is_shallow) {
+ $rh->{bug} = $self->_bug($attachment->bug);
+ }
+ return $rh;
}
sub _comment {
- my ($self, $comment, $is_shallow) = @_;
- my $rh = {
- id => _integer($comment->bug_id),
- body => _string($comment->body),
- creation_time => _time($comment->creation_ts),
- is_private => _boolean($comment->is_private),
- number => _integer($comment->count),
- };
- if (!$is_shallow) {
- $rh->{bug} = $self->_bug($comment->bug);
- }
- return $rh;
+ my ($self, $comment, $is_shallow) = @_;
+ my $rh = {
+ id => _integer($comment->bug_id),
+ body => _string($comment->body),
+ creation_time => _time($comment->creation_ts),
+ is_private => _boolean($comment->is_private),
+ number => _integer($comment->count),
+ };
+ if (!$is_shallow) {
+ $rh->{bug} = $self->_bug($comment->bug);
+ }
+ return $rh;
}
sub _product {
- my ($self, $product) = @_;
- return {
- id => _integer($product->id),
- name => _string($product->name),
- };
+ my ($self, $product) = @_;
+ return {id => _integer($product->id), name => _string($product->name),};
}
sub _flag {
- my ($self, $flag) = @_;
- my $rh = {
- id => _integer($flag->id),
- name => _string($flag->type->name),
- value => _string($flag->status),
- };
- if ($flag->requestee) {
- $rh->{'requestee'} = $self->_user($flag->requestee);
- }
- return $rh;
+ my ($self, $flag) = @_;
+ my $rh = {
+ id => _integer($flag->id),
+ name => _string($flag->type->name),
+ value => _string($flag->status),
+ };
+ if ($flag->requestee) {
+ $rh->{'requestee'} = $self->_user($flag->requestee);
+ }
+ return $rh;
}
sub _version {
- my ($self, $version) = @_;
- return {
- id => _integer($version->id),
- name => _string($version->name),
- };
+ my ($self, $version) = @_;
+ return {id => _integer($version->id), name => _string($version->name),};
}
sub _milestone {
- my ($self, $milestone) = @_;
- return undef unless $milestone;
- return {
- id => _integer($milestone->id),
- name => _string($milestone->name),
- };
+ my ($self, $milestone) = @_;
+ return undef unless $milestone;
+ return {id => _integer($milestone->id), name => _string($milestone->name),};
}
sub _status {
- my ($self, $status) = @_;
- return {
- id => _integer($status->id),
- name => _string($status->name),
- };
+ my ($self, $status) = @_;
+ return {id => _integer($status->id), name => _string($status->name),};
}
1;
diff --git a/extensions/Push/lib/Util.pm b/extensions/Push/lib/Util.pm
index bda6331bf..34a0879ea 100644
--- a/extensions/Push/lib/Util.pm
+++ b/extensions/Push/lib/Util.pm
@@ -22,142 +22,147 @@ use Time::HiRes;
use base qw(Exporter);
our @EXPORT = qw(
- datetime_to_timestamp
- debug_dump
- get_first_value
- hash_undef_to_empty
- is_public
- mapr
- clean_error
- change_set_id
- canon_email
- to_json from_json
+ datetime_to_timestamp
+ debug_dump
+ get_first_value
+ hash_undef_to_empty
+ is_public
+ mapr
+ clean_error
+ change_set_id
+ canon_email
+ to_json from_json
);
# returns true if the specified object is public
sub is_public {
- my ($object) = @_;
+ my ($object) = @_;
- my $default_user = Bugzilla::User->new();
+ my $default_user = Bugzilla::User->new();
- if ($object->isa('Bugzilla::Bug')) {
- return unless $default_user->can_see_bug($object->bug_id);
- return 1;
+ if ($object->isa('Bugzilla::Bug')) {
+ return unless $default_user->can_see_bug($object->bug_id);
+ return 1;
- } elsif ($object->isa('Bugzilla::Comment')) {
- return if $object->is_private;
- return unless $default_user->can_see_bug($object->bug_id);
- return 1;
+ }
+ elsif ($object->isa('Bugzilla::Comment')) {
+ return if $object->is_private;
+ return unless $default_user->can_see_bug($object->bug_id);
+ return 1;
- } elsif ($object->isa('Bugzilla::Attachment')) {
- return if $object->isprivate;
- return unless $default_user->can_see_bug($object->bug_id);
- return 1;
+ }
+ elsif ($object->isa('Bugzilla::Attachment')) {
+ return if $object->isprivate;
+ return unless $default_user->can_see_bug($object->bug_id);
+ return 1;
- } else {
- warn "Unsupported class " . blessed($object) . " passed to is_public()\n";
- }
+ }
+ else {
+ warn "Unsupported class " . blessed($object) . " passed to is_public()\n";
+ }
- return 1;
+ return 1;
}
# return the first existing value from the hashref for the given list of keys
sub get_first_value {
- my ($rh, @keys) = @_;
- foreach my $field (@keys) {
- return $rh->{$field} if exists $rh->{$field};
- }
- return;
+ my ($rh, @keys) = @_;
+ foreach my $field (@keys) {
+ return $rh->{$field} if exists $rh->{$field};
+ }
+ return;
}
# wrapper for map that works on array references
sub mapr(&$) {
- my ($filter, $ra) = @_;
- my @result = map(&$filter, @$ra);
- return \@result;
+ my ($filter, $ra) = @_;
+ my @result = map(&$filter, @$ra);
+ return \@result;
}
# convert datetime string (from db) to a UTC json friendly datetime
sub datetime_to_timestamp {
- my ($datetime_string) = @_;
- return '' unless $datetime_string;
- return datetime_from($datetime_string, 'UTC')->datetime();
+ my ($datetime_string) = @_;
+ return '' unless $datetime_string;
+ return datetime_from($datetime_string, 'UTC')->datetime();
}
# replaces all undef values in a hashref with an empty string (deep)
sub hash_undef_to_empty {
- my ($rh) = @_;
- foreach my $key (keys %$rh) {
- my $value = $rh->{$key};
- if (!defined($value)) {
- $rh->{$key} = '';
- } elsif (ref($value) eq 'HASH') {
- hash_undef_to_empty($value);
- }
+ my ($rh) = @_;
+ foreach my $key (keys %$rh) {
+ my $value = $rh->{$key};
+ if (!defined($value)) {
+ $rh->{$key} = '';
+ }
+ elsif (ref($value) eq 'HASH') {
+ hash_undef_to_empty($value);
}
+ }
}
# debugging methods
sub debug_dump {
- my ($object) = @_;
- local $Data::Dumper::Sortkeys = 1;
- my $output = Dumper($object);
- $output =~ s/</&lt;/g;
- print "<pre>$output</pre>";
+ my ($object) = @_;
+ local $Data::Dumper::Sortkeys = 1;
+ my $output = Dumper($object);
+ $output =~ s/</&lt;/g;
+ print "<pre>$output</pre>";
}
# removes stacktrace and "at /some/path ..." from errors
sub clean_error {
- my ($error) = @_;
- my $path = bz_locations->{'extensionsdir'};
- $error = $1 if $error =~ /^(.+?) at \Q$path/s;
- $path = '/loader/0x';
- $error = $1 if $error =~ /^(.+?) at \Q$path/s;
- $error =~ s/(^\s+|\s+$)//g;
- return $error;
+ my ($error) = @_;
+ my $path = bz_locations->{'extensionsdir'};
+ $error = $1 if $error =~ /^(.+?) at \Q$path/s;
+ $path = '/loader/0x';
+ $error = $1 if $error =~ /^(.+?) at \Q$path/s;
+ $error =~ s/(^\s+|\s+$)//g;
+ return $error;
}
# generate a new change_set id
sub change_set_id {
- return "$$." . Time::HiRes::time();
+ return "$$." . Time::HiRes::time();
}
# remove guff from email addresses
sub clean_email {
- my $email = shift;
- $email = trim($email);
- $email = $1 if $email =~ /^(\S+)/;
- $email =~ s/&#64;/@/;
- $email = lc $email;
- return $email;
+ my $email = shift;
+ $email = trim($email);
+ $email = $1 if $email =~ /^(\S+)/;
+ $email =~ s/&#64;/@/;
+ $email = lc $email;
+ return $email;
}
# resolve to canonised email form
# eg. glob+bmo@mozilla.com --> glob@mozilla.com
sub canon_email {
- my $email = shift;
- $email = clean_email($email);
- $email =~ s/^([^\+]+)\+[^\@]+(\@.+)$/$1$2/;
- return $email;
+ my $email = shift;
+ $email = clean_email($email);
+ $email =~ s/^([^\+]+)\+[^\@]+(\@.+)$/$1$2/;
+ return $email;
}
# json helpers
sub to_json {
- my ($object, $pretty) = @_;
- if ($pretty) {
- return decode('utf8', JSON->new->utf8(1)->pretty(1)->encode($object));
- } else {
- return JSON->new->ascii(1)->shrink(1)->encode($object);
- }
+ my ($object, $pretty) = @_;
+ if ($pretty) {
+ return decode('utf8', JSON->new->utf8(1)->pretty(1)->encode($object));
+ }
+ else {
+ return JSON->new->ascii(1)->shrink(1)->encode($object);
+ }
}
sub from_json {
- my ($json) = @_;
- if (utf8::is_utf8($json)) {
- $json = encode('utf8', $json);
- }
- return JSON->new->utf8(1)->decode($json);
+ my ($json) = @_;
+ if (utf8::is_utf8($json)) {
+ $json = encode('utf8', $json);
+ }
+ return JSON->new->utf8(1)->decode($json);
}
1;
diff --git a/extensions/Push/template/en/default/setup/strings.txt.pl b/extensions/Push/template/en/default/setup/strings.txt.pl
index 6f41f26d5..7bdab77bd 100644
--- a/extensions/Push/template/en/default/setup/strings.txt.pl
+++ b/extensions/Push/template/en/default/setup/strings.txt.pl
@@ -10,6 +10,6 @@ use warnings;
use 5.10.1;
%strings = (
- feature_push_amqp => 'Push: AMQP Support',
- feature_push_stomp => 'Push: STOMP Support',
+ feature_push_amqp => 'Push: AMQP Support',
+ feature_push_stomp => 'Push: STOMP Support',
);
diff --git a/extensions/REMO/Config.pm b/extensions/REMO/Config.pm
index a679d64a0..a1d8327de 100644
--- a/extensions/REMO/Config.pm
+++ b/extensions/REMO/Config.pm
@@ -28,10 +28,8 @@ use warnings;
use constant NAME => 'REMO';
-use constant REQUIRED_MODULES => [
-];
+use constant REQUIRED_MODULES => [];
-use constant OPTIONAL_MODULES => [
-];
+use constant OPTIONAL_MODULES => [];
__PACKAGE__->NAME;
diff --git a/extensions/REMO/Extension.pm b/extensions/REMO/Extension.pm
index df4e70c80..7ca74e081 100644
--- a/extensions/REMO/Extension.pm
+++ b/extensions/REMO/Extension.pm
@@ -37,304 +37,311 @@ use List::Util qw(first);
our $VERSION = '0.01';
sub page_before_template {
- my ($self, $args) = @_;
- my $page = $args->{'page_id'};
- my $vars = $args->{'vars'};
+ my ($self, $args) = @_;
+ my $page = $args->{'page_id'};
+ my $vars = $args->{'vars'};
- if ($page eq 'remo-form-payment.html') {
- _remo_form_payment($vars);
- }
+ if ($page eq 'remo-form-payment.html') {
+ _remo_form_payment($vars);
+ }
}
sub _remo_form_payment {
- my ($vars) = @_;
- my $input = Bugzilla->input_params;
-
- my $user = Bugzilla->login(LOGIN_REQUIRED);
-
- if ($input->{'action'} eq 'commit') {
- my $template = Bugzilla->template;
- my $cgi = Bugzilla->cgi;
- my $dbh = Bugzilla->dbh;
-
- my $bug_id = $input->{'bug_id'};
- detaint_natural($bug_id);
- my $bug = Bugzilla::Bug->check($bug_id);
-
- # Detect if the user already used the same form to submit again
- my $token = trim($input->{'token'});
- if ($token) {
- my ($creator_id, $date, $old_attach_id) = Bugzilla::Token::GetTokenData($token);
- if (!$creator_id
- || $creator_id != $user->id
- || $old_attach_id !~ "^remo_form_payment:")
- {
- # The token is invalid.
- ThrowUserError('token_does_not_exist');
- }
-
- $old_attach_id =~ s/^remo_form_payment://;
- if ($old_attach_id) {
- ThrowUserError('remo_payment_cancel_dupe',
- { bugid => $bug_id, attachid => $old_attach_id });
- }
- }
-
- # Make sure the user can attach to this bug
- if (!$bug->user->{'canedit'}) {
- ThrowUserError("remo_payment_bug_edit_denied",
- { bug_id => $bug->id });
- }
-
- # Make sure the bug is under the correct product/component
- if ($bug->product ne 'Mozilla Reps'
- || $bug->component ne 'Budget Requests')
- {
- ThrowUserError('remo_payment_invalid_product');
- }
-
- my ($timestamp) = $dbh->selectrow_array("SELECT NOW()");
-
- $dbh->bz_start_transaction;
-
- # Create the comment to be added based on the form fields from rep-payment-form
- my $comment;
- $template->process("pages/comment-remo-form-payment.txt.tmpl", $vars, \$comment)
- || ThrowTemplateError($template->error());
- $bug->add_comment($comment, { isprivate => 0 });
-
- # Attach expense report
- # FIXME: Would be nice to be able to have the above prefilled comment and
- # the following attachments all show up under a single comment. But the longdescs
- # table can only handle one attach_id per comment currently. At least only one
- # email is sent the way it is done below.
- my $attachment;
- if (defined $cgi->upload('expenseform')) {
- # Determine content-type
- my $content_type = $cgi->uploadInfo($cgi->param('expenseform'))->{'Content-Type'};
-
- $attachment = Bugzilla::Attachment->create(
- { bug => $bug,
- creation_ts => $timestamp,
- data => $cgi->upload('expenseform'),
- description => 'Expense Form',
- filename => scalar $cgi->upload('expenseform'),
- ispatch => 0,
- isprivate => 0,
- mimetype => $content_type,
- });
-
- # Insert comment for attachment
- $bug->add_comment('', { isprivate => 0,
- type => CMT_ATTACHMENT_CREATED,
- extra_data => $attachment->id });
- }
-
- # Attach receipts file
- if (defined $cgi->upload("receipts")) {
- # Determine content-type
- my $content_type = $cgi->uploadInfo($cgi->param("receipts"))->{'Content-Type'};
-
- $attachment = Bugzilla::Attachment->create(
- { bug => $bug,
- creation_ts => $timestamp,
- data => $cgi->upload('receipts'),
- description => "Receipts",
- filename => scalar $cgi->upload("receipts"),
- ispatch => 0,
- isprivate => 0,
- mimetype => $content_type,
- });
-
- # Insert comment for attachment
- $bug->add_comment('', { isprivate => 0,
- type => CMT_ATTACHMENT_CREATED,
- extra_data => $attachment->id });
- }
-
- $bug->update($timestamp);
-
- if ($token) {
- trick_taint($token);
- $dbh->do('UPDATE tokens SET eventdata = ? WHERE token = ?', undef,
- ("remo_form_payment:" . $attachment->id, $token));
- }
-
- $dbh->bz_commit_transaction;
-
- # Define the variables and functions that will be passed to the UI template.
- $vars->{'attachment'} = $attachment;
- $vars->{'bugs'} = [ new Bugzilla::Bug($bug_id) ];
- $vars->{'header_done'} = 1;
- $vars->{'contenttypemethod'} = 'autodetect';
-
- my $recipients = { 'changer' => $user };
- $vars->{'sent_bugmail'} = Bugzilla::BugMail::Send($bug_id, $recipients);
-
- print $cgi->header();
- # Generate and return the UI (HTML page) from the appropriate template.
- $template->process("attachment/created.html.tmpl", $vars)
- || ThrowTemplateError($template->error());
- exit;
+ my ($vars) = @_;
+ my $input = Bugzilla->input_params;
+
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
+
+ if ($input->{'action'} eq 'commit') {
+ my $template = Bugzilla->template;
+ my $cgi = Bugzilla->cgi;
+ my $dbh = Bugzilla->dbh;
+
+ my $bug_id = $input->{'bug_id'};
+ detaint_natural($bug_id);
+ my $bug = Bugzilla::Bug->check($bug_id);
+
+ # Detect if the user already used the same form to submit again
+ my $token = trim($input->{'token'});
+ if ($token) {
+ my ($creator_id, $date, $old_attach_id) = Bugzilla::Token::GetTokenData($token);
+ if (!$creator_id
+ || $creator_id != $user->id
+ || $old_attach_id !~ "^remo_form_payment:")
+ {
+ # The token is invalid.
+ ThrowUserError('token_does_not_exist');
+ }
+
+ $old_attach_id =~ s/^remo_form_payment://;
+ if ($old_attach_id) {
+ ThrowUserError('remo_payment_cancel_dupe',
+ {bugid => $bug_id, attachid => $old_attach_id});
+ }
}
- else {
- $vars->{'token'} = issue_session_token('remo_form_payment:');
+
+ # Make sure the user can attach to this bug
+ if (!$bug->user->{'canedit'}) {
+ ThrowUserError("remo_payment_bug_edit_denied", {bug_id => $bug->id});
+ }
+
+ # Make sure the bug is under the correct product/component
+ if ($bug->product ne 'Mozilla Reps' || $bug->component ne 'Budget Requests') {
+ ThrowUserError('remo_payment_invalid_product');
+ }
+
+ my ($timestamp) = $dbh->selectrow_array("SELECT NOW()");
+
+ $dbh->bz_start_transaction;
+
+ # Create the comment to be added based on the form fields from rep-payment-form
+ my $comment;
+ $template->process("pages/comment-remo-form-payment.txt.tmpl", $vars, \$comment)
+ || ThrowTemplateError($template->error());
+ $bug->add_comment($comment, {isprivate => 0});
+
+ # Attach expense report
+ # FIXME: Would be nice to be able to have the above prefilled comment and
+ # the following attachments all show up under a single comment. But the longdescs
+ # table can only handle one attach_id per comment currently. At least only one
+ # email is sent the way it is done below.
+ my $attachment;
+ if (defined $cgi->upload('expenseform')) {
+
+ # Determine content-type
+ my $content_type
+ = $cgi->uploadInfo($cgi->param('expenseform'))->{'Content-Type'};
+
+ $attachment = Bugzilla::Attachment->create({
+ bug => $bug,
+ creation_ts => $timestamp,
+ data => $cgi->upload('expenseform'),
+ description => 'Expense Form',
+ filename => scalar $cgi->upload('expenseform'),
+ ispatch => 0,
+ isprivate => 0,
+ mimetype => $content_type,
+ });
+
+ # Insert comment for attachment
+ $bug->add_comment('',
+ {isprivate => 0, type => CMT_ATTACHMENT_CREATED, extra_data => $attachment->id}
+ );
+ }
+
+ # Attach receipts file
+ if (defined $cgi->upload("receipts")) {
+
+ # Determine content-type
+ my $content_type = $cgi->uploadInfo($cgi->param("receipts"))->{'Content-Type'};
+
+ $attachment = Bugzilla::Attachment->create({
+ bug => $bug,
+ creation_ts => $timestamp,
+ data => $cgi->upload('receipts'),
+ description => "Receipts",
+ filename => scalar $cgi->upload("receipts"),
+ ispatch => 0,
+ isprivate => 0,
+ mimetype => $content_type,
+ });
+
+ # Insert comment for attachment
+ $bug->add_comment('',
+ {isprivate => 0, type => CMT_ATTACHMENT_CREATED, extra_data => $attachment->id}
+ );
+ }
+
+ $bug->update($timestamp);
+
+ if ($token) {
+ trick_taint($token);
+ $dbh->do('UPDATE tokens SET eventdata = ? WHERE token = ?',
+ undef, ("remo_form_payment:" . $attachment->id, $token));
}
+
+ $dbh->bz_commit_transaction;
+
+ # Define the variables and functions that will be passed to the UI template.
+ $vars->{'attachment'} = $attachment;
+ $vars->{'bugs'} = [new Bugzilla::Bug($bug_id)];
+ $vars->{'header_done'} = 1;
+ $vars->{'contenttypemethod'} = 'autodetect';
+
+ my $recipients = {'changer' => $user};
+ $vars->{'sent_bugmail'} = Bugzilla::BugMail::Send($bug_id, $recipients);
+
+ print $cgi->header();
+
+ # Generate and return the UI (HTML page) from the appropriate template.
+ $template->process("attachment/created.html.tmpl", $vars)
+ || ThrowTemplateError($template->error());
+ exit;
+ }
+ else {
+ $vars->{'token'} = issue_session_token('remo_form_payment:');
+ }
}
my %CSV_COLUMNS = (
- "Date Required" => { pos => 1, value => '%cf_due_date' },
- "Requester" => { pos => 2, value => 'Rizki Kelimutu' },
- "Email 1" => { pos => 3, value => 'rkelimutu@mozilla.com' },
- "Mozilla Space" => { pos => 4, value => 'Remote' },
- "Team" => { pos => 5, value => 'Participation' },
- "Department Code" => { pos => 6, value => '1002' },
- "Purpose" => { pos => 7, value => 'Rep event: %eventpage' },
- "Item 1" => { pos => 8 },
- "Item 2" => { pos => 9 },
- "Item 3" => { pos => 10 },
- "Item 4" => { pos => 11 },
- "Item 5" => { pos => 12 },
- "Item 6" => { pos => 13 },
- "Item 7" => { pos => 14 },
- "Item 8" => { pos => 15 },
- "Item 9" => { pos => 16 },
- "Item 10" => { pos => 17 },
- "Item 11" => { pos => 18 },
- "Item 12" => { pos => 19 },
- "Item 13" => { pos => 20 },
- "Item 14" => { pos => 21 },
- "Recipient Name" => { pos => 22, value => '%shiptofirstname %shiptolastname' },
- "Email 2" => { pos => 23, value => sub { Bugzilla->user->email } },
- "Address 1" => { pos => 24, value => '%shiptoaddress1' },
- "Address 2" => { pos => 25, value => '%shiptoaddress2' },
- "City" => { pos => 26, value => '%shiptocity' },
- "State" => { pos => 27, value => '%shiptostate' },
- "Zip" => { pos => 28, value => '%shiptopcode' },
- "Country" => { pos => 29, value => '%shiptocountry' },
- "Phone number" => { pos => 30, value => '%shiptophone' },
- "Notes" => { pos => 31, value => '%shipadditional' },
+ "Date Required" => {pos => 1, value => '%cf_due_date'},
+ "Requester" => {pos => 2, value => 'Rizki Kelimutu'},
+ "Email 1" => {pos => 3, value => 'rkelimutu@mozilla.com'},
+ "Mozilla Space" => {pos => 4, value => 'Remote'},
+ "Team" => {pos => 5, value => 'Participation'},
+ "Department Code" => {pos => 6, value => '1002'},
+ "Purpose" => {pos => 7, value => 'Rep event: %eventpage'},
+ "Item 1" => {pos => 8},
+ "Item 2" => {pos => 9},
+ "Item 3" => {pos => 10},
+ "Item 4" => {pos => 11},
+ "Item 5" => {pos => 12},
+ "Item 6" => {pos => 13},
+ "Item 7" => {pos => 14},
+ "Item 8" => {pos => 15},
+ "Item 9" => {pos => 16},
+ "Item 10" => {pos => 17},
+ "Item 11" => {pos => 18},
+ "Item 12" => {pos => 19},
+ "Item 13" => {pos => 20},
+ "Item 14" => {pos => 21},
+ "Recipient Name" => {pos => 22, value => '%shiptofirstname %shiptolastname'},
+ "Email 2" => {
+ pos => 23,
+ value => sub { Bugzilla->user->email }
+ },
+ "Address 1" => {pos => 24, value => '%shiptoaddress1'},
+ "Address 2" => {pos => 25, value => '%shiptoaddress2'},
+ "City" => {pos => 26, value => '%shiptocity'},
+ "State" => {pos => 27, value => '%shiptostate'},
+ "Zip" => {pos => 28, value => '%shiptopcode'},
+ "Country" => {pos => 29, value => '%shiptocountry'},
+ "Phone number" => {pos => 30, value => '%shiptophone'},
+ "Notes" => {pos => 31, value => '%shipadditional'},
);
sub _expand_value {
- my $value = shift;
- if (ref $value && ref $value eq 'CODE') {
- return $value->();
- }
- else {
- my $cgi = Bugzilla->cgi;
- $value =~ s/%(\w+)/$cgi->param($1)/ge;
- return $value;
- }
+ my $value = shift;
+ if (ref $value && ref $value eq 'CODE') {
+ return $value->();
+ }
+ else {
+ my $cgi = Bugzilla->cgi;
+ $value =~ s/%(\w+)/$cgi->param($1)/ge;
+ return $value;
+ }
}
sub _csv_quote {
- my $s = shift;
- $s =~ s/"/""/g;
- return qq{"$s"};
+ my $s = shift;
+ $s =~ s/"/""/g;
+ return qq{"$s"};
}
sub _csv_line {
- return join(",", map { _csv_quote($_) } @_);
+ return join(",", map { _csv_quote($_) } @_);
}
sub _csv_encode {
- return join("\r\n", map { _csv_line(@$_) } @_) . "\r\n";
+ return join("\r\n", map { _csv_line(@$_) } @_) . "\r\n";
}
sub post_bug_after_creation {
- my ($self, $args) = @_;
- my $vars = $args->{vars};
- my $bug = $vars->{bug};
- my $template = Bugzilla->template;
-
- my $format = Bugzilla->input_params->{format};
-
- return unless defined $format;
-
- if ($format eq 'remo-swag') {
- # If the attachment cannot be successfully added to the bug,
- # we notify the user, but we don't interrupt the bug creation process.
- my $error_mode_cache = Bugzilla->error_mode;
- Bugzilla->error_mode(ERROR_MODE_DIE);
-
- my @attachments;
- eval {
- my $xml;
- $template->process("bug/create/create-remo-swag.xml.tmpl", {}, \$xml)
- || ThrowTemplateError($template->error());
-
- push @attachments, Bugzilla::Attachment->create(
- { bug => $bug,
- creation_ts => $bug->creation_ts,
- data => $xml,
- description => 'Remo Swag Request (XML)',
- filename => 'remo-swag.xml',
- ispatch => 0,
- isprivate => 0,
- mimetype => 'text/xml',
- });
-
- my @columns_raw = sort { $CSV_COLUMNS{$a}{pos} <=> $CSV_COLUMNS{$b}{pos} } keys %CSV_COLUMNS;
- my @data = map { _expand_value( $CSV_COLUMNS{$_}{value} ) } @columns_raw;
- my @columns = map { s/^(Item|Email) \d+$/$1/g; $_ } @columns_raw;
- my $csv = _csv_encode(\@columns, \@data);
-
- push @attachments, Bugzilla::Attachment->create({
- bug => $bug,
- creation_ts => $bug->creation_ts,
- data => $csv,
- description => 'Remo Swag Request (CSV)',
- filename => 'remo-swag.csv',
- ispatch => 0,
- isprivate => 0,
- mimetype => 'text/csv',
- });
- };
- if ($@) {
- warn "$@";
- }
-
- if (@attachments) {
- # Insert comment for attachment
- foreach my $attachment (@attachments) {
- $bug->add_comment('', { isprivate => 0,
- type => CMT_ATTACHMENT_CREATED,
- extra_data => $attachment->id });
- }
- $bug->update($bug->creation_ts);
- delete $bug->{attachments};
- }
- else {
- $vars->{'message'} = 'attachment_creation_failed';
- }
-
- Bugzilla->error_mode($error_mode_cache);
+ my ($self, $args) = @_;
+ my $vars = $args->{vars};
+ my $bug = $vars->{bug};
+ my $template = Bugzilla->template;
+
+ my $format = Bugzilla->input_params->{format};
+
+ return unless defined $format;
+
+ if ($format eq 'remo-swag') {
+
+ # If the attachment cannot be successfully added to the bug,
+ # we notify the user, but we don't interrupt the bug creation process.
+ my $error_mode_cache = Bugzilla->error_mode;
+ Bugzilla->error_mode(ERROR_MODE_DIE);
+
+ my @attachments;
+ eval {
+ my $xml;
+ $template->process("bug/create/create-remo-swag.xml.tmpl", {}, \$xml)
+ || ThrowTemplateError($template->error());
+
+ push @attachments,
+ Bugzilla::Attachment->create({
+ bug => $bug,
+ creation_ts => $bug->creation_ts,
+ data => $xml,
+ description => 'Remo Swag Request (XML)',
+ filename => 'remo-swag.xml',
+ ispatch => 0,
+ isprivate => 0,
+ mimetype => 'text/xml',
+ });
+
+ my @columns_raw
+ = sort { $CSV_COLUMNS{$a}{pos} <=> $CSV_COLUMNS{$b}{pos} } keys %CSV_COLUMNS;
+ my @data = map { _expand_value($CSV_COLUMNS{$_}{value}) } @columns_raw;
+ my @columns = map { s/^(Item|Email) \d+$/$1/g; $_ } @columns_raw;
+ my $csv = _csv_encode(\@columns, \@data);
+
+ push @attachments,
+ Bugzilla::Attachment->create({
+ bug => $bug,
+ creation_ts => $bug->creation_ts,
+ data => $csv,
+ description => 'Remo Swag Request (CSV)',
+ filename => 'remo-swag.csv',
+ ispatch => 0,
+ isprivate => 0,
+ mimetype => 'text/csv',
+ });
+ };
+ if ($@) {
+ warn "$@";
}
- elsif ($format eq 'mozreps') {
- my $needinfo_type = first { $_->name eq 'needinfo' } @{$bug->flag_types};
- return unless $needinfo_type;
- my %original_cc = map { $_ => 1 } Bugzilla->cgi->param('cc');
- my @cc_users = grep { $_->is_enabled && $original_cc{$_->login}} @{$bug->cc_users};
- my @new_flags = map {
- { type_id => $needinfo_type->id,
- status => '?',
- requestee => $_->login }
- } @cc_users;
- ThrowUserError('remo_missing_voucher') unless @cc_users;
-
- $bug->set_flags(\@new_flags, []) if @new_flags;
- $bug->add_comment(
- join(", ", map { $_->name || $_->login } @cc_users) .
- ": You have been added as a voucher to this Reps application.\n" .
- "Please provide a comment describing why you endorse this application.\n" .
- "Thanks!"
- );
+ if (@attachments) {
- $bug->update($bug->creation_ts);
- Bugzilla::BugMail::Send($bug->id, { changer => Bugzilla->user });
+ # Insert comment for attachment
+ foreach my $attachment (@attachments) {
+ $bug->add_comment('',
+ {isprivate => 0, type => CMT_ATTACHMENT_CREATED, extra_data => $attachment->id}
+ );
+ }
+ $bug->update($bug->creation_ts);
+ delete $bug->{attachments};
}
+ else {
+ $vars->{'message'} = 'attachment_creation_failed';
+ }
+
+ Bugzilla->error_mode($error_mode_cache);
+ }
+
+ elsif ($format eq 'mozreps') {
+ my $needinfo_type = first { $_->name eq 'needinfo' } @{$bug->flag_types};
+ return unless $needinfo_type;
+ my %original_cc = map { $_ => 1 } Bugzilla->cgi->param('cc');
+ my @cc_users
+ = grep { $_->is_enabled && $original_cc{$_->login} } @{$bug->cc_users};
+ my @new_flags
+ = map { {type_id => $needinfo_type->id, status => '?', requestee => $_->login
+ } } @cc_users;
+ ThrowUserError('remo_missing_voucher') unless @cc_users;
+
+ $bug->set_flags(\@new_flags, []) if @new_flags;
+ $bug->add_comment(
+ join(", ", map { $_->name || $_->login } @cc_users)
+ . ": You have been added as a voucher to this Reps application.\n"
+ . "Please provide a comment describing why you endorse this application.\n"
+ . "Thanks!");
+
+ $bug->update($bug->creation_ts);
+ Bugzilla::BugMail::Send($bug->id, {changer => Bugzilla->user});
+ }
}
__PACKAGE__->NAME;
diff --git a/extensions/RequestNagger/Config.pm b/extensions/RequestNagger/Config.pm
index a338cd441..7bcaf3013 100644
--- a/extensions/RequestNagger/Config.pm
+++ b/extensions/RequestNagger/Config.pm
@@ -11,8 +11,8 @@ use 5.10.1;
use strict;
use warnings;
-use constant NAME => 'RequestNagger';
-use constant REQUIRED_MODULES => [ ];
-use constant OPTIONAL_MODULES => [ ];
+use constant NAME => 'RequestNagger';
+use constant REQUIRED_MODULES => [];
+use constant OPTIONAL_MODULES => [];
__PACKAGE__->NAME;
diff --git a/extensions/RequestNagger/Extension.pm b/extensions/RequestNagger/Extension.pm
index e0f97c9f7..54a11ff5b 100644
--- a/extensions/RequestNagger/Extension.pm
+++ b/extensions/RequestNagger/Extension.pm
@@ -25,241 +25,239 @@ use DateTime;
our $VERSION = '1';
BEGIN {
- *Bugzilla::Flag::age = \&_flag_age;
- *Bugzilla::Flag::deferred = \&_flag_deferred;
- *Bugzilla::Product::nag_interval = \&_product_nag_interval;
+ *Bugzilla::Flag::age = \&_flag_age;
+ *Bugzilla::Flag::deferred = \&_flag_deferred;
+ *Bugzilla::Product::nag_interval = \&_product_nag_interval;
}
sub _flag_age {
- return time_ago(datetime_from($_[0]->modification_date));
+ return time_ago(datetime_from($_[0]->modification_date));
}
sub _flag_deferred {
- my ($self) = @_;
- if (!exists $self->{deferred}) {
- my $dbh = Bugzilla->dbh;
- my ($defer_until) = $dbh->selectrow_array(
- "SELECT defer_until FROM nag_defer WHERE flag_id=?",
- undef,
- $self->id
- );
- $self->{deferred} = $defer_until ? datetime_from($defer_until) : undef;
- }
- return $self->{deferred};
+ my ($self) = @_;
+ if (!exists $self->{deferred}) {
+ my $dbh = Bugzilla->dbh;
+ my ($defer_until)
+ = $dbh->selectrow_array("SELECT defer_until FROM nag_defer WHERE flag_id=?",
+ undef, $self->id);
+ $self->{deferred} = $defer_until ? datetime_from($defer_until) : undef;
+ }
+ return $self->{deferred};
}
sub _product_nag_interval { $_[0]->{nag_interval} }
sub object_columns {
- my ($self, $args) = @_;
- my ($class, $columns) = @$args{qw(class columns)};
- if ($class->isa('Bugzilla::Product')) {
- push @$columns, 'nag_interval';
- }
+ my ($self, $args) = @_;
+ my ($class, $columns) = @$args{qw(class columns)};
+ if ($class->isa('Bugzilla::Product')) {
+ push @$columns, 'nag_interval';
+ }
}
sub object_update_columns {
- my ($self, $args) = @_;
- my ($object, $columns) = @$args{qw(object columns)};
- if ($object->isa('Bugzilla::Product')) {
- push @$columns, 'nag_interval';
- }
+ my ($self, $args) = @_;
+ my ($object, $columns) = @$args{qw(object columns)};
+ if ($object->isa('Bugzilla::Product')) {
+ push @$columns, 'nag_interval';
+ }
}
sub object_before_create {
- my ($self, $args) = @_;
- my ($class, $params) = @$args{qw(class params)};
- return unless $class->isa('Bugzilla::Product');
- my $input = Bugzilla->input_params;
- if (exists $input->{nag_interval}) {
- my $interval = _check_nag_interval($input->{nag_interval});
- $params->{nag_interval} = $interval;
- }
+ my ($self, $args) = @_;
+ my ($class, $params) = @$args{qw(class params)};
+ return unless $class->isa('Bugzilla::Product');
+ my $input = Bugzilla->input_params;
+ if (exists $input->{nag_interval}) {
+ my $interval = _check_nag_interval($input->{nag_interval});
+ $params->{nag_interval} = $interval;
+ }
}
sub object_end_of_set_all {
- my ($self, $args) = @_;
- my ($object, $params) = @$args{qw(object params)};
- return unless $object->isa('Bugzilla::Product');
- my $input = Bugzilla->input_params;
- if (exists $input->{nag_interval}) {
- my $interval = _check_nag_interval($input->{nag_interval});
- $object->set('nag_interval', $interval);
- }
+ my ($self, $args) = @_;
+ my ($object, $params) = @$args{qw(object params)};
+ return unless $object->isa('Bugzilla::Product');
+ my $input = Bugzilla->input_params;
+ if (exists $input->{nag_interval}) {
+ my $interval = _check_nag_interval($input->{nag_interval});
+ $object->set('nag_interval', $interval);
+ }
}
sub _check_nag_interval {
- my ($value) = @_;
- detaint_natural($value)
- || ThrowUserError('invalid_parameter', { name => 'request reminding interval', err => 'must be numeric' });
- return $value < 0 ? 0 : $value * 24;
+ my ($value) = @_;
+ detaint_natural($value)
+ || ThrowUserError('invalid_parameter',
+ {name => 'request reminding interval', err => 'must be numeric'});
+ return $value < 0 ? 0 : $value * 24;
}
sub page_before_template {
- my ($self, $args) = @_;
- my ($vars, $page) = @$args{qw(vars page_id)};
- return unless $page eq 'request_defer.html';
-
- my $user = Bugzilla->login(LOGIN_REQUIRED);
- my $input = Bugzilla->input_params;
-
- # load flag
- my $flag_id = scalar($input->{flag})
- || ThrowUserError('request_nagging_flag_invalid');
- detaint_natural($flag_id)
- || ThrowUserError('request_nagging_flag_invalid');
- my $flag = Bugzilla::Flag->new({ id => $flag_id, cache => 1 })
- || ThrowUserError('request_nagging_flag_invalid');
-
- # you can only defer flags directed at you
- $user->can_see_bug($flag->bug->id)
- || ThrowUserError("bug_access_denied", { bug_id => $flag->bug->id });
- $flag->status eq '?'
- || ThrowUserError('request_nagging_flag_set');
- $flag->requestee
- || ThrowUserError('request_nagging_flag_wind');
- $flag->requestee->id == $user->id
- || ThrowUserError('request_nagging_flag_not_owned');
-
- my $date = DateTime->now()->truncate(to => 'day');
- my $defer_until;
- if ($input->{'defer-until'}
- && $input->{'defer-until'} =~ /^(\d\d\d\d)-(\d\d)-(\d\d)$/)
- {
- $defer_until = DateTime->new(year => $1, month => $2, day => $3);
- if ($defer_until > $date->clone->add(days => 7)) {
- $defer_until = undef;
- }
+ my ($self, $args) = @_;
+ my ($vars, $page) = @$args{qw(vars page_id)};
+ return unless $page eq 'request_defer.html';
+
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
+ my $input = Bugzilla->input_params;
+
+ # load flag
+ my $flag_id
+ = scalar($input->{flag}) || ThrowUserError('request_nagging_flag_invalid');
+ detaint_natural($flag_id) || ThrowUserError('request_nagging_flag_invalid');
+ my $flag = Bugzilla::Flag->new({id => $flag_id, cache => 1})
+ || ThrowUserError('request_nagging_flag_invalid');
+
+ # you can only defer flags directed at you
+ $user->can_see_bug($flag->bug->id)
+ || ThrowUserError("bug_access_denied", {bug_id => $flag->bug->id});
+ $flag->status eq '?' || ThrowUserError('request_nagging_flag_set');
+ $flag->requestee || ThrowUserError('request_nagging_flag_wind');
+ $flag->requestee->id == $user->id
+ || ThrowUserError('request_nagging_flag_not_owned');
+
+ my $date = DateTime->now()->truncate(to => 'day');
+ my $defer_until;
+ if ( $input->{'defer-until'}
+ && $input->{'defer-until'} =~ /^(\d\d\d\d)-(\d\d)-(\d\d)$/)
+ {
+ $defer_until = DateTime->new(year => $1, month => $2, day => $3);
+ if ($defer_until > $date->clone->add(days => 7)) {
+ $defer_until = undef;
}
-
- if ($input->{save} && $defer_until) {
- $self->_defer_until($flag_id, $defer_until);
- $vars->{saved} = "1";
- $vars->{defer_until} = $defer_until;
- }
- else {
- my @dates;
- foreach my $i (1..7) {
- $date->add(days => 1);
- unshift @dates, { days => $i, date => $date->clone };
- }
- $vars->{defer_until} = \@dates;
+ }
+
+ if ($input->{save} && $defer_until) {
+ $self->_defer_until($flag_id, $defer_until);
+ $vars->{saved} = "1";
+ $vars->{defer_until} = $defer_until;
+ }
+ else {
+ my @dates;
+ foreach my $i (1 .. 7) {
+ $date->add(days => 1);
+ unshift @dates, {days => $i, date => $date->clone};
}
+ $vars->{defer_until} = \@dates;
+ }
- $vars->{flag} = $flag;
+ $vars->{flag} = $flag;
}
sub _defer_until {
- my ($self, $flag_id, $defer_until) = @_;
- my $dbh = Bugzilla->dbh;
-
- $dbh->bz_start_transaction();
-
- my ($defer_id) = $dbh->selectrow_array("SELECT id FROM nag_defer WHERE flag_id=?", undef, $flag_id);
- if ($defer_id) {
- $dbh->do("UPDATE nag_defer SET defer_until=? WHERE id=?", undef, $defer_until->ymd, $flag_id);
- } else {
- $dbh->do("INSERT INTO nag_defer(flag_id, defer_until) VALUES (?, ?)", undef, $flag_id, $defer_until->ymd);
- }
-
- $dbh->bz_commit_transaction();
+ my ($self, $flag_id, $defer_until) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ $dbh->bz_start_transaction();
+
+ my ($defer_id)
+ = $dbh->selectrow_array("SELECT id FROM nag_defer WHERE flag_id=?",
+ undef, $flag_id);
+ if ($defer_id) {
+ $dbh->do("UPDATE nag_defer SET defer_until=? WHERE id=?",
+ undef, $defer_until->ymd, $flag_id);
+ }
+ else {
+ $dbh->do("INSERT INTO nag_defer(flag_id, defer_until) VALUES (?, ?)",
+ undef, $flag_id, $defer_until->ymd);
+ }
+
+ $dbh->bz_commit_transaction();
}
sub object_end_of_update {
- my ($self, $args) = @_;
- if ($args->{object}->isa("Bugzilla::Flag") && exists $args->{changes}) {
- # any change to the flag (setting, clearing, or retargetting) will clear the deferals
- my $flag = $args->{object};
- Bugzilla->dbh->do("DELETE FROM nag_defer WHERE flag_id=?", undef, $flag->id);
- }
+ my ($self, $args) = @_;
+ if ($args->{object}->isa("Bugzilla::Flag") && exists $args->{changes}) {
+
+# any change to the flag (setting, clearing, or retargetting) will clear the deferals
+ my $flag = $args->{object};
+ Bugzilla->dbh->do("DELETE FROM nag_defer WHERE flag_id=?", undef, $flag->id);
+ }
}
sub user_preferences {
- my ($self, $args) = @_;
- my $tab = $args->{'current_tab'};
- return unless $tab eq 'request_nagging';
-
- my $save = $args->{'save_changes'};
- my $vars = $args->{'vars'};
- my $user = Bugzilla->user;
- my $dbh = Bugzilla->dbh;
-
- my %watching =
- map { $_ => 1 }
- @{ $dbh->selectcol_arrayref(
- "SELECT profiles.login_name
+ my ($self, $args) = @_;
+ my $tab = $args->{'current_tab'};
+ return unless $tab eq 'request_nagging';
+
+ my $save = $args->{'save_changes'};
+ my $vars = $args->{'vars'};
+ my $user = Bugzilla->user;
+ my $dbh = Bugzilla->dbh;
+
+ my %watching = map { $_ => 1 } @{
+ $dbh->selectcol_arrayref(
+ "SELECT profiles.login_name
FROM nag_watch
INNER JOIN profiles ON nag_watch.nagged_id = profiles.userid
WHERE nag_watch.watcher_id = ?
- ORDER BY profiles.login_name",
- undef,
- $user->id
- ) };
-
- my $nag_settings = Bugzilla::Extension::RequestNagger::Settings->new($user->id);
-
- if ($save) {
- my $input = Bugzilla->input_params;
- Bugzilla::User::match_field({ 'add_watching' => {'type' => 'multi'} });
-
- $dbh->bz_start_transaction();
-
- # user preference
- if (my $value = $input->{request_nagging}) {
- my $settings = $user->settings;
- my $setting = new Bugzilla::User::Setting('request_nagging');
- if ($value eq 'default') {
- $settings->{request_nagging}->reset_to_default;
- }
- else {
- $setting->validate_value($value);
- $settings->{request_nagging}->set($value);
- }
- }
-
- # watching
- if ($input->{remove_watched_users}) {
- my $del_watching = ref($input->{del_watching}) ? $input->{del_watching} : [ $input->{del_watching} ];
- foreach my $login (@$del_watching) {
- my $u = Bugzilla::User->new({ name => $login, cache => 1 })
- || next;
- next unless exists $watching{$u->login};
- $dbh->do(
- "DELETE FROM nag_watch WHERE watcher_id=? AND nagged_id=?",
- undef,
- $user->id, $u->id
- );
- delete $watching{$u->login};
- }
- }
- if ($input->{add_watching}) {
- my $add_watching = ref($input->{add_watching}) ? $input->{add_watching} : [ $input->{add_watching} ];
- foreach my $login (@$add_watching) {
- my $u = Bugzilla::User->new({ name => $login, cache => 1 })
- || next;
- next if exists $watching{$u->login};
- $dbh->do(
- "INSERT INTO nag_watch(watcher_id, nagged_id) VALUES(?, ?)",
- undef,
- $user->id, $u->id
- );
- $watching{$u->login} = 1;
- }
- }
-
- # watching settings
- foreach my $field (Bugzilla::Extension::RequestNagger::Settings::FIELDS()) {
- $nag_settings->set($field, $input->{$field});
- }
-
- $dbh->bz_commit_transaction();
+ ORDER BY profiles.login_name", undef, $user->id
+ )
+ };
+
+ my $nag_settings = Bugzilla::Extension::RequestNagger::Settings->new($user->id);
+
+ if ($save) {
+ my $input = Bugzilla->input_params;
+ Bugzilla::User::match_field({'add_watching' => {'type' => 'multi'}});
+
+ $dbh->bz_start_transaction();
+
+ # user preference
+ if (my $value = $input->{request_nagging}) {
+ my $settings = $user->settings;
+ my $setting = new Bugzilla::User::Setting('request_nagging');
+ if ($value eq 'default') {
+ $settings->{request_nagging}->reset_to_default;
+ }
+ else {
+ $setting->validate_value($value);
+ $settings->{request_nagging}->set($value);
+ }
}
- $vars->{watching} = [ sort keys %watching ];
- $vars->{settings} = $nag_settings;
+ # watching
+ if ($input->{remove_watched_users}) {
+ my $del_watching
+ = ref($input->{del_watching})
+ ? $input->{del_watching}
+ : [$input->{del_watching}];
+ foreach my $login (@$del_watching) {
+ my $u = Bugzilla::User->new({name => $login, cache => 1}) || next;
+ next unless exists $watching{$u->login};
+ $dbh->do("DELETE FROM nag_watch WHERE watcher_id=? AND nagged_id=?",
+ undef, $user->id, $u->id);
+ delete $watching{$u->login};
+ }
+ }
+ if ($input->{add_watching}) {
+ my $add_watching
+ = ref($input->{add_watching})
+ ? $input->{add_watching}
+ : [$input->{add_watching}];
+ foreach my $login (@$add_watching) {
+ my $u = Bugzilla::User->new({name => $login, cache => 1}) || next;
+ next if exists $watching{$u->login};
+ $dbh->do("INSERT INTO nag_watch(watcher_id, nagged_id) VALUES(?, ?)",
+ undef, $user->id, $u->id);
+ $watching{$u->login} = 1;
+ }
+ }
+
+ # watching settings
+ foreach my $field (Bugzilla::Extension::RequestNagger::Settings::FIELDS()) {
+ $nag_settings->set($field, $input->{$field});
+ }
- my $handled = $args->{'handled'};
- $$handled = 1;
+ $dbh->bz_commit_transaction();
+ }
+
+ $vars->{watching} = [sort keys %watching];
+ $vars->{settings} = $nag_settings;
+
+ my $handled = $args->{'handled'};
+ $$handled = 1;
}
#
@@ -267,125 +265,77 @@ sub user_preferences {
#
sub db_schema_abstract_schema {
- my ($self, $args) = @_;
- $args->{'schema'}->{'nag_watch'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- nagged_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'CASCADE',
- }
- },
- watcher_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'CASCADE',
- }
- },
- ],
- INDEXES => [
- nag_watch_idx => {
- FIELDS => [ 'nagged_id', 'watcher_id' ],
- TYPE => 'UNIQUE',
- },
- ],
- };
- $args->{'schema'}->{'nag_defer'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- flag_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'flags',
- COLUMN => 'id',
- DELETE => 'CASCADE',
- }
- },
- defer_until => {
- TYPE => 'DATETIME',
- NOTNULL => 1,
- },
- ],
- INDEXES => [
- nag_defer_idx => {
- FIELDS => [ 'flag_id' ],
- TYPE => 'UNIQUE',
- },
- ],
- };
- $args->{'schema'}->{'nag_settings'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- user_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'CASCADE',
- }
- },
- setting_name => {
- TYPE => 'VARCHAR(16)',
- NOTNULL => 1,
- },
- setting_value => {
- TYPE => 'VARCHAR(16)',
- NOTNULL => 1,
- },
- ],
- INDEXES => [
- nag_setting_idx => {
- FIELDS => [ 'user_id', 'setting_name' ],
- TYPE => 'UNIQUE',
- },
- ],
- };
+ my ($self, $args) = @_;
+ $args->{'schema'}->{'nag_watch'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ nagged_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE',}
+ },
+ watcher_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE',}
+ },
+ ],
+ INDEXES => [
+ nag_watch_idx => {FIELDS => ['nagged_id', 'watcher_id'], TYPE => 'UNIQUE',},
+ ],
+ };
+ $args->{'schema'}->{'nag_defer'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ flag_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'flags', COLUMN => 'id', DELETE => 'CASCADE',}
+ },
+ defer_until => {TYPE => 'DATETIME', NOTNULL => 1,},
+ ],
+ INDEXES => [nag_defer_idx => {FIELDS => ['flag_id'], TYPE => 'UNIQUE',},],
+ };
+ $args->{'schema'}->{'nag_settings'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ user_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE',}
+ },
+ setting_name => {TYPE => 'VARCHAR(16)', NOTNULL => 1,},
+ setting_value => {TYPE => 'VARCHAR(16)', NOTNULL => 1,},
+ ],
+ INDEXES => [
+ nag_setting_idx => {FIELDS => ['user_id', 'setting_name'], TYPE => 'UNIQUE',},
+ ],
+ };
}
sub install_update_db {
- my $dbh = Bugzilla->dbh;
- $dbh->bz_add_column('products', 'nag_interval', { TYPE => 'INT2', NOTNULL => 1, DEFAULT => 7 * 24 });
+ my $dbh = Bugzilla->dbh;
+ $dbh->bz_add_column('products', 'nag_interval',
+ {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 7 * 24});
}
sub install_filesystem {
- my ($self, $args) = @_;
- my $files = $args->{'files'};
- my $extensions_dir = bz_locations()->{'extensionsdir'};
- my $script_name = $extensions_dir . "/" . __PACKAGE__->NAME . "/bin/send-request-nags.pl";
- $files->{$script_name} = {
- perms => Bugzilla::Install::Filesystem::WS_EXECUTE
- };
+ my ($self, $args) = @_;
+ my $files = $args->{'files'};
+ my $extensions_dir = bz_locations()->{'extensionsdir'};
+ my $script_name
+ = $extensions_dir . "/" . __PACKAGE__->NAME . "/bin/send-request-nags.pl";
+ $files->{$script_name} = {perms => Bugzilla::Install::Filesystem::WS_EXECUTE};
}
sub install_before_final_checks {
- my ($self, $args) = @_;
- add_setting({
- name => 'request_nagging',
- options => ['on', 'off'],
- default => 'on',
- category => 'Reviews and Needinfo'
- });
+ my ($self, $args) = @_;
+ add_setting({
+ name => 'request_nagging',
+ options => ['on', 'off'],
+ default => 'on',
+ category => 'Reviews and Needinfo'
+ });
}
__PACKAGE__->NAME;
diff --git a/extensions/RequestNagger/bin/send-request-nags.pl b/extensions/RequestNagger/bin/send-request-nags.pl
index f823fc197..33c49d2b6 100755
--- a/extensions/RequestNagger/bin/send-request-nags.pl
+++ b/extensions/RequestNagger/bin/send-request-nags.pl
@@ -39,8 +39,8 @@ my $DO_NOT_NAG = grep { $_ eq '-d' } @ARGV;
@ARGV = grep { !/^-/ } @ARGV;
if (my $filename = shift @ARGV) {
- _send_email(decode_json(scalar read_file($filename)));
- exit;
+ _send_email(decode_json(scalar read_file($filename)));
+ exit;
}
my $dbh = Bugzilla->dbh;
@@ -53,269 +53,272 @@ Bugzilla->switch_to_shadow_db();
# send nags to requestees
send_nags(
- reports => [ 'requestee' ],
- requestee_sql => REQUESTEE_NAG_SQL,
- setter_sql => SETTER_NAG_SQL,
- template => 'user',
- date => $date,
+ reports => ['requestee'],
+ requestee_sql => REQUESTEE_NAG_SQL,
+ setter_sql => SETTER_NAG_SQL,
+ template => 'user',
+ date => $date,
);
# send nags to watchers
send_nags(
- reports => [ 'requestee', 'setter' ],
- requestee_sql => WATCHING_REQUESTEE_NAG_SQL,
- setter_sql => WATCHING_SETTER_NAG_SQL,
- template => 'watching',
- date => $date,
+ reports => ['requestee', 'setter'],
+ requestee_sql => WATCHING_REQUESTEE_NAG_SQL,
+ setter_sql => WATCHING_SETTER_NAG_SQL,
+ template => 'watching',
+ date => $date,
);
sub send_nags {
- my (%args) = @_;
- my $requests = {};
- my $watching = $args{template} eq 'watching';
-
- # get requests
-
- foreach my $report (@{ $args{reports} }) {
-
- # collate requests
- my $rows = $dbh->selectall_arrayref($args{$report . '_sql'}, { Slice => {} });
- foreach my $request (@$rows) {
- next unless _include_request($request, $report);
-
- my $target = Bugzilla::User->new({ id => $request->{target_id}, cache => 1 });
- push @{
- $requests
- ->{$request->{recipient_id}}
- ->{$target->login}
- ->{$report}
- ->{$request->{flag_type}}
- }, $request;
- push @{
- $requests
- ->{$request->{recipient_id}}
- ->{$target->login}
- ->{bug_ids}
- ->{$report}
- }, $request->{bug_id};
- }
+ my (%args) = @_;
+ my $requests = {};
+ my $watching = $args{template} eq 'watching';
- # process requests here to avoid doing it in the templates
- foreach my $recipient_id (keys %$requests) {
- foreach my $target_login (keys %{ $requests->{$recipient_id} }) {
- my $rh = $requests->{$recipient_id}->{$target_login};
-
- # build a list of valid types in the correct order
- $rh->{types}->{$report} = [];
- foreach my $type (map { $_->{type} } FLAG_TYPES) {
- next unless exists $rh->{$report}->{$type};
- push @{ $rh->{types}->{$report} }, $type;
- }
-
- # build a summary
- $rh->{summary}->{$report} = join(', ',
- map { scalar(@{ $rh->{$report}->{$_} }) . ' ' . $_ }
- @{ $rh->{types}->{$report} }
- );
-
- if ($watching && $report eq 'setter') {
- # remove links to reports with too many items to display
- my $total = 0;
- foreach my $type (@{ $rh->{types}->{$report} }) {
- $total += scalar(@{ $rh->{$report}->{$type} });
- }
- if ($total > MAX_SETTER_COUNT) {
- $rh->{types}->{$report} = [];
- }
- }
- }
- }
- }
+ # get requests
- # send emails
-
- foreach my $recipient_id (sort keys %$requests) {
- # send the email in a separate process to avoid excessive memory usage
- my $params = {
- recipient_id => $recipient_id,
- template => $args{template},
- date => $args{date},
- reports => $args{reports},
- requests => $requests->{$recipient_id},
- };
- my ($fh, $filename) = tempfile();
- print $fh encode_json($params);
- close($fh);
-
- my @command = ($0, $filename);
- push @command, '-d' if $DO_NOT_NAG;
- system(@command);
- unlink($filename);
- }
-}
-
-sub _include_request {
- my ($request, $report) = @_;
- state $now = datetime_from($db_date, 'UTC')->truncate( to => 'day' );
-
- my $recipient = Bugzilla::User->new({ id => $request->{recipient_id}, cache => 1 });
-
- if ($report eq 'requestee') {
- # check recipient group membership
- my $group;
- foreach my $type (FLAG_TYPES) {
- next unless $type->{type} eq $request->{flag_type};
- $group = $type->{group};
- last;
- }
- return 0 unless $recipient->in_group($group);
- }
+ foreach my $report (@{$args{reports}}) {
- # check bug visibility
- return 0 unless $recipient->can_see_bug($request->{bug_id});
+ # collate requests
+ my $rows = $dbh->selectall_arrayref($args{$report . '_sql'}, {Slice => {}});
+ foreach my $request (@$rows) {
+ next unless _include_request($request, $report);
- # check attachment visibility
- if ($request->{attach_id}) {
- my $attachment = Bugzilla::Attachment->new({ id => $request->{attach_id}, cache => 1 });
- return 0 if $attachment->isprivate && !$recipient->is_insider;
+ my $target = Bugzilla::User->new({id => $request->{target_id}, cache => 1});
+ push @{$requests->{$request->{recipient_id}}->{$target->login}->{$report}
+ ->{$request->{flag_type}}}, $request;
+ push @{$requests->{$request->{recipient_id}}->{$target->login}->{bug_ids}
+ ->{$report}}, $request->{bug_id};
}
- # exclude weekends and re-check nag-interval
- my $date = datetime_from($request->{modification_date}, 'UTC');
- my $hours = 0;
- $hours += 24 - $date->hour if $date->day_of_week <= 5;
- $date->add( days => 1 )->truncate( to => 'day' );
- while ($date < $now) {
- $hours += 24 if $date->day_of_week <= 5;
- $date->add( days => 1 );
- }
- return 0 if $hours < ($request->{extended_period} ? $request->{nag_interval} + 24 : $request->{nag_interval});
+ # process requests here to avoid doing it in the templates
+ foreach my $recipient_id (keys %$requests) {
+ foreach my $target_login (keys %{$requests->{$recipient_id}}) {
+ my $rh = $requests->{$recipient_id}->{$target_login};
- return 1;
-}
+ # build a list of valid types in the correct order
+ $rh->{types}->{$report} = [];
+ foreach my $type (map { $_->{type} } FLAG_TYPES) {
+ next unless exists $rh->{$report}->{$type};
+ push @{$rh->{types}->{$report}}, $type;
+ }
-sub _send_email {
- my ($params) = @_;
-
- my @reports = @{ $params->{reports} };
- my $recipient_id = $params->{recipient_id};
- my $requests = $params->{requests};
- my $watching = $params->{template} eq 'watching';
- my $recipient = Bugzilla::User->new({ id => $recipient_id, cache => 1 });
- my $securemail = Bugzilla::User->can('public_key');
- my $has_key = $securemail && $recipient->public_key;
- my $has_private_bug = 0;
-
- my $settings = Bugzilla::Extension::RequestNagger::Settings->new($recipient_id);
- if ($watching && $settings->no_encryption) {
- $has_key = 0;
- }
+ # build a summary
+ $rh->{summary}->{$report} = join(', ',
+ map { scalar(@{$rh->{$report}->{$_}}) . ' ' . $_ } @{$rh->{types}->{$report}});
+
+ if ($watching && $report eq 'setter') {
- foreach my $target_login (keys %$requests) {
- my $rh = $requests->{$target_login};
- $rh->{target} = Bugzilla::User->new({ name => $target_login, cache => 1 });
- foreach my $report (@reports) {
- foreach my $type (keys %{ $rh->{$report} }) {
- foreach my $request (@{ $rh->{$report}->{$type} }) {
-
- _create_objects($request);
-
- # we need to encrypt or censor emails which contain
- # non-public bugs
- if ($request->{bug}->is_private) {
- $has_private_bug = 1;
- $request->{bug}->{sanitise_bug} = !$securemail || !$has_key;
- }
- else {
- $request->{bug}->{sanitise_bug} = 0;
- }
- }
- }
+ # remove links to reports with too many items to display
+ my $total = 0;
+ foreach my $type (@{$rh->{types}->{$report}}) {
+ $total += scalar(@{$rh->{$report}->{$type}});
+ }
+ if ($total > MAX_SETTER_COUNT) {
+ $rh->{types}->{$report} = [];
+ }
}
+ }
}
- my $encrypt = $securemail && $has_private_bug && $has_key;
-
- # generate email
- my $template = Bugzilla->template_inner($recipient->setting('lang'));
- my $template_file = $params->{template};
- my $vars = {
- recipient => $recipient,
- requests => $requests,
- date => $params->{date},
- };
+ }
- my ($header, $text);
- $template->process("email/request_nagging-$template_file-header.txt.tmpl", $vars, \$header)
- || ThrowTemplateError($template->error());
- $header .= "\n";
- $template->process("email/request_nagging-$template_file.txt.tmpl", $vars, \$text)
- || ThrowTemplateError($template->error());
-
- my @parts = (
- Email::MIME->create(
- attributes => {
- content_type => 'text/plain',
- charset => 'UTF-8',
- encoding => 'quoted-printable',
- },
- body_str => $text,
- )
- );
- if ($recipient->setting('email_format') eq 'html') {
- my $html;
- $template->process("email/request_nagging-$template_file.html.tmpl", $vars, \$html)
- || ThrowTemplateError($template->error());
- push @parts, Email::MIME->create(
- attributes => {
- content_type => 'text/html',
- charset => 'UTF-8',
- encoding => 'quoted-printable',
- },
- body_str => $html,
- );
- }
+ # send emails
- my $email = Email::MIME->new($header);
- $email->header_set('X-Generated-By' => hostname());
- if (scalar(@parts) == 1) {
- $email->content_type_set($parts[0]->content_type);
- }
- else {
- $email->content_type_set('multipart/alternative');
- }
- $email->parts_set(\@parts);
- if ($encrypt) {
- $email->header_set('X-Bugzilla-Encrypt' => '1');
- }
+ foreach my $recipient_id (sort keys %$requests) {
- # send
- if ($DO_NOT_NAG) {
- # uncomment the following line to enable other extensions to
- # process this email, including encryption
- # Bugzilla::Hook::process('mailer_before_send', { email => $email });
- print $email->as_string, "\n";
- }
- else {
- MessageToMTA($email);
- }
+ # send the email in a separate process to avoid excessive memory usage
+ my $params = {
+ recipient_id => $recipient_id,
+ template => $args{template},
+ date => $args{date},
+ reports => $args{reports},
+ requests => $requests->{$recipient_id},
+ };
+ my ($fh, $filename) = tempfile();
+ print $fh encode_json($params);
+ close($fh);
+
+ my @command = ($0, $filename);
+ push @command, '-d' if $DO_NOT_NAG;
+ system(@command);
+ unlink($filename);
+ }
}
-sub _create_objects {
- my ($request) = @_;
+sub _include_request {
+ my ($request, $report) = @_;
+ state $now = datetime_from($db_date, 'UTC')->truncate(to => 'day');
- $request->{recipient} = Bugzilla::User->new({ id => $request->{recipient_id}, cache => 1 });
- $request->{setter} = Bugzilla::User->new({ id => $request->{setter_id}, cache => 1 });
+ my $recipient
+ = Bugzilla::User->new({id => $request->{recipient_id}, cache => 1});
- if (defined $request->{requestee_id}) {
- $request->{requestee} = Bugzilla::User->new({ id => $request->{requestee_id}, cache => 1 });
- }
- if (exists $request->{watcher_id}) {
- $request->{watcher} = Bugzilla::User->new({ id => $request->{watcher_id}, cache => 1 });
+ if ($report eq 'requestee') {
+
+ # check recipient group membership
+ my $group;
+ foreach my $type (FLAG_TYPES) {
+ next unless $type->{type} eq $request->{flag_type};
+ $group = $type->{group};
+ last;
}
+ return 0 unless $recipient->in_group($group);
+ }
+
+ # check bug visibility
+ return 0 unless $recipient->can_see_bug($request->{bug_id});
+
+ # check attachment visibility
+ if ($request->{attach_id}) {
+ my $attachment
+ = Bugzilla::Attachment->new({id => $request->{attach_id}, cache => 1});
+ return 0 if $attachment->isprivate && !$recipient->is_insider;
+ }
+
+ # exclude weekends and re-check nag-interval
+ my $date = datetime_from($request->{modification_date}, 'UTC');
+ my $hours = 0;
+ $hours += 24 - $date->hour if $date->day_of_week <= 5;
+ $date->add(days => 1)->truncate(to => 'day');
+ while ($date < $now) {
+ $hours += 24 if $date->day_of_week <= 5;
+ $date->add(days => 1);
+ }
+ return 0
+ if $hours < ($request->{extended_period}
+ ? $request->{nag_interval} + 24
+ : $request->{nag_interval});
+
+ return 1;
+}
- $request->{bug} = Bugzilla::Extension::RequestNagger::Bug->new({ id => $request->{bug_id}, cache => 1 });
- $request->{flag} = Bugzilla::Flag->new({ id => $request->{flag_id}, cache => 1 });
- if (defined $request->{attach_id}) {
- $request->{attachment} = Bugzilla::Attachment->new({ id => $request->{attach_id}, cache => 1 });
+sub _send_email {
+ my ($params) = @_;
+
+ my @reports = @{$params->{reports}};
+ my $recipient_id = $params->{recipient_id};
+ my $requests = $params->{requests};
+ my $watching = $params->{template} eq 'watching';
+ my $recipient = Bugzilla::User->new({id => $recipient_id, cache => 1});
+ my $securemail = Bugzilla::User->can('public_key');
+ my $has_key = $securemail && $recipient->public_key;
+ my $has_private_bug = 0;
+
+ my $settings = Bugzilla::Extension::RequestNagger::Settings->new($recipient_id);
+ if ($watching && $settings->no_encryption) {
+ $has_key = 0;
+ }
+
+ foreach my $target_login (keys %$requests) {
+ my $rh = $requests->{$target_login};
+ $rh->{target} = Bugzilla::User->new({name => $target_login, cache => 1});
+ foreach my $report (@reports) {
+ foreach my $type (keys %{$rh->{$report}}) {
+ foreach my $request (@{$rh->{$report}->{$type}}) {
+
+ _create_objects($request);
+
+ # we need to encrypt or censor emails which contain
+ # non-public bugs
+ if ($request->{bug}->is_private) {
+ $has_private_bug = 1;
+ $request->{bug}->{sanitise_bug} = !$securemail || !$has_key;
+ }
+ else {
+ $request->{bug}->{sanitise_bug} = 0;
+ }
+ }
+ }
}
+ }
+ my $encrypt = $securemail && $has_private_bug && $has_key;
+
+ # generate email
+ my $template = Bugzilla->template_inner($recipient->setting('lang'));
+ my $template_file = $params->{template};
+ my $vars
+ = {recipient => $recipient, requests => $requests, date => $params->{date},};
+
+ my ($header, $text);
+ $template->process("email/request_nagging-$template_file-header.txt.tmpl",
+ $vars, \$header)
+ || ThrowTemplateError($template->error());
+ $header .= "\n";
+ $template->process("email/request_nagging-$template_file.txt.tmpl",
+ $vars, \$text)
+ || ThrowTemplateError($template->error());
+
+ my @parts = (Email::MIME->create(
+ attributes => {
+ content_type => 'text/plain',
+ charset => 'UTF-8',
+ encoding => 'quoted-printable',
+ },
+ body_str => $text,
+ ));
+
+ if ($recipient->setting('email_format') eq 'html') {
+ my $html;
+ $template->process("email/request_nagging-$template_file.html.tmpl",
+ $vars, \$html)
+ || ThrowTemplateError($template->error());
+ push @parts,
+ Email::MIME->create(
+ attributes => {
+ content_type => 'text/html',
+ charset => 'UTF-8',
+ encoding => 'quoted-printable',
+ },
+ body_str => $html,
+ );
+ }
+
+ my $email = Email::MIME->new($header);
+ $email->header_set('X-Generated-By' => hostname());
+ if (scalar(@parts) == 1) {
+ $email->content_type_set($parts[0]->content_type);
+ }
+ else {
+ $email->content_type_set('multipart/alternative');
+ }
+ $email->parts_set(\@parts);
+ if ($encrypt) {
+ $email->header_set('X-Bugzilla-Encrypt' => '1');
+ }
+
+ # send
+ if ($DO_NOT_NAG) {
+
+ # uncomment the following line to enable other extensions to
+ # process this email, including encryption
+ # Bugzilla::Hook::process('mailer_before_send', { email => $email });
+ print $email->as_string, "\n";
+ }
+ else {
+ MessageToMTA($email);
+ }
+}
+
+sub _create_objects {
+ my ($request) = @_;
+
+ $request->{recipient}
+ = Bugzilla::User->new({id => $request->{recipient_id}, cache => 1});
+ $request->{setter}
+ = Bugzilla::User->new({id => $request->{setter_id}, cache => 1});
+
+ if (defined $request->{requestee_id}) {
+ $request->{requestee}
+ = Bugzilla::User->new({id => $request->{requestee_id}, cache => 1});
+ }
+ if (exists $request->{watcher_id}) {
+ $request->{watcher}
+ = Bugzilla::User->new({id => $request->{watcher_id}, cache => 1});
+ }
+
+ $request->{bug} = Bugzilla::Extension::RequestNagger::Bug->new(
+ {id => $request->{bug_id}, cache => 1});
+ $request->{flag} = Bugzilla::Flag->new({id => $request->{flag_id}, cache => 1});
+ if (defined $request->{attach_id}) {
+ $request->{attachment}
+ = Bugzilla::Attachment->new({id => $request->{attach_id}, cache => 1});
+ }
}
diff --git a/extensions/RequestNagger/lib/Bug.pm b/extensions/RequestNagger/lib/Bug.pm
index 974a688ea..dc510d486 100644
--- a/extensions/RequestNagger/lib/Bug.pm
+++ b/extensions/RequestNagger/lib/Bug.pm
@@ -17,29 +17,29 @@ use feature 'state';
use Bugzilla::User;
sub short_desc {
- my ($self) = @_;
- return $self->{sanitise_bug} ? '(Secure bug)' : $self->SUPER::short_desc;
+ my ($self) = @_;
+ return $self->{sanitise_bug} ? '(Secure bug)' : $self->SUPER::short_desc;
}
sub is_private {
- my ($self) = @_;
- if (!exists $self->{is_private}) {
- state $default_user //= Bugzilla::User->new();
- $self->{is_private} = !$default_user->can_see_bug($self);
- }
- return $self->{is_private};
+ my ($self) = @_;
+ if (!exists $self->{is_private}) {
+ state $default_user //= Bugzilla::User->new();
+ $self->{is_private} = !$default_user->can_see_bug($self);
+ }
+ return $self->{is_private};
}
sub tooltip {
- my ($self) = @_;
- my $tooltip = $self->bug_status;
- if ($self->bug_status eq 'RESOLVED') {
- $tooltip .= '/' . $self->resolution;
- }
- if (!$self->{sanitise_bug}) {
- $tooltip .= ' ' . $self->product . ' :: ' . $self->component;
- }
- return $tooltip;
+ my ($self) = @_;
+ my $tooltip = $self->bug_status;
+ if ($self->bug_status eq 'RESOLVED') {
+ $tooltip .= '/' . $self->resolution;
+ }
+ if (!$self->{sanitise_bug}) {
+ $tooltip .= ' ' . $self->product . ' :: ' . $self->component;
+ }
+ return $tooltip;
}
1;
diff --git a/extensions/RequestNagger/lib/Constants.pm b/extensions/RequestNagger/lib/Constants.pm
index bc6cf3371..1309e06c9 100644
--- a/extensions/RequestNagger/lib/Constants.pm
+++ b/extensions/RequestNagger/lib/Constants.pm
@@ -14,13 +14,13 @@ use warnings;
use base qw(Exporter);
our @EXPORT = qw(
- MAX_SETTER_COUNT
- MAX_REQUEST_AGE
- FLAG_TYPES
- REQUESTEE_NAG_SQL
- SETTER_NAG_SQL
- WATCHING_REQUESTEE_NAG_SQL
- WATCHING_SETTER_NAG_SQL
+ MAX_SETTER_COUNT
+ MAX_REQUEST_AGE
+ FLAG_TYPES
+ REQUESTEE_NAG_SQL
+ SETTER_NAG_SQL
+ WATCHING_REQUESTEE_NAG_SQL
+ WATCHING_SETTER_NAG_SQL
);
# if there are more than this many requests that a user is waiting on, show a
@@ -29,33 +29,24 @@ use constant MAX_SETTER_COUNT => 7;
# ignore any request older than this many days in the requestee emails
# massively overdue requests will still be included in the 'watching' emails
-use constant MAX_REQUEST_AGE => 90; # about three months
+use constant MAX_REQUEST_AGE => 90; # about three months
# the order of this array determines the order used in email
use constant FLAG_TYPES => (
- {
- type => 'review', # flag_type.name
- group => 'everyone', # the user must be a member of this group to receive reminders
- },
- {
- type => 'superview',
- group => 'everyone',
- },
- {
- type => 'feedback',
- group => 'everyone',
- },
- {
- type => 'needinfo',
- group => 'editbugs',
- },
+ {
+ type => 'review', # flag_type.name
+ group => 'everyone', # the user must be a member of this group to receive reminders
+ },
+ {type => 'superview', group => 'everyone',},
+ {type => 'feedback', group => 'everyone',},
+ {type => 'needinfo', group => 'editbugs',},
);
sub REQUESTEE_NAG_SQL {
- my $dbh = Bugzilla->dbh;
- my @flag_types_sql = map { $dbh->quote($_->{type}) } FLAG_TYPES;
+ my $dbh = Bugzilla->dbh;
+ my @flag_types_sql = map { $dbh->quote($_->{type}) } FLAG_TYPES;
- return "
+ return "
SELECT
flagtypes.name AS flag_type,
flags.id AS flag_id,
@@ -84,7 +75,8 @@ sub REQUESTEE_NAG_SQL {
AND flags.status = '?'
AND products.nag_interval != 0
AND TIMESTAMPDIFF(HOUR, flags.modification_date, CURRENT_DATE()) >= products.nag_interval
- AND TIMESTAMPDIFF(DAY, flags.modification_date, CURRENT_DATE()) <= " . MAX_REQUEST_AGE . "
+ AND TIMESTAMPDIFF(DAY, flags.modification_date, CURRENT_DATE()) <= "
+ . MAX_REQUEST_AGE . "
AND (profile_setting.setting_value IS NULL OR profile_setting.setting_value = 'on')
AND requestee.disable_mail = 0
AND nag_defer.id IS NULL
@@ -96,10 +88,10 @@ sub REQUESTEE_NAG_SQL {
}
sub SETTER_NAG_SQL {
- my $dbh = Bugzilla->dbh;
- my @flag_types_sql = map { $dbh->quote($_->{type}) } FLAG_TYPES;
+ my $dbh = Bugzilla->dbh;
+ my @flag_types_sql = map { $dbh->quote($_->{type}) } FLAG_TYPES;
- return "
+ return "
SELECT
flagtypes.name AS flag_type,
flags.id AS flag_id,
@@ -128,7 +120,8 @@ sub SETTER_NAG_SQL {
AND flags.status = '?'
AND products.nag_interval != 0
AND TIMESTAMPDIFF(HOUR, flags.modification_date, CURRENT_DATE()) >= products.nag_interval
- AND TIMESTAMPDIFF(DAY, flags.modification_date, CURRENT_DATE()) <= " . MAX_REQUEST_AGE . "
+ AND TIMESTAMPDIFF(DAY, flags.modification_date, CURRENT_DATE()) <= "
+ . MAX_REQUEST_AGE . "
AND (profile_setting.setting_value IS NULL OR profile_setting.setting_value = 'on')
AND setter.disable_mail = 0
AND nag_defer.id IS NULL
@@ -140,10 +133,10 @@ sub SETTER_NAG_SQL {
}
sub WATCHING_REQUESTEE_NAG_SQL {
- my $dbh = Bugzilla->dbh;
- my @flag_types_sql = map { $dbh->quote($_->{type}) } FLAG_TYPES;
+ my $dbh = Bugzilla->dbh;
+ my @flag_types_sql = map { $dbh->quote($_->{type}) } FLAG_TYPES;
- return "
+ return "
SELECT
nag_watch.watcher_id,
flagtypes.name AS flag_type,
@@ -192,10 +185,10 @@ sub WATCHING_REQUESTEE_NAG_SQL {
}
sub WATCHING_SETTER_NAG_SQL {
- my $dbh = Bugzilla->dbh;
- my @flag_types_sql = map { $dbh->quote($_->{type}) } FLAG_TYPES;
+ my $dbh = Bugzilla->dbh;
+ my @flag_types_sql = map { $dbh->quote($_->{type}) } FLAG_TYPES;
- return "
+ return "
SELECT
nag_watch.watcher_id,
flagtypes.name AS flag_type,
diff --git a/extensions/RequestNagger/lib/Settings.pm b/extensions/RequestNagger/lib/Settings.pm
index 393d224ba..839c37485 100644
--- a/extensions/RequestNagger/lib/Settings.pm
+++ b/extensions/RequestNagger/lib/Settings.pm
@@ -17,47 +17,48 @@ use List::MoreUtils qw( any );
use constant FIELDS => qw( reviews_only extended_period no_encryption );
sub new {
- my ($class, $user_id) = @_;
-
- my $dbh = Bugzilla->dbh;
- my $self = { user_id => $user_id };
- foreach my $row (@{ $dbh->selectall_arrayref(
- "SELECT setting_name,setting_value FROM nag_settings WHERE user_id = ?",
- { Slice => {} },
- $user_id
- ) }) {
- $self->{$row->{setting_name}} = $row->{setting_value};
- }
-
- return bless($self, $class);
+ my ($class, $user_id) = @_;
+
+ my $dbh = Bugzilla->dbh;
+ my $self = {user_id => $user_id};
+ foreach my $row (@{
+ $dbh->selectall_arrayref(
+ "SELECT setting_name,setting_value FROM nag_settings WHERE user_id = ?",
+ {Slice => {}}, $user_id)
+ })
+ {
+ $self->{$row->{setting_name}} = $row->{setting_value};
+ }
+
+ return bless($self, $class);
}
-sub reviews_only { exists $_[0]->{reviews_only} ? $_[0]->{reviews_only} : 0 }
-sub extended_period { exists $_[0]->{extended_period} ? $_[0]->{extended_period} : 0 }
-sub no_encryption { exists $_[0]->{no_encryption} ? $_[0]->{no_encryption} : 0 }
+sub reviews_only { exists $_[0]->{reviews_only} ? $_[0]->{reviews_only} : 0 }
+
+sub extended_period {
+ exists $_[0]->{extended_period} ? $_[0]->{extended_period} : 0;
+}
+sub no_encryption { exists $_[0]->{no_encryption} ? $_[0]->{no_encryption} : 0 }
sub set {
- my ($self, $field, $value) = @_;
- return unless any { $_ eq $field } FIELDS;
- $value = $value ? 1 : 0;
-
- my $dbh = Bugzilla->dbh;
- if (exists $self->{$field}) {
- $dbh->do(
- "UPDATE nag_settings SET setting_value=? WHERE user_id=? AND setting_name=?",
- undef,
- $value, $self->{user_id}, $field
- );
- }
- else {
- $dbh->do(
- "INSERT INTO nag_settings(user_id, setting_name, setting_value) VALUES (?, ?, ?)",
- undef,
- $self->{user_id}, $field, $value
- );
- }
-
- $self->{$field} = $value;
+ my ($self, $field, $value) = @_;
+ return unless any { $_ eq $field } FIELDS;
+ $value = $value ? 1 : 0;
+
+ my $dbh = Bugzilla->dbh;
+ if (exists $self->{$field}) {
+ $dbh->do(
+ "UPDATE nag_settings SET setting_value=? WHERE user_id=? AND setting_name=?",
+ undef, $value, $self->{user_id}, $field);
+ }
+ else {
+ $dbh->do(
+ "INSERT INTO nag_settings(user_id, setting_name, setting_value) VALUES (?, ?, ?)",
+ undef, $self->{user_id}, $field, $value
+ );
+ }
+
+ $self->{$field} = $value;
}
1;
diff --git a/extensions/RestrictComments/Config.pm b/extensions/RestrictComments/Config.pm
index be703bed7..eb99e5a94 100644
--- a/extensions/RestrictComments/Config.pm
+++ b/extensions/RestrictComments/Config.pm
@@ -11,7 +11,7 @@ use 5.10.1;
use strict;
use warnings;
-use constant NAME => 'RestrictComments';
+use constant NAME => 'RestrictComments';
use constant REQUIRED_MODULES => [];
use constant OPTIONAL_MODULES => [];
diff --git a/extensions/RestrictComments/Extension.pm b/extensions/RestrictComments/Extension.pm
index e93540d5a..83075ac23 100644
--- a/extensions/RestrictComments/Extension.pm
+++ b/extensions/RestrictComments/Extension.pm
@@ -17,84 +17,86 @@ use Bugzilla::Constants;
use Bugzilla::Util qw(i_am_webservice);
BEGIN {
- *Bugzilla::Bug::restrict_comments = \&_bug_restrict_comments;
+ *Bugzilla::Bug::restrict_comments = \&_bug_restrict_comments;
}
sub _bug_restrict_comments {
- my ($self) = @_;
- return $self->{restrict_comments};
+ my ($self) = @_;
+ return $self->{restrict_comments};
}
sub bug_check_can_change_field {
- my ($self, $args) = @_;
- my ($bug, $priv_results) = @$args{qw(bug priv_results)};
- my $user = Bugzilla->user;
-
- if ($user->id
- && $bug->restrict_comments
- && !$user->in_group(Bugzilla->params->{'restrict_comments_group'}))
- {
- push(@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
- return;
- }
+ my ($self, $args) = @_;
+ my ($bug, $priv_results) = @$args{qw(bug priv_results)};
+ my $user = Bugzilla->user;
+
+ if ( $user->id
+ && $bug->restrict_comments
+ && !$user->in_group(Bugzilla->params->{'restrict_comments_group'}))
+ {
+ push(@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
+ return;
+ }
}
sub _can_restrict_comments {
- my ($self, $object) = @_;
- return unless $object->isa('Bugzilla::Bug');
- $self->{setter_group} ||= Bugzilla->params->{'restrict_comments_enable_group'};
- return Bugzilla->user->in_group($self->{setter_group});
+ my ($self, $object) = @_;
+ return unless $object->isa('Bugzilla::Bug');
+ $self->{setter_group} ||= Bugzilla->params->{'restrict_comments_enable_group'};
+ return Bugzilla->user->in_group($self->{setter_group});
}
sub object_end_of_set_all {
- my ($self, $args) = @_;
- my $object = $args->{object};
- my $input = Bugzilla->input_params;
- my $update_restrict_comments = !i_am_webservice() || exists $input->{restrict_comments};
- if ($update_restrict_comments && $self->_can_restrict_comments($object)) {
- $object->set('restrict_comments', $input->{restrict_comments} ? 1 : undef);
- }
+ my ($self, $args) = @_;
+ my $object = $args->{object};
+ my $input = Bugzilla->input_params;
+ my $update_restrict_comments
+ = !i_am_webservice() || exists $input->{restrict_comments};
+ if ($update_restrict_comments && $self->_can_restrict_comments($object)) {
+ $object->set('restrict_comments', $input->{restrict_comments} ? 1 : undef);
+ }
}
sub object_update_columns {
- my ($self, $args) = @_;
- my ($object, $columns) = @$args{qw(object columns)};
- if ($self->_can_restrict_comments($object)) {
- push(@$columns, 'restrict_comments');
- }
+ my ($self, $args) = @_;
+ my ($object, $columns) = @$args{qw(object columns)};
+ if ($self->_can_restrict_comments($object)) {
+ push(@$columns, 'restrict_comments');
+ }
}
sub object_columns {
- my ($self, $args) = @_;
- my ($class, $columns) = @$args{qw(class columns)};
- if ($class->isa('Bugzilla::Bug')) {
- if (Bugzilla->dbh->bz_column_info($class->DB_TABLE, 'restrict_comments')) {
- push @$columns, 'restrict_comments';
- }
+ my ($self, $args) = @_;
+ my ($class, $columns) = @$args{qw(class columns)};
+ if ($class->isa('Bugzilla::Bug')) {
+ if (Bugzilla->dbh->bz_column_info($class->DB_TABLE, 'restrict_comments')) {
+ push @$columns, 'restrict_comments';
}
+ }
}
sub bug_fields {
- my ($self, $args) = @_;
- my $fields = $args->{'fields'};
- push (@$fields, 'restrict_comments')
+ my ($self, $args) = @_;
+ my $fields = $args->{'fields'};
+ push(@$fields, 'restrict_comments');
}
sub config_add_panels {
- my ($self, $args) = @_;
- my $modules = $args->{panel_modules};
- $modules->{RestrictComments} = "Bugzilla::Extension::RestrictComments::Config";
+ my ($self, $args) = @_;
+ my $modules = $args->{panel_modules};
+ $modules->{RestrictComments} = "Bugzilla::Extension::RestrictComments::Config";
}
sub install_update_db {
- my $dbh = Bugzilla->dbh;
+ my $dbh = Bugzilla->dbh;
- my $field = new Bugzilla::Field({ name => 'restrict_comments' });
- if (!$field) {
- Bugzilla::Field->create({ name => 'restrict_comments', description => 'Restrict Comments' });
- }
+ my $field = new Bugzilla::Field({name => 'restrict_comments'});
+ if (!$field) {
+ Bugzilla::Field->create(
+ {name => 'restrict_comments', description => 'Restrict Comments'});
+ }
- $dbh->bz_add_column('bugs', 'restrict_comments', { TYPE => 'BOOLEAN' });
+ $dbh->bz_add_column('bugs', 'restrict_comments', {TYPE => 'BOOLEAN'});
}
__PACKAGE__->NAME;
diff --git a/extensions/RestrictComments/lib/Config.pm b/extensions/RestrictComments/lib/Config.pm
index e5dbc518c..c1f9829f9 100644
--- a/extensions/RestrictComments/lib/Config.pm
+++ b/extensions/RestrictComments/lib/Config.pm
@@ -17,26 +17,26 @@ use Bugzilla::Group;
our $sortkey = 510;
sub get_param_list {
- my ($class) = @_;
-
- my @param_list = (
- {
- name => 'restrict_comments_group',
- type => 's',
- choices => \&get_all_group_names,
- default => '',
- checker => \&check_group
- },
- {
- name => 'restrict_comments_enable_group',
- type => 's',
- choices => \&get_all_group_names,
- default => '',
- checker => \&check_group
- },
- );
-
- return @param_list;
+ my ($class) = @_;
+
+ my @param_list = (
+ {
+ name => 'restrict_comments_group',
+ type => 's',
+ choices => \&get_all_group_names,
+ default => '',
+ checker => \&check_group
+ },
+ {
+ name => 'restrict_comments_enable_group',
+ type => 's',
+ choices => \&get_all_group_names,
+ default => '',
+ checker => \&check_group
+ },
+ );
+
+ return @param_list;
}
1;
diff --git a/extensions/Review/Config.pm b/extensions/Review/Config.pm
index ea7e8a725..aa9a8c32d 100644
--- a/extensions/Review/Config.pm
+++ b/extensions/Review/Config.pm
@@ -11,7 +11,7 @@ use 5.10.1;
use strict;
use warnings;
-use constant NAME => 'Review';
+use constant NAME => 'Review';
use constant REQUIRED_MODULES => [];
use constant OPTIONAL_MODULES => [];
diff --git a/extensions/Review/Extension.pm b/extensions/Review/Extension.pm
index a918a5ca5..975857cf7 100644
--- a/extensions/Review/Extension.pm
+++ b/extensions/Review/Extension.pm
@@ -36,95 +36,96 @@ use constant MENTOR_LIMIT => 10;
#
BEGIN {
- *Bugzilla::Product::reviewers = \&_product_reviewers;
- *Bugzilla::Product::reviewers_objs = \&_product_reviewers_objs;
- *Bugzilla::Product::reviewer_required = \&_product_reviewer_required;
- *Bugzilla::Component::reviewers = \&_component_reviewers;
- *Bugzilla::Component::reviewers_objs = \&_component_reviewers_objs;
- *Bugzilla::Bug::mentors = \&_bug_mentors;
- *Bugzilla::Bug::bug_mentors = \&_bug_mentors;
- *Bugzilla::Bug::bug_mentor = \&_bug_mentors;
- *Bugzilla::Bug::is_mentor = \&_bug_is_mentor;
- *Bugzilla::Bug::set_bug_mentors = \&_bug_set_bug_mentors;
- *Bugzilla::User::review_count = \&_user_review_count;
- *Bugzilla::User::reviews_blocked = \&_user_reviews_blocked;
- *Bugzilla::User::is_active = \&_user_is_active;
+ *Bugzilla::Product::reviewers = \&_product_reviewers;
+ *Bugzilla::Product::reviewers_objs = \&_product_reviewers_objs;
+ *Bugzilla::Product::reviewer_required = \&_product_reviewer_required;
+ *Bugzilla::Component::reviewers = \&_component_reviewers;
+ *Bugzilla::Component::reviewers_objs = \&_component_reviewers_objs;
+ *Bugzilla::Bug::mentors = \&_bug_mentors;
+ *Bugzilla::Bug::bug_mentors = \&_bug_mentors;
+ *Bugzilla::Bug::bug_mentor = \&_bug_mentors;
+ *Bugzilla::Bug::is_mentor = \&_bug_is_mentor;
+ *Bugzilla::Bug::set_bug_mentors = \&_bug_set_bug_mentors;
+ *Bugzilla::User::review_count = \&_user_review_count;
+ *Bugzilla::User::reviews_blocked = \&_user_reviews_blocked;
+ *Bugzilla::User::is_active = \&_user_is_active;
}
#
# monkey-patched methods
#
-sub _product_reviewers { _reviewers($_[0], 'product', $_[1]) }
-sub _product_reviewers_objs { _reviewers_objs($_[0], 'product', $_[1]) }
-sub _component_reviewers { _reviewers($_[0], 'component', $_[1]) }
-sub _component_reviewers_objs { _reviewers_objs($_[0], 'component', $_[1]) }
+sub _product_reviewers { _reviewers($_[0], 'product', $_[1]) }
+sub _product_reviewers_objs { _reviewers_objs($_[0], 'product', $_[1]) }
+sub _component_reviewers { _reviewers($_[0], 'component', $_[1]) }
+sub _component_reviewers_objs { _reviewers_objs($_[0], 'component', $_[1]) }
sub _reviewers {
- my ($object, $type, $unfiltered) = @_;
- return join(', ', map { $_->login } @{ _reviewers_objs($object, $type, $unfiltered) });
+ my ($object, $type, $unfiltered) = @_;
+ return
+ join(', ', map { $_->login } @{_reviewers_objs($object, $type, $unfiltered)});
}
sub _reviewers_objs {
- my ($object, $type, $unfiltered) = @_;
- if (!$object->{reviewers}) {
- my $dbh = Bugzilla->dbh;
- my $user_ids = $dbh->selectcol_arrayref(
- "SELECT user_id FROM ${type}_reviewers WHERE ${type}_id = ? ORDER BY sortkey",
- undef,
- $object->id,
- );
- # new_from_list always sorts according to the object's definition,
- # so we have to reorder the list
- my $users = Bugzilla::User->new_from_list($user_ids);
- my %user_map = map { $_->id => $_ } @$users;
- my @reviewers = map { $user_map{$_} } @$user_ids;
- if (!$unfiltered) {
- @reviewers = grep {
- $_->is_enabled
- && $_->is_active
- && $_->name !~ UNAVAILABLE_RE
- && !$_->reviews_blocked
- } @reviewers;
- }
- $object->{reviewers} = \@reviewers;
+ my ($object, $type, $unfiltered) = @_;
+ if (!$object->{reviewers}) {
+ my $dbh = Bugzilla->dbh;
+ my $user_ids
+ = $dbh->selectcol_arrayref(
+ "SELECT user_id FROM ${type}_reviewers WHERE ${type}_id = ? ORDER BY sortkey",
+ undef, $object->id,);
+
+ # new_from_list always sorts according to the object's definition,
+ # so we have to reorder the list
+ my $users = Bugzilla::User->new_from_list($user_ids);
+ my %user_map = map { $_->id => $_ } @$users;
+ my @reviewers = map { $user_map{$_} } @$user_ids;
+ if (!$unfiltered) {
+ @reviewers = grep {
+ $_->is_enabled
+ && $_->is_active
+ && $_->name !~ UNAVAILABLE_RE
+ && !$_->reviews_blocked
+ } @reviewers;
}
- return $object->{reviewers};
+ $object->{reviewers} = \@reviewers;
+ }
+ return $object->{reviewers};
}
sub _user_is_active {
- my ($self) = @_;
+ my ($self) = @_;
- # never consider .bugs or .tld addresses as inactive.
- return 1 if $self->login =~ /\.(?:bugs|tld)$/;
- return 1 unless Bugzilla->params->{max_reviewer_last_seen};
- return 0 if !defined($self->last_seen_date);
+ # never consider .bugs or .tld addresses as inactive.
+ return 1 if $self->login =~ /\.(?:bugs|tld)$/;
+ return 1 unless Bugzilla->params->{max_reviewer_last_seen};
+ return 0 if !defined($self->last_seen_date);
- my $dt = datetime_from($self->last_seen_date);
- my $days_ago = $dt->delta_days(DateTime->now())->in_units('days');
+ my $dt = datetime_from($self->last_seen_date);
+ my $days_ago = $dt->delta_days(DateTime->now())->in_units('days');
- return $days_ago <= Bugzilla->params->{max_reviewer_last_seen};
+ return $days_ago <= Bugzilla->params->{max_reviewer_last_seen};
}
sub _user_review_count {
- my ($self) = @_;
- if (!exists $self->{review_count}) {
- my $dbh = Bugzilla->dbh;
- ($self->{review_count}) = $dbh->selectrow_array(
- "SELECT COUNT(*)
+ my ($self) = @_;
+ if (!exists $self->{review_count}) {
+ my $dbh = Bugzilla->dbh;
+ ($self->{review_count}) = $dbh->selectrow_array(
+ "SELECT COUNT(*)
FROM flags
INNER JOIN flagtypes ON flagtypes.id = flags.type_id
WHERE flags.requestee_id = ?
- AND " . $dbh->sql_in('flagtypes.name', [ "'review'", "'feedback'" ]),
- undef,
- $self->id,
- );
- }
- return $self->{review_count};
+ AND "
+ . $dbh->sql_in('flagtypes.name', ["'review'", "'feedback'"]), undef,
+ $self->id,
+ );
+ }
+ return $self->{review_count};
}
sub _user_reviews_blocked {
- return $_[0]->settings->{block_reviews}->{value} eq 'on';
+ return $_[0]->settings->{block_reviews}->{value} eq 'on';
}
#
@@ -132,148 +133,143 @@ sub _user_reviews_blocked {
#
sub _bug_mentors {
- my ($self, $options) = @_;
- $options //= {};
- my $dbh = Bugzilla->dbh;
- if (!$self->{bug_mentors}) {
- my $mentor_ids = $dbh->selectcol_arrayref("
- SELECT user_id FROM bug_mentors WHERE bug_id = ?",
- undef,
- $self->id);
- $self->{bug_mentors} = [];
- foreach my $mentor_id (@$mentor_ids) {
- push(@{ $self->{bug_mentors} }, Bugzilla::User->new({ id => $mentor_id, cache => 1 }));
- }
- $self->{bug_mentors} = [
- sort { $a->login cmp $b->login } @{ $self->{bug_mentors} }
- ];
- }
- my @result = @{ $self->{bug_mentors} };
- if ($options->{exclude_needinfo_blocked}) {
- @result = grep { !$_->needinfo_blocked } @result;
- }
- if ($options->{exclude_review_blocked}) {
- @result = grep { !$_->reviews_blocked } @result;
+ my ($self, $options) = @_;
+ $options //= {};
+ my $dbh = Bugzilla->dbh;
+ if (!$self->{bug_mentors}) {
+ my $mentor_ids = $dbh->selectcol_arrayref("
+ SELECT user_id FROM bug_mentors WHERE bug_id = ?", undef, $self->id);
+ $self->{bug_mentors} = [];
+ foreach my $mentor_id (@$mentor_ids) {
+ push(
+ @{$self->{bug_mentors}},
+ Bugzilla::User->new({id => $mentor_id, cache => 1})
+ );
}
- return \@result;
+ $self->{bug_mentors}
+ = [sort { $a->login cmp $b->login } @{$self->{bug_mentors}}];
+ }
+ my @result = @{$self->{bug_mentors}};
+ if ($options->{exclude_needinfo_blocked}) {
+ @result = grep { !$_->needinfo_blocked } @result;
+ }
+ if ($options->{exclude_review_blocked}) {
+ @result = grep { !$_->reviews_blocked } @result;
+ }
+ return \@result;
}
sub _bug_is_mentor {
- my ($self, $user) = @_;
- my $user_id = ($user || Bugzilla->user)->id;
- return (grep { $_->id == $user_id} @{ $self->mentors }) ? 1 : 0;
+ my ($self, $user) = @_;
+ my $user_id = ($user || Bugzilla->user)->id;
+ return (grep { $_->id == $user_id } @{$self->mentors}) ? 1 : 0;
}
sub _bug_set_bug_mentors {
- my ($self, $value) = @_;
- $self->set('bug_mentors', $value);
+ my ($self, $value) = @_;
+ $self->set('bug_mentors', $value);
}
sub object_validators {
- my ($self, $args) = @_;
- return unless $args->{class} eq 'Bugzilla::Bug';
- $args->{validators}->{bug_mentors} = \&_bug_check_bug_mentors;
+ my ($self, $args) = @_;
+ return unless $args->{class} eq 'Bugzilla::Bug';
+ $args->{validators}->{bug_mentors} = \&_bug_check_bug_mentors;
}
sub _bug_check_bug_mentors {
- my ($self, $value) = @_;
- my %seen;
- my $mentors = [
- grep { !$seen{$_->id}++ }
- map { Bugzilla::User->check({ name => $_, cache => 1 }) }
- ref($value) ? @$value : ($value)
- ];
- if (scalar(@$mentors) > MENTOR_LIMIT) {
- ThrowUserError('mentor_limit_exceeded', { limit => MENTOR_LIMIT });
- }
- return $mentors;
+ my ($self, $value) = @_;
+ my %seen;
+ my $mentors
+ = [grep { !$seen{$_->id}++ }
+ map { Bugzilla::User->check({name => $_, cache => 1}) }
+ ref($value) ? @$value : ($value)];
+ if (scalar(@$mentors) > MENTOR_LIMIT) {
+ ThrowUserError('mentor_limit_exceeded', {limit => MENTOR_LIMIT});
+ }
+ return $mentors;
}
sub bug_user_match_fields {
- my ($self, $args) = @_;
- $args->{fields}->{bug_mentors} = { type => 'multi' };
+ my ($self, $args) = @_;
+ $args->{fields}->{bug_mentors} = {type => 'multi'};
}
sub bug_before_create {
- my ($self, $args) = @_;
- my $params = $args->{params};
- my $stash = $args->{stash};
- $stash->{bug_mentors} = delete $params->{bug_mentors};
+ my ($self, $args) = @_;
+ my $params = $args->{params};
+ my $stash = $args->{stash};
+ $stash->{bug_mentors} = delete $params->{bug_mentors};
}
sub bug_end_of_create {
- my ($self, $args) = @_;
- my $bug = $args->{bug};
- my $stash = $args->{stash};
- if (my $mentors = $stash->{bug_mentors}) {
- $self->_update_user_table({
- object => $bug,
- old_users => [],
- new_users => $self->_bug_check_bug_mentors($mentors),
- table => 'bug_mentors',
- id_field => 'bug_id',
- });
- }
+ my ($self, $args) = @_;
+ my $bug = $args->{bug};
+ my $stash = $args->{stash};
+ if (my $mentors = $stash->{bug_mentors}) {
+ $self->_update_user_table({
+ object => $bug,
+ old_users => [],
+ new_users => $self->_bug_check_bug_mentors($mentors),
+ table => 'bug_mentors',
+ id_field => 'bug_id',
+ });
+ }
}
sub _update_user_table {
- my ($self, $args) = @_;
- my ($object, $old_users, $new_users, $table, $id_field, $has_sortkey, $return) =
- @$args{qw(object old_users new_users table id_field has_sortkey return)};
- my $dbh = Bugzilla->dbh;
- my (@removed, @added);
-
- # remove deleted users
- foreach my $old_user (@$old_users) {
- if (!grep { $_->id == $old_user->id } @$new_users) {
- $dbh->do(
- "DELETE FROM $table WHERE $id_field = ? AND user_id = ?",
- undef,
- $object->id, $old_user->id,
- );
- push @removed, $old_user;
- }
+ my ($self, $args) = @_;
+ my ($object, $old_users, $new_users, $table, $id_field, $has_sortkey, $return)
+ = @$args{qw(object old_users new_users table id_field has_sortkey return)};
+ my $dbh = Bugzilla->dbh;
+ my (@removed, @added);
+
+ # remove deleted users
+ foreach my $old_user (@$old_users) {
+ if (!grep { $_->id == $old_user->id } @$new_users) {
+ $dbh->do("DELETE FROM $table WHERE $id_field = ? AND user_id = ?",
+ undef, $object->id, $old_user->id,);
+ push @removed, $old_user;
}
- # add new users
- foreach my $new_user (@$new_users) {
- if (!grep { $_->id == $new_user->id } @$old_users) {
- $dbh->do(
- "INSERT INTO $table ($id_field, user_id) VALUES (?, ?)",
- undef,
- $object->id, $new_user->id,
- );
- push @added, $new_user;
- }
+ }
+
+ # add new users
+ foreach my $new_user (@$new_users) {
+ if (!grep { $_->id == $new_user->id } @$old_users) {
+ $dbh->do("INSERT INTO $table ($id_field, user_id) VALUES (?, ?)",
+ undef, $object->id, $new_user->id,);
+ push @added, $new_user;
}
+ }
- return unless @removed || @added;
-
- if ($has_sortkey) {
- # update the sortkey for all users
- for (my $i = 0; $i < scalar(@$new_users); $i++) {
- $dbh->do(
- "UPDATE $table SET sortkey=? WHERE $id_field = ? AND user_id = ?",
- undef,
- ($i + 1) * 10, $object->id, $new_users->[$i]->id,
- );
- }
- }
+ return unless @removed || @added;
- if (!$return) {
- return undef;
- }
- elsif ($return eq 'diff') {
- return [
- @removed ? join(', ', map { $_->login } @removed) : undef,
- @added ? join(', ', map { $_->login } @added) : undef,
- ];
- }
- elsif ($return eq 'old-new') {
- return [
- @$old_users ? join(', ', map { $_->login } @$old_users) : '',
- @$new_users ? join(', ', map { $_->login } @$new_users) : '',
- ];
+ if ($has_sortkey) {
+
+ # update the sortkey for all users
+ for (my $i = 0; $i < scalar(@$new_users); $i++) {
+ $dbh->do(
+ "UPDATE $table SET sortkey=? WHERE $id_field = ? AND user_id = ?",
+ undef, ($i + 1) * 10,
+ $object->id, $new_users->[$i]->id,
+ );
}
+ }
+
+ if (!$return) {
+ return undef;
+ }
+ elsif ($return eq 'diff') {
+ return [
+ @removed ? join(', ', map { $_->login } @removed) : undef,
+ @added ? join(', ', map { $_->login } @added) : undef,
+ ];
+ }
+ elsif ($return eq 'old-new') {
+ return [
+ @$old_users ? join(', ', map { $_->login } @$old_users) : '',
+ @$new_users ? join(', ', map { $_->login } @$new_users) : '',
+ ];
+ }
}
#
@@ -283,44 +279,46 @@ sub _update_user_table {
sub _product_reviewer_required { $_[0]->{reviewer_required} }
sub object_columns {
- my ($self, $args) = @_;
- my ($class, $columns) = @$args{qw(class columns)};
- if ($class->isa('Bugzilla::Product')) {
- my $dbh = Bugzilla->dbh;
- my @new_columns = qw(reviewer_required);
- push @$columns, grep { $dbh->bz_column_info($class->DB_TABLE, $_) } @new_columns;
- }
- elsif ($class->isa('Bugzilla::User')) {
- my $dbh = Bugzilla->dbh;
- my @new_columns = qw(review_request_count feedback_request_count needinfo_request_count);
- push @$columns, grep { $dbh->bz_column_info($class->DB_TABLE, $_) } @new_columns;
- }
+ my ($self, $args) = @_;
+ my ($class, $columns) = @$args{qw(class columns)};
+ if ($class->isa('Bugzilla::Product')) {
+ my $dbh = Bugzilla->dbh;
+ my @new_columns = qw(reviewer_required);
+ push @$columns,
+ grep { $dbh->bz_column_info($class->DB_TABLE, $_) } @new_columns;
+ }
+ elsif ($class->isa('Bugzilla::User')) {
+ my $dbh = Bugzilla->dbh;
+ my @new_columns
+ = qw(review_request_count feedback_request_count needinfo_request_count);
+ push @$columns,
+ grep { $dbh->bz_column_info($class->DB_TABLE, $_) } @new_columns;
+ }
}
sub object_update_columns {
- my ($self, $args) = @_;
- my ($object, $columns) = @$args{qw(object columns)};
- if ($object->isa('Bugzilla::Product')) {
- push @$columns, 'reviewer_required';
- }
- elsif ($object->isa('Bugzilla::User')) {
- push @$columns, qw(review_request_count feedback_request_count needinfo_request_count);
- }
+ my ($self, $args) = @_;
+ my ($object, $columns) = @$args{qw(object columns)};
+ if ($object->isa('Bugzilla::Product')) {
+ push @$columns, 'reviewer_required';
+ }
+ elsif ($object->isa('Bugzilla::User')) {
+ push @$columns,
+ qw(review_request_count feedback_request_count needinfo_request_count);
+ }
}
sub _new_users_from_input {
- my ($field) = @_;
- my $input_params = Bugzilla->input_params;
- return undef unless exists $input_params->{$field};
- return [] unless $input_params->{$field};
- Bugzilla::User::match_field({ $field => {'type' => 'multi'} });;
- my $value = $input_params->{$field};
- my %seen;
- return [
- grep { !$seen{$_->id}++ }
- map { Bugzilla::User->check({ name => $_, cache => 1 }) }
- ref($value) ? @$value : ($value)
- ];
+ my ($field) = @_;
+ my $input_params = Bugzilla->input_params;
+ return undef unless exists $input_params->{$field};
+ return [] unless $input_params->{$field};
+ Bugzilla::User::match_field({$field => {'type' => 'multi'}});
+ my $value = $input_params->{$field};
+ my %seen;
+ return [grep { !$seen{$_->id}++ }
+ map { Bugzilla::User->check({name => $_, cache => 1}) }
+ ref($value) ? @$value : ($value)];
}
#
@@ -328,311 +326,329 @@ sub _new_users_from_input {
#
sub object_before_create {
- my ($self, $args) = @_;
- my ($class, $params) = @$args{qw(class params)};
- return unless $class->isa('Bugzilla::Product');
+ my ($self, $args) = @_;
+ my ($class, $params) = @$args{qw(class params)};
+ return unless $class->isa('Bugzilla::Product');
- $params->{reviewer_required} = Bugzilla->cgi->param('reviewer_required') ? 1 : 0;
+ $params->{reviewer_required}
+ = Bugzilla->cgi->param('reviewer_required') ? 1 : 0;
}
sub object_end_of_set_all {
- my ($self, $args) = @_;
- my ($object, $params) = @$args{qw(object params)};
- return unless $object->isa('Bugzilla::Product');
+ my ($self, $args) = @_;
+ my ($object, $params) = @$args{qw(object params)};
+ return unless $object->isa('Bugzilla::Product');
- $object->set('reviewer_required', Bugzilla->cgi->param('reviewer_required') ? 1 : 0);
+ $object->set('reviewer_required',
+ Bugzilla->cgi->param('reviewer_required') ? 1 : 0);
}
sub object_end_of_create {
- my ($self, $args) = @_;
- my ($object, $params) = @$args{qw(object params)};
-
- if ($object->isa('Bugzilla::Product')) {
- $self->_update_user_table({
- object => $object,
- old_users => [],
- new_users => _new_users_from_input('reviewers'),
- table => 'product_reviewers',
- id_field => 'product_id',
- has_sortkey => 1,
- });
- }
- elsif ($object->isa('Bugzilla::Component')) {
- $self->_update_user_table({
- object => $object,
- old_users => [],
- new_users => _new_users_from_input('reviewers'),
- table => 'component_reviewers',
- id_field => 'component_id',
- has_sortkey => 1,
- });
- }
- elsif (_is_countable_flag($object) && $object->requestee_id && $object->status eq '?') {
- _check_requestee($object);
- _adjust_request_count($object, +1);
- }
- if (_is_countable_flag($object)) {
- $self->_log_flag_state_activity($object, $object->status, $object->modification_date);
- }
+ my ($self, $args) = @_;
+ my ($object, $params) = @$args{qw(object params)};
+
+ if ($object->isa('Bugzilla::Product')) {
+ $self->_update_user_table({
+ object => $object,
+ old_users => [],
+ new_users => _new_users_from_input('reviewers'),
+ table => 'product_reviewers',
+ id_field => 'product_id',
+ has_sortkey => 1,
+ });
+ }
+ elsif ($object->isa('Bugzilla::Component')) {
+ $self->_update_user_table({
+ object => $object,
+ old_users => [],
+ new_users => _new_users_from_input('reviewers'),
+ table => 'component_reviewers',
+ id_field => 'component_id',
+ has_sortkey => 1,
+ });
+ }
+ elsif (_is_countable_flag($object)
+ && $object->requestee_id
+ && $object->status eq '?')
+ {
+ _check_requestee($object);
+ _adjust_request_count($object, +1);
+ }
+ if (_is_countable_flag($object)) {
+ $self->_log_flag_state_activity($object, $object->status,
+ $object->modification_date);
+ }
}
sub object_end_of_update {
- my ($self, $args) = @_;
- my ($object, $old_object, $changes) = @$args{qw(object old_object changes)};
-
- if ($object->isa('Bugzilla::Product') && exists Bugzilla->input_params->{reviewers}) {
- my $diff = $self->_update_user_table({
- object => $object,
- old_users => $old_object->reviewers_objs(1),
- new_users => _new_users_from_input('reviewers'),
- table => 'product_reviewers',
- id_field => 'product_id',
- has_sortkey => 1,
- return => 'old-new',
- });
- $changes->{reviewers} = $diff if $diff;
+ my ($self, $args) = @_;
+ my ($object, $old_object, $changes) = @$args{qw(object old_object changes)};
+
+ if ($object->isa('Bugzilla::Product')
+ && exists Bugzilla->input_params->{reviewers})
+ {
+ my $diff = $self->_update_user_table({
+ object => $object,
+ old_users => $old_object->reviewers_objs(1),
+ new_users => _new_users_from_input('reviewers'),
+ table => 'product_reviewers',
+ id_field => 'product_id',
+ has_sortkey => 1,
+ return => 'old-new',
+ });
+ $changes->{reviewers} = $diff if $diff;
+ }
+ elsif ($object->isa('Bugzilla::Component')) {
+ my $diff = $self->_update_user_table({
+ object => $object,
+ old_users => $old_object->reviewers_objs(1),
+ new_users => _new_users_from_input('reviewers'),
+ table => 'component_reviewers',
+ id_field => 'component_id',
+ has_sortkey => 1,
+ return => 'old-new',
+ });
+ $changes->{reviewers} = $diff if $diff;
+ }
+ elsif ($object->isa('Bugzilla::Bug')) {
+ my $diff = $self->_update_user_table({
+ object => $object,
+ old_users => $old_object->mentors,
+ new_users => $object->mentors,
+ table => 'bug_mentors',
+ id_field => 'bug_id',
+ return => 'diff',
+ });
+ $changes->{bug_mentor} = $diff if $diff;
+ }
+ elsif (_is_countable_flag($object)) {
+ my ($old_status, $new_status) = ($old_object->status, $object->status);
+ if ($old_status ne '?' && $new_status eq '?') {
+
+ # setting flag to ?
+ _adjust_request_count($object, +1);
+ if ($object->requestee_id) {
+ _check_requestee($object);
+ }
}
- elsif ($object->isa('Bugzilla::Component')) {
- my $diff = $self->_update_user_table({
- object => $object,
- old_users => $old_object->reviewers_objs(1),
- new_users => _new_users_from_input('reviewers'),
- table => 'component_reviewers',
- id_field => 'component_id',
- has_sortkey => 1,
- return => 'old-new',
- });
- $changes->{reviewers} = $diff if $diff;
+ elsif ($old_status eq '?' && $new_status ne '?') {
+
+ # setting flag from ?
+ _adjust_request_count($old_object, -1);
}
- elsif ($object->isa('Bugzilla::Bug')) {
- my $diff = $self->_update_user_table({
- object => $object,
- old_users => $old_object->mentors,
- new_users => $object->mentors,
- table => 'bug_mentors',
- id_field => 'bug_id',
- return => 'diff',
- });
- $changes->{bug_mentor} = $diff if $diff;
+ elsif ($old_object->requestee_id && !$object->requestee_id) {
+
+ # removing requestee
+ _adjust_request_count($old_object, -1);
}
- elsif (_is_countable_flag($object)) {
- my ($old_status, $new_status) = ($old_object->status, $object->status);
- if ($old_status ne '?' && $new_status eq '?') {
- # setting flag to ?
- _adjust_request_count($object, +1);
- if ($object->requestee_id) {
- _check_requestee($object);
- }
- }
- elsif ($old_status eq '?' && $new_status ne '?') {
- # setting flag from ?
- _adjust_request_count($old_object, -1);
- }
- elsif ($old_object->requestee_id && !$object->requestee_id) {
- # removing requestee
- _adjust_request_count($old_object, -1);
- }
- elsif (!$old_object->requestee_id && $object->requestee_id) {
- # setting requestee
- _check_requestee($object);
- _adjust_request_count($object, +1);
- }
- elsif ($old_object->requestee_id && $object->requestee_id
- && $old_object->requestee_id != $object->requestee_id)
- {
- # changing requestee
- _check_requestee($object);
- _adjust_request_count($old_object, -1);
- _adjust_request_count($object, +1);
- }
+ elsif (!$old_object->requestee_id && $object->requestee_id) {
+
+ # setting requestee
+ _check_requestee($object);
+ _adjust_request_count($object, +1);
}
+ elsif ($old_object->requestee_id
+ && $object->requestee_id
+ && $old_object->requestee_id != $object->requestee_id)
+ {
+ # changing requestee
+ _check_requestee($object);
+ _adjust_request_count($old_object, -1);
+ _adjust_request_count($object, +1);
+ }
+ }
}
sub flag_updated {
- my ($self, $args) = @_;
- my $flag = $args->{flag};
- my $timestamp = $args->{timestamp};
- my $changes = $args->{changes};
-
- return unless scalar(keys %$changes);
- if (_is_countable_flag($flag)) {
- $self->_log_flag_state_activity($flag, $flag->status, $timestamp);
- }
+ my ($self, $args) = @_;
+ my $flag = $args->{flag};
+ my $timestamp = $args->{timestamp};
+ my $changes = $args->{changes};
+
+ return unless scalar(keys %$changes);
+ if (_is_countable_flag($flag)) {
+ $self->_log_flag_state_activity($flag, $flag->status, $timestamp);
+ }
}
sub flag_deleted {
- my ($self, $args) = @_;
- my $flag = $args->{flag};
- my $timestamp = $args->{timestamp};
+ my ($self, $args) = @_;
+ my $flag = $args->{flag};
+ my $timestamp = $args->{timestamp};
- if (_is_countable_flag($flag) && $flag->requestee_id && $flag->status eq '?') {
- _adjust_request_count($flag, -1);
- }
+ if (_is_countable_flag($flag) && $flag->requestee_id && $flag->status eq '?') {
+ _adjust_request_count($flag, -1);
+ }
- if (_is_countable_flag($flag)) {
- $self->_log_flag_state_activity($flag, 'X', $timestamp, Bugzilla->user->id);
- }
+ if (_is_countable_flag($flag)) {
+ $self->_log_flag_state_activity($flag, 'X', $timestamp, Bugzilla->user->id);
+ }
}
sub _is_countable_flag {
- my ($object) = @_;
- return unless $object->isa('Bugzilla::Flag');
- my $type_name = $object->type->name;
- return $type_name eq 'review' || $type_name eq 'feedback' || $type_name eq 'needinfo';
+ my ($object) = @_;
+ return unless $object->isa('Bugzilla::Flag');
+ my $type_name = $object->type->name;
+ return
+ $type_name eq 'review'
+ || $type_name eq 'feedback'
+ || $type_name eq 'needinfo';
}
sub _check_requestee {
- my ($flag) = @_;
- return unless $flag->type->name eq 'review' || $flag->type->name eq 'feedback';
- if ($flag->requestee->reviews_blocked) {
- ThrowUserError('reviews_blocked',
- { requestee => $flag->requestee, flagtype => $flag->type->name });
- }
+ my ($flag) = @_;
+ return unless $flag->type->name eq 'review' || $flag->type->name eq 'feedback';
+ if ($flag->requestee->reviews_blocked) {
+ ThrowUserError('reviews_blocked',
+ {requestee => $flag->requestee, flagtype => $flag->type->name});
+ }
}
sub _log_flag_state_activity {
- my ($self, $flag, $status, $timestamp, $setter_id) = @_;
-
- $setter_id //= $flag->setter_id;
-
- Bugzilla::Extension::Review::FlagStateActivity->create({
- flag_when => $timestamp,
- setter_id => $setter_id,
- status => $status,
- type_id => $flag->type_id,
- flag_id => $flag->id,
- requestee_id => $flag->requestee_id,
- bug_id => $flag->bug_id,
- attachment_id => $flag->attach_id,
- });
+ my ($self, $flag, $status, $timestamp, $setter_id) = @_;
+
+ $setter_id //= $flag->setter_id;
+
+ Bugzilla::Extension::Review::FlagStateActivity->create({
+ flag_when => $timestamp,
+ setter_id => $setter_id,
+ status => $status,
+ type_id => $flag->type_id,
+ flag_id => $flag->id,
+ requestee_id => $flag->requestee_id,
+ bug_id => $flag->bug_id,
+ attachment_id => $flag->attach_id,
+ });
}
sub _adjust_request_count {
- my ($flag, $add) = @_;
- return unless my $requestee_id = $flag->requestee_id;
- my $field = $flag->type->name . '_request_count';
-
- # update the current user's object so things are display correctly on the
- # post-processing page
- my $user = Bugzilla->user;
- if ($requestee_id == $user->id) {
- $user->{$field} += $add;
- }
-
- # update database directly to avoid creating audit_log entries
- $add = $add == -1 ? ' - 1' : ' + 1';
- Bugzilla->dbh->do(
- "UPDATE profiles SET $field = $field $add WHERE userid = ?",
- undef,
- $requestee_id
- );
- Bugzilla->memcached->clear({ table => 'profiles', id => $requestee_id });
+ my ($flag, $add) = @_;
+ return unless my $requestee_id = $flag->requestee_id;
+ my $field = $flag->type->name . '_request_count';
+
+ # update the current user's object so things are display correctly on the
+ # post-processing page
+ my $user = Bugzilla->user;
+ if ($requestee_id == $user->id) {
+ $user->{$field} += $add;
+ }
+
+ # update database directly to avoid creating audit_log entries
+ $add = $add == -1 ? ' - 1' : ' + 1';
+ Bugzilla->dbh->do("UPDATE profiles SET $field = $field $add WHERE userid = ?",
+ undef, $requestee_id);
+ Bugzilla->memcached->clear({table => 'profiles', id => $requestee_id});
}
# bugzilla's handling of requestee matching when creating bugs is "if it's
# wrong, or matches too many, default to empty", which breaks mandatory
# reviewer requirements. instead we just throw an error.
sub post_bug_attachment_flags {
- my ($self, $args) = @_;
- $self->_check_review_flag($args);
+ my ($self, $args) = @_;
+ $self->_check_review_flag($args);
}
sub create_attachment_flags {
- my ($self, $args) = @_;
- $self->_check_review_flag($args);
+ my ($self, $args) = @_;
+ $self->_check_review_flag($args);
}
sub _check_review_flag {
- my ($self, $args) = @_;
- my ($bug, $attachment) = @$args{qw( bug attachment )};
- my $cgi = Bugzilla->cgi;
-
- # extract the set flag-types
- my @flagtype_ids = map { /^flag_type-(\d+)$/ ? $1 : () } $cgi->param();
- @flagtype_ids = grep { $cgi->param("flag_type-$_") eq '?' } @flagtype_ids;
- return unless scalar(@flagtype_ids);
-
- # find valid review flagtypes
- my $flag_types = Bugzilla::FlagType::match({
- product_id => $bug->product_id,
- component_id => $bug->component_id,
- is_active => 1
- });
- foreach my $flag_type (@$flag_types) {
- next unless $flag_type->name eq 'review'
- && $flag_type->target_type eq 'attachment';
- my $type_id = $flag_type->id;
- next unless scalar(grep { $_ == $type_id } @flagtype_ids);
-
- my $reviewers = clean_text($cgi->param("requestee_type-$type_id") || '');
- if ($reviewers eq '' && $bug->product_obj->reviewer_required) {
- ThrowUserError('reviewer_required');
- }
-
- foreach my $reviewer (split(/[,;]+/, $reviewers)) {
- # search on the reviewer
- my $users = Bugzilla::User::match($reviewer, 2, 1);
-
- # no matches
- if (scalar(@$users) == 0) {
- ThrowUserError('user_match_failed', { name => $reviewer });
- }
-
- # more than one match, throw error
- if (scalar(@$users) > 1) {
- ThrowUserError('user_match_too_many', { fields => [ 'review' ] });
- }
-
- # we want to throw an error if the requestee does not have access
- # to the bug. bugzilla's default behaviour is to sliently drop the
- # requestee, which results in a confusing 'reviewer required'
- # error.
- # fake it by creating a flag and try to set the requestee.
- # bugzilla's flags don't have a normal constructor or property
- # setters, so we have to bless it directly then call the internal
- # check_requestee method. urgh.
- my $flag = bless({
- type_id => $flag_type->id,
- status => '?',
- bug_id => $bug->id,
- attach_id => $attachment->id
- }, 'Bugzilla::Flag');
- $flag->_check_requestee($users->[0]->login, $bug, $attachment);
- }
+ my ($self, $args) = @_;
+ my ($bug, $attachment) = @$args{qw( bug attachment )};
+ my $cgi = Bugzilla->cgi;
+
+ # extract the set flag-types
+ my @flagtype_ids = map { /^flag_type-(\d+)$/ ? $1 : () } $cgi->param();
+ @flagtype_ids = grep { $cgi->param("flag_type-$_") eq '?' } @flagtype_ids;
+ return unless scalar(@flagtype_ids);
+
+ # find valid review flagtypes
+ my $flag_types = Bugzilla::FlagType::match({
+ product_id => $bug->product_id,
+ component_id => $bug->component_id,
+ is_active => 1
+ });
+ foreach my $flag_type (@$flag_types) {
+ next
+ unless $flag_type->name eq 'review'
+ && $flag_type->target_type eq 'attachment';
+ my $type_id = $flag_type->id;
+ next unless scalar(grep { $_ == $type_id } @flagtype_ids);
+
+ my $reviewers = clean_text($cgi->param("requestee_type-$type_id") || '');
+ if ($reviewers eq '' && $bug->product_obj->reviewer_required) {
+ ThrowUserError('reviewer_required');
}
+
+ foreach my $reviewer (split(/[,;]+/, $reviewers)) {
+
+ # search on the reviewer
+ my $users = Bugzilla::User::match($reviewer, 2, 1);
+
+ # no matches
+ if (scalar(@$users) == 0) {
+ ThrowUserError('user_match_failed', {name => $reviewer});
+ }
+
+ # more than one match, throw error
+ if (scalar(@$users) > 1) {
+ ThrowUserError('user_match_too_many', {fields => ['review']});
+ }
+
+ # we want to throw an error if the requestee does not have access
+ # to the bug. bugzilla's default behaviour is to sliently drop the
+ # requestee, which results in a confusing 'reviewer required'
+ # error.
+ # fake it by creating a flag and try to set the requestee.
+ # bugzilla's flags don't have a normal constructor or property
+ # setters, so we have to bless it directly then call the internal
+ # check_requestee method. urgh.
+ my $flag = bless(
+ {
+ type_id => $flag_type->id,
+ status => '?',
+ bug_id => $bug->id,
+ attach_id => $attachment->id
+ },
+ 'Bugzilla::Flag'
+ );
+ $flag->_check_requestee($users->[0]->login, $bug, $attachment);
+ }
+ }
}
sub flag_end_of_update {
- my ($self, $args) = @_;
- my ($object, $old_flags, $new_flags) = @$args{qw(object old_flags new_flags)};
- my $bug = $object->isa('Bugzilla::Attachment') ? $object->bug : $object;
-
- my (undef, $added) = diff_arrays($old_flags, $new_flags);
- foreach my $change (@$added) {
- $change =~ s/^[^:]+://;
- my $reviewer = '';
- if ($change =~ s/\(([^\)]+)\)$//) {
- $reviewer = $1;
- }
- my ($name, $value) = $change =~ /^(.+)(.)$/;
-
- if ($name eq 'review' && $value eq '?') {
- if ($reviewer eq '') {
- ThrowUserError('reviewer_required') if $bug->product_obj->reviewer_required;
- }
- else {
- my $reviewer_obj = Bugzilla::User->check({
- name => $reviewer,
- cache => 1
- });
-
- ThrowUserError('reviewer_inactive', {
- reviewer => $reviewer_obj,
- timeout => Bugzilla->params->{max_reviewer_last_seen}
- }) unless $reviewer_obj->is_active;
- }
- }
+ my ($self, $args) = @_;
+ my ($object, $old_flags, $new_flags) = @$args{qw(object old_flags new_flags)};
+ my $bug = $object->isa('Bugzilla::Attachment') ? $object->bug : $object;
+
+ my (undef, $added) = diff_arrays($old_flags, $new_flags);
+ foreach my $change (@$added) {
+ $change =~ s/^[^:]+://;
+ my $reviewer = '';
+ if ($change =~ s/\(([^\)]+)\)$//) {
+ $reviewer = $1;
+ }
+ my ($name, $value) = $change =~ /^(.+)(.)$/;
+
+ if ($name eq 'review' && $value eq '?') {
+ if ($reviewer eq '') {
+ ThrowUserError('reviewer_required') if $bug->product_obj->reviewer_required;
+ }
+ else {
+ my $reviewer_obj = Bugzilla::User->check({name => $reviewer, cache => 1});
+
+ ThrowUserError(
+ 'reviewer_inactive',
+ {
+ reviewer => $reviewer_obj,
+ timeout => Bugzilla->params->{max_reviewer_last_seen}
+ }
+ ) unless $reviewer_obj->is_active;
+ }
}
+ }
}
#
@@ -640,44 +656,45 @@ sub flag_end_of_update {
#
sub buglist_columns {
- my ($self, $args) = @_;
- my $dbh = Bugzilla->dbh;
- my $columns = $args->{columns};
- $columns->{bug_mentor} = { title => 'Mentor' };
- if (Bugzilla->user->id) {
- $columns->{bug_mentor}->{name}
- = $dbh->sql_group_concat('map_mentors_names.login_name');
- }
- else {
- $columns->{bug_mentor}->{name}
- = $dbh->sql_group_concat('map_mentors_names.realname');
-
- }
+ my ($self, $args) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $columns = $args->{columns};
+ $columns->{bug_mentor} = {title => 'Mentor'};
+ if (Bugzilla->user->id) {
+ $columns->{bug_mentor}->{name}
+ = $dbh->sql_group_concat('map_mentors_names.login_name');
+ }
+ else {
+ $columns->{bug_mentor}->{name}
+ = $dbh->sql_group_concat('map_mentors_names.realname');
+
+ }
}
sub buglist_column_joins {
- my ($self, $args) = @_;
- my $column_joins = $args->{column_joins};
- $column_joins->{bug_mentor} = {
- as => 'map_mentors',
- table => 'bug_mentors',
- then_to => {
- as => 'map_mentors_names',
- table => 'profiles',
- from => 'map_mentors.user_id',
- to => 'userid',
- },
+ my ($self, $args) = @_;
+ my $column_joins = $args->{column_joins};
+ $column_joins->{bug_mentor} = {
+ as => 'map_mentors',
+ table => 'bug_mentors',
+ then_to => {
+ as => 'map_mentors_names',
+ table => 'profiles',
+ from => 'map_mentors.user_id',
+ to => 'userid',
},
+ },
+ ;
}
sub search_operator_field_override {
- my ($self, $args) = @_;
- my $operators = $args->{operators};
- $operators->{bug_mentor} = {
- _non_changed => sub {
- Bugzilla::Search::_user_nonchanged(@_)
- }
- };
+ my ($self, $args) = @_;
+ my $operators = $args->{operators};
+ $operators->{bug_mentor} = {
+ _non_changed => sub {
+ Bugzilla::Search::_user_nonchanged(@_);
+ }
+ };
}
#
@@ -685,105 +702,103 @@ sub search_operator_field_override {
#
sub webservice {
- my ($self, $args) = @_;
- my $dispatch = $args->{dispatch};
- $dispatch->{Review} = "Bugzilla::Extension::Review::WebService";
+ my ($self, $args) = @_;
+ my $dispatch = $args->{dispatch};
+ $dispatch->{Review} = "Bugzilla::Extension::Review::WebService";
}
sub user_preferences {
- my ($self, $args) = @_;
- return unless
- $args->{current_tab} eq 'account'
- && $args->{save_changes};
-
- my $input = Bugzilla->input_params;
- my $settings = Bugzilla->user->settings;
-
- my $value = $input->{block_reviews} ? 'on' : 'off';
- $settings->{block_reviews}->validate_value($value);
- $settings->{block_reviews}->set($value);
- clear_settings_cache(Bugzilla->user->id);
+ my ($self, $args) = @_;
+ return unless $args->{current_tab} eq 'account' && $args->{save_changes};
+
+ my $input = Bugzilla->input_params;
+ my $settings = Bugzilla->user->settings;
+
+ my $value = $input->{block_reviews} ? 'on' : 'off';
+ $settings->{block_reviews}->validate_value($value);
+ $settings->{block_reviews}->set($value);
+ clear_settings_cache(Bugzilla->user->id);
}
sub page_before_template {
- my ($self, $args) = @_;
-
- if ($args->{page_id} eq 'review_suggestions.html') {
- $self->review_suggestions_report($args);
- }
- elsif ($args->{page_id} eq 'review_requests_rebuild.html') {
- $self->review_requests_rebuild($args);
- }
- elsif ($args->{page_id} eq 'review_history.html') {
- $self->review_history($args);
- }
+ my ($self, $args) = @_;
+
+ if ($args->{page_id} eq 'review_suggestions.html') {
+ $self->review_suggestions_report($args);
+ }
+ elsif ($args->{page_id} eq 'review_requests_rebuild.html') {
+ $self->review_requests_rebuild($args);
+ }
+ elsif ($args->{page_id} eq 'review_history.html') {
+ $self->review_history($args);
+ }
}
sub review_suggestions_report {
- my ($self, $args) = @_;
-
- my $user = Bugzilla->login(LOGIN_REQUIRED);
- my $products = [];
- my @products = sort { lc($a->name) cmp lc($b->name) }
- @{ Bugzilla->user->get_accessible_products };
- foreach my $product_obj (@products) {
- my $has_reviewers = 0;
- my $product = {
- name => $product_obj->name,
- components => [],
- reviewers => $product_obj->reviewers_objs(1),
- };
- $has_reviewers = scalar @{ $product->{reviewers} };
-
- foreach my $component_obj (@{ $product_obj->components }) {
- my $component = {
- name => $component_obj->name,
- reviewers => $component_obj->reviewers_objs(1),
- };
- if (@{ $component->{reviewers} }) {
- push @{ $product->{components} }, $component;
- $has_reviewers = 1;
- }
- }
-
- if ($has_reviewers) {
- push @$products, $product;
- }
+ my ($self, $args) = @_;
+
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
+ my $products = [];
+ my @products = sort { lc($a->name) cmp lc($b->name) }
+ @{Bugzilla->user->get_accessible_products};
+ foreach my $product_obj (@products) {
+ my $has_reviewers = 0;
+ my $product = {
+ name => $product_obj->name,
+ components => [],
+ reviewers => $product_obj->reviewers_objs(1),
+ };
+ $has_reviewers = scalar @{$product->{reviewers}};
+
+ foreach my $component_obj (@{$product_obj->components}) {
+ my $component = {
+ name => $component_obj->name,
+ reviewers => $component_obj->reviewers_objs(1),
+ };
+ if (@{$component->{reviewers}}) {
+ push @{$product->{components}}, $component;
+ $has_reviewers = 1;
+ }
+ }
+
+ if ($has_reviewers) {
+ push @$products, $product;
}
- $args->{vars}->{products} = $products;
+ }
+ $args->{vars}->{products} = $products;
}
sub review_requests_rebuild {
- my ($self, $args) = @_;
-
- Bugzilla->user->in_group('admin')
- || ThrowUserError('auth_failure', { group => 'admin',
- action => 'run',
- object => 'review_requests_rebuild' });
- if (Bugzilla->cgi->param('rebuild')) {
- my $processed_users = 0;
- rebuild_review_counters(sub {
- my ($count, $total) = @_;
- $processed_users = $total;
- });
- $args->{vars}->{rebuild} = 1;
- $args->{vars}->{total} = $processed_users;
- }
+ my ($self, $args) = @_;
+
+ Bugzilla->user->in_group('admin')
+ || ThrowUserError('auth_failure',
+ {group => 'admin', action => 'run', object => 'review_requests_rebuild'});
+ if (Bugzilla->cgi->param('rebuild')) {
+ my $processed_users = 0;
+ rebuild_review_counters(sub {
+ my ($count, $total) = @_;
+ $processed_users = $total;
+ });
+ $args->{vars}->{rebuild} = 1;
+ $args->{vars}->{total} = $processed_users;
+ }
}
sub review_history {
- my ($self, $args) = @_;
-
- my $user = Bugzilla->login(LOGIN_REQUIRED);
-
- Bugzilla::User::match_field({ 'requestee' => { 'type' => 'single' } });
- my $requestee = Bugzilla->input_params->{requestee};
- if ($requestee) {
- $args->{vars}{requestee} = Bugzilla::User->check({ name => $requestee, cache => 1 });
- }
- else {
- $args->{vars}{requestee} = $user;
- }
+ my ($self, $args) = @_;
+
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
+
+ Bugzilla::User::match_field({'requestee' => {'type' => 'single'}});
+ my $requestee = Bugzilla->input_params->{requestee};
+ if ($requestee) {
+ $args->{vars}{requestee}
+ = Bugzilla::User->check({name => $requestee, cache => 1});
+ }
+ else {
+ $args->{vars}{requestee} = $user;
+ }
}
#
@@ -791,282 +806,178 @@ sub review_history {
#
sub db_schema_abstract_schema {
- my ($self, $args) = @_;
- $args->{'schema'}->{'product_reviewers'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- user_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'CASCADE',
- }
- },
- display_name => {
- TYPE => 'VARCHAR(64)',
- },
- product_id => {
- TYPE => 'INT2',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'products',
- COLUMN => 'id',
- DELETE => 'CASCADE',
- }
- },
- sortkey => {
- TYPE => 'INT2',
- NOTNULL => 1,
- DEFAULT => 0,
- },
- ],
- INDEXES => [
- product_reviewers_idx => {
- FIELDS => [ 'user_id', 'product_id' ],
- TYPE => 'UNIQUE',
- },
- ],
- };
- $args->{'schema'}->{'component_reviewers'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- user_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'CASCADE',
- }
- },
- display_name => {
- TYPE => 'VARCHAR(64)',
- },
- component_id => {
- TYPE => 'INT2',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'components',
- COLUMN => 'id',
- DELETE => 'CASCADE',
- }
- },
- sortkey => {
- TYPE => 'INT2',
- NOTNULL => 1,
- DEFAULT => 0,
- },
- ],
- INDEXES => [
- component_reviewers_idx => {
- FIELDS => [ 'user_id', 'component_id' ],
- TYPE => 'UNIQUE',
- },
- ],
- };
-
- $args->{'schema'}->{'flag_state_activity'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
-
- flag_when => {
- TYPE => 'DATETIME',
- NOTNULL => 1,
- },
-
- type_id => {
- TYPE => 'INT2',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'flagtypes',
- COLUMN => 'id',
- DELETE => 'CASCADE'
- }
- },
-
- flag_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- },
-
- setter_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'profiles',
- COLUMN => 'userid',
- },
- },
-
- requestee_id => {
- TYPE => 'INT3',
- REFERENCES => {
- TABLE => 'profiles',
- COLUMN => 'userid',
- },
- },
-
- bug_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'bugs',
- COLUMN => 'bug_id',
- DELETE => 'CASCADE'
- }
- },
-
- attachment_id => {
- TYPE => 'INT3',
- REFERENCES => {
- TABLE => 'attachments',
- COLUMN => 'attach_id',
- DELETE => 'CASCADE'
- }
- },
-
- status => {
- TYPE => 'CHAR(1)',
- NOTNULL => 1,
- },
- ],
- };
-
- $args->{'schema'}->{'bug_mentors'} = {
- FIELDS => [
- bug_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'bugs',
- COLUMN => 'bug_id',
- DELETE => 'CASCADE',
- },
- },
- user_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'CASCADE',
- }
- },
- ],
- INDEXES => [
- bug_mentors_idx => {
- FIELDS => [ 'bug_id', 'user_id' ],
- TYPE => 'UNIQUE',
- },
- bug_mentors_bug_id_idx => [ 'bug_id' ],
- ],
- };
-
- $args->{'schema'}->{'bug_mentors'} = {
- FIELDS => [
- bug_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'bugs',
- COLUMN => 'bug_id',
- DELETE => 'CASCADE',
- },
- },
- user_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'CASCADE',
- }
- },
- ],
- INDEXES => [
- bug_mentors_idx => {
- FIELDS => [ 'bug_id', 'user_id' ],
- TYPE => 'UNIQUE',
- },
- bug_mentors_bug_id_idx => [ 'bug_id' ],
- ],
- };
+ my ($self, $args) = @_;
+ $args->{'schema'}->{'product_reviewers'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ user_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE',}
+ },
+ display_name => {TYPE => 'VARCHAR(64)',},
+ product_id => {
+ TYPE => 'INT2',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'products', COLUMN => 'id', DELETE => 'CASCADE',}
+ },
+ sortkey => {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0,},
+ ],
+ INDEXES => [
+ product_reviewers_idx =>
+ {FIELDS => ['user_id', 'product_id'], TYPE => 'UNIQUE',},
+ ],
+ };
+ $args->{'schema'}->{'component_reviewers'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ user_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE',}
+ },
+ display_name => {TYPE => 'VARCHAR(64)',},
+ component_id => {
+ TYPE => 'INT2',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'components', COLUMN => 'id', DELETE => 'CASCADE',}
+ },
+ sortkey => {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0,},
+ ],
+ INDEXES => [
+ component_reviewers_idx =>
+ {FIELDS => ['user_id', 'component_id'], TYPE => 'UNIQUE',},
+ ],
+ };
+
+ $args->{'schema'}->{'flag_state_activity'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+
+ flag_when => {TYPE => 'DATETIME', NOTNULL => 1,},
+
+ type_id => {
+ TYPE => 'INT2',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'flagtypes', COLUMN => 'id', DELETE => 'CASCADE'}
+ },
+
+ flag_id => {TYPE => 'INT3', NOTNULL => 1,},
+
+ setter_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid',},
+ },
+
+ requestee_id =>
+ {TYPE => 'INT3', REFERENCES => {TABLE => 'profiles', COLUMN => 'userid',},},
+
+ bug_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'bugs', COLUMN => 'bug_id', DELETE => 'CASCADE'}
+ },
+
+ attachment_id => {
+ TYPE => 'INT3',
+ REFERENCES =>
+ {TABLE => 'attachments', COLUMN => 'attach_id', DELETE => 'CASCADE'}
+ },
+
+ status => {TYPE => 'CHAR(1)', NOTNULL => 1,},
+ ],
+ };
+
+ $args->{'schema'}->{'bug_mentors'} = {
+ FIELDS => [
+ bug_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'bugs', COLUMN => 'bug_id', DELETE => 'CASCADE',},
+ },
+ user_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE',}
+ },
+ ],
+ INDEXES => [
+ bug_mentors_idx => {FIELDS => ['bug_id', 'user_id'], TYPE => 'UNIQUE',},
+ bug_mentors_bug_id_idx => ['bug_id'],
+ ],
+ };
+
+ $args->{'schema'}->{'bug_mentors'} = {
+ FIELDS => [
+ bug_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'bugs', COLUMN => 'bug_id', DELETE => 'CASCADE',},
+ },
+ user_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE',}
+ },
+ ],
+ INDEXES => [
+ bug_mentors_idx => {FIELDS => ['bug_id', 'user_id'], TYPE => 'UNIQUE',},
+ bug_mentors_bug_id_idx => ['bug_id'],
+ ],
+ };
}
sub install_update_db {
- my $dbh = Bugzilla->dbh;
- $dbh->bz_add_column(
- 'products',
- 'reviewer_required', { TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE' }
- );
- $dbh->bz_add_column(
- 'profiles',
- 'review_request_count', { TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0 }
- );
- $dbh->bz_add_column(
- 'profiles',
- 'feedback_request_count', { TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0 }
- );
- $dbh->bz_add_column(
- 'profiles',
- 'needinfo_request_count', { TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0 }
- );
-
- my $field = Bugzilla::Field->new({ name => 'bug_mentor' });
- if (!$field) {
- Bugzilla::Field->create({
- name => 'bug_mentor',
- description => 'Mentor',
- mailhead => 1
- });
- }
- elsif (!$field->in_new_bugmail) {
- $field->set_in_new_bugmail(1);
- $field->update();
- }
+ my $dbh = Bugzilla->dbh;
+ $dbh->bz_add_column('products', 'reviewer_required',
+ {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'});
+ $dbh->bz_add_column('profiles', 'review_request_count',
+ {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
+ $dbh->bz_add_column('profiles', 'feedback_request_count',
+ {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
+ $dbh->bz_add_column('profiles', 'needinfo_request_count',
+ {TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0});
+
+ my $field = Bugzilla::Field->new({name => 'bug_mentor'});
+ if (!$field) {
+ Bugzilla::Field->create({
+ name => 'bug_mentor', description => 'Mentor', mailhead => 1
+ });
+ }
+ elsif (!$field->in_new_bugmail) {
+ $field->set_in_new_bugmail(1);
+ $field->update();
+ }
}
sub install_filesystem {
- my ($self, $args) = @_;
- my $files = $args->{files};
- my $extensions_dir = bz_locations()->{extensionsdir};
- $files->{"$extensions_dir/Review/bin/review_requests_rebuild.pl"} = {
- perms => Bugzilla::Install::Filesystem::OWNER_EXECUTE
- };
+ my ($self, $args) = @_;
+ my $files = $args->{files};
+ my $extensions_dir = bz_locations()->{extensionsdir};
+ $files->{"$extensions_dir/Review/bin/review_requests_rebuild.pl"}
+ = {perms => Bugzilla::Install::Filesystem::OWNER_EXECUTE};
}
sub install_before_final_checks {
- my ($self, $args) = @_;
- add_setting({
- name => 'block_reviews',
- options => ['on', 'off'],
- default => 'off',
- category => 'Reviews and Needinfo'
- });
+ my ($self, $args) = @_;
+ add_setting({
+ name => 'block_reviews',
+ options => ['on', 'off'],
+ default => 'off',
+ category => 'Reviews and Needinfo'
+ });
}
sub config_modify_panels {
- my ($self, $args) = @_;
- push @{ $args->{panels}->{advanced}->{params} }, {
- name => 'max_reviewer_last_seen',
- type => 't',
- default => '',
- default => 0,
- checker => \&check_numeric,
+ my ($self, $args) = @_;
+ push @{$args->{panels}->{advanced}->{params}},
+ {
+ name => 'max_reviewer_last_seen',
+ type => 't',
+ default => '',
+ default => 0,
+ checker => \&check_numeric,
};
}
@@ -1075,46 +986,44 @@ sub config_modify_panels {
#
sub webservice_user_get {
- my ($self, $args) = @_;
- my ($webservice, $params, $users) = @$args{qw(webservice params users)};
+ my ($self, $args) = @_;
+ my ($webservice, $params, $users) = @$args{qw(webservice params users)};
- return unless filter_wants($params, 'requests');
+ return unless filter_wants($params, 'requests');
- my $ids = [
- map { blessed($_->{id}) ? $_->{id}->value : $_->{id} }
- grep { exists $_->{id} }
- @$users
- ];
+ my $ids = [map { blessed($_->{id}) ? $_->{id}->value : $_->{id} }
+ grep { exists $_->{id} } @$users];
- return unless @$ids;
-
- my %user_map = map { $_->id => $_ } @{ Bugzilla::User->new_from_list($ids) };
-
- foreach my $user (@$users) {
- my $id = blessed($user->{id}) ? $user->{id}->value : $user->{id};
- my $user_obj = $user_map{$id};
-
- $user->{requests} = {
- review => {
- blocked => $webservice->type('boolean', $user_obj->reviews_blocked),
- pending => $webservice->type('int', $user_obj->{review_request_count}),
- },
- feedback => {
- # reviews_blocked includes feedback as well
- blocked => $webservice->type('boolean', $user_obj->reviews_blocked),
- pending => $webservice->type('int', $user_obj->{feedback_request_count}),
- },
- needinfo => {
- blocked => $webservice->type('boolean', $user_obj->needinfo_blocked),
- pending => $webservice->type('int', $user_obj->{needinfo_request_count}),
- },
- };
- }
+ return unless @$ids;
+
+ my %user_map = map { $_->id => $_ } @{Bugzilla::User->new_from_list($ids)};
+
+ foreach my $user (@$users) {
+ my $id = blessed($user->{id}) ? $user->{id}->value : $user->{id};
+ my $user_obj = $user_map{$id};
+
+ $user->{requests} = {
+ review => {
+ blocked => $webservice->type('boolean', $user_obj->reviews_blocked),
+ pending => $webservice->type('int', $user_obj->{review_request_count}),
+ },
+ feedback => {
+
+ # reviews_blocked includes feedback as well
+ blocked => $webservice->type('boolean', $user_obj->reviews_blocked),
+ pending => $webservice->type('int', $user_obj->{feedback_request_count}),
+ },
+ needinfo => {
+ blocked => $webservice->type('boolean', $user_obj->needinfo_blocked),
+ pending => $webservice->type('int', $user_obj->{needinfo_request_count}),
+ },
+ };
+ }
}
sub webservice_user_suggest {
- my ($self, $args) = @_;
- $self->webservice_user_get($args);
+ my ($self, $args) = @_;
+ $self->webservice_user_get($args);
}
__PACKAGE__->NAME;
diff --git a/extensions/Review/bin/migrate_mentor_from_whiteboard.pl b/extensions/Review/bin/migrate_mentor_from_whiteboard.pl
index debf173a7..19837779e 100755
--- a/extensions/Review/bin/migrate_mentor_from_whiteboard.pl
+++ b/extensions/Review/bin/migrate_mentor_from_whiteboard.pl
@@ -36,11 +36,11 @@ EOF
<>;
# we need to be logged in to do user searching and update bugs
-my $nobody = Bugzilla::User->check({ name => Bugzilla->params->{'nobody_user'} });
-$nobody->{groups} = [ Bugzilla::Group->get_all ];
+my $nobody = Bugzilla::User->check({name => Bugzilla->params->{'nobody_user'}});
+$nobody->{groups} = [Bugzilla::Group->get_all];
Bugzilla->set_user($nobody);
-my $mentor_field = Bugzilla::Field->check({ name => 'bug_mentor' });
+my $mentor_field = Bugzilla::Field->check({name => 'bug_mentor'});
my $dbh = Bugzilla->dbh;
# fix broken migration
@@ -54,48 +54,40 @@ my $sth = $dbh->prepare("
$sth->execute($mentor_field->id);
my %pair;
while (my $row = $sth->fetchrow_hashref) {
- if ($row->{added} && $row->{removed}) {
- %pair = ();
- next;
- }
- if ($row->{added}) {
- $pair{bug_id} = $row->{bug_id};
- $pair{bug_when} = $row->{bug_when};
- $pair{who} = $row->{added};
- next;
- }
- if (!$pair{bug_id}) {
- next;
- }
- if ($row->{removed}) {
- if ($row->{bug_id} == $pair{bug_id}
- && $row->{bug_when} eq $pair{bug_when}
- && $row->{removed} eq $pair{who})
- {
- print "Fixing mentor on bug $row->{bug_id}\n";
- my $user = Bugzilla::User->check({ name => $row->{removed} });
- $dbh->bz_start_transaction;
- $dbh->do(
- "DELETE FROM bugs_activity WHERE id = ?",
- undef,
- $row->{id}
- );
- my ($exists) = $dbh->selectrow_array(
- "SELECT 1 FROM bug_mentors WHERE bug_id = ? AND user_id = ?",
- undef,
- $row->{bug_id}, $user->id
- );
- if (!$exists) {
- $dbh->do(
- "INSERT INTO bug_mentors (bug_id, user_id) VALUES (?, ?)",
- undef,
- $row->{bug_id}, $user->id,
- );
- }
- $dbh->bz_commit_transaction;
- %pair = ();
- }
+ if ($row->{added} && $row->{removed}) {
+ %pair = ();
+ next;
+ }
+ if ($row->{added}) {
+ $pair{bug_id} = $row->{bug_id};
+ $pair{bug_when} = $row->{bug_when};
+ $pair{who} = $row->{added};
+ next;
+ }
+ if (!$pair{bug_id}) {
+ next;
+ }
+ if ($row->{removed}) {
+ if ( $row->{bug_id} == $pair{bug_id}
+ && $row->{bug_when} eq $pair{bug_when}
+ && $row->{removed} eq $pair{who})
+ {
+ print "Fixing mentor on bug $row->{bug_id}\n";
+ my $user = Bugzilla::User->check({name => $row->{removed}});
+ $dbh->bz_start_transaction;
+ $dbh->do("DELETE FROM bugs_activity WHERE id = ?", undef, $row->{id});
+ my ($exists)
+ = $dbh->selectrow_array(
+ "SELECT 1 FROM bug_mentors WHERE bug_id = ? AND user_id = ?",
+ undef, $row->{bug_id}, $user->id);
+ if (!$exists) {
+ $dbh->do("INSERT INTO bug_mentors (bug_id, user_id) VALUES (?, ?)",
+ undef, $row->{bug_id}, $user->id,);
+ }
+ $dbh->bz_commit_transaction;
+ %pair = ();
}
+ }
}
# migrate remaining bugs
@@ -110,119 +102,95 @@ my $bug_ids = $dbh->selectcol_arrayref("
print "Bugs found: " . scalar(@$bug_ids) . "\n";
my $bugs = Bugzilla::Bug->new_from_list($bug_ids);
foreach my $bug (@$bugs) {
- my $whiteboard = $bug->status_whiteboard;
- my $orig_whiteboard = $whiteboard;
- my ($mentors, $errors) = extract_mentors($whiteboard);
-
- printf "%7s %s\n", $bug->id, $whiteboard;
- foreach my $error (@$errors) {
- print " $error\n";
+ my $whiteboard = $bug->status_whiteboard;
+ my $orig_whiteboard = $whiteboard;
+ my ($mentors, $errors) = extract_mentors($whiteboard);
+
+ printf "%7s %s\n", $bug->id, $whiteboard;
+ foreach my $error (@$errors) {
+ print " $error\n";
+ }
+ foreach my $user (@$mentors) {
+ print " Mentor: " . $user->identity . "\n";
+ }
+ next if @$errors;
+ $whiteboard =~ s/\[mentor=[^\]]+\]//g;
+
+ my $migrated
+ = $dbh->selectcol_arrayref("SELECT user_id FROM bug_mentors WHERE bug_id = ?",
+ undef, $bug->id);
+ if (@$migrated) {
+ foreach my $migrated_id (@$migrated) {
+ $mentors = [grep { $_->id != $migrated_id } @$mentors];
}
- foreach my $user (@$mentors) {
- print " Mentor: " . $user->identity . "\n";
+ if (!@$mentors) {
+ print " mentor(s) already migrated\n";
+ next;
}
- next if @$errors;
- $whiteboard =~ s/\[mentor=[^\]]+\]//g;
-
- my $migrated = $dbh->selectcol_arrayref(
- "SELECT user_id FROM bug_mentors WHERE bug_id = ?",
- undef,
- $bug->id
- );
- if (@$migrated) {
- foreach my $migrated_id (@$migrated) {
- $mentors = [
- grep { $_->id != $migrated_id }
- @$mentors
- ];
- }
- if (!@$mentors) {
- print " mentor(s) already migrated\n";
- next;
- }
- }
-
- my $delta_ts = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
- $dbh->bz_start_transaction;
- $dbh->do(
- "UPDATE bugs SET status_whiteboard=? WHERE bug_id=?",
- undef,
- $whiteboard, $bug->id
- );
- Bugzilla::Bug::LogActivityEntry(
- $bug->id,
- 'status_whiteboard',
- $orig_whiteboard,
- $whiteboard,
- $nobody->id,
- $delta_ts,
- );
- foreach my $mentor (@$mentors) {
- $dbh->do(
- "INSERT INTO bug_mentors (bug_id, user_id) VALUES (?, ?)",
- undef,
- $bug->id, $mentor->id,
- );
- Bugzilla::Bug::LogActivityEntry(
- $bug->id,
- 'bug_mentor',
- '',
- $mentor->login,
- $nobody->id,
- $delta_ts,
- );
- }
- $dbh->do(
- "UPDATE bugs SET lastdiffed = delta_ts WHERE bug_id = ?",
- undef,
- $bug->id,
- );
- $dbh->bz_commit_transaction;
+ }
+
+ my $delta_ts = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
+ $dbh->bz_start_transaction;
+ $dbh->do("UPDATE bugs SET status_whiteboard=? WHERE bug_id=?",
+ undef, $whiteboard, $bug->id);
+ Bugzilla::Bug::LogActivityEntry($bug->id, 'status_whiteboard',
+ $orig_whiteboard, $whiteboard, $nobody->id, $delta_ts,);
+ foreach my $mentor (@$mentors) {
+ $dbh->do("INSERT INTO bug_mentors (bug_id, user_id) VALUES (?, ?)",
+ undef, $bug->id, $mentor->id,);
+ Bugzilla::Bug::LogActivityEntry($bug->id, 'bug_mentor', '', $mentor->login,
+ $nobody->id, $delta_ts,);
+ }
+ $dbh->do("UPDATE bugs SET lastdiffed = delta_ts WHERE bug_id = ?",
+ undef, $bug->id,);
+ $dbh->bz_commit_transaction;
}
sub extract_mentors {
- my ($whiteboard) = @_;
-
- my (@mentors, @errors);
- my $logout = 0;
- while ($whiteboard =~ /\[mentor=([^\]]+)\]/g) {
- my $mentor_string = $1;
- $mentor_string =~ s/(^\s+|\s+$)//g;
- if ($mentor_string =~ /\@/) {
- # assume it's a full username if it contains an @
- my $user = Bugzilla::User->new({ name => $mentor_string });
- if (!$user) {
- push @errors, "'$mentor_string' failed to match any users";
- } else {
- push @mentors, $user;
- }
- } else {
- # otherwise assume it's a : prefixed nick
-
- $mentor_string =~ s/^://;
- my $matches = find_users(":$mentor_string");
- if (!@$matches) {
- $matches = find_users($mentor_string);
- }
-
- if (!$matches || !@$matches) {
- push @errors, "'$mentor_string' failed to match any users";
- } elsif (scalar(@$matches) > 1) {
- push @errors, "'$mentor_string' matches more than one user: " .
- join(', ', map { $_->identity } @$matches);
- } else {
- push @mentors, $matches->[0];
- }
- }
+ my ($whiteboard) = @_;
+
+ my (@mentors, @errors);
+ my $logout = 0;
+ while ($whiteboard =~ /\[mentor=([^\]]+)\]/g) {
+ my $mentor_string = $1;
+ $mentor_string =~ s/(^\s+|\s+$)//g;
+ if ($mentor_string =~ /\@/) {
+
+ # assume it's a full username if it contains an @
+ my $user = Bugzilla::User->new({name => $mentor_string});
+ if (!$user) {
+ push @errors, "'$mentor_string' failed to match any users";
+ }
+ else {
+ push @mentors, $user;
+ }
+ }
+ else {
+ # otherwise assume it's a : prefixed nick
+
+ $mentor_string =~ s/^://;
+ my $matches = find_users(":$mentor_string");
+ if (!@$matches) {
+ $matches = find_users($mentor_string);
+ }
+
+ if (!$matches || !@$matches) {
+ push @errors, "'$mentor_string' failed to match any users";
+ }
+ elsif (scalar(@$matches) > 1) {
+ push @errors, "'$mentor_string' matches more than one user: "
+ . join(', ', map { $_->identity } @$matches);
+ }
+ else {
+ push @mentors, $matches->[0];
+ }
}
- return (\@mentors, \@errors);
+ }
+ return (\@mentors, \@errors);
}
sub find_users {
- my ($query) = @_;
- my $matches = Bugzilla::User::match("*$query*", 2);
- return [
- grep { $_->name =~ /:?\Q$query\E\b/i }
- @$matches
- ];
+ my ($query) = @_;
+ my $matches = Bugzilla::User::match("*$query*", 2);
+ return [grep { $_->name =~ /:?\Q$query\E\b/i } @$matches];
}
diff --git a/extensions/Review/bin/review_requests_rebuild.pl b/extensions/Review/bin/review_requests_rebuild.pl
index 03d25d045..8bda4119c 100755
--- a/extensions/Review/bin/review_requests_rebuild.pl
+++ b/extensions/Review/bin/review_requests_rebuild.pl
@@ -24,7 +24,7 @@ use Bugzilla::Extension::Review::Util;
Bugzilla->usage_mode(USAGE_MODE_CMDLINE);
-rebuild_review_counters(sub{
- my ($count, $total) = @_;
- indicate_progress({ current => $count, total => $total, every => 5 });
+rebuild_review_counters(sub {
+ my ($count, $total) = @_;
+ indicate_progress({current => $count, total => $total, every => 5});
});
diff --git a/extensions/Review/lib/FlagStateActivity.pm b/extensions/Review/lib/FlagStateActivity.pm
index 35da42351..92efb6c02 100644
--- a/extensions/Review/lib/FlagStateActivity.pm
+++ b/extensions/Review/lib/FlagStateActivity.pm
@@ -24,65 +24,58 @@ use constant AUDIT_UPDATES => 0;
use constant AUDIT_REMOVES => 0;
use constant DB_COLUMNS => qw(
- id
- flag_when
- type_id
- flag_id
- setter_id
- requestee_id
- bug_id
- attachment_id
- status
+ id
+ flag_when
+ type_id
+ flag_id
+ setter_id
+ requestee_id
+ bug_id
+ attachment_id
+ status
);
sub _check_param_required {
- my ($param) = @_;
-
- return sub {
- my ($invocant, $value) = @_;
- $value = trim($value)
- or ThrowCodeError('param_required', {param => $param});
- return $value;
- },
+ my ($param) = @_;
+
+ return sub {
+ my ($invocant, $value) = @_;
+ $value = trim($value) or ThrowCodeError('param_required', {param => $param});
+ return $value;
+ },;
}
sub _check_date {
- my ($invocant, $date) = @_;
+ my ($invocant, $date) = @_;
- $date = trim($date);
- datetime_from($date)
- or ThrowUserError('illegal_date', { date => $date,
- format => 'YYYY-MM-DD HH24:MI:SS' });
- return $date;
+ $date = trim($date);
+ datetime_from($date)
+ or ThrowUserError('illegal_date',
+ {date => $date, format => 'YYYY-MM-DD HH24:MI:SS'});
+ return $date;
}
sub _check_status {
- my ($self, $status) = @_;
-
- # - Make sure the status is valid.
- # - Make sure the user didn't request the flag unless it's requestable.
- # If the flag existed and was requested before it became unrequestable,
- # leave it as is.
- if (none { $status eq $_ } qw( X + - ? )) {
- ThrowUserError(
- 'flag_status_invalid',
- {
- id => $self->id,
- status => $status
- }
- );
- }
- return $status;
+ my ($self, $status) = @_;
+
+ # - Make sure the status is valid.
+ # - Make sure the user didn't request the flag unless it's requestable.
+ # If the flag existed and was requested before it became unrequestable,
+ # leave it as is.
+ if (none { $status eq $_ } qw( X + - ? )) {
+ ThrowUserError('flag_status_invalid', {id => $self->id, status => $status});
+ }
+ return $status;
}
use constant VALIDATORS => {
- flag_when => \&_check_date,
- type_id => _check_param_required('type_id'),
- flag_id => _check_param_required('flag_id'),
- setter_id => _check_param_required('setter_id'),
- bug_id => _check_param_required('bug_id'),
- status => \&_check_status,
+ flag_when => \&_check_date,
+ type_id => _check_param_required('type_id'),
+ flag_id => _check_param_required('flag_id'),
+ setter_id => _check_param_required('setter_id'),
+ bug_id => _check_param_required('bug_id'),
+ status => \&_check_status,
};
sub flag_when { return $_[0]->{flag_when} }
@@ -95,30 +88,33 @@ sub attachment_id { return $_[0]->{attachment_id} }
sub status { return $_[0]->{status} }
sub type {
- my ($self) = @_;
- return $self->{type} //= Bugzilla::FlagType->new({ id => $self->type_id, cache => 1 });
+ my ($self) = @_;
+ return $self->{type}
+ //= Bugzilla::FlagType->new({id => $self->type_id, cache => 1});
}
sub setter {
- my ($self) = @_;
- return $self->{setter} //= Bugzilla::User->new({ id => $self->setter_id, cache => 1 });
+ my ($self) = @_;
+ return $self->{setter}
+ //= Bugzilla::User->new({id => $self->setter_id, cache => 1});
}
sub requestee {
- my ($self) = @_;
- return undef unless defined $self->requestee_id;
- return $self->{requestee} //= Bugzilla::User->new({ id => $self->requestee_id, cache => 1 });
+ my ($self) = @_;
+ return undef unless defined $self->requestee_id;
+ return $self->{requestee}
+ //= Bugzilla::User->new({id => $self->requestee_id, cache => 1});
}
sub bug {
- my ($self) = @_;
- return $self->{bug} //= Bugzilla::Bug->new({ id => $self->bug_id, cache => 1 });
+ my ($self) = @_;
+ return $self->{bug} //= Bugzilla::Bug->new({id => $self->bug_id, cache => 1});
}
sub attachment {
- my ($self) = @_;
- return $self->{attachment} //=
- Bugzilla::Attachment->new({ id => $self->attachment_id, cache => 1 });
+ my ($self) = @_;
+ return $self->{attachment}
+ //= Bugzilla::Attachment->new({id => $self->attachment_id, cache => 1});
}
1;
diff --git a/extensions/Review/lib/Util.pm b/extensions/Review/lib/Util.pm
index a8744079d..61d4e9117 100644
--- a/extensions/Review/lib/Util.pm
+++ b/extensions/Review/lib/Util.pm
@@ -17,12 +17,12 @@ use Bugzilla;
our @EXPORT = qw( rebuild_review_counters );
sub rebuild_review_counters {
- my ($callback) = @_;
- my $dbh = Bugzilla->dbh;
+ my ($callback) = @_;
+ my $dbh = Bugzilla->dbh;
- $dbh->bz_start_transaction;
+ $dbh->bz_start_transaction;
- my $rows = $dbh->selectall_arrayref("
+ my $rows = $dbh->selectall_arrayref("
SELECT flags.requestee_id AS user_id,
flagtypes.name AS flagtype,
COUNT(*) as count
@@ -32,55 +32,48 @@ sub rebuild_review_counters {
WHERE flags.status = '?'
AND flagtypes.name IN ('review', 'feedback', 'needinfo')
GROUP BY flags.requestee_id, flagtypes.name
- ", { Slice => {} });
+ ", {Slice => {}});
- my ($count, $total, $current) = (1, scalar(@$rows), { id => 0 });
- foreach my $row (@$rows) {
- $callback->($count++, $total) if $callback;
- if ($row->{user_id} != $current->{id}) {
- _update_profile($dbh, $current) if $current->{id};
- $current = { id => $row->{user_id} };
- }
- $current->{$row->{flagtype}} = $row->{count};
+ my ($count, $total, $current) = (1, scalar(@$rows), {id => 0});
+ foreach my $row (@$rows) {
+ $callback->($count++, $total) if $callback;
+ if ($row->{user_id} != $current->{id}) {
+ _update_profile($dbh, $current) if $current->{id};
+ $current = {id => $row->{user_id}};
}
- _update_profile($dbh, $current) if $current->{id};
+ $current->{$row->{flagtype}} = $row->{count};
+ }
+ _update_profile($dbh, $current) if $current->{id};
- foreach my $field (qw( review feedback needinfo )) {
- _fix_negatives($dbh, $field);
- }
+ foreach my $field (qw( review feedback needinfo )) {
+ _fix_negatives($dbh, $field);
+ }
- $dbh->bz_commit_transaction;
+ $dbh->bz_commit_transaction;
}
sub _fix_negatives {
- my ($dbh, $field) = @_;
- my $user_ids = $dbh->selectcol_arrayref(
- "SELECT userid FROM profiles WHERE ${field}_request_count < 0"
- );
- return unless @$user_ids;
- $dbh->do(
- "UPDATE profiles SET ${field}_request_count = 0 WHERE " . $dbh->sql_in('userid', $user_ids)
- );
- foreach my $user_id (@$user_ids) {
- Bugzilla->memcached->clear({ table => 'profiles', id => $user_id });
- }
+ my ($dbh, $field) = @_;
+ my $user_ids = $dbh->selectcol_arrayref(
+ "SELECT userid FROM profiles WHERE ${field}_request_count < 0");
+ return unless @$user_ids;
+ $dbh->do("UPDATE profiles SET ${field}_request_count = 0 WHERE "
+ . $dbh->sql_in('userid', $user_ids));
+ foreach my $user_id (@$user_ids) {
+ Bugzilla->memcached->clear({table => 'profiles', id => $user_id});
+ }
}
sub _update_profile {
- my ($dbh, $data) = @_;
- $dbh->do("
+ my ($dbh, $data) = @_;
+ $dbh->do("
UPDATE profiles
SET review_request_count = ?,
feedback_request_count = ?,
needinfo_request_count = ?
- WHERE userid = ?",
- undef,
- $data->{review} || 0,
- $data->{feedback} || 0,
- $data->{needinfo} || 0,
- $data->{id}
- );
- Bugzilla->memcached->clear({ table => 'profiles', id => $data->{id} });
+ WHERE userid = ?", undef, $data->{review} || 0, $data->{feedback} || 0,
+ $data->{needinfo} || 0, $data->{id});
+ Bugzilla->memcached->clear({table => 'profiles', id => $data->{id}});
}
1;
diff --git a/extensions/Review/lib/WebService.pm b/extensions/Review/lib/WebService.pm
index 0c54d725a..79843cf2c 100644
--- a/extensions/Review/lib/WebService.pm
+++ b/extensions/Review/lib/WebService.pm
@@ -20,277 +20,295 @@ use Bugzilla::Util qw(detaint_natural trick_taint);
use Bugzilla::WebService::Util 'filter';
use constant PUBLIC_METHODS => qw(
- flag_activity
- suggestions
+ flag_activity
+ suggestions
);
sub suggestions {
- my ($self, $params) = @_;
- my $dbh = Bugzilla->switch_to_shadow_db();
-
- my ($bug, $product, $component);
- if (exists $params->{bug_id}) {
- $bug = Bugzilla::Bug->check($params->{bug_id});
- $product = $bug->product_obj;
- $component = $bug->component_obj;
- }
- elsif (exists $params->{product}) {
- $product = Bugzilla::Product->check($params->{product});
- if (exists $params->{component}) {
- $component = Bugzilla::Component->check({
- product => $product, name => $params->{component}
- });
- }
- }
- else {
- ThrowUserError("reviewer_suggestions_param_required");
+ my ($self, $params) = @_;
+ my $dbh = Bugzilla->switch_to_shadow_db();
+
+ my ($bug, $product, $component);
+ if (exists $params->{bug_id}) {
+ $bug = Bugzilla::Bug->check($params->{bug_id});
+ $product = $bug->product_obj;
+ $component = $bug->component_obj;
+ }
+ elsif (exists $params->{product}) {
+ $product = Bugzilla::Product->check($params->{product});
+ if (exists $params->{component}) {
+ $component
+ = Bugzilla::Component->check({
+ product => $product, name => $params->{component}
+ });
}
+ }
+ else {
+ ThrowUserError("reviewer_suggestions_param_required");
+ }
- my @reviewers;
- if ($bug) {
- # we always need to be authentiated to perform user matching
- my $user = Bugzilla->user;
- if (!$user->id) {
- Bugzilla->set_user(Bugzilla::User->check({ name => Bugzilla->params->{'nobody_user'} }));
- push @reviewers, @{ $bug->mentors };
- Bugzilla->set_user($user);
- } else {
- push @reviewers, @{ $bug->mentors };
- }
- }
- if ($component) {
- push @reviewers, @{ $component->reviewers_objs };
- }
- if (!$component || !@{ $component->reviewers_objs }) {
- push @reviewers, @{ $product->reviewers_objs };
- }
+ my @reviewers;
+ if ($bug) {
- my @result;
- foreach my $reviewer (@reviewers) {
- push @result, {
- id => $self->type('int', $reviewer->id),
- email => $self->type('email', $reviewer->login),
- name => $self->type('string', $reviewer->name),
- review_count => $self->type('int', $reviewer->review_count),
- };
+ # we always need to be authentiated to perform user matching
+ my $user = Bugzilla->user;
+ if (!$user->id) {
+ Bugzilla->set_user(Bugzilla::User->check(
+ {name => Bugzilla->params->{'nobody_user'}}));
+ push @reviewers, @{$bug->mentors};
+ Bugzilla->set_user($user);
+ }
+ else {
+ push @reviewers, @{$bug->mentors};
}
- return \@result;
+ }
+ if ($component) {
+ push @reviewers, @{$component->reviewers_objs};
+ }
+ if (!$component || !@{$component->reviewers_objs}) {
+ push @reviewers, @{$product->reviewers_objs};
+ }
+
+ my @result;
+ foreach my $reviewer (@reviewers) {
+ push @result,
+ {
+ id => $self->type('int', $reviewer->id),
+ email => $self->type('email', $reviewer->login),
+ name => $self->type('string', $reviewer->name),
+ review_count => $self->type('int', $reviewer->review_count),
+ };
+ }
+ return \@result;
}
sub flag_activity {
- my ($self, $params) = @_;
- my $dbh = Bugzilla->switch_to_shadow_db();
- my %match_criteria;
+ my ($self, $params) = @_;
+ my $dbh = Bugzilla->switch_to_shadow_db();
+ my %match_criteria;
- if (my $flag_id = $params->{flag_id}) {
- detaint_natural($flag_id)
- or ThrowUserError('invalid_flag_id', { flag_id => $flag_id });
+ if (my $flag_id = $params->{flag_id}) {
+ detaint_natural($flag_id)
+ or ThrowUserError('invalid_flag_id', {flag_id => $flag_id});
- $match_criteria{flag_id} = $flag_id;
- }
-
- if (my $flag_ids = $params->{flag_ids}) {
- foreach my $flag_id (@$flag_ids) {
- detaint_natural($flag_id)
- or ThrowUserError('invalid_flag_id', { flag_id => $flag_id });
- }
+ $match_criteria{flag_id} = $flag_id;
+ }
- $match_criteria{flag_id} = $flag_ids;
+ if (my $flag_ids = $params->{flag_ids}) {
+ foreach my $flag_id (@$flag_ids) {
+ detaint_natural($flag_id)
+ or ThrowUserError('invalid_flag_id', {flag_id => $flag_id});
}
- if (my $type_id = $params->{type_id}) {
- detaint_natural($type_id)
- or ThrowUserError('invalid_flag_type_id', { type_id => $type_id });
+ $match_criteria{flag_id} = $flag_ids;
+ }
- $match_criteria{type_id} = $type_id;
- }
+ if (my $type_id = $params->{type_id}) {
+ detaint_natural($type_id)
+ or ThrowUserError('invalid_flag_type_id', {type_id => $type_id});
- if (my $type_name = $params->{type_name}) {
- trick_taint($type_name);
- my $flag_types = Bugzilla::FlagType::match({ name => $type_name });
- $match_criteria{type_id} = [map { $_->id } @$flag_types];
- }
+ $match_criteria{type_id} = $type_id;
+ }
- foreach my $user_field (qw( requestee setter )) {
- if (my $user_name = $params->{$user_field}) {
- my $user = Bugzilla::User->check({ name => $user_name, cache => 1, _error => 'invalid_username' });
+ if (my $type_name = $params->{type_name}) {
+ trick_taint($type_name);
+ my $flag_types = Bugzilla::FlagType::match({name => $type_name});
+ $match_criteria{type_id} = [map { $_->id } @$flag_types];
+ }
- $match_criteria{ $user_field . "_id" } = $user->id;
- }
- }
+ foreach my $user_field (qw( requestee setter )) {
+ if (my $user_name = $params->{$user_field}) {
+ my $user = Bugzilla::User->check(
+ {name => $user_name, cache => 1, _error => 'invalid_username'});
- foreach my $field (qw( bug_id status )) {
- if (exists $params->{$field}) {
- $match_criteria{$field} = $params->{$field};
- }
+ $match_criteria{$user_field . "_id"} = $user->id;
}
+ }
- ThrowCodeError('param_required', { param => 'limit', function => 'Review.flag_activity()' })
- if defined $params->{offset} && !defined $params->{limit};
-
- my $limit = delete $params->{limit};
- my $offset = delete $params->{offset};
- my $after = delete $params->{after};
- my $before = delete $params->{before};
- my $max_results = Bugzilla->params->{max_search_results};
-
- if (!$limit || $limit > $max_results) {
- $limit = $max_results;
+ foreach my $field (qw( bug_id status )) {
+ if (exists $params->{$field}) {
+ $match_criteria{$field} = $params->{$field};
}
-
- if ($after && $after =~ /^(\d{4}-\d{1,2}-\d{1,2})$/) {
- $after = $1;
- }
- else {
- my $now = DateTime->now;
- $now->subtract(days => 30);
- $after = $now->ymd('-');
- }
-
- if ($before && $before =~ /^(\d{4}-\d{1,2}-\d{1,2})$/) {
- $before = $1;
- }
- else {
- my $now = DateTime->now;
- $before = $now->ymd('-');
- }
-
- $match_criteria{LIMIT} = $limit;
- $match_criteria{OFFSET} = $offset if defined $offset;
- $match_criteria{WHERE} = { 'date(flag_when) BETWEEN ? AND ?' => [$after, $before] };
-
- # Throw error if no other parameters have been passed other than limit and offset
- if (!grep(!/^(LIMIT|OFFSET)$/, keys %match_criteria)) {
- ThrowUserError('flag_activity_parameters_required');
- }
-
- my $matches = Bugzilla::Extension::Review::FlagStateActivity->match(\%match_criteria);
- my $user = Bugzilla->user;
- $user->visible_bugs([ map { $_->bug_id } @$matches ]);
- my @results = map { $self->_flag_state_activity_to_hash($_, $params) }
- grep { $user->can_see_bug($_->bug_id) && _can_see_attachment($user, $_) }
- @$matches;
- return \@results;
+ }
+
+ ThrowCodeError('param_required',
+ {param => 'limit', function => 'Review.flag_activity()'})
+ if defined $params->{offset} && !defined $params->{limit};
+
+ my $limit = delete $params->{limit};
+ my $offset = delete $params->{offset};
+ my $after = delete $params->{after};
+ my $before = delete $params->{before};
+ my $max_results = Bugzilla->params->{max_search_results};
+
+ if (!$limit || $limit > $max_results) {
+ $limit = $max_results;
+ }
+
+ if ($after && $after =~ /^(\d{4}-\d{1,2}-\d{1,2})$/) {
+ $after = $1;
+ }
+ else {
+ my $now = DateTime->now;
+ $now->subtract(days => 30);
+ $after = $now->ymd('-');
+ }
+
+ if ($before && $before =~ /^(\d{4}-\d{1,2}-\d{1,2})$/) {
+ $before = $1;
+ }
+ else {
+ my $now = DateTime->now;
+ $before = $now->ymd('-');
+ }
+
+ $match_criteria{LIMIT} = $limit;
+ $match_criteria{OFFSET} = $offset if defined $offset;
+ $match_criteria{WHERE}
+ = {'date(flag_when) BETWEEN ? AND ?' => [$after, $before]};
+
+ # Throw error if no other parameters have been passed other than limit and offset
+ if (!grep(!/^(LIMIT|OFFSET)$/, keys %match_criteria)) {
+ ThrowUserError('flag_activity_parameters_required');
+ }
+
+ my $matches
+ = Bugzilla::Extension::Review::FlagStateActivity->match(\%match_criteria);
+ my $user = Bugzilla->user;
+ $user->visible_bugs([map { $_->bug_id } @$matches]);
+ my @results
+ = map { $self->_flag_state_activity_to_hash($_, $params) }
+ grep { $user->can_see_bug($_->bug_id) && _can_see_attachment($user, $_) }
+ @$matches;
+ return \@results;
}
sub _can_see_attachment {
- my ($user, $flag_state_activity) = @_;
+ my ($user, $flag_state_activity) = @_;
- return 1 if !$flag_state_activity->attachment_id;
- return 0 if $flag_state_activity->attachment->isprivate && !$user->is_insider;
- return 1;
+ return 1 if !$flag_state_activity->attachment_id;
+ return 0 if $flag_state_activity->attachment->isprivate && !$user->is_insider;
+ return 1;
}
sub rest_resources {
- return [
- # bug-id
- qr{^/review/suggestions/(\d+)$}, {
- GET => {
- method => 'suggestions',
- params => sub {
- return { bug_id => $_[0] };
- },
- },
- },
- # product/component
- qr{^/review/suggestions/([^/]+)/(.+)$}, {
- GET => {
- method => 'suggestions',
- params => sub {
- return { product => $_[0], component => $_[1] };
- },
- },
+ return [
+ # bug-id
+ qr{^/review/suggestions/(\d+)$},
+ {
+ GET => {
+ method => 'suggestions',
+ params => sub {
+ return {bug_id => $_[0]};
},
- # just product
- qr{^/review/suggestions/([^/]+)$}, {
- GET => {
- method => 'suggestions',
- params => sub {
- return { product => $_[0] };
- },
- },
+ },
+ },
+
+ # product/component
+ qr{^/review/suggestions/([^/]+)/(.+)$},
+ {
+ GET => {
+ method => 'suggestions',
+ params => sub {
+ return {product => $_[0], component => $_[1]};
},
- # named parameters
- qr{^/review/suggestions$}, {
- GET => {
- method => 'suggestions',
- },
+ },
+ },
+
+ # just product
+ qr{^/review/suggestions/([^/]+)$},
+ {
+ GET => {
+ method => 'suggestions',
+ params => sub {
+ return {product => $_[0]};
},
- # flag activity by flag id
- qr{^/review/flag_activity/(\d+)$}, {
- GET => {
- method => 'flag_activity',
- params => sub {
- return { flag_id => $_[0] }
- },
- },
+ },
+ },
+
+ # named parameters
+ qr{^/review/suggestions$},
+ {GET => {method => 'suggestions',},},
+
+ # flag activity by flag id
+ qr{^/review/flag_activity/(\d+)$},
+ {
+ GET => {
+ method => 'flag_activity',
+ params => sub {
+ return {flag_id => $_[0]};
},
- qr{^/review/flag_activity/type_name/(\w+)$}, {
- GET => {
- method => 'flag_activity',
- params => sub {
- return { type_name => $_[0] }
- },
- },
+ },
+ },
+ qr{^/review/flag_activity/type_name/(\w+)$},
+ {
+ GET => {
+ method => 'flag_activity',
+ params => sub {
+ return {type_name => $_[0]};
},
- # flag activity by user
- qr{^/review/flag_activity/(requestee|setter|type_id)/(.*)$}, {
- GET => {
- method => 'flag_activity',
- params => sub {
- return { $_[0] => $_[1] };
- },
- },
+ },
+ },
+
+ # flag activity by user
+ qr{^/review/flag_activity/(requestee|setter|type_id)/(.*)$},
+ {
+ GET => {
+ method => 'flag_activity',
+ params => sub {
+ return {$_[0] => $_[1]};
},
- # flag activity with only query strings
- qr{^/review/flag_activity$}, {
- GET => { method => 'flag_activity' },
- },
- ];
+ },
+ },
+
+ # flag activity with only query strings
+ qr{^/review/flag_activity$},
+ {GET => {method => 'flag_activity'},},
+ ];
}
sub _flag_state_activity_to_hash {
- my ($self, $fsa, $params) = @_;
-
- my %flag = (
- id => $self->type('int', $fsa->id),
- creation_time => $self->type('string', $fsa->flag_when),
- type => $self->_flagtype_to_hash($fsa->type),
- setter => $self->_user_to_hash($fsa->setter),
- bug_id => $self->type('int', $fsa->bug_id),
- attachment_id => $self->type('int', $fsa->attachment_id),
- status => $self->type('string', $fsa->status),
- );
-
- $flag{requestee} = $self->_user_to_hash($fsa->requestee) if $fsa->requestee;
- $flag{flag_id} = $self->type('int', $fsa->flag_id) unless $params->{flag_id};
-
- return filter($params, \%flag);
+ my ($self, $fsa, $params) = @_;
+
+ my %flag = (
+ id => $self->type('int', $fsa->id),
+ creation_time => $self->type('string', $fsa->flag_when),
+ type => $self->_flagtype_to_hash($fsa->type),
+ setter => $self->_user_to_hash($fsa->setter),
+ bug_id => $self->type('int', $fsa->bug_id),
+ attachment_id => $self->type('int', $fsa->attachment_id),
+ status => $self->type('string', $fsa->status),
+ );
+
+ $flag{requestee} = $self->_user_to_hash($fsa->requestee) if $fsa->requestee;
+ $flag{flag_id} = $self->type('int', $fsa->flag_id) unless $params->{flag_id};
+
+ return filter($params, \%flag);
}
sub _flagtype_to_hash {
- my ($self, $flagtype) = @_;
- my $user = Bugzilla->user;
-
- return {
- id => $self->type('int', $flagtype->id),
- name => $self->type('string', $flagtype->name),
- description => $self->type('string', $flagtype->description),
- type => $self->type('string', $flagtype->target_type),
- is_active => $self->type('boolean', $flagtype->is_active),
- is_requesteeble => $self->type('boolean', $flagtype->is_requesteeble),
- is_multiplicable => $self->type('boolean', $flagtype->is_multiplicable),
- };
+ my ($self, $flagtype) = @_;
+ my $user = Bugzilla->user;
+
+ return {
+ id => $self->type('int', $flagtype->id),
+ name => $self->type('string', $flagtype->name),
+ description => $self->type('string', $flagtype->description),
+ type => $self->type('string', $flagtype->target_type),
+ is_active => $self->type('boolean', $flagtype->is_active),
+ is_requesteeble => $self->type('boolean', $flagtype->is_requesteeble),
+ is_multiplicable => $self->type('boolean', $flagtype->is_multiplicable),
+ };
}
sub _user_to_hash {
- my ($self, $user) = @_;
+ my ($self, $user) = @_;
- return {
- id => $self->type('int', $user->id),
- real_name => $self->type('string', $user->name),
- name => $self->type('email', $user->login),
- };
+ return {
+ id => $self->type('int', $user->id),
+ real_name => $self->type('string', $user->name),
+ name => $self->type('email', $user->login),
+ };
}
1;
diff --git a/extensions/SecureMail/Config.pm b/extensions/SecureMail/Config.pm
index 8d877a253..5c2dc615a 100644
--- a/extensions/SecureMail/Config.pm
+++ b/extensions/SecureMail/Config.pm
@@ -28,25 +28,19 @@ use warnings;
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 2b5e1bdd6..9790c0828 100644
--- a/extensions/SecureMail/Extension.pm
+++ b/extensions/SecureMail/Extension.pm
@@ -59,12 +59,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'});
}
##############################################################################
@@ -72,194 +72,193 @@ sub install_update_db {
##############################################################################
BEGIN {
- *Bugzilla::Group::secure_mail = \&_group_secure_mail;
- *Bugzilla::User::public_key = \&_user_public_key;
- *Bugzilla::securemail_groups = \&_securemail_groups;
+ *Bugzilla::Group::secure_mail = \&_group_secure_mail;
+ *Bugzilla::User::public_key = \&_user_public_key;
+ *Bugzilla::securemail_groups = \&_securemail_groups;
}
sub _group_secure_mail { return $_[0]->{'secure_mail'}; }
sub _securemail_groups {
- return Bugzilla->dbh->selectcol_arrayref("SELECT name FROM groups WHERE secure_mail = 1") // [];
+ return Bugzilla->dbh->selectcol_arrayref(
+ "SELECT name FROM groups WHERE secure_mail = 1") // [];
}
# 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'};
-
- if ($class->isa('Bugzilla::Group')) {
- my $dbh = Bugzilla->dbh;
- if ($dbh->bz_column_info($class->DB_TABLE, 'secure_mail')) {
- push @$columns, 'secure_mail';
- }
+ my ($self, $args) = @_;
+ my $class = $args->{'class'};
+ my $columns = $args->{'columns'};
+
+ if ($class->isa('Bugzilla::Group')) {
+ my $dbh = Bugzilla->dbh;
+ if ($dbh->bz_column_info($class->DB_TABLE, 'secure_mail')) {
+ 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.
- my $tct = Bugzilla::Extension::SecureMail::TCT->new(
- public_key => $value,
- command => Bugzilla->localconfig->{tct_bin},
- );
- unless ($tct->is_valid->get) {
- ThrowUserError( 'securemail_invalid_key', { errstr => 'key is invalid or expired' } );
- }
- }
- 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;
- };
- }
+ 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.
+ my $tct = Bugzilla::Extension::SecureMail::TCT->new(
+ public_key => $value,
+ command => Bugzilla->localconfig->{tct_bin},
+ );
+ unless ($tct->is_valid->get) {
+ ThrowUserError('securemail_invalid_key',
+ {errstr => 'key is invalid or expired'});
+ }
+ }
+ 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;
+ };
+ }
}
# 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);
}
##############################################################################
@@ -268,365 +267,382 @@ sub _send_test_email {
# determine if the bug should be encrypted at the time it is generated
sub bugmail_enqueue {
- my ($self, $args) = @_;
- my $vars = $args->{vars};
- if (_should_secure_bug($vars->{bug})) {
- $vars->{bugzilla_encrypt} = 1;
- }
+ my ($self, $args) = @_;
+ my $vars = $args->{vars};
+ if (_should_secure_bug($vars->{bug})) {
+ $vars->{bugzilla_encrypt} = 1;
+ }
}
sub bugmail_generate {
- my ($self, $args) = @_;
- my $vars = $args->{vars};
- my $email = $args->{email};
- if ($vars->{bugzilla_encrypt}) {
- $email->header_set('X-Bugzilla-Encrypt', 1);
- }
+ my ($self, $args) = @_;
+ my $vars = $args->{vars};
+ my $email = $args->{email};
+ if ($vars->{bugzilla_encrypt}) {
+ $email->header_set('X-Bugzilla-Encrypt', 1);
+ }
}
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
- if (scalar $email->parts > 1) {
- $email->walk_parts(sub {
- my $part = shift;
- my $content_type = $part->content_type;
- $body = $part->body if $content_type && $content_type =~ /^text\/plain/;
- });
- }
- 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-Attach-ID'))
- {
- my $attachment = Bugzilla::Attachment->new($email->header('X-Bugzilla-Attach-ID'));
- 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;
+ 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
+ if (scalar $email->parts > 1) {
+ $email->walk_parts(sub {
+ my $part = shift;
+ my $content_type = $part->content_type;
+ $body = $part->body if $content_type && $content_type =~ /^text\/plain/;
+ });
}
- 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');
+ 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-Attach-ID'))
+ {
+ my $attachment
+ = Bugzilla::Attachment->new($email->header('X-Bugzilla-Attach-ID'));
+ 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 $tct = Bugzilla::Extension::SecureMail::TCT->new(
+ public_key => $key,
+ command => Bugzilla->localconfig->{tct_bin},
+ );
+
+ 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 => _tct_encrypt($tct, $to_encrypt, $bug_id)
+ ),
+ );
+ $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(_tct_encrypt($tct, $email->body, $bug_id));
}
+ }
- if ($key_type eq 'PGP') {
- ##################
- # PGP Encryption #
- ##################
+ elsif ($key_type eq 'S/MIME') {
+ #####################
+ # S/MIME Encryption #
+ #####################
- my $tct = Bugzilla::Extension::SecureMail::TCT->new(
- public_key => $key,
- command => Bugzilla->localconfig->{tct_bin},
- );
+ $email->walk_parts(\&_fix_encoding);
- 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 => _tct_encrypt($tct, $to_encrypt, $bug_id)
- ),
- );
- $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(_tct_encrypt($tct, $email->body, $bug_id));
- }
+ 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 $email_type = $email->header('X-Bugzilla-Type');
- my $vars = {
- 'urlbase' => Bugzilla->localconfig->{urlbase},
- 'bug_id' => $bug_id,
- 'maintainer' => Bugzilla->params->{'maintainer'},
- 'email_type' => $email_type
- };
-
- $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 $email_type = $email->header('X-Bugzilla-Type');
+ my $vars = {
+ 'urlbase' => Bugzilla->localconfig->{urlbase},
+ 'bug_id' => $bug_id,
+ 'maintainer' => Bugzilla->params->{'maintainer'},
+ 'email_type' => $email_type
+ };
- 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)/;
- {
- # avoid excessive line wrapping done by Encode.
- local $Encode::Encoding{'MIME-Q'}->{'bpl'} = 998;
- $email->header_set('Subject', encode('MIME-Q', $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)/;
+ {
+ # avoid excessive line wrapping done by Encode.
+ local $Encode::Encoding{'MIME-Q'}->{'bpl'} = 998;
+ $email->header_set('Subject', encode('MIME-Q', $subject));
}
+ }
}
sub _tct_encrypt {
- my ($tct, $text, $bug_id) = @_;
-
- my $comment = Bugzilla->localconfig->{urlbase} . ( $bug_id ? 'show_bug.cgi?id=' . $bug_id : '' );
- my $encrypted;
- my $ok = eval { $encrypted = $tct->encrypt( $text, $comment )->get; 1 };
- if (!$ok) {
- WARN("Error: $@");
- $encrypted = "$comment\nOpenPGP Encryption failed. Check if your key is expired.";
- }
- elsif (!$encrypted) {
- WARN('message empty!');
- $encrypted = "$comment\nOpenPGP Encryption failed for unknown reason.";
- }
-
- return $encrypted;
+ my ($tct, $text, $bug_id) = @_;
+
+ my $comment = Bugzilla->localconfig->{urlbase}
+ . ($bug_id ? 'show_bug.cgi?id=' . $bug_id : '');
+ my $encrypted;
+ my $ok = eval { $encrypted = $tct->encrypt($text, $comment)->get; 1 };
+ if (!$ok) {
+ WARN("Error: $@");
+ $encrypted
+ = "$comment\nOpenPGP Encryption failed. Check if your key is expired.";
+ }
+ elsif (!$encrypted) {
+ WARN('message empty!');
+ $encrypted = "$comment\nOpenPGP Encryption failed for unknown reason.";
+ }
+
+ return $encrypted;
}
# Insert the subject into the part's body, as the subject of the message will
@@ -634,69 +650,67 @@ sub _tct_encrypt {
# XXX this incorrectly assumes all parts of the message are the body
# we should only alter parts whose parent is multipart/alternative
sub _insert_subject {
- my ($part, $subject) = @_;
- my $content_type = $part->content_type or return;
- if ($content_type =~ /^text\/plain/) {
- $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(['strong', "Subject: $subject"], ['br']);
- $part->body_str_set($tree->as_HTML);
- $tree->delete;
- }
+ my ($part, $subject) = @_;
+ my $content_type = $part->content_type or return;
+ if ($content_type =~ /^text\/plain/) {
+ $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(['strong', "Subject: $subject"], ['br']);
+ $part->body_str_set($tree->as_HTML);
+ $tree->delete;
+ }
}
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) {
- $part->body_str_set($tree->as_HTML);
- }
- $tree->delete;
- });
+ 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) {
+ $part->body_str_set($tree->as_HTML);
+ }
+ $tree->delete;
+ });
}
__PACKAGE__->NAME;
diff --git a/extensions/SecureMail/lib/TCT.pm b/extensions/SecureMail/lib/TCT.pm
index 3a16309c2..f3de8ca39 100644
--- a/extensions/SecureMail/lib/TCT.pm
+++ b/extensions/SecureMail/lib/TCT.pm
@@ -15,76 +15,64 @@ use Future::Utils qw(call);
use Future;
use IO::Async::Process;
-has 'public_key' => ( is => 'ro', required => 1 );
-has 'public_key_file' => ( is => 'lazy' );
-has 'is_valid' => ( is => 'lazy' );
-has 'command' => ( is => 'ro', default => 'tct' );
+has 'public_key' => (is => 'ro', required => 1);
+has 'public_key_file' => (is => 'lazy');
+has 'is_valid' => (is => 'lazy');
+has 'command' => (is => 'ro', default => 'tct');
sub _build_public_key_file {
- my ($self) = @_;
- my $fh = File::Temp->new(SUFFIX => '.pubkey');
- $fh->print($self->public_key);
- $fh->close;
- return $fh;
+ my ($self) = @_;
+ my $fh = File::Temp->new(SUFFIX => '.pubkey');
+ $fh->print($self->public_key);
+ $fh->close;
+ return $fh;
}
sub _build_is_valid {
- my ($self) = @_;
-
- my $loop = IO::Async::Loop->new;
- my $exit_f = $loop->new_future;
- my ($stderr, $stdout);
- my $process = IO::Async::Process->new(
- command => [$self->command, 'check', '-k', $self->public_key_file ],
- stderr => {
- into => \$stderr,
- },
- stdout => {
- into => \$stdout,
- },
- on_finish => on_finish($exit_f),
- on_exception => on_exception($self->command, $exit_f),
- );
- $loop->add($process);
-
- return $exit_f->then(
- sub {
- my ($rv) = @_;
- Future->wrap($rv == 0);
- }
- );
+ my ($self) = @_;
+
+ my $loop = IO::Async::Loop->new;
+ my $exit_f = $loop->new_future;
+ my ($stderr, $stdout);
+ my $process = IO::Async::Process->new(
+ command => [$self->command, 'check', '-k', $self->public_key_file],
+ stderr => {into => \$stderr,},
+ stdout => {into => \$stdout,},
+ on_finish => on_finish($exit_f),
+ on_exception => on_exception($self->command, $exit_f),
+ );
+ $loop->add($process);
+
+ return $exit_f->then(sub {
+ my ($rv) = @_;
+ Future->wrap($rv == 0);
+ });
}
sub encrypt {
- my ($self, $input, $comment) = @_;
- $self->is_valid->then(
- sub {
- my ($is_valid) = @_;
- call {
- die 'invalid public key!' unless $is_valid;
-
- my $output;
- my $loop = IO::Async::Loop->new;
- my $exit_f = $loop->new_future;
- my @command = ( $self->command, 'encrypt', '-k', $self->public_key_file );
- push @command, '--comment', $comment if $comment;
- my $process = IO::Async::Process->new(
- command => \@command,
- stdin => {
- from => $input,
- },
- stdout => {
- into => \$output,
- },
- on_finish => on_finish($exit_f),
- on_exception => on_exception($self->command, $exit_f),
- );
- $loop->add($process);
-
- return $exit_f->then(sub { Future->wrap($output) });
- }
- }
- );
+ my ($self, $input, $comment) = @_;
+ $self->is_valid->then(sub {
+ my ($is_valid) = @_;
+ call {
+ die 'invalid public key!' unless $is_valid;
+
+ my $output;
+ my $loop = IO::Async::Loop->new;
+ my $exit_f = $loop->new_future;
+ my @command = ($self->command, 'encrypt', '-k', $self->public_key_file);
+ push @command, '--comment', $comment if $comment;
+ my $process = IO::Async::Process->new(
+ command => \@command,
+ stdin => {from => $input,},
+ stdout => {into => \$output,},
+ on_finish => on_finish($exit_f),
+ on_exception => on_exception($self->command, $exit_f),
+ );
+ $loop->add($process);
+
+ return $exit_f->then(sub { Future->wrap($output) });
+ }
+ });
}
1;
diff --git a/extensions/ShadowBugs/Config.pm b/extensions/ShadowBugs/Config.pm
index 6999edaf3..a45948dd4 100644
--- a/extensions/ShadowBugs/Config.pm
+++ b/extensions/ShadowBugs/Config.pm
@@ -8,7 +8,7 @@
package Bugzilla::Extension::ShadowBugs;
use strict;
-use constant NAME => 'ShadowBugs';
+use constant NAME => 'ShadowBugs';
use constant REQUIRED_MODULES => [];
use constant OPTIONAL_MODULES => [];
diff --git a/extensions/ShadowBugs/Extension.pm b/extensions/ShadowBugs/Extension.pm
index a9a1e0861..a1eb4a8c1 100644
--- a/extensions/ShadowBugs/Extension.pm
+++ b/extensions/ShadowBugs/Extension.pm
@@ -19,81 +19,83 @@ use Bugzilla::User;
our $VERSION = '1';
BEGIN {
- *Bugzilla::is_cf_shadow_bug_hidden = \&_is_cf_shadow_bug_hidden;
- *Bugzilla::Bug::cf_shadow_bug_obj = \&_cf_shadow_bug_obj;
+ *Bugzilla::is_cf_shadow_bug_hidden = \&_is_cf_shadow_bug_hidden;
+ *Bugzilla::Bug::cf_shadow_bug_obj = \&_cf_shadow_bug_obj;
}
# Determine if the shadow-bug / shadowed-by fields are visibile on the
# specified bug.
sub _is_cf_shadow_bug_hidden {
- my ($self, $bug) = @_;
+ my ($self, $bug) = @_;
- # completely hide unless you're a member of the right group
- return 1 unless Bugzilla->user->in_group('can_shadow_bugs');
+ # completely hide unless you're a member of the right group
+ return 1 unless Bugzilla->user->in_group('can_shadow_bugs');
- my $is_public = Bugzilla::User->new()->can_see_bug($bug->id);
- if ($is_public) {
- # hide on public bugs, unless it's shadowed
- my $related = $bug->related_bugs(Bugzilla->process_cache->{shadow_bug_field});
- return 1 if !@$related;
- }
+ my $is_public = Bugzilla::User->new()->can_see_bug($bug->id);
+ if ($is_public) {
+
+ # hide on public bugs, unless it's shadowed
+ my $related = $bug->related_bugs(Bugzilla->process_cache->{shadow_bug_field});
+ return 1 if !@$related;
+ }
}
sub _cf_shadow_bug_obj {
- my ($self) = @_;
- return unless $self->cf_shadow_bug;
- return $self->{cf_shadow_bug_obj} ||= Bugzilla::Bug->new($self->cf_shadow_bug);
+ my ($self) = @_;
+ return unless $self->cf_shadow_bug;
+ return $self->{cf_shadow_bug_obj} ||= Bugzilla::Bug->new($self->cf_shadow_bug);
}
sub template_before_process {
- my ($self, $args) = @_;
- my $file = $args->{'file'};
- my $vars = $args->{'vars'};
-
- Bugzilla->process_cache->{shadow_bug_field} ||= Bugzilla::Field->new({ name => 'cf_shadow_bug' });
-
- return unless Bugzilla->user->in_group('can_shadow_bugs');
- return unless
- $file eq 'bug/edit.html.tmpl'
- || $file eq 'bug/show.html.tmpl'
- || $file eq 'bug/show-header.html.tmpl';
- my $bug = exists $vars->{'bugs'} ? $vars->{'bugs'}[0] : $vars->{'bug'};
- return unless $bug && $bug->cf_shadow_bug;
- $vars->{is_shadow_bug} = 1;
-
- if ($file eq 'bug/edit.html.tmpl') {
- # load comments from other bug
- $vars->{shadow_comments} = $bug->cf_shadow_bug_obj->comments;
- }
+ my ($self, $args) = @_;
+ my $file = $args->{'file'};
+ my $vars = $args->{'vars'};
+
+ Bugzilla->process_cache->{shadow_bug_field}
+ ||= Bugzilla::Field->new({name => 'cf_shadow_bug'});
+
+ return unless Bugzilla->user->in_group('can_shadow_bugs');
+ return
+ unless $file eq 'bug/edit.html.tmpl'
+ || $file eq 'bug/show.html.tmpl'
+ || $file eq 'bug/show-header.html.tmpl';
+ my $bug = exists $vars->{'bugs'} ? $vars->{'bugs'}[0] : $vars->{'bug'};
+ return unless $bug && $bug->cf_shadow_bug;
+ $vars->{is_shadow_bug} = 1;
+
+ if ($file eq 'bug/edit.html.tmpl') {
+
+ # load comments from other bug
+ $vars->{shadow_comments} = $bug->cf_shadow_bug_obj->comments;
+ }
}
sub bug_end_of_update {
- my ($self, $args) = @_;
-
- # don't allow shadowing non-public bugs
- if (exists $args->{changes}->{cf_shadow_bug}) {
- my ($old_id, $new_id) = @{ $args->{changes}->{cf_shadow_bug} };
- if ($new_id) {
- if (!Bugzilla::User->new()->can_see_bug($new_id)) {
- ThrowUserError('illegal_shadow_bug_public', { id => $new_id });
- }
- }
+ my ($self, $args) = @_;
+
+ # don't allow shadowing non-public bugs
+ if (exists $args->{changes}->{cf_shadow_bug}) {
+ my ($old_id, $new_id) = @{$args->{changes}->{cf_shadow_bug}};
+ if ($new_id) {
+ if (!Bugzilla::User->new()->can_see_bug($new_id)) {
+ ThrowUserError('illegal_shadow_bug_public', {id => $new_id});
+ }
}
+ }
+
+ # if a shadow bug is made public, clear the shadow_bug field
+ if (exists $args->{changes}->{bug_group}) {
+ my $bug = $args->{bug};
+ return unless my $shadow_id = $bug->cf_shadow_bug;
+ my $is_public = Bugzilla::User->new()->can_see_bug($bug->id);
+ if ($is_public) {
+ Bugzilla->dbh->do("UPDATE bugs SET cf_shadow_bug=NULL WHERE bug_id=?",
+ undef, $bug->id);
+ LogActivityEntry($bug->id, 'cf_shadow_bug', $shadow_id, '', Bugzilla->user->id,
+ $args->{timestamp});
- # if a shadow bug is made public, clear the shadow_bug field
- if (exists $args->{changes}->{bug_group}) {
- my $bug = $args->{bug};
- return unless my $shadow_id = $bug->cf_shadow_bug;
- my $is_public = Bugzilla::User->new()->can_see_bug($bug->id);
- if ($is_public) {
- Bugzilla->dbh->do(
- "UPDATE bugs SET cf_shadow_bug=NULL WHERE bug_id=?",
- undef, $bug->id);
- LogActivityEntry($bug->id, 'cf_shadow_bug', $shadow_id, '',
- Bugzilla->user->id, $args->{timestamp});
-
- }
}
+ }
}
__PACKAGE__->NAME;
diff --git a/extensions/SiteMapIndex/Config.pm b/extensions/SiteMapIndex/Config.pm
index 980938503..3ff922167 100644
--- a/extensions/SiteMapIndex/Config.pm
+++ b/extensions/SiteMapIndex/Config.pm
@@ -28,12 +28,8 @@ use warnings;
use constant NAME => 'SiteMapIndex';
-use constant REQUIRED_MODULES => [
- {
- package => 'IO-Compress-Gzip',
- module => 'IO::Compress::Gzip',
- version => 0,
- }
-];
+use constant REQUIRED_MODULES =>
+ [{package => 'IO-Compress-Gzip', module => 'IO::Compress::Gzip', version => 0,
+ }];
__PACKAGE__->NAME;
diff --git a/extensions/SiteMapIndex/Extension.pm b/extensions/SiteMapIndex/Extension.pm
index 3d4342e0e..c606702ae 100644
--- a/extensions/SiteMapIndex/Extension.pm
+++ b/extensions/SiteMapIndex/Extension.pm
@@ -46,32 +46,32 @@ use POSIX;
#########
sub template_before_process {
- my ($self, $args) = @_;
- my ($vars, $file) = @$args{qw(vars file)};
-
- return if $file ne 'global/header.html.tmpl';
- return unless (exists $vars->{bug} || exists $vars->{bugs});
- my $bugs = exists $vars->{bugs} ? $vars->{bugs} : [$vars->{bug}];
- return if ref $bugs ne 'ARRAY';
-
- foreach my $bug (@$bugs) {
- if (!bug_is_ok_to_index($bug)) {
- $vars->{sitemap_noindex} = 1;
- last;
- }
+ my ($self, $args) = @_;
+ my ($vars, $file) = @$args{qw(vars file)};
+
+ return if $file ne 'global/header.html.tmpl';
+ return unless (exists $vars->{bug} || exists $vars->{bugs});
+ my $bugs = exists $vars->{bugs} ? $vars->{bugs} : [$vars->{bug}];
+ return if ref $bugs ne 'ARRAY';
+
+ foreach my $bug (@$bugs) {
+ if (!bug_is_ok_to_index($bug)) {
+ $vars->{sitemap_noindex} = 1;
+ last;
}
+ }
}
sub page_before_template {
- my ($self, $args) = @_;
- my $page = $args->{page_id};
-
- if ($page =~ m{^sitemap/sitemap\.}) {
- my $map = generate_sitemap(__PACKAGE__->NAME);
- print Bugzilla->cgi->header('text/xml');
- print $map;
- exit;
- }
+ my ($self, $args) = @_;
+ my $page = $args->{page_id};
+
+ if ($page =~ m{^sitemap/sitemap\.}) {
+ my $map = generate_sitemap(__PACKAGE__->NAME);
+ print Bugzilla->cgi->header('text/xml');
+ print $map;
+ exit;
+ }
}
################
@@ -79,54 +79,54 @@ sub page_before_template {
################
sub install_before_final_checks {
- my ($self) = @_;
- if (!Bugzilla->localconfig->{urlbase}) {
- print STDERR get_text('sitemap_no_urlbase'), "\n";
- return;
- }
- if (Bugzilla->params->{'requirelogin'}) {
- print STDERR get_text('sitemap_requirelogin'), "\n";
- return;
- }
-
- return if (Bugzilla->localconfig->{urlbase} ne 'https://bugzilla.mozilla.org/');
+ my ($self) = @_;
+ if (!Bugzilla->localconfig->{urlbase}) {
+ print STDERR get_text('sitemap_no_urlbase'), "\n";
+ return;
+ }
+ if (Bugzilla->params->{'requirelogin'}) {
+ print STDERR get_text('sitemap_requirelogin'), "\n";
+ return;
+ }
+
+ return if (Bugzilla->localconfig->{urlbase} ne 'https://bugzilla.mozilla.org/');
}
sub install_filesystem {
- my ($self, $args) = @_;
- my $create_dirs = $args->{'create_dirs'};
- my $recurse_dirs = $args->{'recurse_dirs'};
- my $htaccess = $args->{'htaccess'};
-
- # Create the sitemap directory to store the index and sitemap files
- my $sitemap_path = bz_locations->{'datadir'} . "/" . __PACKAGE__->NAME;
-
- $create_dirs->{$sitemap_path} = Bugzilla::Install::Filesystem::DIR_CGI_WRITE
- | Bugzilla::Install::Filesystem::DIR_ALSO_WS_SERVE;
-
- $recurse_dirs->{$sitemap_path} = {
- files => Bugzilla::Install::Filesystem::CGI_WRITE
- | Bugzilla::Install::Filesystem::DIR_ALSO_WS_SERVE,
- dirs => Bugzilla::Install::Filesystem::DIR_CGI_WRITE
- | Bugzilla::Install::Filesystem::DIR_ALSO_WS_SERVE
- };
-
- # Create a htaccess file that allows the sitemap files to be served out
- $htaccess->{"$sitemap_path/.htaccess"} = {
- perms => Bugzilla::Install::Filesystem::WS_SERVE,
- contents => <<EOT
+ my ($self, $args) = @_;
+ my $create_dirs = $args->{'create_dirs'};
+ my $recurse_dirs = $args->{'recurse_dirs'};
+ my $htaccess = $args->{'htaccess'};
+
+ # Create the sitemap directory to store the index and sitemap files
+ my $sitemap_path = bz_locations->{'datadir'} . "/" . __PACKAGE__->NAME;
+
+ $create_dirs->{$sitemap_path} = Bugzilla::Install::Filesystem::DIR_CGI_WRITE
+ | Bugzilla::Install::Filesystem::DIR_ALSO_WS_SERVE;
+
+ $recurse_dirs->{$sitemap_path} = {
+ files => Bugzilla::Install::Filesystem::CGI_WRITE
+ | Bugzilla::Install::Filesystem::DIR_ALSO_WS_SERVE,
+ dirs => Bugzilla::Install::Filesystem::DIR_CGI_WRITE
+ | Bugzilla::Install::Filesystem::DIR_ALSO_WS_SERVE
+ };
+
+ # Create a htaccess file that allows the sitemap files to be served out
+ $htaccess->{"$sitemap_path/.htaccess"} = {
+ perms => Bugzilla::Install::Filesystem::WS_SERVE,
+ contents => <<EOT
# Allow access to sitemap files created by the SiteMapIndex extension
<FilesMatch ^sitemap.*\\.xml(.gz)?\$>
Allow from all
</FilesMatch>
Deny from all
EOT
- };
+ };
}
sub before_robots_txt {
- my ($self, $args) = @_;
- $args->{vars}{SITEMAP_URL} = Bugzilla->localconfig->{urlbase} . SITEMAP_URL;
+ my ($self, $args) = @_;
+ $args->{vars}{SITEMAP_URL} = Bugzilla->localconfig->{urlbase} . SITEMAP_URL;
}
__PACKAGE__->NAME;
diff --git a/extensions/SiteMapIndex/lib/Constants.pm b/extensions/SiteMapIndex/lib/Constants.pm
index 4f404c8b1..bd098d16a 100644
--- a/extensions/SiteMapIndex/lib/Constants.pm
+++ b/extensions/SiteMapIndex/lib/Constants.pm
@@ -27,10 +27,10 @@ use warnings;
use base qw(Exporter);
our @EXPORT = qw(
- SITEMAP_AGE
- SITEMAP_MAX
- SITEMAP_DELAY
- SITEMAP_URL
+ SITEMAP_AGE
+ SITEMAP_MAX
+ SITEMAP_DELAY
+ SITEMAP_URL
);
# This is the amount of hours a sitemap index and it's files are considered
diff --git a/extensions/SiteMapIndex/lib/Util.pm b/extensions/SiteMapIndex/lib/Util.pm
index 4519461b4..fb945e324 100644
--- a/extensions/SiteMapIndex/lib/Util.pm
+++ b/extensions/SiteMapIndex/lib/Util.pm
@@ -28,8 +28,8 @@ use warnings;
use base qw(Exporter);
our @EXPORT = qw(
- generate_sitemap
- bug_is_ok_to_index
+ generate_sitemap
+ bug_is_ok_to_index
);
use Bugzilla::Extension::SiteMapIndex::Constants;
@@ -41,169 +41,176 @@ use Scalar::Util qw(blessed);
use IO::Compress::Gzip qw(gzip $GzipError);
sub too_young_date {
- my $hours_ago = DateTime->now(time_zone => Bugzilla->local_timezone);
- $hours_ago->subtract(hours => SITEMAP_DELAY);
- return $hours_ago;
+ my $hours_ago = DateTime->now(time_zone => Bugzilla->local_timezone);
+ $hours_ago->subtract(hours => SITEMAP_DELAY);
+ return $hours_ago;
}
sub bug_is_ok_to_index {
- my ($bug) = @_;
- return 1 unless blessed($bug) && $bug->isa('Bugzilla::Bug') && !$bug->{error};
- my $creation_ts = datetime_from($bug->creation_ts);
- return ($creation_ts && $creation_ts lt too_young_date()) ? 1 : 0;
+ my ($bug) = @_;
+ return 1 unless blessed($bug) && $bug->isa('Bugzilla::Bug') && !$bug->{error};
+ my $creation_ts = datetime_from($bug->creation_ts);
+ return ($creation_ts && $creation_ts lt too_young_date()) ? 1 : 0;
}
# We put two things in the Sitemap: a list of Browse links for products,
# and links to bugs.
sub generate_sitemap {
- my ($extension_name) = @_;
-
- # If file is less than SITEMAP_AGE hours old, then read in and send to caller.
- # If greater, then regenerate and send the new version.
- my $index_file = bz_locations->{'datadir'} . "/$extension_name/sitemap_index.xml";
- if (-e $index_file) {
- my $index_mtime = (stat($index_file))[9];
- my $index_hours = sprintf("%d", (time() - $index_mtime) / 60 / 60); # in hours
- if ($index_hours < SITEMAP_AGE) {
- my $index_fh = new IO::File($index_file, 'r');
- $index_fh || die "Could not open current sitemap index: $!";
- my $index_xml;
- { local $/; $index_xml = <$index_fh> }
- $index_fh->close() || die "Could not close current sitemap index: $!";
-
- return $index_xml;
- }
+ my ($extension_name) = @_;
+
+ # If file is less than SITEMAP_AGE hours old, then read in and send to caller.
+ # If greater, then regenerate and send the new version.
+ my $index_file
+ = bz_locations->{'datadir'} . "/$extension_name/sitemap_index.xml";
+ if (-e $index_file) {
+ my $index_mtime = (stat($index_file))[9];
+ my $index_hours = sprintf("%d", (time() - $index_mtime) / 60 / 60); # in hours
+ if ($index_hours < SITEMAP_AGE) {
+ my $index_fh = new IO::File($index_file, 'r');
+ $index_fh || die "Could not open current sitemap index: $!";
+ my $index_xml;
+ { local $/; $index_xml = <$index_fh> }
+ $index_fh->close() || die "Could not close current sitemap index: $!";
+
+ return $index_xml;
}
-
- # Set the atime and mtime of the index file to the current time
- # in case another request is made before we finish.
- utime(undef, undef, $index_file);
-
- # Sitemaps must never contain private data.
- Bugzilla->logout_request();
- my $user = Bugzilla->user;
- my $products = $user->get_accessible_products;
-
- my $num_bugs = SITEMAP_MAX - scalar(@$products);
- # We do this date math outside of the database because databases
- # usually do better with a straight comparison value.
- my $hours_ago = too_young_date();
-
- # We don't use Bugzilla::Bug objects, because this could be a tremendous
- # amount of data, and we only want a little. Also, we only display
- # bugs that are not in any group. We show the last $num_bugs
- # most-recently-updated bugs.
- my $dbh = Bugzilla->dbh;
- my $bug_sth = $dbh->prepare(
- 'SELECT bugs.bug_id, bugs.delta_ts
+ }
+
+ # Set the atime and mtime of the index file to the current time
+ # in case another request is made before we finish.
+ utime(undef, undef, $index_file);
+
+ # Sitemaps must never contain private data.
+ Bugzilla->logout_request();
+ my $user = Bugzilla->user;
+ my $products = $user->get_accessible_products;
+
+ my $num_bugs = SITEMAP_MAX - scalar(@$products);
+
+ # We do this date math outside of the database because databases
+ # usually do better with a straight comparison value.
+ my $hours_ago = too_young_date();
+
+ # We don't use Bugzilla::Bug objects, because this could be a tremendous
+ # amount of data, and we only want a little. Also, we only display
+ # bugs that are not in any group. We show the last $num_bugs
+ # most-recently-updated bugs.
+ my $dbh = Bugzilla->dbh;
+ my $bug_sth = $dbh->prepare(
+ 'SELECT bugs.bug_id, bugs.delta_ts
FROM bugs
LEFT JOIN bug_group_map ON bugs.bug_id = bug_group_map.bug_id
WHERE bug_group_map.bug_id IS NULL AND creation_ts < ?
- ' . $dbh->sql_limit($num_bugs, '?'));
+ ' . $dbh->sql_limit($num_bugs, '?')
+ );
- my $filecount = 1;
- my $filelist = [];
- my $offset = 0;
+ my $filecount = 1;
+ my $filelist = [];
+ my $offset = 0;
- while (1) {
- my $bugs = [];
+ while (1) {
+ my $bugs = [];
- $bug_sth->execute($hours_ago, $offset);
+ $bug_sth->execute($hours_ago, $offset);
- while (my ($bug_id, $delta_ts) = $bug_sth->fetchrow_array()) {
- push(@$bugs, { bug_id => $bug_id, delta_ts => $delta_ts });
- }
+ while (my ($bug_id, $delta_ts) = $bug_sth->fetchrow_array()) {
+ push(@$bugs, {bug_id => $bug_id, delta_ts => $delta_ts});
+ }
- last if !@$bugs;
+ last if !@$bugs;
- # We only need the product links in the first sitemap file
- $products = [] if $filecount > 1;
+ # We only need the product links in the first sitemap file
+ $products = [] if $filecount > 1;
- push(@$filelist, _generate_sitemap_file($extension_name, $filecount, $products, $bugs));
+ push(@$filelist,
+ _generate_sitemap_file($extension_name, $filecount, $products, $bugs));
- $filecount++;
- $offset += $num_bugs;
- }
+ $filecount++;
+ $offset += $num_bugs;
+ }
- # Generate index file
- return _generate_sitemap_index($extension_name, $filelist);
+ # Generate index file
+ return _generate_sitemap_index($extension_name, $filelist);
}
sub _generate_sitemap_index {
- my ($extension_name, $filelist) = @_;
+ my ($extension_name, $filelist) = @_;
- my $dbh = Bugzilla->dbh;
- my $timestamp = $dbh->selectrow_array(
- "SELECT " . $dbh->sql_date_format('NOW()', '%Y-%m-%d'));
+ my $dbh = Bugzilla->dbh;
+ my $timestamp = $dbh->selectrow_array(
+ "SELECT " . $dbh->sql_date_format('NOW()', '%Y-%m-%d'));
- my $index_xml = <<END;
+ my $index_xml = <<END;
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
END
- foreach my $filename (@$filelist) {
- $index_xml .= "
+ foreach my $filename (@$filelist) {
+ $index_xml .= "
<sitemap>
- <loc>" . Bugzilla->localconfig->{urlbase} . "data/$extension_name/$filename</loc>
+ <loc>"
+ . Bugzilla->localconfig->{urlbase} . "data/$extension_name/$filename</loc>
<lastmod>$timestamp</lastmod>
</sitemap>
";
- }
+ }
- $index_xml .= <<END;
+ $index_xml .= <<END;
</sitemapindex>
END
- my $index_file = bz_locations->{'datadir'} . "/$extension_name/sitemap_index.xml";
- my $index_fh = new IO::File($index_file, 'w');
- $index_fh || die "Could not open new sitemap index: $!";
- print $index_fh $index_xml;
- $index_fh->close() || die "Could not close new sitemap index: $!";
+ my $index_file
+ = bz_locations->{'datadir'} . "/$extension_name/sitemap_index.xml";
+ my $index_fh = new IO::File($index_file, 'w');
+ $index_fh || die "Could not open new sitemap index: $!";
+ print $index_fh $index_xml;
+ $index_fh->close() || die "Could not close new sitemap index: $!";
- return $index_xml;
+ return $index_xml;
}
sub _generate_sitemap_file {
- my ($extension_name, $filecount, $products, $bugs) = @_;
+ my ($extension_name, $filecount, $products, $bugs) = @_;
- my $bug_url = Bugzilla->localconfig->{urlbase} . 'show_bug.cgi?id=';
- my $product_url = Bugzilla->localconfig->{urlbase} . 'describecomponents.cgi?product=';
+ my $bug_url = Bugzilla->localconfig->{urlbase} . 'show_bug.cgi?id=';
+ my $product_url
+ = Bugzilla->localconfig->{urlbase} . 'describecomponents.cgi?product=';
- my $sitemap_xml = <<END;
+ my $sitemap_xml = <<END;
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
END
- foreach my $product (@$products) {
- $sitemap_xml .= "
+ foreach my $product (@$products) {
+ $sitemap_xml .= "
<url>
<loc>" . $product_url . url_quote($product->name) . "</loc>
<changefreq>daily</changefreq>
<priority>0.4</priority>
</url>
";
- }
+ }
- foreach my $bug (@$bugs) {
- $sitemap_xml .= "
+ foreach my $bug (@$bugs) {
+ $sitemap_xml .= "
<url>
<loc>" . $bug_url . $bug->{bug_id} . "</loc>
<lastmod>" . datetime_from($bug->{delta_ts}, 'UTC')->iso8601 . 'Z' . "</lastmod>
</url>
";
- }
+ }
- $sitemap_xml .= <<END;
+ $sitemap_xml .= <<END;
</urlset>
END
- # Write the compressed sitemap data to a file in the cgi root so that they can
- # be accessed by the search engines.
- my $filename = "sitemap$filecount.xml.gz";
- gzip \$sitemap_xml => bz_locations->{'datadir'} . "/$extension_name/$filename"
- || die "gzip failed: $GzipError\n";
+ # Write the compressed sitemap data to a file in the cgi root so that they can
+ # be accessed by the search engines.
+ my $filename = "sitemap$filecount.xml.gz";
+ gzip \$sitemap_xml => bz_locations->{'datadir'} . "/$extension_name/$filename"
+ || die "gzip failed: $GzipError\n";
- return $filename;
+ return $filename;
}
1;
diff --git a/extensions/Splinter/Extension.pm b/extensions/Splinter/Extension.pm
index eb2006f47..f5a2f41cb 100644
--- a/extensions/Splinter/Extension.pm
+++ b/extensions/Splinter/Extension.pm
@@ -28,135 +28,140 @@ use Bugzilla::Extension::Splinter::Util;
our $VERSION = '0.1';
BEGIN {
- *Bugzilla::splinter_review_base = \&get_review_base;
- *Bugzilla::splinter_review_url = \&_get_review_url;
+ *Bugzilla::splinter_review_base = \&get_review_base;
+ *Bugzilla::splinter_review_url = \&_get_review_url;
}
sub _get_review_url {
- my ($class, $bug_id, $attach_id) = @_;
- return get_review_url(Bugzilla::Bug->check({ id => $bug_id, cache => 1 }), $attach_id);
+ my ($class, $bug_id, $attach_id) = @_;
+ return get_review_url(Bugzilla::Bug->check({id => $bug_id, cache => 1}),
+ $attach_id);
}
sub page_before_template {
- my ($self, $args) = @_;
- my ($vars, $page) = @$args{qw(vars page_id)};
-
- if ($page eq 'splinter.html') {
- my $user = Bugzilla->user;
-
- # We can either provide just a bug id to see a list
- # of prior reviews by the user, or just an attachment
- # id to go directly to a review page for the attachment.
- # If both are give they will be checked later to make
- # sure they are connected.
-
- my $input = Bugzilla->input_params;
- if ($input->{'bug'}) {
- $vars->{'bug_id'} = $input->{'bug'};
- $vars->{'attach_id'} = $input->{'attachment'};
- $vars->{'bug'} = Bugzilla::Bug->check({ id => $input->{'bug'}, cache => 1 });
- }
-
- if ($input->{'attachment'}) {
- my $attachment = Bugzilla::Attachment->check({ id => $input->{'attachment'} });
-
- # Check to see if the user can see the bug this attachment is connected to.
- Bugzilla::Bug->check($attachment->bug_id);
- if ($attachment->isprivate
- && $user->id != $attachment->attacher->id
- && !$user->is_insider)
- {
- ThrowUserError('auth_failure', {action => 'access',
- object => 'attachment'});
- }
-
- # If the user provided both a bug id and an attachment id, they must
- # be connected to each other
- if ($input->{'bug'} && $input->{'bug'} != $attachment->bug_id) {
- ThrowUserError('bug_attach_id_mismatch');
- }
-
- # The patch is going to be displayed in a HTML page and if the utf8
- # param is enabled, we have to encode attachment data as utf8.
- if (Bugzilla->params->{'utf8'}) {
- $attachment->data; # load data
- utf8::decode($attachment->{data});
- }
-
- $vars->{'attach_id'} = $attachment->id;
- if ($user->id && $attachment->contenttype eq "text/x-github-pull-request" && $attachment->can_review) {
- $vars->{'attach_data'} = $attachment->fetch_github_pr_diff;
- }
- else {
- $vars->{'attach_data'} = $attachment->data;
- }
- $vars->{'attach_is_crlf'} = $vars->{'attach_data'} =~ /\012\015/ ? 1 : 0;
- }
-
- my $field_object = new Bugzilla::Field({ name => 'attachments.status' });
- my $statuses;
- if ($field_object) {
- $statuses = [map { $_->name } @{ $field_object->legal_values }];
- } else {
- $statuses = [];
- }
- $vars->{'attachment_statuses'} = $statuses;
+ my ($self, $args) = @_;
+ my ($vars, $page) = @$args{qw(vars page_id)};
+
+ if ($page eq 'splinter.html') {
+ my $user = Bugzilla->user;
+
+ # We can either provide just a bug id to see a list
+ # of prior reviews by the user, or just an attachment
+ # id to go directly to a review page for the attachment.
+ # If both are give they will be checked later to make
+ # sure they are connected.
+
+ my $input = Bugzilla->input_params;
+ if ($input->{'bug'}) {
+ $vars->{'bug_id'} = $input->{'bug'};
+ $vars->{'attach_id'} = $input->{'attachment'};
+ $vars->{'bug'} = Bugzilla::Bug->check({id => $input->{'bug'}, cache => 1});
}
+
+ if ($input->{'attachment'}) {
+ my $attachment = Bugzilla::Attachment->check({id => $input->{'attachment'}});
+
+ # Check to see if the user can see the bug this attachment is connected to.
+ Bugzilla::Bug->check($attachment->bug_id);
+ if ( $attachment->isprivate
+ && $user->id != $attachment->attacher->id
+ && !$user->is_insider)
+ {
+ ThrowUserError('auth_failure', {action => 'access', object => 'attachment'});
+ }
+
+ # If the user provided both a bug id and an attachment id, they must
+ # be connected to each other
+ if ($input->{'bug'} && $input->{'bug'} != $attachment->bug_id) {
+ ThrowUserError('bug_attach_id_mismatch');
+ }
+
+ # The patch is going to be displayed in a HTML page and if the utf8
+ # param is enabled, we have to encode attachment data as utf8.
+ if (Bugzilla->params->{'utf8'}) {
+ $attachment->data; # load data
+ utf8::decode($attachment->{data});
+ }
+
+ $vars->{'attach_id'} = $attachment->id;
+ if ( $user->id
+ && $attachment->contenttype eq "text/x-github-pull-request"
+ && $attachment->can_review)
+ {
+ $vars->{'attach_data'} = $attachment->fetch_github_pr_diff;
+ }
+ else {
+ $vars->{'attach_data'} = $attachment->data;
+ }
+ $vars->{'attach_is_crlf'} = $vars->{'attach_data'} =~ /\012\015/ ? 1 : 0;
+ }
+
+ my $field_object = new Bugzilla::Field({name => 'attachments.status'});
+ my $statuses;
+ if ($field_object) {
+ $statuses = [map { $_->name } @{$field_object->legal_values}];
+ }
+ else {
+ $statuses = [];
+ }
+ $vars->{'attachment_statuses'} = $statuses;
+ }
}
sub bug_format_comment {
- my ($self, $args) = @_;
-
- my $bug = $args->{'bug'};
- my $regexes = $args->{'regexes'};
- my $text = $args->{'text'};
-
- # Add [review] link to the end of "Created attachment" comments
- #
- # We need to work around the way that the hook works, which is intended
- # to avoid overlapping matches, since we *want* an overlapping match
- # here (the normal handling of "Created attachment"), so we add in
- # dummy text and then replace in the regular expression we return from
- # the hook.
- $$text =~ s~((?:^Created\ |\b)attachment\s*\#?\s*(\d+)(\s\[details\])?)
+ my ($self, $args) = @_;
+
+ my $bug = $args->{'bug'};
+ my $regexes = $args->{'regexes'};
+ my $text = $args->{'text'};
+
+ # Add [review] link to the end of "Created attachment" comments
+ #
+ # We need to work around the way that the hook works, which is intended
+ # to avoid overlapping matches, since we *want* an overlapping match
+ # here (the normal handling of "Created attachment"), so we add in
+ # dummy text and then replace in the regular expression we return from
+ # the hook.
+ $$text =~ s~((?:^Created\ |\b)attachment\s*\#?\s*(\d+)(\s\[details\])?)
~(push(@$regexes, { match => qr/__REVIEW__$2/,
replace => get_review_link("$2", "[review]") })) &&
(attachment_id_is_patch($2) ? "$1 __REVIEW__$2" : $1)
~egmx;
- # And linkify "Review of attachment", this is less of a workaround since
- # there is no issue with overlap; note that there is an assumption that
- # there is only one match in the text we are linkifying, since they all
- # get the same link.
- my $REVIEW_RE = qr/Review\s+of\s+attachment\s+(\d+)\s*:/;
-
- if ($$text =~ $REVIEW_RE) {
- my $attach_id = $1;
- my $review_link = get_review_link($attach_id, "Review");
- my $attach_link = Bugzilla::Template::get_attachment_link($attach_id, "attachment $attach_id");
-
- push(@$regexes, { match => $REVIEW_RE,
- replace => "$review_link of $attach_link:"});
- }
+ # And linkify "Review of attachment", this is less of a workaround since
+ # there is no issue with overlap; note that there is an assumption that
+ # there is only one match in the text we are linkifying, since they all
+ # get the same link.
+ my $REVIEW_RE = qr/Review\s+of\s+attachment\s+(\d+)\s*:/;
+
+ if ($$text =~ $REVIEW_RE) {
+ my $attach_id = $1;
+ my $review_link = get_review_link($attach_id, "Review");
+ my $attach_link = Bugzilla::Template::get_attachment_link($attach_id,
+ "attachment $attach_id");
+
+ push(@$regexes,
+ {match => $REVIEW_RE, replace => "$review_link of $attach_link:"});
+ }
}
sub config_add_panels {
- my ($self, $args) = @_;
+ my ($self, $args) = @_;
- my $modules = $args->{panel_modules};
- $modules->{Splinter} = "Bugzilla::Extension::Splinter::Config";
+ my $modules = $args->{panel_modules};
+ $modules->{Splinter} = "Bugzilla::Extension::Splinter::Config";
}
sub mailer_before_send {
- my ($self, $args) = @_;
-
- # Post-process bug mail to add review links to bug mail.
- # It would be nice to be able to hook in earlier in the
- # process when the email body is being formatted in the
- # style of the bug-format_comment link for HTML but this
- # is the only hook available as of Bugzilla-3.4.
- add_review_links_to_email($args->{'email'});
+ my ($self, $args) = @_;
+
+ # Post-process bug mail to add review links to bug mail.
+ # It would be nice to be able to hook in earlier in the
+ # process when the email body is being formatted in the
+ # style of the bug-format_comment link for HTML but this
+ # is the only hook available as of Bugzilla-3.4.
+ add_review_links_to_email($args->{'email'});
}
__PACKAGE__->NAME;
diff --git a/extensions/Splinter/lib/Config.pm b/extensions/Splinter/lib/Config.pm
index fb3c16074..d3675c111 100644
--- a/extensions/Splinter/lib/Config.pm
+++ b/extensions/Splinter/lib/Config.pm
@@ -31,17 +31,13 @@ use Bugzilla::Config::Common;
our $sortkey = 1350;
sub get_param_list {
- my ($class) = @_;
+ my ($class) = @_;
- my @param_list = (
- {
- name => 'splinter_base',
- type => 't',
- default => 'page.cgi?id=splinter.html',
- },
- );
+ my @param_list = (
+ {name => 'splinter_base', type => 't', default => 'page.cgi?id=splinter.html',},
+ );
- return @param_list;
+ return @param_list;
}
1;
diff --git a/extensions/Splinter/lib/Util.pm b/extensions/Splinter/lib/Util.pm
index c85bb9b3b..0b7b2ff12 100644
--- a/extensions/Splinter/lib/Util.pm
+++ b/extensions/Splinter/lib/Util.pm
@@ -32,12 +32,12 @@ use Email::MIME::ContentType qw(parse_content_type);
use base qw(Exporter);
@Bugzilla::Extension::Splinter::Util::EXPORT = qw(
- attachment_is_visible
- attachment_id_is_patch
- get_review_base
- get_review_url
- get_review_link
- add_review_links_to_email
+ attachment_is_visible
+ attachment_id_is_patch
+ get_review_base
+ get_review_url
+ get_review_link
+ add_review_links_to_email
);
# Validates an attachment ID.
@@ -51,81 +51,95 @@ use base qw(Exporter);
# Returns an attachment object.
# Based on code from attachment.cgi
sub attachment_id_is_valid {
- my ($attach_id, $dont_validate_access) = @_;
+ my ($attach_id, $dont_validate_access) = @_;
- # Validate the specified attachment id.
- detaint_natural($attach_id) || return 0;
+ # Validate the specified attachment id.
+ detaint_natural($attach_id) || return 0;
- # Make sure the attachment exists in the database.
- my $attachment = new Bugzilla::Attachment({ id => $attach_id, cache => 1 })
- || return 0;
+ # Make sure the attachment exists in the database.
+ my $attachment
+ = new Bugzilla::Attachment({id => $attach_id, cache => 1}) || return 0;
- return $attachment
- if ($dont_validate_access || attachment_is_visible($attachment));
+ return $attachment
+ if ($dont_validate_access || attachment_is_visible($attachment));
}
# Checks if the current user can see an attachment
# Based on code from attachment.cgi
sub attachment_is_visible {
- my $attachment = shift;
+ my $attachment = shift;
- $attachment->isa('Bugzilla::Attachment') || return 0;
+ $attachment->isa('Bugzilla::Attachment') || return 0;
- return (Bugzilla->user->can_see_bug($attachment->bug->id)
- && (!$attachment->isprivate
- || Bugzilla->user->id == $attachment->attacher->id
- || Bugzilla->user->is_insider));
+ return (
+ Bugzilla->user->can_see_bug($attachment->bug->id)
+ && (!$attachment->isprivate
+ || Bugzilla->user->id == $attachment->attacher->id
+ || Bugzilla->user->is_insider)
+ );
}
sub attachment_id_is_patch {
- my $attach_id = shift;
- my $attachment = attachment_id_is_valid($attach_id);
- return ($attachment
- && ($attachment->ispatch
- || ($attachment->contenttype eq "text/x-github-pull-request" && $attachment->external_redirect)));
+ my $attach_id = shift;
+ my $attachment = attachment_id_is_valid($attach_id);
+ return (
+ $attachment
+ && (
+ $attachment->ispatch
+ || ( $attachment->contenttype eq "text/x-github-pull-request"
+ && $attachment->external_redirect)
+ )
+ );
}
sub get_review_base {
- my $base = Bugzilla->params->{'splinter_base'};
- $base =~ s!/$!!;
- my $urlbase = Bugzilla->localconfig->{urlbase};
- $urlbase =~ s!/$!! if $base =~ "^/";
- $base = $urlbase . $base;
- return $base;
+ my $base = Bugzilla->params->{'splinter_base'};
+ $base =~ s!/$!!;
+ my $urlbase = Bugzilla->localconfig->{urlbase};
+ $urlbase =~ s!/$!! if $base =~ "^/";
+ $base = $urlbase . $base;
+ return $base;
}
sub get_review_url {
- my ($bug, $attach_id) = @_;
- my $base = get_review_base();
- my $bug_id = $bug->id;
- return $base . ($base =~ /\?/ ? '&' : '?') . "bug=$bug_id&attachment=$attach_id";
+ my ($bug, $attach_id) = @_;
+ my $base = get_review_base();
+ my $bug_id = $bug->id;
+ return
+ $base
+ . ($base =~ /\?/ ? '&' : '?')
+ . "bug=$bug_id&attachment=$attach_id";
}
sub get_review_link {
- my ($attach_id, $link_text) = @_;
-
- my $attachment = attachment_id_is_valid($attach_id);
-
- if (attachment_id_is_patch($attach_id)) {
- return "<a href='" . html_quote(get_review_url($attachment->bug, $attach_id)) .
- "'>$link_text</a>";
- }
- else {
- return $link_text;
- }
+ my ($attach_id, $link_text) = @_;
+
+ my $attachment = attachment_id_is_valid($attach_id);
+
+ if (attachment_id_is_patch($attach_id)) {
+ return
+ "<a href='"
+ . html_quote(get_review_url($attachment->bug, $attach_id))
+ . "'>$link_text</a>";
+ }
+ else {
+ return $link_text;
+ }
}
sub munge_create_attachment {
- my ($bug, $intro_text, $attach_id, $view_link) = @_;
-
- if (attachment_id_is_patch($attach_id)) {
- return ("$intro_text" .
- " View: $view_link\015\012" .
- " Review: " . get_review_url($bug, $attach_id, 1) . "\015\012");
- }
- else {
- return ("$intro_text --> ($view_link)");
- }
+ my ($bug, $intro_text, $attach_id, $view_link) = @_;
+
+ if (attachment_id_is_patch($attach_id)) {
+ return ("$intro_text"
+ . " View: $view_link\015\012"
+ . " Review: "
+ . get_review_url($bug, $attach_id, 1)
+ . "\015\012");
+ }
+ else {
+ return ("$intro_text --> ($view_link)");
+ }
}
# This adds review links into a bug mail before we send it out.
@@ -133,64 +147,62 @@ sub munge_create_attachment {
# RFC-2822 style \r\n, we need handle line ends carefully.
# (\015 and \012 are used because Perl \n is platform-dependent)
sub add_review_links_to_email {
- my $email = shift;
- return if $email->parts > 1;
- return unless $email->content_type =~ m#^text/#;
+ my $email = shift;
+ return if $email->parts > 1;
+ return unless $email->content_type =~ m#^text/#;
- _fix_encoding($email);
- my $body = $email->body_str;
+ _fix_encoding($email);
+ my $body = $email->body_str;
- my $new_body = 0;
- my $bug;
+ my $new_body = 0;
+ my $bug;
- if ($email->header('Subject') =~ /^\[Bug\s+(\d+)\]/
- && Bugzilla->user->can_see_bug($1))
- {
- $bug = Bugzilla::Bug->new({ id => $1, cache => 1 });
- }
+ if ($email->header('Subject') =~ /^\[Bug\s+(\d+)\]/
+ && Bugzilla->user->can_see_bug($1))
+ {
+ $bug = Bugzilla::Bug->new({id => $1, cache => 1});
+ }
- return unless defined $bug;
+ return unless defined $bug;
- if ($body =~ /Review\s+of\s+attachment\s+\d+\s*:/) {
- $body =~ s~(Review\s+of\s+attachment\s+(\d+)\s*:)
+ if ($body =~ /Review\s+of\s+attachment\s+\d+\s*:/) {
+ $body =~ s~(Review\s+of\s+attachment\s+(\d+)\s*:)
~"$1\015\012 --> (" . get_review_url($bug, $2, 1) . ")"
~egx;
- $new_body = 1;
- }
+ $new_body = 1;
+ }
- if ($body =~ /Created attachment \d+\015\012 --> /) {
- $body =~ s~(Created\ attachment\ (\d+)\015\012)
+ if ($body =~ /Created attachment \d+\015\012 --> /) {
+ $body =~ s~(Created\ attachment\ (\d+)\015\012)
\ -->\ \(([^\015\012]*)\)[^\015\012]*
~munge_create_attachment($bug, $1, $2, $3)
~egx;
- $new_body = 1;
- }
+ $new_body = 1;
+ }
- $email->body_str_set($body) if $new_body;
+ $email->body_str_set($body) if $new_body;
}
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');
}
1;
diff --git a/extensions/TagNewUsers/Config.pm b/extensions/TagNewUsers/Config.pm
index c791e3a07..cc3676bff 100644
--- a/extensions/TagNewUsers/Config.pm
+++ b/extensions/TagNewUsers/Config.pm
@@ -11,8 +11,8 @@ use 5.10.1;
use strict;
use warnings;
-use constant NAME => 'TagNewUsers';
-use constant REQUIRED_MODULES => [ ];
-use constant OPTIONAL_MODULES => [ ];
+use constant NAME => 'TagNewUsers';
+use constant REQUIRED_MODULES => [];
+use constant OPTIONAL_MODULES => [];
__PACKAGE__->NAME;
diff --git a/extensions/TagNewUsers/Extension.pm b/extensions/TagNewUsers/Extension.pm
index 1810f204f..6280f1697 100644
--- a/extensions/TagNewUsers/Extension.pm
+++ b/extensions/TagNewUsers/Extension.pm
@@ -34,85 +34,88 @@ our $VERSION = '1';
#
sub install_update_db {
- my ($self) = @_;
- my $dbh = Bugzilla->dbh;
-
- if (!$dbh->bz_column_info('profiles', 'comment_count')) {
- $dbh->bz_add_column('profiles', 'comment_count',
- {TYPE => 'INT3', NOTNULL => 1, DEFAULT => 0});
- my $sth = $dbh->prepare('UPDATE profiles SET comment_count=? WHERE userid=?');
- my $ra = $dbh->selectall_arrayref('SELECT who,COUNT(*) FROM longdescs GROUP BY who');
- my $count = 1;
- my $total = scalar @$ra;
- foreach my $ra_row (@$ra) {
- indicate_progress({ current => $count++, total => $total, every => 25 });
- my ($user_id, $count) = @$ra_row;
- $sth->execute($count, $user_id);
- }
+ my ($self) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ if (!$dbh->bz_column_info('profiles', 'comment_count')) {
+ $dbh->bz_add_column('profiles', 'comment_count',
+ {TYPE => 'INT3', NOTNULL => 1, DEFAULT => 0});
+ my $sth = $dbh->prepare('UPDATE profiles SET comment_count=? WHERE userid=?');
+ my $ra
+ = $dbh->selectall_arrayref('SELECT who,COUNT(*) FROM longdescs GROUP BY who');
+ my $count = 1;
+ my $total = scalar @$ra;
+ foreach my $ra_row (@$ra) {
+ indicate_progress({current => $count++, total => $total, every => 25});
+ my ($user_id, $count) = @$ra_row;
+ $sth->execute($count, $user_id);
}
+ }
- if (!$dbh->bz_column_info('profiles', 'creation_ts')) {
- $dbh->bz_add_column('profiles', 'creation_ts',
- {TYPE => 'DATETIME'});
- my $creation_date_fieldid = get_field_id('creation_ts');
- my $sth = $dbh->prepare('UPDATE profiles SET creation_ts=? WHERE userid=?');
- my $ra = $dbh->selectall_arrayref("
+ if (!$dbh->bz_column_info('profiles', 'creation_ts')) {
+ $dbh->bz_add_column('profiles', 'creation_ts', {TYPE => 'DATETIME'});
+ my $creation_date_fieldid = get_field_id('creation_ts');
+ my $sth = $dbh->prepare('UPDATE profiles SET creation_ts=? WHERE userid=?');
+ my $ra = $dbh->selectall_arrayref("
SELECT p.userid, a.profiles_when
FROM profiles p
LEFT JOIN profiles_activity a ON a.userid=p.userid
AND a.fieldid=$creation_date_fieldid
");
- my ($now) = Bugzilla->dbh->selectrow_array("SELECT NOW()");
- my $count = 1;
- my $total = scalar @$ra;
- foreach my $ra_row (@$ra) {
- indicate_progress({ current => $count++, total => $total, every => 25 });
- my ($user_id, $when) = @$ra_row;
- if (!$when) {
- ($when) = $dbh->selectrow_array(
- "SELECT bug_when FROM bugs_activity WHERE who=? ORDER BY bug_when " .
- $dbh->sql_limit(1),
- undef, $user_id
- );
- }
- if (!$when) {
- ($when) = $dbh->selectrow_array(
- "SELECT bug_when FROM longdescs WHERE who=? ORDER BY bug_when " .
- $dbh->sql_limit(1),
- undef, $user_id
- );
- }
- if (!$when) {
- ($when) = $dbh->selectrow_array(
- "SELECT creation_ts FROM bugs WHERE reporter=? ORDER BY creation_ts " .
- $dbh->sql_limit(1),
- undef, $user_id
- );
- }
- if (!$when) {
- $when = $now;
- }
-
- $sth->execute($when, $user_id);
- }
- }
-
- if (!$dbh->bz_column_info('profiles', 'first_patch_bug_id')) {
- $dbh->bz_add_column('profiles', 'first_patch_bug_id', {TYPE => 'INT3'});
- my $sth_update = $dbh->prepare('UPDATE profiles SET first_patch_bug_id=? WHERE userid=?');
- my $sth_select = $dbh->prepare(
- 'SELECT bug_id FROM attachments WHERE submitter_id=? AND ispatch=1 ORDER BY creation_ts ' . $dbh->sql_limit(1)
+ my ($now) = Bugzilla->dbh->selectrow_array("SELECT NOW()");
+ my $count = 1;
+ my $total = scalar @$ra;
+ foreach my $ra_row (@$ra) {
+ indicate_progress({current => $count++, total => $total, every => 25});
+ my ($user_id, $when) = @$ra_row;
+ if (!$when) {
+ ($when) = $dbh->selectrow_array(
+ "SELECT bug_when FROM bugs_activity WHERE who=? ORDER BY bug_when "
+ . $dbh->sql_limit(1),
+ undef, $user_id
+ );
+ }
+ if (!$when) {
+ ($when) = $dbh->selectrow_array(
+ "SELECT bug_when FROM longdescs WHERE who=? ORDER BY bug_when "
+ . $dbh->sql_limit(1),
+ undef, $user_id
+ );
+ }
+ if (!$when) {
+ ($when) = $dbh->selectrow_array(
+ "SELECT creation_ts FROM bugs WHERE reporter=? ORDER BY creation_ts "
+ . $dbh->sql_limit(1),
+ undef, $user_id
);
- my $ra = $dbh->selectcol_arrayref('SELECT DISTINCT submitter_id FROM attachments WHERE ispatch=1');
- my $count = 1;
- my $total = scalar @$ra;
- foreach my $user_id (@$ra) {
- indicate_progress({ current => $count++, total => $total, every => 25 });
- $sth_select->execute($user_id);
- my ($bug_id) = $sth_select->fetchrow_array;
- $sth_update->execute($bug_id, $user_id);
- }
+ }
+ if (!$when) {
+ $when = $now;
+ }
+
+ $sth->execute($when, $user_id);
+ }
+ }
+
+ if (!$dbh->bz_column_info('profiles', 'first_patch_bug_id')) {
+ $dbh->bz_add_column('profiles', 'first_patch_bug_id', {TYPE => 'INT3'});
+ my $sth_update
+ = $dbh->prepare('UPDATE profiles SET first_patch_bug_id=? WHERE userid=?');
+ my $sth_select
+ = $dbh->prepare(
+ 'SELECT bug_id FROM attachments WHERE submitter_id=? AND ispatch=1 ORDER BY creation_ts '
+ . $dbh->sql_limit(1));
+ my $ra = $dbh->selectcol_arrayref(
+ 'SELECT DISTINCT submitter_id FROM attachments WHERE ispatch=1');
+ my $count = 1;
+ my $total = scalar @$ra;
+ foreach my $user_id (@$ra) {
+ indicate_progress({current => $count++, total => $total, every => 25});
+ $sth_select->execute($user_id);
+ my ($bug_id) = $sth_select->fetchrow_array;
+ $sth_update->execute($bug_id, $user_id);
}
+ }
}
#
@@ -120,32 +123,34 @@ sub install_update_db {
#
sub object_columns {
- my ($self, $args) = @_;
- my ($class, $columns) = @$args{qw(class columns)};
- if ($class->isa('Bugzilla::User')) {
- my $dbh = Bugzilla->dbh;
- my @new_columns = qw(comment_count creation_ts first_patch_bug_id);
- push @$columns, grep { $dbh->bz_column_info($class->DB_TABLE, $_) } @new_columns;
- }
+ my ($self, $args) = @_;
+ my ($class, $columns) = @$args{qw(class columns)};
+ if ($class->isa('Bugzilla::User')) {
+ my $dbh = Bugzilla->dbh;
+ my @new_columns = qw(comment_count creation_ts first_patch_bug_id);
+ push @$columns,
+ grep { $dbh->bz_column_info($class->DB_TABLE, $_) } @new_columns;
+ }
}
sub object_before_create {
- my ($self, $args) = @_;
- my ($class, $params) = @$args{qw(class params)};
- if ($class->isa('Bugzilla::User')) {
- my $dbh = Bugzilla->dbh;
- my ($timestamp) = $dbh->selectrow_array("SELECT NOW()");
- if ($dbh->bz_column_info($class->DB_TABLE, 'comment_count')) {
- $params->{comment_count} = 0;
- }
- if ($dbh->bz_column_info($class->DB_TABLE, 'creation_ts')) {
- $params->{creation_ts} = $timestamp;
- }
- } elsif ($class->isa('Bugzilla::Attachment')) {
- if ($params->{ispatch} && !Bugzilla->user->first_patch_bug_id) {
- Bugzilla->user->first_patch_bug_id($params->{bug}->id);
- }
+ my ($self, $args) = @_;
+ my ($class, $params) = @$args{qw(class params)};
+ if ($class->isa('Bugzilla::User')) {
+ my $dbh = Bugzilla->dbh;
+ my ($timestamp) = $dbh->selectrow_array("SELECT NOW()");
+ if ($dbh->bz_column_info($class->DB_TABLE, 'comment_count')) {
+ $params->{comment_count} = 0;
+ }
+ if ($dbh->bz_column_info($class->DB_TABLE, 'creation_ts')) {
+ $params->{creation_ts} = $timestamp;
+ }
+ }
+ elsif ($class->isa('Bugzilla::Attachment')) {
+ if ($params->{ispatch} && !Bugzilla->user->first_patch_bug_id) {
+ Bugzilla->user->first_patch_bug_id($params->{bug}->id);
}
+ }
}
#
@@ -153,74 +158,70 @@ sub object_before_create {
#
BEGIN {
- *Bugzilla::User::comment_count = \&_comment_count;
- *Bugzilla::User::creation_ts = \&_creation_ts;
- *Bugzilla::User::update_comment_count = \&_update_comment_count;
- *Bugzilla::User::first_patch_bug_id = \&_first_patch_bug_id;
- *Bugzilla::User::is_new = \&_is_new;
- *Bugzilla::User::creation_age = \&_creation_age;
+ *Bugzilla::User::comment_count = \&_comment_count;
+ *Bugzilla::User::creation_ts = \&_creation_ts;
+ *Bugzilla::User::update_comment_count = \&_update_comment_count;
+ *Bugzilla::User::first_patch_bug_id = \&_first_patch_bug_id;
+ *Bugzilla::User::is_new = \&_is_new;
+ *Bugzilla::User::creation_age = \&_creation_age;
}
sub _comment_count { return $_[0]->{comment_count} }
-sub _creation_ts { return $_[0]->{creation_ts} }
+sub _creation_ts { return $_[0]->{creation_ts} }
sub _update_comment_count {
- my $self = shift;
- my $dbh = Bugzilla->dbh;
-
- # no need to update this counter for users which are no longer new
- return unless $self->is_new;
-
- my $id = $self->id;
- my ($count) = $dbh->selectrow_array(
- "SELECT COUNT(*) FROM longdescs WHERE who=?",
- undef, $id
- );
- return if $self->{comment_count} == $count;
- $dbh->do(
- 'UPDATE profiles SET comment_count=? WHERE userid=?',
- undef, $count, $id
- );
- Bugzilla->memcached->clear({ table => 'profiles', id => $id });
- $self->{comment_count} = $count;
+ my $self = shift;
+ my $dbh = Bugzilla->dbh;
+
+ # no need to update this counter for users which are no longer new
+ return unless $self->is_new;
+
+ my $id = $self->id;
+ my ($count)
+ = $dbh->selectrow_array("SELECT COUNT(*) FROM longdescs WHERE who=?",
+ undef, $id);
+ return if $self->{comment_count} == $count;
+ $dbh->do('UPDATE profiles SET comment_count=? WHERE userid=?',
+ undef, $count, $id);
+ Bugzilla->memcached->clear({table => 'profiles', id => $id});
+ $self->{comment_count} = $count;
}
sub _first_patch_bug_id {
- my ($self, $bug_id) = @_;
- return $self->{first_patch_bug_id} unless defined $bug_id;
-
- Bugzilla->dbh->do(
- 'UPDATE profiles SET first_patch_bug_id=? WHERE userid=?',
- undef, $bug_id, $self->id
- );
- Bugzilla->memcached->clear({ table => 'profiles', id => $self->id });
- $self->{first_patch_bug_id} = $bug_id;
+ my ($self, $bug_id) = @_;
+ return $self->{first_patch_bug_id} unless defined $bug_id;
+
+ Bugzilla->dbh->do('UPDATE profiles SET first_patch_bug_id=? WHERE userid=?',
+ undef, $bug_id, $self->id);
+ Bugzilla->memcached->clear({table => 'profiles', id => $self->id});
+ $self->{first_patch_bug_id} = $bug_id;
}
sub _is_new {
- my ($self) = @_;
-
- if (!exists $self->{is_new}) {
- if ($self->in_group('editbugs')) {
- $self->{is_new} = 0;
- } else {
- $self->{is_new} = ($self->comment_count <= COMMENT_COUNT)
- || ($self->creation_age <= PROFILE_AGE);
- }
+ my ($self) = @_;
+
+ if (!exists $self->{is_new}) {
+ if ($self->in_group('editbugs')) {
+ $self->{is_new} = 0;
+ }
+ else {
+ $self->{is_new} = ($self->comment_count <= COMMENT_COUNT)
+ || ($self->creation_age <= PROFILE_AGE);
}
+ }
- return $self->{is_new};
+ return $self->{is_new};
}
sub _creation_age {
- my ($self) = @_;
+ my ($self) = @_;
- if (!exists $self->{creation_age}) {
- my $age = sprintf("%.0f", (time() - str2time($self->creation_ts)) / 86400);
- $self->{creation_age} = $age;
- }
+ if (!exists $self->{creation_age}) {
+ my $age = sprintf("%.0f", (time() - str2time($self->creation_ts)) / 86400);
+ $self->{creation_age} = $age;
+ }
- return $self->{creation_age};
+ return $self->{creation_age};
}
#
@@ -228,49 +229,48 @@ sub _creation_age {
#
sub bug_end_of_create {
- Bugzilla->user->update_comment_count();
+ Bugzilla->user->update_comment_count();
}
sub bug_end_of_update {
- Bugzilla->user->update_comment_count();
+ Bugzilla->user->update_comment_count();
}
sub mailer_before_send {
- my ($self, $args) = @_;
- my $email = $args->{email};
-
- my ($bug_id) = ($email->header('Subject') =~ /^[^\d]+(\d+)/);
- my $changer_login = $email->header('X-Bugzilla-Who');
- my $changed_fields = $email->header('X-Bugzilla-Changed-Fields');
-
- if ($bug_id
- && $changer_login
- && $changed_fields =~ /attachments.created/)
+ my ($self, $args) = @_;
+ my $email = $args->{email};
+
+ my ($bug_id) = ($email->header('Subject') =~ /^[^\d]+(\d+)/);
+ my $changer_login = $email->header('X-Bugzilla-Who');
+ my $changed_fields = $email->header('X-Bugzilla-Changed-Fields');
+
+ if ($bug_id && $changer_login && $changed_fields =~ /attachments.created/) {
+ my $changer = Bugzilla::User->new({name => $changer_login});
+ if ( $changer
+ && $changer->first_patch_bug_id
+ && $changer->first_patch_bug_id == $bug_id)
{
- my $changer = Bugzilla::User->new({ name => $changer_login });
- if ($changer
- && $changer->first_patch_bug_id
- && $changer->first_patch_bug_id == $bug_id)
- {
- $email->header_set('X-Bugzilla-FirstPatch' => $bug_id);
- }
+ $email->header_set('X-Bugzilla-FirstPatch' => $bug_id);
}
+ }
}
sub webservice_user_get {
- my ($self, $args) = @_;
- my ($webservice, $params, $users) = @$args{qw(webservice params users)};
-
- return unless filter_wants($params, 'is_new');
-
- foreach my $user (@$users) {
- # Most of the time the hash values are XMLRPC::Data objects
- my $email = blessed $user->{'email'} ? $user->{'email'}->value : $user->{'email'};
- if ($email) {
- my $user_obj = Bugzilla::User->new({ name => $email });
- $user->{'is_new'} = $webservice->type('boolean', $user_obj->is_new ? 1 : 0);
- }
+ my ($self, $args) = @_;
+ my ($webservice, $params, $users) = @$args{qw(webservice params users)};
+
+ return unless filter_wants($params, 'is_new');
+
+ foreach my $user (@$users) {
+
+ # Most of the time the hash values are XMLRPC::Data objects
+ my $email
+ = blessed $user->{'email'} ? $user->{'email'}->value : $user->{'email'};
+ if ($email) {
+ my $user_obj = Bugzilla::User->new({name => $email});
+ $user->{'is_new'} = $webservice->type('boolean', $user_obj->is_new ? 1 : 0);
}
+ }
}
__PACKAGE__->NAME;
diff --git a/extensions/TrackingFlags/Config.pm b/extensions/TrackingFlags/Config.pm
index d0bc5ca20..6f5b9be39 100644
--- a/extensions/TrackingFlags/Config.pm
+++ b/extensions/TrackingFlags/Config.pm
@@ -13,15 +13,9 @@ use warnings;
use constant NAME => 'TrackingFlags';
-use constant REQUIRED_MODULES => [
- {
- package => 'JSON-XS',
- module => 'JSON::XS',
- version => '2.0'
- },
-];
+use constant REQUIRED_MODULES =>
+ [{package => 'JSON-XS', module => 'JSON::XS', version => '2.0'},];
-use constant OPTIONAL_MODULES => [
-];
+use constant OPTIONAL_MODULES => [];
__PACKAGE__->NAME;
diff --git a/extensions/TrackingFlags/Extension.pm b/extensions/TrackingFlags/Extension.pm
index 5f6715fc8..fea0240c8 100644
--- a/extensions/TrackingFlags/Extension.pm
+++ b/extensions/TrackingFlags/Extension.pm
@@ -34,823 +34,701 @@ our $VERSION = '1';
our @FLAG_CACHE;
BEGIN {
- *Bugzilla::tracking_flags = \&_tracking_flags;
- *Bugzilla::tracking_flag_names = \&_tracking_flag_names;
+ *Bugzilla::tracking_flags = \&_tracking_flags;
+ *Bugzilla::tracking_flag_names = \&_tracking_flag_names;
}
sub _tracking_flags {
- return Bugzilla::Extension::TrackingFlags::Flag->get_all();
+ return Bugzilla::Extension::TrackingFlags::Flag->get_all();
}
sub _tracking_flag_names {
- return Bugzilla::Extension::TrackingFlags::Flag->get_all_names();
+ return Bugzilla::Extension::TrackingFlags::Flag->get_all_names();
}
sub page_before_template {
- my ($self, $args) = @_;
- my $page = $args->{'page_id'};
- my $vars = $args->{'vars'};
-
- if ($page eq 'tracking_flags_admin_list.html') {
- Bugzilla->user->in_group('admin')
- || ThrowUserError('auth_failure',
- { group => 'admin',
- action => 'access',
- object => 'administrative_pages' });
- admin_list($vars);
-
- } elsif ($page eq 'tracking_flags_admin_edit.html') {
- Bugzilla->user->in_group('admin')
- || ThrowUserError('auth_failure',
- { group => 'admin',
- action => 'access',
- object => 'administrative_pages' });
- admin_edit($vars);
- }
+ my ($self, $args) = @_;
+ my $page = $args->{'page_id'};
+ my $vars = $args->{'vars'};
+
+ if ($page eq 'tracking_flags_admin_list.html') {
+ Bugzilla->user->in_group('admin')
+ || ThrowUserError('auth_failure',
+ {group => 'admin', action => 'access', object => 'administrative_pages'});
+ admin_list($vars);
+
+ }
+ elsif ($page eq 'tracking_flags_admin_edit.html') {
+ Bugzilla->user->in_group('admin')
+ || ThrowUserError('auth_failure',
+ {group => 'admin', action => 'access', object => 'administrative_pages'});
+ admin_edit($vars);
+ }
}
sub template_before_process {
- my ($self, $args) = @_;
- my $file = $args->{'file'};
- my $vars = $args->{'vars'};
-
- if ($file eq 'bug/create/create.html.tmpl') {
- my $flags = Bugzilla::Extension::TrackingFlags::Flag->match({
- product => $vars->{'product'}->name,
- enter_bug => 1,
- is_active => 1,
- });
-
- $vars->{tracking_flags} = $flags;
- $vars->{tracking_flags_json} = _flags_to_json($flags);
- $vars->{tracking_flag_types} = FLAG_TYPES;
- $vars->{tracking_flag_components} = _flags_to_components($flags, $vars->{product});
- $vars->{highest_status_firefox} = _get_highest_status_firefox($flags);
- }
- elsif ($file eq 'bug/edit.html.tmpl'|| $file eq 'bug/show.xml.tmpl'
- || $file eq 'email/bugmail.html.tmpl' || $file eq 'email/bugmail.txt.tmpl')
- {
- # note: bug/edit.html.tmpl doesn't support multiple bugs
- my $bug = exists $vars->{'bugs'} ? $vars->{'bugs'}[0] : $vars->{'bug'};
-
- if ($bug && !$bug->{error}) {
- my $flags = Bugzilla::Extension::TrackingFlags::Flag->match({
- product => $bug->product,
- component => $bug->component,
- bug_id => $bug->id,
- is_active => 1,
- });
-
- $vars->{tracking_flags} = $flags;
- $vars->{tracking_flags_json} = _flags_to_json($flags);
- }
+ my ($self, $args) = @_;
+ my $file = $args->{'file'};
+ my $vars = $args->{'vars'};
+
+ if ($file eq 'bug/create/create.html.tmpl') {
+ my $flags
+ = Bugzilla::Extension::TrackingFlags::Flag->match({
+ product => $vars->{'product'}->name, enter_bug => 1, is_active => 1,
+ });
+
+ $vars->{tracking_flags} = $flags;
+ $vars->{tracking_flags_json} = _flags_to_json($flags);
+ $vars->{tracking_flag_types} = FLAG_TYPES;
+ $vars->{tracking_flag_components}
+ = _flags_to_components($flags, $vars->{product});
+ $vars->{highest_status_firefox} = _get_highest_status_firefox($flags);
+ }
+ elsif ($file eq 'bug/edit.html.tmpl'
+ || $file eq 'bug/show.xml.tmpl'
+ || $file eq 'email/bugmail.html.tmpl'
+ || $file eq 'email/bugmail.txt.tmpl')
+ {
+ # note: bug/edit.html.tmpl doesn't support multiple bugs
+ my $bug = exists $vars->{'bugs'} ? $vars->{'bugs'}[0] : $vars->{'bug'};
+
+ if ($bug && !$bug->{error}) {
+ my $flags = Bugzilla::Extension::TrackingFlags::Flag->match({
+ product => $bug->product,
+ component => $bug->component,
+ bug_id => $bug->id,
+ is_active => 1,
+ });
- $vars->{'tracking_flag_types'} = FLAG_TYPES;
- }
- elsif ($file eq 'list/edit-multiple.html.tmpl' && $vars->{'one_product'}) {
- $vars->{'tracking_flags'} = Bugzilla::Extension::TrackingFlags::Flag->match({
- product => $vars->{'one_product'}->name,
- is_active => 1
- });
+ $vars->{tracking_flags} = $flags;
+ $vars->{tracking_flags_json} = _flags_to_json($flags);
}
+
+ $vars->{'tracking_flag_types'} = FLAG_TYPES;
+ }
+ elsif ($file eq 'list/edit-multiple.html.tmpl' && $vars->{'one_product'}) {
+ $vars->{'tracking_flags'}
+ = Bugzilla::Extension::TrackingFlags::Flag->match({
+ product => $vars->{'one_product'}->name, is_active => 1
+ });
+ }
}
sub _flags_to_json {
- my ($flags) = @_;
+ my ($flags) = @_;
- my $json = {
- flags => {},
- types => [],
- comments => {},
- };
+ my $json = {flags => {}, types => [], comments => {},};
- my %type_map = map { $_->{name} => $_ } @{ FLAG_TYPES() };
- foreach my $flag (@$flags) {
- my $flag_type = $flag->flag_type;
+ my %type_map = map { $_->{name} => $_ } @{FLAG_TYPES()};
+ foreach my $flag (@$flags) {
+ my $flag_type = $flag->flag_type;
- $json->{flags}->{$flag_type}->{$flag->name} = $flag->bug_flag->value;
+ $json->{flags}->{$flag_type}->{$flag->name} = $flag->bug_flag->value;
- if ($type_map{$flag_type}->{collapsed}
- && !grep { $_ eq $flag_type } @{ $json->{types} })
- {
- push @{ $json->{types} }, $flag_type;
- }
+ if ($type_map{$flag_type}->{collapsed} && !grep { $_ eq $flag_type }
+ @{$json->{types}})
+ {
+ push @{$json->{types}}, $flag_type;
+ }
- foreach my $value (@{ $flag->values }) {
- if (defined($value->comment) && $value->comment ne '') {
- $json->{comments}->{$flag->name}->{$value->value} = $value->comment;
- }
- }
+ foreach my $value (@{$flag->values}) {
+ if (defined($value->comment) && $value->comment ne '') {
+ $json->{comments}->{$flag->name}->{$value->value} = $value->comment;
+ }
}
+ }
- return encode_json($json);
+ return encode_json($json);
}
sub _flags_to_components {
- my ($flags, $product) = @_;
-
- # for each component, generate a list of visible tracking flags
- my $json = {};
- foreach my $component (@{ $product->components }) {
- next unless $component->is_active;
- foreach my $flag (@$flags) {
- foreach my $visibility (@{ $flag->visibility }) {
- if ($visibility->product_id == $product->id
- && (!$visibility->component_id || $visibility->component_id == $component->id))
- {
- $json->{$component->name} //= [];
- push @{ $json->{$component->name} }, $flag->name;
- }
- }
+ my ($flags, $product) = @_;
+
+ # for each component, generate a list of visible tracking flags
+ my $json = {};
+ foreach my $component (@{$product->components}) {
+ next unless $component->is_active;
+ foreach my $flag (@$flags) {
+ foreach my $visibility (@{$flag->visibility}) {
+ if ($visibility->product_id == $product->id
+ && (!$visibility->component_id || $visibility->component_id == $component->id))
+ {
+ $json->{$component->name} //= [];
+ push @{$json->{$component->name}}, $flag->name;
}
+ }
}
- return encode_json($json);
+ }
+ return encode_json($json);
}
sub _get_highest_status_firefox {
- my ($flags) = @_;
-
- my @status_flags =
- sort { $b <=> $a }
- map { $_->name =~ /(\d+)$/; $1 }
- grep { $_->is_active && $_->name =~ /^cf_status_firefox\d/ }
- @$flags;
- return @status_flags ? $status_flags[0] : undef;
+ my ($flags) = @_;
+
+ my @status_flags
+ = sort { $b <=> $a }
+ map { $_->name =~ /(\d+)$/; $1 }
+ grep { $_->is_active && $_->name =~ /^cf_status_firefox\d/ } @$flags;
+ return @status_flags ? $status_flags[0] : undef;
}
sub db_schema_abstract_schema {
- my ($self, $args) = @_;
- $args->{'schema'}->{'tracking_flags'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- field_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'fielddefs',
- COLUMN => 'id',
- DELETE => 'CASCADE'
- }
- },
- name => {
- TYPE => 'varchar(64)',
- NOTNULL => 1,
- },
- description => {
- TYPE => 'varchar(64)',
- NOTNULL => 1,
- },
- type => {
- TYPE => 'varchar(64)',
- NOTNULL => 1,
- },
- sortkey => {
- TYPE => 'INT2',
- NOTNULL => 1,
- DEFAULT => '0',
- },
- enter_bug => {
- TYPE => 'BOOLEAN',
- NOTNULL => 1,
- DEFAULT => 'TRUE',
- },
- is_active => {
- TYPE => 'BOOLEAN',
- NOTNULL => 1,
- DEFAULT => 'TRUE',
- },
- ],
- INDEXES => [
- tracking_flags_idx => {
- FIELDS => ['name'],
- TYPE => 'UNIQUE',
- },
- ],
- };
- $args->{'schema'}->{'tracking_flags_values'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- tracking_flag_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'tracking_flags',
- COLUMN => 'id',
- DELETE => 'CASCADE',
- },
- },
- setter_group_id => {
- TYPE => 'INT3',
- NOTNULL => 0,
- REFERENCES => {
- TABLE => 'groups',
- COLUMN => 'id',
- DELETE => 'SET NULL',
- },
- },
- value => {
- TYPE => 'varchar(64)',
- NOTNULL => 1,
- },
- sortkey => {
- TYPE => 'INT2',
- NOTNULL => 1,
- DEFAULT => '0',
- },
- enter_bug => {
- TYPE => 'BOOLEAN',
- NOTNULL => 1,
- DEFAULT => 'TRUE',
- },
- is_active => {
- TYPE => 'BOOLEAN',
- NOTNULL => 1,
- DEFAULT => 'TRUE',
- },
- comment => {
- TYPE => 'TEXT',
- NOTNULL => 0,
- },
- ],
- INDEXES => [
- tracking_flags_values_idx => {
- FIELDS => ['tracking_flag_id', 'value'],
- TYPE => 'UNIQUE',
- },
- ],
- };
- $args->{'schema'}->{'tracking_flags_bugs'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- tracking_flag_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'tracking_flags',
- COLUMN => 'id',
- DELETE => 'CASCADE',
- },
- },
- bug_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'bugs',
- COLUMN => 'bug_id',
- DELETE => 'CASCADE',
- },
- },
- value => {
- TYPE => 'varchar(64)',
- NOTNULL => 1,
- },
- ],
- INDEXES => [
- tracking_flags_bugs_idx => {
- FIELDS => ['tracking_flag_id', 'bug_id'],
- TYPE => 'UNIQUE',
- },
- ],
- };
- $args->{'schema'}->{'tracking_flags_visibility'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- tracking_flag_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'tracking_flags',
- COLUMN => 'id',
- DELETE => 'CASCADE',
- },
- },
- product_id => {
- TYPE => 'INT2',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'products',
- COLUMN => 'id',
- DELETE => 'CASCADE',
- },
- },
- component_id => {
- TYPE => 'INT2',
- NOTNULL => 0,
- REFERENCES => {
- TABLE => 'components',
- COLUMN => 'id',
- DELETE => 'CASCADE',
- },
- },
- ],
- INDEXES => [
- tracking_flags_visibility_idx => {
- FIELDS => ['tracking_flag_id', 'product_id', 'component_id'],
- TYPE => 'UNIQUE',
- },
- ],
- };
+ my ($self, $args) = @_;
+ $args->{'schema'}->{'tracking_flags'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ field_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'fielddefs', COLUMN => 'id', DELETE => 'CASCADE'}
+ },
+ name => {TYPE => 'varchar(64)', NOTNULL => 1,},
+ description => {TYPE => 'varchar(64)', NOTNULL => 1,},
+ type => {TYPE => 'varchar(64)', NOTNULL => 1,},
+ sortkey => {TYPE => 'INT2', NOTNULL => 1, DEFAULT => '0',},
+ enter_bug => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'TRUE',},
+ is_active => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'TRUE',},
+ ],
+ INDEXES => [tracking_flags_idx => {FIELDS => ['name'], TYPE => 'UNIQUE',},],
+ };
+ $args->{'schema'}->{'tracking_flags_values'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ tracking_flag_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'tracking_flags', COLUMN => 'id', DELETE => 'CASCADE',},
+ },
+ setter_group_id => {
+ TYPE => 'INT3',
+ NOTNULL => 0,
+ REFERENCES => {TABLE => 'groups', COLUMN => 'id', DELETE => 'SET NULL',},
+ },
+ value => {TYPE => 'varchar(64)', NOTNULL => 1,},
+ sortkey => {TYPE => 'INT2', NOTNULL => 1, DEFAULT => '0',},
+ enter_bug => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'TRUE',},
+ is_active => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'TRUE',},
+ comment => {TYPE => 'TEXT', NOTNULL => 0,},
+ ],
+ INDEXES => [
+ tracking_flags_values_idx =>
+ {FIELDS => ['tracking_flag_id', 'value'], TYPE => 'UNIQUE',},
+ ],
+ };
+ $args->{'schema'}->{'tracking_flags_bugs'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ tracking_flag_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'tracking_flags', COLUMN => 'id', DELETE => 'CASCADE',},
+ },
+ bug_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'bugs', COLUMN => 'bug_id', DELETE => 'CASCADE',},
+ },
+ value => {TYPE => 'varchar(64)', NOTNULL => 1,},
+ ],
+ INDEXES => [
+ tracking_flags_bugs_idx =>
+ {FIELDS => ['tracking_flag_id', 'bug_id'], TYPE => 'UNIQUE',},
+ ],
+ };
+ $args->{'schema'}->{'tracking_flags_visibility'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ tracking_flag_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'tracking_flags', COLUMN => 'id', DELETE => 'CASCADE',},
+ },
+ product_id => {
+ TYPE => 'INT2',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'products', COLUMN => 'id', DELETE => 'CASCADE',},
+ },
+ component_id => {
+ TYPE => 'INT2',
+ NOTNULL => 0,
+ REFERENCES => {TABLE => 'components', COLUMN => 'id', DELETE => 'CASCADE',},
+ },
+ ],
+ INDEXES => [
+ tracking_flags_visibility_idx => {
+ FIELDS => ['tracking_flag_id', 'product_id', 'component_id'],
+ TYPE => 'UNIQUE',
+ },
+ ],
+ };
}
sub install_update_db {
- my $dbh = Bugzilla->dbh;
-
- my $fk = $dbh->bz_fk_info('tracking_flags', 'field_id');
- if ($fk and !defined $fk->{DELETE}) {
- $fk->{DELETE} = 'CASCADE';
- $dbh->bz_alter_fk('tracking_flags', 'field_id', $fk);
- }
-
- $dbh->bz_add_column(
- 'tracking_flags',
- 'enter_bug',
- {
- TYPE => 'BOOLEAN',
- NOTNULL => 1,
- DEFAULT => 'TRUE',
- }
- );
- $dbh->bz_add_column(
- 'tracking_flags_values',
- 'comment',
- {
- TYPE => 'TEXT',
- NOTNULL => 0,
- },
- );
+ my $dbh = Bugzilla->dbh;
+
+ my $fk = $dbh->bz_fk_info('tracking_flags', 'field_id');
+ if ($fk and !defined $fk->{DELETE}) {
+ $fk->{DELETE} = 'CASCADE';
+ $dbh->bz_alter_fk('tracking_flags', 'field_id', $fk);
+ }
+
+ $dbh->bz_add_column('tracking_flags', 'enter_bug',
+ {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'TRUE',});
+ $dbh->bz_add_column('tracking_flags_values', 'comment',
+ {TYPE => 'TEXT', NOTNULL => 0,},
+ );
}
sub install_filesystem {
- my ($self, $args) = @_;
- my $files = $args->{files};
- my $extensions_dir = bz_locations()->{extensionsdir};
- $files->{"$extensions_dir/TrackingFlags/bin/bulk_flag_clear.pl"} = {
- perms => Bugzilla::Install::Filesystem::OWNER_EXECUTE
- };
+ my ($self, $args) = @_;
+ my $files = $args->{files};
+ my $extensions_dir = bz_locations()->{extensionsdir};
+ $files->{"$extensions_dir/TrackingFlags/bin/bulk_flag_clear.pl"}
+ = {perms => Bugzilla::Install::Filesystem::OWNER_EXECUTE};
}
sub active_custom_fields {
- my ($self, $args) = @_;
- my $fields = $args->{'fields'};
- my $params = $args->{'params'};
- my $product = $params->{'product'};
- my $component = $params->{'component'};
-
- return if $params->{skip_extensions};
- # Create a hash of current fields based on field names
- my %field_hash = map { $_->name => $_ } @$$fields;
-
- my @tracking_flags;
- if ($product) {
- $params->{'product_id'} = $product->id;
- $params->{'component_id'} = $component->id if $component;
- $params->{'is_active'} = 1;
- @tracking_flags = @{ Bugzilla::Extension::TrackingFlags::Flag->match($params) };
- }
- else {
- @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all;
- }
-
- # Add tracking flags to fields hash replacing if already exists for our
- # flag object instead of the usual Field.pm object
- foreach my $flag (@tracking_flags) {
- $field_hash{$flag->name} = $flag;
- }
-
- @$$fields = sort { $a->sortkey <=> $b->sortkey } values %field_hash;
+ my ($self, $args) = @_;
+ my $fields = $args->{'fields'};
+ my $params = $args->{'params'};
+ my $product = $params->{'product'};
+ my $component = $params->{'component'};
+
+ return if $params->{skip_extensions};
+
+ # Create a hash of current fields based on field names
+ my %field_hash = map { $_->name => $_ } @$$fields;
+
+ my @tracking_flags;
+ if ($product) {
+ $params->{'product_id'} = $product->id;
+ $params->{'component_id'} = $component->id if $component;
+ $params->{'is_active'} = 1;
+ @tracking_flags = @{Bugzilla::Extension::TrackingFlags::Flag->match($params)};
+ }
+ else {
+ @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all;
+ }
+
+ # Add tracking flags to fields hash replacing if already exists for our
+ # flag object instead of the usual Field.pm object
+ foreach my $flag (@tracking_flags) {
+ $field_hash{$flag->name} = $flag;
+ }
+
+ @$$fields = sort { $a->sortkey <=> $b->sortkey } values %field_hash;
}
sub buglist_columns {
- my ($self, $args) = @_;
- my $columns = $args->{columns};
- my $dbh = Bugzilla->dbh;
- my @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all;
- foreach my $flag (@tracking_flags) {
- $columns->{$flag->name} = {
- name => "COALESCE(map_" . $flag->name . ".value, '---')",
- title => $flag->description
- };
- }
+ my ($self, $args) = @_;
+ my $columns = $args->{columns};
+ my $dbh = Bugzilla->dbh;
+ my @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all;
+ foreach my $flag (@tracking_flags) {
+ $columns->{$flag->name} = {
+ name => "COALESCE(map_" . $flag->name . ".value, '---')",
+ title => $flag->description
+ };
+ }
}
sub buglist_column_joins {
- my ($self, $args) = @_;
- # if there are elements in the tracking_flags array, then they have been
- # removed from the query, so we mustn't generate joins
- return if scalar @{ $args->{search}->{tracking_flags} };
-
- my $column_joins = $args->{'column_joins'};
- my @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all;
- foreach my $flag (@tracking_flags) {
- $column_joins->{$flag->name} = {
- as => 'map_' . $flag->name,
- table => 'tracking_flags_bugs',
- extra => [ 'map_' . $flag->name . '.tracking_flag_id = ' . $flag->flag_id ]
- };
- }
+ my ($self, $args) = @_;
+
+ # if there are elements in the tracking_flags array, then they have been
+ # removed from the query, so we mustn't generate joins
+ return if scalar @{$args->{search}->{tracking_flags}};
+
+ my $column_joins = $args->{'column_joins'};
+ my @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all;
+ foreach my $flag (@tracking_flags) {
+ $column_joins->{$flag->name} = {
+ as => 'map_' . $flag->name,
+ table => 'tracking_flags_bugs',
+ extra => ['map_' . $flag->name . '.tracking_flag_id = ' . $flag->flag_id]
+ };
+ }
}
sub bug_create_cf_accessors {
- my ($self, $args) = @_;
- # Create the custom accessors for the flag values
- my @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all;
- foreach my $flag (@tracking_flags) {
- my $flag_name = $flag->name;
- if (!Bugzilla::Bug->can($flag_name)) {
- my $accessor = sub {
- my $self = shift;
- return $self->{$flag_name} if defined $self->{$flag_name};
- if (!exists $self->{'_tf_bug_values_preloaded'}) {
- # preload all values currently set for this bug
- my $bug_values
- = Bugzilla::Extension::TrackingFlags::Flag::Bug->match({ bug_id => $self->id });
- foreach my $value (@$bug_values) {
- $self->{$value->tracking_flag->name} = $value->value;
- }
- $self->{'_tf_bug_values_preloaded'} = 1;
- }
- return $self->{$flag_name} ||= '---';
- };
- no strict 'refs';
- *{"Bugzilla::Bug::$flag_name"} = $accessor;
- }
- if (!Bugzilla::Bug->can("set_$flag_name")) {
- my $setter = sub {
- my ($self, $value) = @_;
- $value = ref($value) eq 'ARRAY'
- ? $value->[0]
- : $value;
- $self->set($flag_name, $value);
- };
- no strict 'refs';
- *{"Bugzilla::Bug::set_$flag_name"} = $setter;
+ my ($self, $args) = @_;
+
+ # Create the custom accessors for the flag values
+ my @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all;
+ foreach my $flag (@tracking_flags) {
+ my $flag_name = $flag->name;
+ if (!Bugzilla::Bug->can($flag_name)) {
+ my $accessor = sub {
+ my $self = shift;
+ return $self->{$flag_name} if defined $self->{$flag_name};
+ if (!exists $self->{'_tf_bug_values_preloaded'}) {
+
+ # preload all values currently set for this bug
+ my $bug_values
+ = Bugzilla::Extension::TrackingFlags::Flag::Bug->match({bug_id => $self->id});
+ foreach my $value (@$bug_values) {
+ $self->{$value->tracking_flag->name} = $value->value;
+ }
+ $self->{'_tf_bug_values_preloaded'} = 1;
}
+ return $self->{$flag_name} ||= '---';
+ };
+ no strict 'refs';
+ *{"Bugzilla::Bug::$flag_name"} = $accessor;
+ }
+ if (!Bugzilla::Bug->can("set_$flag_name")) {
+ my $setter = sub {
+ my ($self, $value) = @_;
+ $value = ref($value) eq 'ARRAY' ? $value->[0] : $value;
+ $self->set($flag_name, $value);
+ };
+ no strict 'refs';
+ *{"Bugzilla::Bug::set_$flag_name"} = $setter;
}
+ }
}
sub bug_editable_bug_fields {
- my ($self, $args) = @_;
- my $fields = $args->{'fields'};
- my @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all;
- foreach my $flag (@tracking_flags) {
- push(@$fields, $flag->name);
- }
+ my ($self, $args) = @_;
+ my $fields = $args->{'fields'};
+ my @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all;
+ foreach my $flag (@tracking_flags) {
+ push(@$fields, $flag->name);
+ }
}
sub search_operator_field_override {
- my ($self, $args) = @_;
- my $operators = $args->{'operators'};
- my @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all;
- foreach my $flag (@tracking_flags) {
- $operators->{$flag->name} = {
- _non_changed => sub {
- _tracking_flags_search_nonchanged($flag->flag_id, @_)
- }
- };
- }
+ my ($self, $args) = @_;
+ my $operators = $args->{'operators'};
+ my @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all;
+ foreach my $flag (@tracking_flags) {
+ $operators->{$flag->name} = {
+ _non_changed => sub {
+ _tracking_flags_search_nonchanged($flag->flag_id, @_);
+ }
+ };
+ }
}
sub _tracking_flags_search_nonchanged {
- my ($flag_id, $search, $args) = @_;
- my ($bugs_table, $chart_id, $joins, $value, $operator) =
- @$args{qw(bugs_table chart_id joins value operator)};
- my $dbh = Bugzilla->dbh;
-
- return if ($operator =~ m/^changed/);
-
- my $bugs_alias = "tracking_flags_bugs_$chart_id";
- my $flags_alias = "tracking_flags_$chart_id";
-
- my $bugs_join = {
- table => 'tracking_flags_bugs',
- as => $bugs_alias,
- from => $bugs_table . ".bug_id",
- to => "bug_id",
- extra => [$bugs_alias . ".tracking_flag_id = $flag_id"]
- };
-
- push(@$joins, $bugs_join);
-
- if ($operator eq 'isempty' or $operator eq 'isnotempty') {
- $args->{'full_field'} = "$bugs_alias.value";
- }
- else {
- $args->{'full_field'} = "COALESCE($bugs_alias.value, '---')";
- }
+ my ($flag_id, $search, $args) = @_;
+ my ($bugs_table, $chart_id, $joins, $value, $operator)
+ = @$args{qw(bugs_table chart_id joins value operator)};
+ my $dbh = Bugzilla->dbh;
+
+ return if ($operator =~ m/^changed/);
+
+ my $bugs_alias = "tracking_flags_bugs_$chart_id";
+ my $flags_alias = "tracking_flags_$chart_id";
+
+ my $bugs_join = {
+ table => 'tracking_flags_bugs',
+ as => $bugs_alias,
+ from => $bugs_table . ".bug_id",
+ to => "bug_id",
+ extra => [$bugs_alias . ".tracking_flag_id = $flag_id"]
+ };
+
+ push(@$joins, $bugs_join);
+
+ if ($operator eq 'isempty' or $operator eq 'isnotempty') {
+ $args->{'full_field'} = "$bugs_alias.value";
+ }
+ else {
+ $args->{'full_field'} = "COALESCE($bugs_alias.value, '---')";
+ }
}
sub request_cleanup {
- foreach my $flag (@FLAG_CACHE) {
- my $bug_flag = delete $flag->{bug_flag};
- if ($bug_flag) {
- delete $bug_flag->{bug};
- delete $bug_flag->{tracking_flag};
- }
- foreach my $value (@{ $flag->{values} }) {
- delete $value->{tracking_flag};
- }
+ foreach my $flag (@FLAG_CACHE) {
+ my $bug_flag = delete $flag->{bug_flag};
+ if ($bug_flag) {
+ delete $bug_flag->{bug};
+ delete $bug_flag->{tracking_flag};
+ }
+ foreach my $value (@{$flag->{values}}) {
+ delete $value->{tracking_flag};
}
- @FLAG_CACHE = ();
+ }
+ @FLAG_CACHE = ();
}
sub bug_end_of_create {
- my ($self, $args) = @_;
- my $bug = $args->{'bug'};
- my $timestamp = $args->{'timestamp'};
- my $user = Bugzilla->user;
+ my ($self, $args) = @_;
+ my $bug = $args->{'bug'};
+ my $timestamp = $args->{'timestamp'};
+ my $user = Bugzilla->user;
- my $params = Bugzilla->request_cache->{tracking_flags_create_params};
- return if !$params;
+ my $params = Bugzilla->request_cache->{tracking_flags_create_params};
+ return if !$params;
- my $tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->match({
- product => $bug->product,
- component => $bug->component,
- is_active => 1,
+ my $tracking_flags
+ = Bugzilla::Extension::TrackingFlags::Flag->match({
+ product => $bug->product, component => $bug->component, is_active => 1,
});
- foreach my $flag (@$tracking_flags) {
- next if !$params->{$flag->name};
- foreach my $value (@{$flag->values}) {
- next if $value->value ne $params->{$flag->name};
- next if $value->value eq '---'; # do not insert if value is '---', same as empty
- if (!$flag->can_set_value($value->value)) {
- ThrowUserError('tracking_flags_change_denied',
- { flag => $flag, value => $value });
- }
- Bugzilla::Extension::TrackingFlags::Flag::Bug->create({
- tracking_flag_id => $flag->flag_id,
- bug_id => $bug->id,
- value => $value->value,
- });
- # Add the name/value pair to the bug object
- $bug->{$flag->name} = $value->value;
- }
+ foreach my $flag (@$tracking_flags) {
+ next if !$params->{$flag->name};
+ foreach my $value (@{$flag->values}) {
+ next if $value->value ne $params->{$flag->name};
+ next if $value->value eq '---'; # do not insert if value is '---', same as empty
+ if (!$flag->can_set_value($value->value)) {
+ ThrowUserError('tracking_flags_change_denied',
+ {flag => $flag, value => $value});
+ }
+ Bugzilla::Extension::TrackingFlags::Flag::Bug->create({
+ tracking_flag_id => $flag->flag_id,
+ bug_id => $bug->id,
+ value => $value->value,
+ });
+
+ # Add the name/value pair to the bug object
+ $bug->{$flag->name} = $value->value;
}
+ }
}
sub object_end_of_set_all {
- my ($self, $args) = @_;
- my $object = $args->{object};
- my $params = $args->{params};
+ my ($self, $args) = @_;
+ my $object = $args->{object};
+ my $params = $args->{params};
- return unless $object->isa('Bugzilla::Bug');
+ return unless $object->isa('Bugzilla::Bug');
- # Do not filter by product/component as we may be changing those
- my $tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->match({
- bug_id => $object->id,
- is_active => 1,
+ # Do not filter by product/component as we may be changing those
+ my $tracking_flags
+ = Bugzilla::Extension::TrackingFlags::Flag->match({
+ bug_id => $object->id, is_active => 1,
});
- foreach my $flag (@$tracking_flags) {
- my $flag_name = $flag->name;
- if (exists $params->{$flag_name}) {
- my $value = ref($params->{$flag_name}) eq 'ARRAY'
- ? $params->{$flag_name}->[0]
- : $params->{$flag_name};
- $object->set($flag_name, $value);
- }
+ foreach my $flag (@$tracking_flags) {
+ my $flag_name = $flag->name;
+ if (exists $params->{$flag_name}) {
+ my $value
+ = ref($params->{$flag_name}) eq 'ARRAY'
+ ? $params->{$flag_name}->[0]
+ : $params->{$flag_name};
+ $object->set($flag_name, $value);
}
+ }
}
sub bug_check_can_change_field {
- my ($self, $args) = @_;
- my ($bug, $field, $old_value, $new_value, $priv_results)
- = @$args{qw(bug field old_value new_value priv_results)};
-
- return if $field !~ /^cf_/ or $old_value eq $new_value;
- return unless my $flag = Bugzilla::Extension::TrackingFlags::Flag->new({ name => $field });
-
- if ($flag->can_set_value($new_value)) {
- push @$priv_results, PRIVILEGES_REQUIRED_NONE;
- }
- else {
- push @$priv_results, PRIVILEGES_REQUIRED_EMPOWERED;
- }
+ my ($self, $args) = @_;
+ my ($bug, $field, $old_value, $new_value, $priv_results)
+ = @$args{qw(bug field old_value new_value priv_results)};
+
+ return if $field !~ /^cf_/ or $old_value eq $new_value;
+ return
+ unless my $flag
+ = Bugzilla::Extension::TrackingFlags::Flag->new({name => $field});
+
+ if ($flag->can_set_value($new_value)) {
+ push @$priv_results, PRIVILEGES_REQUIRED_NONE;
+ }
+ else {
+ push @$priv_results, PRIVILEGES_REQUIRED_EMPOWERED;
+ }
}
sub bug_end_of_update {
- my ($self, $args) = @_;
- my ($bug, $old_bug, $timestamp, $changes)
- = @$args{qw(bug old_bug timestamp changes)};
- my $user = Bugzilla->user;
-
- # Do not filter by product/component as we may be changing those
- my $tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->match({
- bug_id => $bug->id,
- is_active => 1,
+ my ($self, $args) = @_;
+ my ($bug, $old_bug, $timestamp, $changes)
+ = @$args{qw(bug old_bug timestamp changes)};
+ my $user = Bugzilla->user;
+
+ # Do not filter by product/component as we may be changing those
+ my $tracking_flags
+ = Bugzilla::Extension::TrackingFlags::Flag->match({
+ bug_id => $bug->id, is_active => 1,
});
- my $product_id = $bug->product_id;
- my $component_id = $bug->component_id;
- my $is_visible = sub {
- $_->product_id == $product_id && (!$_->component_id || $_->component_id == $component_id);
- };
+ my $product_id = $bug->product_id;
+ my $component_id = $bug->component_id;
+ my $is_visible = sub {
+ $_->product_id == $product_id
+ && (!$_->component_id || $_->component_id == $component_id);
+ };
+
+ my (@flag_changes);
+ foreach my $flag (@$tracking_flags) {
+ my $flag_name = $flag->name;
+ my $new_value = $bug->$flag_name;
+ my $old_value = $old_bug->$flag_name;
+
+ if ($flag->bug_flag->id) {
+ my $visibility = $flag->visibility;
+ if (none { $is_visible->() } @$visibility) {
+ push(@flag_changes, {flag => $flag, added => '---', removed => $new_value});
+ next;
+ }
+ }
- my (@flag_changes);
- foreach my $flag (@$tracking_flags) {
- my $flag_name = $flag->name;
- my $new_value = $bug->$flag_name;
- my $old_value = $old_bug->$flag_name;
-
- if ($flag->bug_flag->id) {
- my $visibility = $flag->visibility;
- if (none { $is_visible->() } @$visibility) {
- push(@flag_changes, { flag => $flag,
- added => '---',
- removed => $new_value });
- next;
- }
- }
+ if ($new_value ne $old_value) {
- if ($new_value ne $old_value) {
- # Do not allow if the user cannot set the old value or the new value
- if (!$flag->can_set_value($new_value)) {
- ThrowUserError('tracking_flags_change_denied',
- { flag => $flag, value => $new_value });
- }
- push(@flag_changes, { flag => $flag,
- added => $new_value,
- removed => $old_value });
- }
+ # Do not allow if the user cannot set the old value or the new value
+ if (!$flag->can_set_value($new_value)) {
+ ThrowUserError('tracking_flags_change_denied',
+ {flag => $flag, value => $new_value});
+ }
+ push(@flag_changes,
+ {flag => $flag, added => $new_value, removed => $old_value});
}
+ }
- foreach my $change (@flag_changes) {
- my $flag = $change->{'flag'};
- my $added = $change->{'added'};
- my $removed = $change->{'removed'};
+ foreach my $change (@flag_changes) {
+ my $flag = $change->{'flag'};
+ my $added = $change->{'added'};
+ my $removed = $change->{'removed'};
- if ($added eq '---') {
- $flag->bug_flag->remove_from_db();
- }
- elsif ($removed eq '---') {
- Bugzilla::Extension::TrackingFlags::Flag::Bug->create({
- tracking_flag_id => $flag->flag_id,
- bug_id => $bug->id,
- value => $added,
- });
- }
- else {
- $flag->bug_flag->set_value($added);
- $flag->bug_flag->update($timestamp);
- }
+ if ($added eq '---') {
+ $flag->bug_flag->remove_from_db();
+ }
+ elsif ($removed eq '---') {
+ Bugzilla::Extension::TrackingFlags::Flag::Bug->create({
+ tracking_flag_id => $flag->flag_id, bug_id => $bug->id, value => $added,
+ });
+ }
+ else {
+ $flag->bug_flag->set_value($added);
+ $flag->bug_flag->update($timestamp);
+ }
- $changes->{$flag->name} = [ $removed, $added ];
- LogActivityEntry($bug->id, $flag->name, $removed, $added, $user->id, $timestamp);
+ $changes->{$flag->name} = [$removed, $added];
+ LogActivityEntry($bug->id, $flag->name, $removed, $added, $user->id,
+ $timestamp);
- # Update the name/value pair in the bug object
- $bug->{$flag->name} = $added;
- }
+ # Update the name/value pair in the bug object
+ $bug->{$flag->name} = $added;
+ }
}
sub bug_end_of_create_validators {
- my ($self, $args) = @_;
- my $params = $args->{params};
-
- # We need to stash away any params that are setting/updating tracking
- # flags early on. Otherwise set_all or insert_create_data will complain.
- my @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all;
- my $cache = Bugzilla->request_cache->{tracking_flags_create_params} ||= {};
- foreach my $flag (@tracking_flags) {
- my $flag_name = $flag->name;
- if (defined $params->{$flag_name}) {
- $cache->{$flag_name} = delete $params->{$flag_name};
- }
+ my ($self, $args) = @_;
+ my $params = $args->{params};
+
+ # We need to stash away any params that are setting/updating tracking
+ # flags early on. Otherwise set_all or insert_create_data will complain.
+ my @tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->get_all;
+ my $cache = Bugzilla->request_cache->{tracking_flags_create_params} ||= {};
+ foreach my $flag (@tracking_flags) {
+ my $flag_name = $flag->name;
+ if (defined $params->{$flag_name}) {
+ $cache->{$flag_name} = delete $params->{$flag_name};
}
+ }
}
sub mailer_before_send {
- my ($self, $args) = @_;
- my $email = $args->{email};
+ my ($self, $args) = @_;
+ my $email = $args->{email};
- # Add X-Bugzilla-Tracking header or add to it
- # if already exists
- if ($email->header('X-Bugzilla-ID')) {
- my $bug_id = $email->header('X-Bugzilla-ID');
+ # Add X-Bugzilla-Tracking header or add to it
+ # if already exists
+ if ($email->header('X-Bugzilla-ID')) {
+ my $bug_id = $email->header('X-Bugzilla-ID');
- my $tracking_flags
- = Bugzilla::Extension::TrackingFlags::Flag->match({ bug_id => $bug_id });
+ my $tracking_flags
+ = Bugzilla::Extension::TrackingFlags::Flag->match({bug_id => $bug_id});
- my @set_values = ();
- foreach my $flag (@$tracking_flags) {
- next if $flag->bug_flag->value eq '---';
- push(@set_values, $flag->description . ":" . $flag->bug_flag->value);
- }
+ my @set_values = ();
+ foreach my $flag (@$tracking_flags) {
+ next if $flag->bug_flag->value eq '---';
+ push(@set_values, $flag->description . ":" . $flag->bug_flag->value);
+ }
- if (@set_values) {
- my $set_values_string = join(' ', @set_values);
- if ($email->header('X-Bugzilla-Tracking')) {
- $set_values_string = $email->header('X-Bugzilla-Tracking') .
- " " . $set_values_string;
- }
- $email->header_set('X-Bugzilla-Tracking' => $set_values_string);
- }
+ if (@set_values) {
+ my $set_values_string = join(' ', @set_values);
+ if ($email->header('X-Bugzilla-Tracking')) {
+ $set_values_string
+ = $email->header('X-Bugzilla-Tracking') . " " . $set_values_string;
+ }
+ $email->header_set('X-Bugzilla-Tracking' => $set_values_string);
}
+ }
}
# Purpose: generically handle generating pretty blocking/status "flags" from
# custom field names.
sub quicksearch_map {
- my ($self, $args) = @_;
- my $map = $args->{'map'};
-
- foreach my $name (keys %$map) {
- if ($name =~ /^cf_(blocking|tracking|status)_([a-z]+)?(\d+)?$/) {
- my $type = $1;
- my $product = $2;
- my $version = $3;
-
- if ($version) {
- $version = join('.', split(//, $version));
- }
-
- my $pretty_name = $type;
- if ($product) {
- $pretty_name .= "-" . $product;
- }
- if ($version) {
- $pretty_name .= $version;
- }
-
- $map->{$pretty_name} = $name;
- }
+ my ($self, $args) = @_;
+ my $map = $args->{'map'};
+
+ foreach my $name (keys %$map) {
+ if ($name =~ /^cf_(blocking|tracking|status)_([a-z]+)?(\d+)?$/) {
+ my $type = $1;
+ my $product = $2;
+ my $version = $3;
+
+ if ($version) {
+ $version = join('.', split(//, $version));
+ }
+
+ my $pretty_name = $type;
+ if ($product) {
+ $pretty_name .= "-" . $product;
+ }
+ if ($version) {
+ $pretty_name .= $version;
+ }
+
+ $map->{$pretty_name} = $name;
}
+ }
}
sub reorg_move_component {
- my ($self, $args) = @_;
- my $new_product = $args->{new_product};
- my $component = $args->{component};
-
- Bugzilla->dbh->do(
- "UPDATE tracking_flags_visibility SET product_id=? WHERE component_id=?",
- undef,
- $new_product->id, $component->id,
- );
+ my ($self, $args) = @_;
+ my $new_product = $args->{new_product};
+ my $component = $args->{component};
+
+ Bugzilla->dbh->do(
+ "UPDATE tracking_flags_visibility SET product_id=? WHERE component_id=?",
+ undef, $new_product->id, $component->id,);
}
sub sanitycheck_check {
- my ($self, $args) = @_;
- my $status = $args->{status};
+ my ($self, $args) = @_;
+ my $status = $args->{status};
- $status->('tracking_flags_check');
+ $status->('tracking_flags_check');
- my ($count) = Bugzilla->dbh->selectrow_array("
+ my ($count) = Bugzilla->dbh->selectrow_array("
SELECT COUNT(*)
FROM tracking_flags_visibility
INNER JOIN components ON components.id = tracking_flags_visibility.component_id
WHERE tracking_flags_visibility.product_id <> components.product_id
");
- if ($count) {
- $status->('tracking_flags_alert', undef, 'alert');
- $status->('tracking_flags_repair');
- }
+ if ($count) {
+ $status->('tracking_flags_alert', undef, 'alert');
+ $status->('tracking_flags_repair');
+ }
}
sub sanitycheck_repair {
- my ($self, $args) = @_;
- return unless Bugzilla->cgi->param('tracking_flags_repair');
+ my ($self, $args) = @_;
+ return unless Bugzilla->cgi->param('tracking_flags_repair');
- my $status = $args->{'status'};
- my $dbh = Bugzilla->dbh;
- $status->('tracking_flags_repairing');
+ my $status = $args->{'status'};
+ my $dbh = Bugzilla->dbh;
+ $status->('tracking_flags_repairing');
- my $rows = $dbh->selectall_arrayref("
+ my $rows = $dbh->selectall_arrayref("
SELECT DISTINCT tracking_flags_visibility.product_id AS bad_product_id,
components.product_id AS good_product_id,
tracking_flags_visibility.component_id
FROM tracking_flags_visibility
INNER JOIN components ON components.id = tracking_flags_visibility.component_id
WHERE tracking_flags_visibility.product_id <> components.product_id
- ",
- { Slice => {} }
- );
- foreach my $row (@$rows) {
- $dbh->do("
+ ", {Slice => {}});
+ foreach my $row (@$rows) {
+ $dbh->do("
UPDATE tracking_flags_visibility
SET product_id=?
WHERE product_id=? AND component_id=?
- ", undef,
- $row->{good_product_id},
- $row->{bad_product_id},
- $row->{component_id},
- );
- }
+ ", undef, $row->{good_product_id}, $row->{bad_product_id},
+ $row->{component_id},);
+ }
}
__PACKAGE__->NAME;
diff --git a/extensions/TrackingFlags/bin/bug_825946.pl b/extensions/TrackingFlags/bin/bug_825946.pl
index 896dc5448..8a340175b 100755
--- a/extensions/TrackingFlags/bin/bug_825946.pl
+++ b/extensions/TrackingFlags/bin/bug_825946.pl
@@ -13,8 +13,8 @@ use 5.10.1;
use lib qw(. lib local/lib/perl5);
BEGIN {
- use Bugzilla;
- Bugzilla->extensions;
+ use Bugzilla;
+ Bugzilla->extensions;
}
use Bugzilla::Constants qw( USAGE_MODE_CMDLINE );
@@ -51,8 +51,8 @@ SQL
my %visible;
foreach my $row (@$tf_vis) {
- my ($tracking_flag_id, $product_id, $component_id) = @$row;
- $visible{$tracking_flag_id}{$product_id}{$component_id // 'ALL'} = 1;
+ my ($tracking_flag_id, $product_id, $component_id) = @$row;
+ $visible{$tracking_flag_id}{$product_id}{$component_id // 'ALL'} = 1;
}
my %bugs = map { $_->[0] => 1 } @$tf_bugs;
@@ -66,13 +66,16 @@ my $removed = 0;
$dbh->bz_start_transaction();
my ($timestamp) = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
foreach my $tf_bug (@$tf_bugs) {
- my ($flag_name, $value, $bug_id, $tf_id, $product_id, $component_id) = @$tf_bug;
- unless ($visible{$tf_id}{$product_id}{$component_id} || $visible{$tf_id}{$product_id}{ALL}) {
- $dbh->do("DELETE FROM tracking_flags_bugs WHERE tracking_flag_id = ? AND bug_id = ?",
- undef, $tf_id, $bug_id);
- LogActivityEntry($bug_id, $flag_name, $value, '---', $user->id, $timestamp);
- $removed++;
- }
+ my ($flag_name, $value, $bug_id, $tf_id, $product_id, $component_id) = @$tf_bug;
+ unless ($visible{$tf_id}{$product_id}{$component_id}
+ || $visible{$tf_id}{$product_id}{ALL})
+ {
+ $dbh->do(
+ "DELETE FROM tracking_flags_bugs WHERE tracking_flag_id = ? AND bug_id = ?",
+ undef, $tf_id, $bug_id);
+ LogActivityEntry($bug_id, $flag_name, $value, '---', $user->id, $timestamp);
+ $removed++;
+ }
}
$dbh->bz_commit_transaction();
diff --git a/extensions/TrackingFlags/bin/bulk_flag_clear.pl b/extensions/TrackingFlags/bin/bulk_flag_clear.pl
index 305fbf883..470269f13 100755
--- a/extensions/TrackingFlags/bin/bulk_flag_clear.pl
+++ b/extensions/TrackingFlags/bin/bulk_flag_clear.pl
@@ -13,8 +13,8 @@ use 5.10.1;
use lib qw(. lib local/lib/perl5);
BEGIN {
- use Bugzilla;
- Bugzilla->extensions;
+ use Bugzilla;
+ Bugzilla->extensions;
}
use Bugzilla::Constants;
@@ -27,21 +27,14 @@ use Getopt::Long;
Bugzilla->usage_mode(USAGE_MODE_CMDLINE);
my $config = {};
-GetOptions(
- $config,
- "trace=i",
- "update_db",
- "flag=s",
- "modified_before=s",
- "modified_after=s",
- "value=s"
-) or exit;
+GetOptions($config, "trace=i", "update_db", "flag=s", "modified_before=s",
+ "modified_after=s", "value=s")
+ or exit;
unless ($config->{flag}
- && ($config->{modified_before}
- || $config->{modified_after}
- || $config->{value}))
+ && ($config->{modified_before} || $config->{modified_after} || $config->{value})
+ )
{
- die <<EOF;
+ die <<EOF;
$0
clears tracking flags matching the specified criteria.
the last-modified will be updated, however bugmail will not be generated.
@@ -64,23 +57,24 @@ EOF
my (@where, @values);
-my $flag = Bugzilla::Extension::TrackingFlags::Flag->check({ name => $config->{flag} });
-push @where, 'tracking_flags_bugs.tracking_flag_id = ?';
+my $flag
+ = Bugzilla::Extension::TrackingFlags::Flag->check({name => $config->{flag}});
+push @where, 'tracking_flags_bugs.tracking_flag_id = ?';
push @values, $flag->flag_id;
if ($config->{modified_before}) {
- push @where, 'bugs.delta_ts < ?';
- push @values, $config->{modified_before};
+ push @where, 'bugs.delta_ts < ?';
+ push @values, $config->{modified_before};
}
if ($config->{modified_after}) {
- push @where, 'bugs.delta_ts > ?';
- push @values, $config->{modified_after};
+ push @where, 'bugs.delta_ts > ?';
+ push @values, $config->{modified_after};
}
if ($config->{value}) {
- push @where, 'tracking_flags_bugs.value = ?';
- push @values, $config->{value};
+ push @where, 'tracking_flags_bugs.value = ?';
+ push @values, $config->{value};
}
my $sql = "
@@ -99,37 +93,35 @@ $dbh->{TraceLevel} = $config->{trace} if $config->{trace};
my $bug_ids = $dbh->selectcol_arrayref($sql, undef, @values);
if (!@$bug_ids) {
- die "no matching bugs found\n";
+ die "no matching bugs found\n";
}
if (!$config->{update_db}) {
- print "bugs found: ", scalar(@$bug_ids), "\n\n", join(',', @$bug_ids), "\n\n";
- print "--update_db not provided, no changes made to the database\n";
- exit;
+ print "bugs found: ", scalar(@$bug_ids), "\n\n", join(',', @$bug_ids), "\n\n";
+ print "--update_db not provided, no changes made to the database\n";
+ exit;
}
# update bugs
-my $nobody = Bugzilla::User->check({ name => Bugzilla->params->{'nobody_user'} });
+my $nobody = Bugzilla::User->check({name => Bugzilla->params->{'nobody_user'}});
+
# put our nobody user into all groups to avoid permissions issues
$nobody->{groups} = [Bugzilla::Group->get_all];
Bugzilla->set_user($nobody);
foreach my $bug_id (@$bug_ids) {
- print "updating bug $bug_id\n";
- $dbh->bz_start_transaction;
-
- # update the bug
- # this will deal with history for us but not send bugmail
- my $bug = Bugzilla::Bug->check({ id => $bug_id });
- $bug->set_all({ $flag->name => '---' });
- $bug->update;
-
- # update lastdiffed to skip bugmail for this change
- $dbh->do(
- "UPDATE bugs SET lastdiffed = delta_ts WHERE bug_id = ?",
- undef,
- $bug->id
- );
- $dbh->bz_commit_transaction;
+ print "updating bug $bug_id\n";
+ $dbh->bz_start_transaction;
+
+ # update the bug
+ # this will deal with history for us but not send bugmail
+ my $bug = Bugzilla::Bug->check({id => $bug_id});
+ $bug->set_all({$flag->name => '---'});
+ $bug->update;
+
+ # update lastdiffed to skip bugmail for this change
+ $dbh->do("UPDATE bugs SET lastdiffed = delta_ts WHERE bug_id = ?",
+ undef, $bug->id);
+ $dbh->bz_commit_transaction;
}
diff --git a/extensions/TrackingFlags/bin/migrate_tracking_flags.pl b/extensions/TrackingFlags/bin/migrate_tracking_flags.pl
index cd55f5f83..97b8eccd5 100755
--- a/extensions/TrackingFlags/bin/migrate_tracking_flags.pl
+++ b/extensions/TrackingFlags/bin/migrate_tracking_flags.pl
@@ -16,8 +16,8 @@ use 5.10.1;
use lib qw(. lib local/lib/perl5);
BEGIN {
- use Bugzilla;
- Bugzilla->extensions;
+ use Bugzilla;
+ Bugzilla->extensions;
}
use Bugzilla::Constants;
@@ -39,10 +39,7 @@ use Data::Dumper;
Bugzilla->usage_mode(USAGE_MODE_CMDLINE);
my ($dry_run, $trace) = (0, 0);
-GetOptions(
- "dry-run" => \$dry_run,
- "trace" => \$trace,
-) or exit;
+GetOptions("dry-run" => \$dry_run, "trace" => \$trace,) or exit;
my $dbh = Bugzilla->dbh;
@@ -52,263 +49,271 @@ my %product_cache;
my %component_cache;
sub migrate_flag_visibility {
- my ($new_flag, $products) = @_;
+ my ($new_flag, $products) = @_;
+
+ # Create product/component visibility
+ foreach my $prod_name (keys %$products) {
+ $product_cache{$prod_name} ||= Bugzilla::Product->new({name => $prod_name});
+ if (!$product_cache{$prod_name}) {
+ warn "No such product $prod_name\n";
+ next;
+ }
- # Create product/component visibility
- foreach my $prod_name (keys %$products) {
- $product_cache{$prod_name} ||= Bugzilla::Product->new({ name => $prod_name });
- if (!$product_cache{$prod_name}) {
- warn "No such product $prod_name\n";
- next;
+ # If no components specified then we do Product/__any__
+ # otherwise, we enter an entry for each Product/Component
+ my $components = $products->{$prod_name};
+ if (!@$components) {
+ Bugzilla::Extension::TrackingFlags::Flag::Visibility->create({
+ tracking_flag_id => $new_flag->flag_id,
+ product_id => $product_cache{$prod_name}->id,
+ component_id => undef
+ });
+ }
+ else {
+ foreach my $comp_name (@$components) {
+ my $comp_matches = [];
+
+ # If the component is a regexp, we need to find all components
+ # matching the regex and insert each individually
+ if (ref $comp_name eq 'Regexp') {
+ my $comp_re = $comp_name;
+ $comp_re =~ s/\?\-xism://;
+ $comp_re =~ s/\(//;
+ $comp_re =~ s/\)//;
+ $comp_matches = $dbh->selectcol_arrayref(
+ 'SELECT components.name FROM components
+ WHERE components.product_id = ?
+ AND '
+ . $dbh->sql_regexp('components.name', $dbh->quote($comp_re)) . '
+ ORDER BY components.name', undef, $product_cache{$prod_name}->id
+ );
+ }
+ else {
+ $comp_matches = [$comp_name];
}
- # If no components specified then we do Product/__any__
- # otherwise, we enter an entry for each Product/Component
- my $components = $products->{$prod_name};
- if (!@$components) {
- Bugzilla::Extension::TrackingFlags::Flag::Visibility->create({
- tracking_flag_id => $new_flag->flag_id,
- product_id => $product_cache{$prod_name}->id,
- component_id => undef
+ foreach my $comp_match (@$comp_matches) {
+ $component_cache{"${prod_name}:${comp_match}"}
+ ||= Bugzilla::Component->new({
+ name => $comp_match, product => $product_cache{$prod_name}
});
+ if (!$component_cache{"${prod_name}:${comp_match}"}) {
+ warn "No such product $prod_name and component $comp_match\n";
+ next;
+ }
+
+ Bugzilla::Extension::TrackingFlags::Flag::Visibility->create({
+ tracking_flag_id => $new_flag->flag_id,
+ product_id => $product_cache{$prod_name}->id,
+ component_id => $component_cache{"${prod_name}:${comp_match}"}->id,
+ });
}
- else {
- foreach my $comp_name (@$components) {
- my $comp_matches = [];
- # If the component is a regexp, we need to find all components
- # matching the regex and insert each individually
- if (ref $comp_name eq 'Regexp') {
- my $comp_re = $comp_name;
- $comp_re =~ s/\?\-xism://;
- $comp_re =~ s/\(//;
- $comp_re =~ s/\)//;
- $comp_matches = $dbh->selectcol_arrayref(
- 'SELECT components.name FROM components
- WHERE components.product_id = ?
- AND ' . $dbh->sql_regexp('components.name', $dbh->quote($comp_re)) . '
- ORDER BY components.name',
- undef,
- $product_cache{$prod_name}->id);
- }
- else {
- $comp_matches = [ $comp_name ];
- }
-
- foreach my $comp_match (@$comp_matches) {
- $component_cache{"${prod_name}:${comp_match}"}
- ||= Bugzilla::Component->new({ name => $comp_match,
- product => $product_cache{$prod_name} });
- if (!$component_cache{"${prod_name}:${comp_match}"}) {
- warn "No such product $prod_name and component $comp_match\n";
- next;
- }
-
- Bugzilla::Extension::TrackingFlags::Flag::Visibility->create({
- tracking_flag_id => $new_flag->flag_id,
- product_id => $product_cache{$prod_name}->id,
- component_id => $component_cache{"${prod_name}:${comp_match}"}->id,
- });
- }
- }
- }
+ }
}
+ }
}
sub migrate_flag_values {
- my ($new_flag, $field) = @_;
-
- print "Migrating flag values...";
-
- my %blocking_trusted_requesters
- = %{$Bugzilla::Extension::BMO::Data::blocking_trusted_requesters};
- my %blocking_trusted_setters
- = %{$Bugzilla::Extension::BMO::Data::blocking_trusted_setters};
- my %status_trusted_wanters
- = %{$Bugzilla::Extension::BMO::Data::status_trusted_wanters};
- my %status_trusted_setters
- = %{$Bugzilla::Extension::BMO::Data::status_trusted_setters};
-
- my %group_cache;
- foreach my $value (@{ $field->legal_values }) {
- my $group_name = 'everyone';
-
- if ($field->name =~ /^cf_(blocking|tracking)_/) {
- if ($value->name ne '---' && $value->name !~ '\?$') {
- $group_name = get_setter_group($field->name, \%blocking_trusted_setters);
- }
- if ($value->name eq '?') {
- $group_name = get_setter_group($field->name, \%blocking_trusted_requesters);
- }
- } elsif ($field->name =~ /^cf_status_/) {
- if ($value->name eq 'wanted') {
- $group_name = get_setter_group($field->name, \%status_trusted_wanters);
- } elsif ($value->name ne '---' && $value->name ne '?') {
- $group_name = get_setter_group($field->name, \%status_trusted_setters);
- }
- }
+ my ($new_flag, $field) = @_;
+
+ print "Migrating flag values...";
+
+ my %blocking_trusted_requesters
+ = %{$Bugzilla::Extension::BMO::Data::blocking_trusted_requesters};
+ my %blocking_trusted_setters
+ = %{$Bugzilla::Extension::BMO::Data::blocking_trusted_setters};
+ my %status_trusted_wanters
+ = %{$Bugzilla::Extension::BMO::Data::status_trusted_wanters};
+ my %status_trusted_setters
+ = %{$Bugzilla::Extension::BMO::Data::status_trusted_setters};
+
+ my %group_cache;
+ foreach my $value (@{$field->legal_values}) {
+ my $group_name = 'everyone';
+
+ if ($field->name =~ /^cf_(blocking|tracking)_/) {
+ if ($value->name ne '---' && $value->name !~ '\?$') {
+ $group_name = get_setter_group($field->name, \%blocking_trusted_setters);
+ }
+ if ($value->name eq '?') {
+ $group_name = get_setter_group($field->name, \%blocking_trusted_requesters);
+ }
+ }
+ elsif ($field->name =~ /^cf_status_/) {
+ if ($value->name eq 'wanted') {
+ $group_name = get_setter_group($field->name, \%status_trusted_wanters);
+ }
+ elsif ($value->name ne '---' && $value->name ne '?') {
+ $group_name = get_setter_group($field->name, \%status_trusted_setters);
+ }
+ }
- $group_cache{$group_name} ||= Bugzilla::Group->new({ name => $group_name });
- $group_cache{$group_name} || die "Setter group '$group_name' does not exist";
+ $group_cache{$group_name} ||= Bugzilla::Group->new({name => $group_name});
+ $group_cache{$group_name} || die "Setter group '$group_name' does not exist";
- Bugzilla::Extension::TrackingFlags::Flag::Value->create({
- tracking_flag_id => $new_flag->flag_id,
- value => $value->name,
- setter_group_id => $group_cache{$group_name}->id,
- sortkey => $value->sortkey,
- is_active => $value->is_active
- });
- }
+ Bugzilla::Extension::TrackingFlags::Flag::Value->create({
+ tracking_flag_id => $new_flag->flag_id,
+ value => $value->name,
+ setter_group_id => $group_cache{$group_name}->id,
+ sortkey => $value->sortkey,
+ is_active => $value->is_active
+ });
+ }
- print "done.\n";
+ print "done.\n";
}
sub get_setter_group {
- my ($field, $trusted) = @_;
- my $setter_group = $trusted->{'_default'} || "";
- foreach my $dfield (keys %$trusted) {
- if ($field =~ $dfield) {
- $setter_group = $trusted->{$dfield};
- }
+ my ($field, $trusted) = @_;
+ my $setter_group = $trusted->{'_default'} || "";
+ foreach my $dfield (keys %$trusted) {
+ if ($field =~ $dfield) {
+ $setter_group = $trusted->{$dfield};
}
- return $setter_group;
+ }
+ return $setter_group;
}
sub migrate_flag_bugs {
- my ($new_flag, $field) = @_;
+ my ($new_flag, $field) = @_;
- print "Migrating bug values...";
+ print "Migrating bug values...";
- my $bugs = $dbh->selectall_arrayref("SELECT bug_id, " . $field->name . "
+ my $bugs = $dbh->selectall_arrayref(
+ "SELECT bug_id, " . $field->name . "
FROM bugs
WHERE " . $field->name . " != '---'
- ORDER BY bug_id");
- local $| = 1;
- my $count = 1;
- my $total = scalar @$bugs;
- foreach my $row (@$bugs) {
- my ($id, $value) = @$row;
- indicate_progress({ current => $count++, total => $total, every => 25 });
- Bugzilla::Extension::TrackingFlags::Flag::Bug->create({
- tracking_flag_id => $new_flag->flag_id,
- bug_id => $id,
- value => $value,
-
- });
- }
-
- print "done.\n";
+ ORDER BY bug_id"
+ );
+ local $| = 1;
+ my $count = 1;
+ my $total = scalar @$bugs;
+ foreach my $row (@$bugs) {
+ my ($id, $value) = @$row;
+ indicate_progress({current => $count++, total => $total, every => 25});
+ Bugzilla::Extension::TrackingFlags::Flag::Bug->create({
+ tracking_flag_id => $new_flag->flag_id,
+ bug_id => $id,
+ value => $value,
+
+ });
+ }
+
+ print "done.\n";
}
sub migrate_flag_activity {
- my ($new_flag, $field) = @_;
+ my ($new_flag, $field) = @_;
- print "Migating flag activity...";
+ print "Migating flag activity...";
- my $new_field = Bugzilla::Field->new({ name => $new_flag->name });
- $dbh->do("UPDATE bugs_activity SET fieldid = ? WHERE fieldid = ?",
- undef, $new_field->id, $field->id);
+ my $new_field = Bugzilla::Field->new({name => $new_flag->name});
+ $dbh->do("UPDATE bugs_activity SET fieldid = ? WHERE fieldid = ?",
+ undef, $new_field->id, $field->id);
- print "done.\n";
+ print "done.\n";
}
sub do_migration {
- my $bmo_tracking_flags = $Bugzilla::Extension::BMO::Data::cf_visible_in_products;
- my $bmo_project_flags = $Bugzilla::Extension::BMO::Data::cf_project_flags;
- my $bmo_disabled_flags = $Bugzilla::Extension::BMO::Data::cf_disabled_flags;
+ my $bmo_tracking_flags
+ = $Bugzilla::Extension::BMO::Data::cf_visible_in_products;
+ my $bmo_project_flags = $Bugzilla::Extension::BMO::Data::cf_project_flags;
+ my $bmo_disabled_flags = $Bugzilla::Extension::BMO::Data::cf_disabled_flags;
- my $fields = Bugzilla::Field->match({ custom => 1,
- type => FIELD_TYPE_SINGLE_SELECT });
+ my $fields
+ = Bugzilla::Field->match({custom => 1, type => FIELD_TYPE_SINGLE_SELECT});
- my @drop_columns;
- foreach my $field (@$fields) {
- next if $field->name !~ /^cf_(blocking|tracking|status)_/;
+ my @drop_columns;
+ foreach my $field (@$fields) {
+ next if $field->name !~ /^cf_(blocking|tracking|status)_/;
- foreach my $field_re (keys %$bmo_tracking_flags) {
- next if $field->name !~ $field_re;
+ foreach my $field_re (keys %$bmo_tracking_flags) {
+ next if $field->name !~ $field_re;
- # Create the new tracking flag if not exists
- my $new_flag
- = Bugzilla::Extension::TrackingFlags::Flag->new({ name => $field->name });
+ # Create the new tracking flag if not exists
+ my $new_flag
+ = Bugzilla::Extension::TrackingFlags::Flag->new({name => $field->name});
- next if $new_flag;
+ next if $new_flag;
- print "----------------------------------\n" .
- "Migrating custom tracking field " . $field->name . "...\n";
+ print "----------------------------------\n"
+ . "Migrating custom tracking field "
+ . $field->name . "...\n";
- my $new_flag_name = $field->name . "_new"; # Temporary name til we delete the old
+ my $new_flag_name = $field->name . "_new"; # Temporary name til we delete the old
- my $type = grep($field->name =~ $_, @$bmo_project_flags)
- ? 'project'
- : 'tracking';
+ my $type
+ = grep($field->name =~ $_, @$bmo_project_flags) ? 'project' : 'tracking';
- my $is_active = grep($_ eq $field->name, @$bmo_disabled_flags) ? 0 : 1;
+ my $is_active = grep($_ eq $field->name, @$bmo_disabled_flags) ? 0 : 1;
- $new_flag = Bugzilla::Extension::TrackingFlags::Flag->create({
- name => $new_flag_name,
- description => $field->description,
- type => $type,
- sortkey => $field->sortkey,
- is_active => $is_active,
- enter_bug => $field->enter_bug,
- });
+ $new_flag = Bugzilla::Extension::TrackingFlags::Flag->create({
+ name => $new_flag_name,
+ description => $field->description,
+ type => $type,
+ sortkey => $field->sortkey,
+ is_active => $is_active,
+ enter_bug => $field->enter_bug,
+ });
- migrate_flag_visibility($new_flag, $bmo_tracking_flags->{$field_re});
+ migrate_flag_visibility($new_flag, $bmo_tracking_flags->{$field_re});
- migrate_flag_values($new_flag, $field);
+ migrate_flag_values($new_flag, $field);
- migrate_flag_bugs($new_flag, $field);
+ migrate_flag_bugs($new_flag, $field);
- migrate_flag_activity($new_flag, $field);
+ migrate_flag_activity($new_flag, $field);
- push(@drop_columns, $field->name);
+ push(@drop_columns, $field->name);
- # Remove the old flag entry from fielddefs
- $dbh->do("DELETE FROM fielddefs WHERE name = ?",
- undef, $field->name);
+ # Remove the old flag entry from fielddefs
+ $dbh->do("DELETE FROM fielddefs WHERE name = ?", undef, $field->name);
- # Rename the new flag
- $dbh->do("UPDATE fielddefs SET name = ? WHERE name = ?",
- undef, $field->name, $new_flag_name);
+ # Rename the new flag
+ $dbh->do("UPDATE fielddefs SET name = ? WHERE name = ?",
+ undef, $field->name, $new_flag_name);
- $new_flag->set_name($field->name);
- $new_flag->update;
+ $new_flag->set_name($field->name);
+ $new_flag->update;
- # more than one regex could possibly match but we only want the first one
- last;
- }
+ # more than one regex could possibly match but we only want the first one
+ last;
}
+ }
- # Drop each custom flag's value table and the column from the bz schema object
- if (!$dry_run && @drop_columns) {
- print "Dropping value tables and updating bz schema object...\n";
+ # Drop each custom flag's value table and the column from the bz schema object
+ if (!$dry_run && @drop_columns) {
+ print "Dropping value tables and updating bz schema object...\n";
- foreach my $column (@drop_columns) {
- # Drop the values table
- $dbh->bz_drop_table($column);
+ foreach my $column (@drop_columns) {
- # Drop the bugs table column from the bz schema object
- $dbh->_bz_real_schema->delete_column('bugs', $column);
- $dbh->_bz_store_real_schema;
- }
+ # Drop the values table
+ $dbh->bz_drop_table($column);
- # Do the one alter table to drop all columns at once
- $dbh->do("ALTER TABLE bugs DROP COLUMN " . join(", DROP COLUMN ", @drop_columns));
+ # Drop the bugs table column from the bz schema object
+ $dbh->_bz_real_schema->delete_column('bugs', $column);
+ $dbh->_bz_store_real_schema;
}
+
+ # Do the one alter table to drop all columns at once
+ $dbh->do(
+ "ALTER TABLE bugs DROP COLUMN " . join(", DROP COLUMN ", @drop_columns));
+ }
}
# Start Main
eval {
- if ($dry_run) {
- print "** dry run : no changes to the database will be made **\n";
- $dbh->bz_start_transaction();
- }
- print "Starting migration...\n";
- do_migration();
- $dbh->bz_rollback_transaction() if $dry_run;
- print "All done!\n";
+ if ($dry_run) {
+ print "** dry run : no changes to the database will be made **\n";
+ $dbh->bz_start_transaction();
+ }
+ print "Starting migration...\n";
+ do_migration();
+ $dbh->bz_rollback_transaction() if $dry_run;
+ print "All done!\n";
};
if ($@) {
- $dbh->bz_rollback_transaction() if $dry_run;
- die "$@" if $@;
+ $dbh->bz_rollback_transaction() if $dry_run;
+ die "$@" if $@;
}
diff --git a/extensions/TrackingFlags/lib/Admin.pm b/extensions/TrackingFlags/lib/Admin.pm
index 50a0e0a61..e6ff9a31a 100644
--- a/extensions/TrackingFlags/lib/Admin.pm
+++ b/extensions/TrackingFlags/lib/Admin.pm
@@ -30,8 +30,8 @@ use Scalar::Util qw(blessed);
use base qw(Exporter);
our @EXPORT = qw(
- admin_list
- admin_edit
+ admin_list
+ admin_edit
);
#
@@ -39,177 +39,189 @@ our @EXPORT = qw(
#
sub admin_list {
- my ($vars) = @_;
- $vars->{show_bug_counts} = Bugzilla->input_params->{show_bug_counts};
- $vars->{flags} = [ Bugzilla::Extension::TrackingFlags::Flag->get_all() ];
+ my ($vars) = @_;
+ $vars->{show_bug_counts} = Bugzilla->input_params->{show_bug_counts};
+ $vars->{flags} = [Bugzilla::Extension::TrackingFlags::Flag->get_all()];
}
sub admin_edit {
- my ($vars, $page) = @_;
- my $input = Bugzilla->input_params;
-
- $vars->{groups} = _groups_to_json();
- $vars->{mode} = $input->{mode} || 'new';
- $vars->{flag_id} = $input->{flag_id} || 0;
- $vars->{tracking_flag_types} = FLAG_TYPES;
-
- if ($input->{delete}) {
- my $token = $input->{token};
- check_hash_token($token, ['tracking_flags_edit']);
- delete_token($token);
-
- my $flag = Bugzilla::Extension::TrackingFlags::Flag->new($vars->{flag_id})
- || ThrowCodeError('tracking_flags_invalid_item_id', { item => 'flag', id => $vars->{flag_id} });
- $flag->remove_from_db();
-
- $vars->{message} = 'tracking_flag_deleted';
- $vars->{flag} = $flag;
- $vars->{flags} = [ Bugzilla::Extension::TrackingFlags::Flag->get_all() ];
-
- print Bugzilla->cgi->header;
- my $template = Bugzilla->template;
- $template->process('pages/tracking_flags_admin_list.html.tmpl', $vars)
- || ThrowTemplateError($template->error());
- exit;
-
- } elsif ($input->{save}) {
- my $token = $input->{token};
- check_hash_token($token, ['tracking_flags_edit']);
- delete_token($token);
-
- my ($flag, $values, $visibilities) = _load_from_input($input, $vars);
- _validate($flag, $values, $visibilities);
- my $flag_obj = _update_db($flag, $values, $visibilities);
-
- $vars->{flag} = $flag_obj;
- $vars->{values} = _flag_values_to_json($values);
- $vars->{visibility} = _flag_visibility_to_json($visibilities);
-
- if ($vars->{mode} eq 'new') {
- $vars->{message} = 'tracking_flag_created';
- } else {
- $vars->{message} = 'tracking_flag_updated';
- }
- $vars->{mode} = 'edit';
-
- } else {
- # initial load
-
- if ($vars->{mode} eq 'edit') {
- # edit - straight load
- my $flag = Bugzilla::Extension::TrackingFlags::Flag->new($vars->{flag_id})
- || ThrowCodeError('tracking_flags_invalid_item_id', { item => 'flag', id => $vars->{flag_id} });
- $vars->{flag} = $flag;
- $vars->{values} = _flag_values_to_json($flag->values);
- $vars->{visibility} = _flag_visibility_to_json($flag->visibility);
- $vars->{can_delete} = !$flag->bug_count;
-
- } elsif ($vars->{mode} eq 'copy') {
- # copy - load the source flag
- $vars->{mode} = 'new';
- my $flag = Bugzilla::Extension::TrackingFlags::Flag->new($input->{copy_from})
- || ThrowCodeError('tracking_flags_invalid_item_id', { item => 'flag', id => $vars->{copy_from} });
-
- # increment the number at the end of the name and description
- if ($flag->name =~ /^(\D+)(\d+)$/) {
- $flag->set_name("$1" . ($2 + 1));
- }
- if ($flag->description =~ /^(\D+)([\d\.]+)$/) {
- my $description = $1;
- my $version = $2;
- if ($version =~ /\./) {
- my ($major, $minor) = split(/\./, $version);
- $minor++;
- $version = "$major.$minor";
- }
- else {
- $version++;
- }
- $flag->set_description($description . $version);
- }
- $flag->set_sortkey(_next_unique_sortkey($flag->sortkey));
- $flag->set_type($flag->flag_type);
- $flag->set_enter_bug($flag->enter_bug);
- # always default new flags as active, even when copying an inactive one
- $flag->set_is_active(1);
-
- $vars->{flag} = $flag;
- $vars->{values} = _flag_values_to_json($flag->values, 1);
- $vars->{visibility} = _flag_visibility_to_json($flag->visibility, 1);
- $vars->{can_delete} = 0;
-
- } else {
- $vars->{mode} = 'new';
- $vars->{flag} = {
- sortkey => 0,
- enter_bug => 1,
- is_active => 1,
- };
- $vars->{values} = _flag_values_to_json([
- {
- id => 0,
- value => '---',
- setter_group_id => '',
- is_active => 1,
- comment => '',
- },
- ]);
- $vars->{visibility} = '';
- $vars->{can_delete} = 0;
- }
+ my ($vars, $page) = @_;
+ my $input = Bugzilla->input_params;
+
+ $vars->{groups} = _groups_to_json();
+ $vars->{mode} = $input->{mode} || 'new';
+ $vars->{flag_id} = $input->{flag_id} || 0;
+ $vars->{tracking_flag_types} = FLAG_TYPES;
+
+ if ($input->{delete}) {
+ my $token = $input->{token};
+ check_hash_token($token, ['tracking_flags_edit']);
+ delete_token($token);
+
+ my $flag
+ = Bugzilla::Extension::TrackingFlags::Flag->new($vars->{flag_id})
+ || ThrowCodeError('tracking_flags_invalid_item_id',
+ {item => 'flag', id => $vars->{flag_id}});
+ $flag->remove_from_db();
+
+ $vars->{message} = 'tracking_flag_deleted';
+ $vars->{flag} = $flag;
+ $vars->{flags} = [Bugzilla::Extension::TrackingFlags::Flag->get_all()];
+
+ print Bugzilla->cgi->header;
+ my $template = Bugzilla->template;
+ $template->process('pages/tracking_flags_admin_list.html.tmpl', $vars)
+ || ThrowTemplateError($template->error());
+ exit;
+
+ }
+ elsif ($input->{save}) {
+ my $token = $input->{token};
+ check_hash_token($token, ['tracking_flags_edit']);
+ delete_token($token);
+
+ my ($flag, $values, $visibilities) = _load_from_input($input, $vars);
+ _validate($flag, $values, $visibilities);
+ my $flag_obj = _update_db($flag, $values, $visibilities);
+
+ $vars->{flag} = $flag_obj;
+ $vars->{values} = _flag_values_to_json($values);
+ $vars->{visibility} = _flag_visibility_to_json($visibilities);
+
+ if ($vars->{mode} eq 'new') {
+ $vars->{message} = 'tracking_flag_created';
}
-}
+ else {
+ $vars->{message} = 'tracking_flag_updated';
+ }
+ $vars->{mode} = 'edit';
+
+ }
+ else {
+ # initial load
+
+ if ($vars->{mode} eq 'edit') {
+
+ # edit - straight load
+ my $flag
+ = Bugzilla::Extension::TrackingFlags::Flag->new($vars->{flag_id})
+ || ThrowCodeError('tracking_flags_invalid_item_id',
+ {item => 'flag', id => $vars->{flag_id}});
+ $vars->{flag} = $flag;
+ $vars->{values} = _flag_values_to_json($flag->values);
+ $vars->{visibility} = _flag_visibility_to_json($flag->visibility);
+ $vars->{can_delete} = !$flag->bug_count;
-sub _load_from_input {
- my ($input, $vars) = @_;
-
- # flag
-
- my $flag = {
- id => ($input->{mode} eq 'edit' ? $input->{flag_id} : 0),
- name => trim($input->{flag_name} || ''),
- description => trim($input->{flag_desc} || ''),
- sortkey => $input->{flag_sort} || 0,
- type => trim($input->{flag_type} || ''),
- enter_bug => $input->{flag_enter_bug} ? 1 : 0,
- is_active => $input->{flag_active} ? 1 : 0,
- };
- detaint_natural($flag->{id});
- detaint_natural($flag->{sortkey});
- detaint_natural($flag->{enter_bug});
- detaint_natural($flag->{is_active});
-
- # values
-
- my $values = decode_json($input->{values} || '[]');
- foreach my $value (@$values) {
- $value->{value} = '' unless exists $value->{value} && defined $value->{value};
- $value->{setter_group_id} = '' unless $value->{setter_group_id};
- $value->{is_active} = $value->{is_active} ? 1 : 0;
}
+ elsif ($vars->{mode} eq 'copy') {
+
+ # copy - load the source flag
+ $vars->{mode} = 'new';
+ my $flag
+ = Bugzilla::Extension::TrackingFlags::Flag->new($input->{copy_from})
+ || ThrowCodeError('tracking_flags_invalid_item_id',
+ {item => 'flag', id => $vars->{copy_from}});
+
+ # increment the number at the end of the name and description
+ if ($flag->name =~ /^(\D+)(\d+)$/) {
+ $flag->set_name("$1" . ($2 + 1));
+ }
+ if ($flag->description =~ /^(\D+)([\d\.]+)$/) {
+ my $description = $1;
+ my $version = $2;
+ if ($version =~ /\./) {
+ my ($major, $minor) = split(/\./, $version);
+ $minor++;
+ $version = "$major.$minor";
+ }
+ else {
+ $version++;
+ }
+ $flag->set_description($description . $version);
+ }
+ $flag->set_sortkey(_next_unique_sortkey($flag->sortkey));
+ $flag->set_type($flag->flag_type);
+ $flag->set_enter_bug($flag->enter_bug);
+
+ # always default new flags as active, even when copying an inactive one
+ $flag->set_is_active(1);
- # vibility
+ $vars->{flag} = $flag;
+ $vars->{values} = _flag_values_to_json($flag->values, 1);
+ $vars->{visibility} = _flag_visibility_to_json($flag->visibility, 1);
+ $vars->{can_delete} = 0;
- my $visibilities = decode_json($input->{visibility} || '[]');
- foreach my $visibility (@$visibilities) {
- $visibility->{product} = '' unless exists $visibility->{product} && defined $visibility->{product};
- $visibility->{component} = '' unless exists $visibility->{component} && defined $visibility->{component};
}
+ else {
+ $vars->{mode} = 'new';
+ $vars->{flag} = {sortkey => 0, enter_bug => 1, is_active => 1,};
+ $vars->{values} = _flag_values_to_json([
+ {
+ id => 0,
+ value => '---',
+ setter_group_id => '',
+ is_active => 1,
+ comment => '',
+ },
+ ]);
+ $vars->{visibility} = '';
+ $vars->{can_delete} = 0;
+ }
+ }
+}
- return ($flag, $values, $visibilities);
+sub _load_from_input {
+ my ($input, $vars) = @_;
+
+ # flag
+
+ my $flag = {
+ id => ($input->{mode} eq 'edit' ? $input->{flag_id} : 0),
+ name => trim($input->{flag_name} || ''),
+ description => trim($input->{flag_desc} || ''),
+ sortkey => $input->{flag_sort} || 0,
+ type => trim($input->{flag_type} || ''),
+ enter_bug => $input->{flag_enter_bug} ? 1 : 0,
+ is_active => $input->{flag_active} ? 1 : 0,
+ };
+ detaint_natural($flag->{id});
+ detaint_natural($flag->{sortkey});
+ detaint_natural($flag->{enter_bug});
+ detaint_natural($flag->{is_active});
+
+ # values
+
+ my $values = decode_json($input->{values} || '[]');
+ foreach my $value (@$values) {
+ $value->{value} = '' unless exists $value->{value} && defined $value->{value};
+ $value->{setter_group_id} = '' unless $value->{setter_group_id};
+ $value->{is_active} = $value->{is_active} ? 1 : 0;
+ }
+
+ # vibility
+
+ my $visibilities = decode_json($input->{visibility} || '[]');
+ foreach my $visibility (@$visibilities) {
+ $visibility->{product} = ''
+ unless exists $visibility->{product} && defined $visibility->{product};
+ $visibility->{component} = ''
+ unless exists $visibility->{component} && defined $visibility->{component};
+ }
+
+ return ($flag, $values, $visibilities);
}
sub _next_unique_sortkey {
- my ($sortkey) = @_;
+ my ($sortkey) = @_;
- my %current;
- foreach my $flag (Bugzilla::Extension::TrackingFlags::Flag->get_all()) {
- $current{$flag->sortkey} = 1;
- }
+ my %current;
+ foreach my $flag (Bugzilla::Extension::TrackingFlags::Flag->get_all()) {
+ $current{$flag->sortkey} = 1;
+ }
- $sortkey += 5;
- $sortkey += 5 while exists $current{$sortkey};
- return $sortkey;
+ $sortkey += 5;
+ $sortkey += 5 while exists $current{$sortkey};
+ return $sortkey;
}
#
@@ -217,77 +229,83 @@ sub _next_unique_sortkey {
#
sub _validate {
- my ($flag, $values, $visibilities) = @_;
-
- # flag
-
- my @missing;
- push @missing, 'Field Name' if $flag->{name} eq '';
- push @missing, 'Field Description' if $flag->{description} eq '';
- push @missing, 'Field Sort Key' if $flag->{sortkey} eq '';
- scalar(@missing)
- && ThrowUserError('tracking_flags_missing_mandatory', { fields => \@missing });
-
- $flag->{name} =~ /^cf_/
- || ThrowUserError('tracking_flags_cf_prefix');
-
- if ($flag->{id}) {
- my $old_flag = Bugzilla::Extension::TrackingFlags::Flag->new($flag->{id})
- || ThrowCodeError('tracking_flags_invalid_item_id', { item => 'flag', id => $flag->{id} });
- if ($flag->{name} ne $old_flag->name) {
- Bugzilla::Field->new({ name => $flag->{name} })
- && ThrowUserError('field_already_exists', { field => { name => $flag->{name} }});
- }
- } else {
- Bugzilla::Field->new({ name => $flag->{name} })
- && ThrowUserError('field_already_exists', { field => { name => $flag->{name} }});
+ my ($flag, $values, $visibilities) = @_;
+
+ # flag
+
+ my @missing;
+ push @missing, 'Field Name' if $flag->{name} eq '';
+ push @missing, 'Field Description' if $flag->{description} eq '';
+ push @missing, 'Field Sort Key' if $flag->{sortkey} eq '';
+ scalar(@missing)
+ && ThrowUserError('tracking_flags_missing_mandatory', {fields => \@missing});
+
+ $flag->{name} =~ /^cf_/ || ThrowUserError('tracking_flags_cf_prefix');
+
+ if ($flag->{id}) {
+ my $old_flag
+ = Bugzilla::Extension::TrackingFlags::Flag->new($flag->{id})
+ || ThrowCodeError('tracking_flags_invalid_item_id',
+ {item => 'flag', id => $flag->{id}});
+ if ($flag->{name} ne $old_flag->name) {
+ Bugzilla::Field->new({name => $flag->{name}})
+ && ThrowUserError('field_already_exists', {field => {name => $flag->{name}}});
}
+ }
+ else {
+ Bugzilla::Field->new({name => $flag->{name}})
+ && ThrowUserError('field_already_exists', {field => {name => $flag->{name}}});
+ }
- # values
+ # values
- scalar(@$values)
- || ThrowUserError('tracking_flags_missing_values');
+ scalar(@$values) || ThrowUserError('tracking_flags_missing_values');
- my %seen;
- foreach my $value (@$values) {
- my $v = $value->{value};
+ my %seen;
+ foreach my $value (@$values) {
+ my $v = $value->{value};
- $v eq ''
- && ThrowUserError('tracking_flags_missing_value');
+ $v eq '' && ThrowUserError('tracking_flags_missing_value');
- exists $seen{$v}
- && ThrowUserError('tracking_flags_duplicate_value', { value => $v });
- $seen{$v} = 1;
+ exists $seen{$v}
+ && ThrowUserError('tracking_flags_duplicate_value', {value => $v});
+ $seen{$v} = 1;
- push @missing, "Setter for $v" if !$value->{setter_group_id};
- }
- scalar(@missing)
- && ThrowUserError('tracking_flags_missing_mandatory', { fields => \@missing });
+ push @missing, "Setter for $v" if !$value->{setter_group_id};
+ }
+ scalar(@missing)
+ && ThrowUserError('tracking_flags_missing_mandatory', {fields => \@missing});
- # visibility
+ # visibility
- scalar(@$visibilities)
- || ThrowUserError('tracking_flags_missing_visibility');
+ scalar(@$visibilities) || ThrowUserError('tracking_flags_missing_visibility');
- %seen = ();
- foreach my $visibility (@$visibilities) {
- my $name = $visibility->{product} . ':' . $visibility->{component};
+ %seen = ();
+ foreach my $visibility (@$visibilities) {
+ my $name = $visibility->{product} . ':' . $visibility->{component};
- exists $seen{$name}
- && ThrowUserError('tracking_flags_duplicate_visibility', { name => $name });
+ exists $seen{$name}
+ && ThrowUserError('tracking_flags_duplicate_visibility', {name => $name});
- $visibility->{product_obj} = Bugzilla::Product->new({ name => $visibility->{product} })
- || ThrowCodeError('tracking_flags_invalid_product', { product => $visibility->{product} });
+ $visibility->{product_obj}
+ = Bugzilla::Product->new({name => $visibility->{product}})
+ || ThrowCodeError('tracking_flags_invalid_product',
+ {product => $visibility->{product}});
- if ($visibility->{component} ne '') {
- $visibility->{component_obj} = Bugzilla::Component->new({ product => $visibility->{product_obj},
- name => $visibility->{component} })
- || ThrowCodeError('tracking_flags_invalid_component', {
- product => $visibility->{product},
- component_name => $visibility->{component},
- });
+ if ($visibility->{component} ne '') {
+ $visibility->{component_obj}
+ = Bugzilla::Component->new({
+ product => $visibility->{product_obj}, name => $visibility->{component}
+ })
+ || ThrowCodeError(
+ 'tracking_flags_invalid_component',
+ {
+ product => $visibility->{product},
+ component_name => $visibility->{component},
}
+ );
}
+ }
}
@@ -296,106 +314,115 @@ sub _validate {
#
sub _update_db {
- my ($flag, $values, $visibilities) = @_;
- my $dbh = Bugzilla->dbh;
+ my ($flag, $values, $visibilities) = @_;
+ my $dbh = Bugzilla->dbh;
- $dbh->bz_start_transaction();
- my $flag_obj = _update_db_flag($flag);
- _update_db_values($flag_obj, $flag, $values);
- _update_db_visibility($flag_obj, $flag, $visibilities);
- $dbh->bz_commit_transaction();
+ $dbh->bz_start_transaction();
+ my $flag_obj = _update_db_flag($flag);
+ _update_db_values($flag_obj, $flag, $values);
+ _update_db_visibility($flag_obj, $flag, $visibilities);
+ $dbh->bz_commit_transaction();
- return $flag_obj;
+ return $flag_obj;
}
sub _update_db_flag {
- my ($flag) = @_;
+ my ($flag) = @_;
+
+ my $object_set = {
+ name => $flag->{name},
+ description => $flag->{description},
+ sortkey => $flag->{sortkey},
+ type => $flag->{type},
+ enter_bug => $flag->{enter_bug},
+ is_active => $flag->{is_active},
+ };
+
+ my $flag_obj;
+ if ($flag->{id}) {
+
+ # update existing flag
+ $flag_obj
+ = Bugzilla::Extension::TrackingFlags::Flag->new($flag->{id})
+ || ThrowCodeError('tracking_flags_invalid_item_id',
+ {item => 'flag', id => $flag->{id}});
+ $flag_obj->set_all($object_set);
+ $flag_obj->update();
+
+ }
+ else {
+ # create new flag
+ $flag_obj = Bugzilla::Extension::TrackingFlags::Flag->create($object_set);
+ }
+
+ return $flag_obj;
+}
- my $object_set = {
- name => $flag->{name},
- description => $flag->{description},
- sortkey => $flag->{sortkey},
- type => $flag->{type},
- enter_bug => $flag->{enter_bug},
- is_active => $flag->{is_active},
- };
+sub _update_db_values {
+ my ($flag_obj, $flag, $values) = @_;
- my $flag_obj;
- if ($flag->{id}) {
- # update existing flag
- $flag_obj = Bugzilla::Extension::TrackingFlags::Flag->new($flag->{id})
- || ThrowCodeError('tracking_flags_invalid_item_id', { item => 'flag', id => $flag->{id} });
- $flag_obj->set_all($object_set);
- $flag_obj->update();
-
- } else {
- # create new flag
- $flag_obj = Bugzilla::Extension::TrackingFlags::Flag->create($object_set);
+ # delete
+ foreach my $current_value (@{$flag_obj->values}) {
+ if (!grep { $_->{id} == $current_value->id } @$values) {
+ $current_value->remove_from_db();
}
+ }
- return $flag_obj;
-}
+ # add/update
+ my $sortkey = 0;
+ foreach my $value (@{$values}) {
+ $sortkey += 10;
-sub _update_db_values {
- my ($flag_obj, $flag, $values) = @_;
+ my $object_set = {
+ value => $value->{value},
+ setter_group_id => $value->{setter_group_id},
+ is_active => $value->{is_active},
+ sortkey => $sortkey,
+ comment => $value->{comment},
+ };
- # delete
- foreach my $current_value (@{ $flag_obj->values }) {
- if (!grep { $_->{id} == $current_value->id } @$values) {
- $current_value->remove_from_db();
- }
+ if ($value->{id}) {
+ my $value_obj
+ = Bugzilla::Extension::TrackingFlags::Flag::Value->new($value->{id})
+ || ThrowCodeError('tracking_flags_invalid_item_id',
+ {item => 'flag value', id => $flag->{id}});
+ my $old_value = $value_obj->value;
+ $value_obj->set_all($object_set);
+ $value_obj->update();
+ Bugzilla::Extension::TrackingFlags::Flag::Bug->update_all_values({
+ value_obj => $value_obj,
+ old_value => $old_value,
+ new_value => $value_obj->value,
+ });
}
-
- # add/update
- my $sortkey = 0;
- foreach my $value (@{ $values }) {
- $sortkey += 10;
-
- my $object_set = {
- value => $value->{value},
- setter_group_id => $value->{setter_group_id},
- is_active => $value->{is_active},
- sortkey => $sortkey,
- comment => $value->{comment},
- };
-
- if ($value->{id}) {
- my $value_obj = Bugzilla::Extension::TrackingFlags::Flag::Value->new($value->{id})
- || ThrowCodeError('tracking_flags_invalid_item_id', { item => 'flag value', id => $flag->{id} });
- my $old_value = $value_obj->value;
- $value_obj->set_all($object_set);
- $value_obj->update();
- Bugzilla::Extension::TrackingFlags::Flag::Bug->update_all_values({
- value_obj => $value_obj,
- old_value => $old_value,
- new_value => $value_obj->value,
- });
- } else {
- $object_set->{tracking_flag_id} = $flag_obj->flag_id;
- Bugzilla::Extension::TrackingFlags::Flag::Value->create($object_set);
- }
+ else {
+ $object_set->{tracking_flag_id} = $flag_obj->flag_id;
+ Bugzilla::Extension::TrackingFlags::Flag::Value->create($object_set);
}
+ }
}
sub _update_db_visibility {
- my ($flag_obj, $flag, $visibilities) = @_;
+ my ($flag_obj, $flag, $visibilities) = @_;
- # delete
- foreach my $current_visibility (@{ $flag_obj->visibility }) {
- if (!grep { $_->{id} == $current_visibility->id } @$visibilities) {
- $current_visibility->remove_from_db();
- }
- }
-
- # add
- foreach my $visibility (@{ $visibilities }) {
- next if $visibility->{id};
- Bugzilla::Extension::TrackingFlags::Flag::Visibility->create({
- tracking_flag_id => $flag_obj->flag_id,
- product_id => $visibility->{product_obj}->id,
- component_id => $visibility->{component} ? $visibility->{component_obj}->id : undef,
- });
+ # delete
+ foreach my $current_visibility (@{$flag_obj->visibility}) {
+ if (!grep { $_->{id} == $current_visibility->id } @$visibilities) {
+ $current_visibility->remove_from_db();
}
+ }
+
+ # add
+ foreach my $visibility (@{$visibilities}) {
+ next if $visibility->{id};
+ Bugzilla::Extension::TrackingFlags::Flag::Visibility->create({
+ tracking_flag_id => $flag_obj->flag_id,
+ product_id => $visibility->{product_obj}->id,
+ component_id => $visibility->{component}
+ ? $visibility->{component_obj}->id
+ : undef,
+ });
+ }
}
#
@@ -403,62 +430,66 @@ sub _update_db_visibility {
#
sub _groups_to_json {
- my @data;
- foreach my $group (sort { $a->name cmp $b->name } Bugzilla::Group->get_all()) {
- push @data, {
- id => $group->id,
- name => $group->name,
- };
- }
- return encode_json(\@data);
+ my @data;
+ foreach my $group (sort { $a->name cmp $b->name } Bugzilla::Group->get_all()) {
+ push @data, {id => $group->id, name => $group->name,};
+ }
+ return encode_json(\@data);
}
sub _flag_values_to_json {
- my ($values, $is_copy) = @_;
- # setting is_copy will set the id's to zero, to force new values rather
- # than editing existing ones
- my @data;
- foreach my $value (@$values) {
- push @data, {
- id => $is_copy ? 0 : $value->{id},
- value => $value->{value},
- setter_group_id => $value->{setter_group_id},
- is_active => $value->{is_active} ? JSON::true : JSON::false,
- comment => $value->{comment} // '',
- };
- }
- return encode_json(\@data);
+ my ($values, $is_copy) = @_;
+
+ # setting is_copy will set the id's to zero, to force new values rather
+ # than editing existing ones
+ my @data;
+ foreach my $value (@$values) {
+ push @data,
+ {
+ id => $is_copy ? 0 : $value->{id},
+ value => $value->{value},
+ setter_group_id => $value->{setter_group_id},
+ is_active => $value->{is_active} ? JSON::true : JSON::false,
+ comment => $value->{comment} // '',
+ };
+ }
+ return encode_json(\@data);
}
sub _flag_visibility_to_json {
- my ($visibilities, $is_copy) = @_;
- # setting is_copy will set the id's to zero, to force new visibilites
- # rather than editing existing ones
- my @data;
-
- foreach my $visibility (@$visibilities) {
- my $product = exists $visibility->{product_id}
- ? $visibility->product->name
- : $visibility->{product};
- my $component;
- if (exists $visibility->{component_id} && $visibility->{component_id}) {
- $component = $visibility->component->name;
- } elsif (exists $visibility->{component}) {
- $component = $visibility->{component};
- } else {
- $component = undef;
- }
- push @data, {
- id => $is_copy ? 0 : $visibility->{id},
- product => $product,
- component => $component,
- };
+ my ($visibilities, $is_copy) = @_;
+
+ # setting is_copy will set the id's to zero, to force new visibilites
+ # rather than editing existing ones
+ my @data;
+
+ foreach my $visibility (@$visibilities) {
+ my $product
+ = exists $visibility->{product_id}
+ ? $visibility->product->name
+ : $visibility->{product};
+ my $component;
+ if (exists $visibility->{component_id} && $visibility->{component_id}) {
+ $component = $visibility->component->name;
+ }
+ elsif (exists $visibility->{component}) {
+ $component = $visibility->{component};
+ }
+ else {
+ $component = undef;
}
- @data = sort {
- lc($a->{product}) cmp lc($b->{product})
- || lc($a->{component}) cmp lc($b->{component})
- } @data;
- return encode_json(\@data);
+ push @data,
+ {
+ id => $is_copy ? 0 : $visibility->{id},
+ product => $product,
+ component => $component,
+ };
+ }
+ @data = sort {
+ lc($a->{product}) cmp lc($b->{product})
+ || lc($a->{component}) cmp lc($b->{component})
+ } @data;
+ return encode_json(\@data);
}
1;
diff --git a/extensions/TrackingFlags/lib/Constants.pm b/extensions/TrackingFlags/lib/Constants.pm
index 00827aa7a..842e7501d 100644
--- a/extensions/TrackingFlags/lib/Constants.pm
+++ b/extensions/TrackingFlags/lib/Constants.pm
@@ -14,31 +14,31 @@ use warnings;
use base qw(Exporter);
our @EXPORT = qw(
- FLAG_TYPES
+ FLAG_TYPES
);
sub FLAG_TYPES {
- my @flag_types = (
- {
- name => 'project',
- description => 'Project Flags',
- collapsed => 0,
- sortkey => 0
- },
- {
- name => 'tracking',
- description => 'Tracking Flags',
- collapsed => 1,
- sortkey => 1
- },
- {
- name => 'blocking',
- description => 'Blocking Flags',
- collapsed => 1,
- sortkey => 2
- },
- );
- return [ sort { $a->{'sortkey'} <=> $b->{'sortkey'} } @flag_types ];
+ my @flag_types = (
+ {
+ name => 'project',
+ description => 'Project Flags',
+ collapsed => 0,
+ sortkey => 0
+ },
+ {
+ name => 'tracking',
+ description => 'Tracking Flags',
+ collapsed => 1,
+ sortkey => 1
+ },
+ {
+ name => 'blocking',
+ description => 'Blocking Flags',
+ collapsed => 1,
+ sortkey => 2
+ },
+ );
+ return [sort { $a->{'sortkey'} <=> $b->{'sortkey'} } @flag_types];
}
1;
diff --git a/extensions/TrackingFlags/lib/Flag.pm b/extensions/TrackingFlags/lib/Flag.pm
index 82c0314e3..c1ecbc4ad 100644
--- a/extensions/TrackingFlags/lib/Flag.pm
+++ b/extensions/TrackingFlags/lib/Flag.pm
@@ -30,43 +30,43 @@ use Bugzilla::Extension::TrackingFlags::Flag::Visibility;
use constant DB_TABLE => 'tracking_flags';
use constant DB_COLUMNS => qw(
- id
- field_id
- name
- description
- type
- sortkey
- enter_bug
- is_active
+ id
+ field_id
+ name
+ description
+ type
+ sortkey
+ enter_bug
+ is_active
);
use constant LIST_ORDER => 'sortkey';
use constant UPDATE_COLUMNS => qw(
- name
- description
- type
- sortkey
- enter_bug
- is_active
+ name
+ description
+ type
+ sortkey
+ enter_bug
+ is_active
);
use constant VALIDATORS => {
- name => \&_check_name,
- description => \&_check_description,
- type => \&_check_type,
- sortkey => \&_check_sortkey,
- enter_bug => \&Bugzilla::Object::check_boolean,
- is_active => \&Bugzilla::Object::check_boolean,
+ name => \&_check_name,
+ description => \&_check_description,
+ type => \&_check_type,
+ sortkey => \&_check_sortkey,
+ enter_bug => \&Bugzilla::Object::check_boolean,
+ is_active => \&Bugzilla::Object::check_boolean,
};
use constant UPDATE_VALIDATORS => {
- name => \&_check_name,
- description => \&_check_description,
- type => \&_check_type,
- sortkey => \&_check_sortkey,
- enter_bug => \&Bugzilla::Object::check_boolean,
- is_active => \&Bugzilla::Object::check_boolean,
+ name => \&_check_name,
+ description => \&_check_description,
+ type => \&_check_type,
+ sortkey => \&_check_sortkey,
+ enter_bug => \&Bugzilla::Object::check_boolean,
+ is_active => \&Bugzilla::Object::check_boolean,
};
###############################
@@ -74,257 +74,260 @@ use constant UPDATE_VALIDATORS => {
###############################
sub new {
- my $class = shift;
- my $param = shift;
- my $cache = Bugzilla->request_cache;
-
- if (!ref $param
- && exists $cache->{'tracking_flags'}
- && exists $cache->{'tracking_flags'}->{$param})
- {
- return $cache->{'tracking_flags'}->{$param};
- }
-
- return $class->SUPER::new($param);
+ my $class = shift;
+ my $param = shift;
+ my $cache = Bugzilla->request_cache;
+
+ if (!ref $param
+ && exists $cache->{'tracking_flags'}
+ && exists $cache->{'tracking_flags'}->{$param})
+ {
+ return $cache->{'tracking_flags'}->{$param};
+ }
+
+ return $class->SUPER::new($param);
}
sub new_from_hash {
- my $class = shift;
- my $cache = Bugzilla->request_cache->{'tracking_flags'} //= {};
- my $flag = $class->SUPER::new_from_hash(@_);
- if ($flag) {
- push @Bugzilla::Extension::TrackingFlags::FLAG_CACHE, $flag;
- }
- return $flag;
+ my $class = shift;
+ my $cache = Bugzilla->request_cache->{'tracking_flags'} //= {};
+ my $flag = $class->SUPER::new_from_hash(@_);
+ if ($flag) {
+ push @Bugzilla::Extension::TrackingFlags::FLAG_CACHE, $flag;
+ }
+ return $flag;
}
sub create {
- my $class = shift;
- my $params = shift;
- my $dbh = Bugzilla->dbh;
- my $flag;
-
- # Disable bug updates temporarily to avoid conflicts.
- SetParam('disable_bug_updates', 1);
- write_params();
-
- eval {
- $dbh->bz_start_transaction();
-
- $params = $class->run_create_validators($params);
-
- # We have to create an entry for this new flag
- # in the fielddefs table for use elsewhere. We cannot
- # use Bugzilla::Field->create as it will create the
- # additional tables needed by custom fields which we
- # do not need. Also we do this so as not to add a
- # another column to the bugs table.
- # We will create the entry as a custom field with a
- # type of FIELD_TYPE_EXTENSION so Bugzilla will skip
- # these field types in certain parts of the core code.
- $dbh->do("INSERT INTO fielddefs
+ my $class = shift;
+ my $params = shift;
+ my $dbh = Bugzilla->dbh;
+ my $flag;
+
+ # Disable bug updates temporarily to avoid conflicts.
+ SetParam('disable_bug_updates', 1);
+ write_params();
+
+ eval {
+ $dbh->bz_start_transaction();
+
+ $params = $class->run_create_validators($params);
+
+ # We have to create an entry for this new flag
+ # in the fielddefs table for use elsewhere. We cannot
+ # use Bugzilla::Field->create as it will create the
+ # additional tables needed by custom fields which we
+ # do not need. Also we do this so as not to add a
+ # another column to the bugs table.
+ # We will create the entry as a custom field with a
+ # type of FIELD_TYPE_EXTENSION so Bugzilla will skip
+ # these field types in certain parts of the core code.
+ $dbh->do(
+ "INSERT INTO fielddefs
(name, description, sortkey, type, custom, obsolete, buglist)
VALUES
- (?, ?, ?, ?, ?, ?, ?)",
- undef,
- $params->{'name'},
- $params->{'description'},
- $params->{'sortkey'},
- FIELD_TYPE_EXTENSION,
- 1, 0, 1);
- $params->{'field_id'} = $dbh->bz_last_key;
-
- $flag = $class->SUPER::create($params);
-
- $dbh->bz_commit_transaction();
- };
- my $error = "$@";
- SetParam('disable_bug_updates', 0);
- write_params();
- die $error if $error;
+ (?, ?, ?, ?, ?, ?, ?)", undef, $params->{'name'},
+ $params->{'description'}, $params->{'sortkey'}, FIELD_TYPE_EXTENSION, 1, 0, 1
+ );
+ $params->{'field_id'} = $dbh->bz_last_key;
+
+ $flag = $class->SUPER::create($params);
- # fielddefs has been changed so we need to clear global config
- Bugzilla->memcached->clear_config();
+ $dbh->bz_commit_transaction();
+ };
+ my $error = "$@";
+ SetParam('disable_bug_updates', 0);
+ write_params();
+ die $error if $error;
- return $flag;
+ # fielddefs has been changed so we need to clear global config
+ Bugzilla->memcached->clear_config();
+
+ return $flag;
}
sub update {
- my $self = shift;
- my $dbh = Bugzilla->dbh;
+ my $self = shift;
+ my $dbh = Bugzilla->dbh;
- my $old_self = $self->new($self->flag_id);
+ my $old_self = $self->new($self->flag_id);
- # HACK! Bugzilla::Object::update uses hardcoded $self->id
- # instead of $self->{ID_FIELD} so we need to reverse field_id
- # and the real id temporarily
- my $field_id = $self->id;
- $self->{'field_id'} = $self->{'id'};
+ # HACK! Bugzilla::Object::update uses hardcoded $self->id
+ # instead of $self->{ID_FIELD} so we need to reverse field_id
+ # and the real id temporarily
+ my $field_id = $self->id;
+ $self->{'field_id'} = $self->{'id'};
- my $changes = $self->SUPER::update(@_);
+ my $changes = $self->SUPER::update(@_);
- $self->{'field_id'} = $field_id;
+ $self->{'field_id'} = $field_id;
- # Update the fielddefs entry
- $dbh->do("UPDATE fielddefs SET name = ?, description = ? WHERE name = ?",
- undef,
- $self->name, $self->description, $old_self->name);
+ # Update the fielddefs entry
+ $dbh->do("UPDATE fielddefs SET name = ?, description = ? WHERE name = ?",
+ undef, $self->name, $self->description, $old_self->name);
- # Update request_cache
- my $cache = Bugzilla->request_cache;
- if (exists $cache->{'tracking_flags'}) {
- $cache->{'tracking_flags'}->{$self->flag_id} = $self;
- }
+ # Update request_cache
+ my $cache = Bugzilla->request_cache;
+ if (exists $cache->{'tracking_flags'}) {
+ $cache->{'tracking_flags'}->{$self->flag_id} = $self;
+ }
- # fielddefs has been changed so we need to clear global config
- Bugzilla->memcached->clear_config();
+ # fielddefs has been changed so we need to clear global config
+ Bugzilla->memcached->clear_config();
- return $changes;
+ return $changes;
}
sub match {
- my $class = shift;
- my ($params) = @_;
-
- # Use later for preload
- my $bug_id = delete $params->{'bug_id'};
-
- # Retrieve all flags relevant for the given product and component
- if (!exists $params->{'id'}
- && ($params->{'component'} || $params->{'component_id'}
- || $params->{'product'} || $params->{'product_id'}))
- {
- my $visible_flags
- = Bugzilla::Extension::TrackingFlags::Flag::Visibility->match(@_);
- my @flag_ids = map { $_->tracking_flag_id } @$visible_flags;
-
- delete $params->{'component'} if exists $params->{'component'};
- delete $params->{'component_id'} if exists $params->{'component_id'};
- delete $params->{'product'} if exists $params->{'product'};
- delete $params->{'product_id'} if exists $params->{'product_id'};
-
- $params->{'id'} = \@flag_ids;
- }
-
- # We need to return inactive flags if a value has been set
- my $is_active_filter = delete $params->{is_active};
-
- my $flags = $class->SUPER::match($params);
- preload_all_the_things($flags, { bug_id => $bug_id });
-
- if ($is_active_filter) {
- $flags = [ grep { $_->is_active || exists $_->{bug_flag} } @$flags ];
- }
- return [ sort { $a->sortkey <=> $b->sortkey } @$flags ];
+ my $class = shift;
+ my ($params) = @_;
+
+ # Use later for preload
+ my $bug_id = delete $params->{'bug_id'};
+
+ # Retrieve all flags relevant for the given product and component
+ if (
+ !exists $params->{'id'}
+ && ( $params->{'component'}
+ || $params->{'component_id'}
+ || $params->{'product'}
+ || $params->{'product_id'})
+ )
+ {
+ my $visible_flags
+ = Bugzilla::Extension::TrackingFlags::Flag::Visibility->match(@_);
+ my @flag_ids = map { $_->tracking_flag_id } @$visible_flags;
+
+ delete $params->{'component'} if exists $params->{'component'};
+ delete $params->{'component_id'} if exists $params->{'component_id'};
+ delete $params->{'product'} if exists $params->{'product'};
+ delete $params->{'product_id'} if exists $params->{'product_id'};
+
+ $params->{'id'} = \@flag_ids;
+ }
+
+ # We need to return inactive flags if a value has been set
+ my $is_active_filter = delete $params->{is_active};
+
+ my $flags = $class->SUPER::match($params);
+ preload_all_the_things($flags, {bug_id => $bug_id});
+
+ if ($is_active_filter) {
+ $flags = [grep { $_->is_active || exists $_->{bug_flag} } @$flags];
+ }
+ return [sort { $a->sortkey <=> $b->sortkey } @$flags];
}
sub get_all {
- my $self = shift;
- my $cache = Bugzilla->request_cache;
- if (!exists $cache->{'tracking_flags'}) {
- my @tracking_flags = $self->SUPER::get_all(@_);
- preload_all_the_things(\@tracking_flags);
- my %tracking_flags_hash = map { $_->flag_id => $_ } @tracking_flags;
- $cache->{'tracking_flags'} = \%tracking_flags_hash;
- }
- return sort { $a->flag_type cmp $b->flag_type || $a->sortkey <=> $b->sortkey }
- values %{ $cache->{'tracking_flags'} };
+ my $self = shift;
+ my $cache = Bugzilla->request_cache;
+ if (!exists $cache->{'tracking_flags'}) {
+ my @tracking_flags = $self->SUPER::get_all(@_);
+ preload_all_the_things(\@tracking_flags);
+ my %tracking_flags_hash = map { $_->flag_id => $_ } @tracking_flags;
+ $cache->{'tracking_flags'} = \%tracking_flags_hash;
+ }
+ return
+ sort { $a->flag_type cmp $b->flag_type || $a->sortkey <=> $b->sortkey }
+ values %{$cache->{'tracking_flags'}};
}
# avoids the overhead of pre-loading if just the field names are required
sub get_all_names {
- my $self = shift;
- my $cache = Bugzilla->request_cache;
- if (!exists $cache->{'tracking_flags_names'}) {
- $cache->{'tracking_flags_names'} =
- Bugzilla->dbh->selectcol_arrayref("SELECT name FROM tracking_flags ORDER BY name");
- }
- return @{ $cache->{'tracking_flags_names'} };
+ my $self = shift;
+ my $cache = Bugzilla->request_cache;
+ if (!exists $cache->{'tracking_flags_names'}) {
+ $cache->{'tracking_flags_names'} = Bugzilla->dbh->selectcol_arrayref(
+ "SELECT name FROM tracking_flags ORDER BY name");
+ }
+ return @{$cache->{'tracking_flags_names'}};
}
sub remove_from_db {
- my $self = shift;
- my $dbh = Bugzilla->dbh;
+ my $self = shift;
+ my $dbh = Bugzilla->dbh;
- # Check to see if tracking_flags_bugs table has records
- if ($self->bug_count) {
- ThrowUserError('tracking_flag_has_contents', { flag => $self });
- }
+ # Check to see if tracking_flags_bugs table has records
+ if ($self->bug_count) {
+ ThrowUserError('tracking_flag_has_contents', {flag => $self});
+ }
- # Disable bug updates temporarily to avoid conflicts.
- SetParam('disable_bug_updates', 1);
- write_params();
+ # Disable bug updates temporarily to avoid conflicts.
+ SetParam('disable_bug_updates', 1);
+ write_params();
- eval {
- $dbh->bz_start_transaction();
+ eval {
+ $dbh->bz_start_transaction();
- $dbh->do('DELETE FROM bugs_activity WHERE fieldid = ?', undef, $self->id);
- $dbh->do('DELETE FROM fielddefs WHERE name = ?', undef, $self->name);
+ $dbh->do('DELETE FROM bugs_activity WHERE fieldid = ?', undef, $self->id);
+ $dbh->do('DELETE FROM fielddefs WHERE name = ?', undef, $self->name);
- $dbh->bz_commit_transaction();
+ $dbh->bz_commit_transaction();
- # Remove from request cache
- my $cache = Bugzilla->request_cache;
- if (exists $cache->{'tracking_flags'}) {
- delete $cache->{'tracking_flags'}->{$self->flag_id};
- }
- };
- my $error = "$@";
- SetParam('disable_bug_updates', 0);
- write_params();
- die $error if $error;
+ # Remove from request cache
+ my $cache = Bugzilla->request_cache;
+ if (exists $cache->{'tracking_flags'}) {
+ delete $cache->{'tracking_flags'}->{$self->flag_id};
+ }
+ };
+ my $error = "$@";
+ SetParam('disable_bug_updates', 0);
+ write_params();
+ die $error if $error;
}
sub preload_all_the_things {
- my ($flags, $params) = @_;
+ my ($flags, $params) = @_;
- my %flag_hash = map { $_->flag_id => $_ } @$flags;
- my @flag_ids = keys %flag_hash;
- return unless @flag_ids;
+ my %flag_hash = map { $_->flag_id => $_ } @$flags;
+ my @flag_ids = keys %flag_hash;
+ return unless @flag_ids;
- # Preload values
- my $value_objects
- = Bugzilla::Extension::TrackingFlags::Flag::Value->match({ tracking_flag_id => \@flag_ids });
+ # Preload values
+ my $value_objects = Bugzilla::Extension::TrackingFlags::Flag::Value->match(
+ {tracking_flag_id => \@flag_ids});
- # Now populate the tracking flags with this set of value objects.
- foreach my $obj (@$value_objects) {
- my $flag_id = $obj->tracking_flag_id;
+ # Now populate the tracking flags with this set of value objects.
+ foreach my $obj (@$value_objects) {
+ my $flag_id = $obj->tracking_flag_id;
- # Prepopulate the tracking flag object in the value object
- $obj->{'tracking_flag'} = $flag_hash{$flag_id};
+ # Prepopulate the tracking flag object in the value object
+ $obj->{'tracking_flag'} = $flag_hash{$flag_id};
- # Prepopulate the current value objects for this tracking flag
- $flag_hash{$flag_id}->{'values'} ||= [];
- push(@{$flag_hash{$flag_id}->{'values'}}, $obj);
- }
+ # Prepopulate the current value objects for this tracking flag
+ $flag_hash{$flag_id}->{'values'} ||= [];
+ push(@{$flag_hash{$flag_id}->{'values'}}, $obj);
+ }
- # Preload bug values if a bug_id is passed
- if ($params && exists $params->{'bug_id'} && $params->{'bug_id'}) {
- # We don't want to use @flag_ids here as we want all flags attached to this bug
- # even if they are inactive.
- my $bug_objects
- = Bugzilla::Extension::TrackingFlags::Flag::Bug->match({ bug_id => $params->{'bug_id'} });
- # Now populate the tracking flags with this set of objects.
- # Also we add them to the flag hash since we want them to be visible even if
- # they are not longer applicable to this product/component.
- foreach my $obj (@$bug_objects) {
- my $flag_id = $obj->tracking_flag_id;
-
- # Load the flag object if it does not yet exist.
- # This can happen if the bug value tracking flag
- # is no longer visible for the product/component
- $flag_hash{$flag_id}
- ||= Bugzilla::Extension::TrackingFlags::Flag->new($flag_id);
-
- # Prepopulate the tracking flag object in the bug flag object
- $obj->{'tracking_flag'} = $flag_hash{$flag_id};
-
- # Prepopulate the the current bug flag object for the tracking flag
- $flag_hash{$flag_id}->{'bug_flag'} = $obj;
- }
+ # Preload bug values if a bug_id is passed
+ if ($params && exists $params->{'bug_id'} && $params->{'bug_id'}) {
+
+ # We don't want to use @flag_ids here as we want all flags attached to this bug
+ # even if they are inactive.
+ my $bug_objects = Bugzilla::Extension::TrackingFlags::Flag::Bug->match(
+ {bug_id => $params->{'bug_id'}});
+
+ # Now populate the tracking flags with this set of objects.
+ # Also we add them to the flag hash since we want them to be visible even if
+ # they are not longer applicable to this product/component.
+ foreach my $obj (@$bug_objects) {
+ my $flag_id = $obj->tracking_flag_id;
+
+ # Load the flag object if it does not yet exist.
+ # This can happen if the bug value tracking flag
+ # is no longer visible for the product/component
+ $flag_hash{$flag_id}
+ ||= Bugzilla::Extension::TrackingFlags::Flag->new($flag_id);
+
+ # Prepopulate the tracking flag object in the bug flag object
+ $obj->{'tracking_flag'} = $flag_hash{$flag_id};
+
+ # Prepopulate the the current bug flag object for the tracking flag
+ $flag_hash{$flag_id}->{'bug_flag'} = $obj;
}
+ }
- @$flags = values %flag_hash;
+ @$flags = values %flag_hash;
}
###############################
@@ -332,125 +335,126 @@ sub preload_all_the_things {
###############################
sub _check_name {
- my ($invocant, $name) = @_;
- $name = trim($name);
- $name || ThrowCodeError('param_required', { param => 'name' });
- return $name;
+ my ($invocant, $name) = @_;
+ $name = trim($name);
+ $name || ThrowCodeError('param_required', {param => 'name'});
+ return $name;
}
sub _check_description {
- my ($invocant, $description) = @_;
- $description = trim($description);
- $description || ThrowCodeError( 'param_required', { param => 'description' } );
- return $description;
+ my ($invocant, $description) = @_;
+ $description = trim($description);
+ $description || ThrowCodeError('param_required', {param => 'description'});
+ return $description;
}
sub _check_type {
- my ($invocant, $type) = @_;
- $type = trim($type);
- $type || ThrowCodeError( 'param_required', { param => 'type' } );
- grep($_->{name} eq $type, @{FLAG_TYPES()})
- || ThrowUserError('tracking_flags_invalid_flag_type', { type => $type });
- return $type;
+ my ($invocant, $type) = @_;
+ $type = trim($type);
+ $type || ThrowCodeError('param_required', {param => 'type'});
+ grep($_->{name} eq $type, @{FLAG_TYPES()})
+ || ThrowUserError('tracking_flags_invalid_flag_type', {type => $type});
+ return $type;
}
sub _check_sortkey {
- my ($invocant, $sortkey) = @_;
- detaint_natural($sortkey)
- || ThrowUserError('field_invalid_sortkey', { sortkey => $sortkey });
- return $sortkey;
+ my ($invocant, $sortkey) = @_;
+ detaint_natural($sortkey)
+ || ThrowUserError('field_invalid_sortkey', {sortkey => $sortkey});
+ return $sortkey;
}
###############################
#### Setters ####
###############################
-sub set_name { $_[0]->set('name', $_[1]); }
+sub set_name { $_[0]->set('name', $_[1]); }
sub set_description { $_[0]->set('description', $_[1]); }
-sub set_type { $_[0]->set('type', $_[1]); }
-sub set_sortkey { $_[0]->set('sortkey', $_[1]); }
-sub set_enter_bug { $_[0]->set('enter_bug', $_[1]); }
-sub set_is_active { $_[0]->set('is_active', $_[1]); }
+sub set_type { $_[0]->set('type', $_[1]); }
+sub set_sortkey { $_[0]->set('sortkey', $_[1]); }
+sub set_enter_bug { $_[0]->set('enter_bug', $_[1]); }
+sub set_is_active { $_[0]->set('is_active', $_[1]); }
###############################
#### Accessors ####
###############################
-sub flag_id { return $_[0]->{'id'}; }
-sub name { return $_[0]->{'name'}; }
+sub flag_id { return $_[0]->{'id'}; }
+sub name { return $_[0]->{'name'}; }
sub description { return $_[0]->{'description'}; }
-sub flag_type { return $_[0]->{'type'}; }
-sub sortkey { return $_[0]->{'sortkey'}; }
-sub enter_bug { return $_[0]->{'enter_bug'}; }
-sub is_active { return $_[0]->{'is_active'}; }
+sub flag_type { return $_[0]->{'type'}; }
+sub sortkey { return $_[0]->{'sortkey'}; }
+sub enter_bug { return $_[0]->{'enter_bug'}; }
+sub is_active { return $_[0]->{'is_active'}; }
sub values {
- return $_[0]->{'values'} ||= Bugzilla::Extension::TrackingFlags::Flag::Value->match({
- tracking_flag_id => $_[0]->flag_id
- });
+ return $_[0]->{'values'}
+ ||= Bugzilla::Extension::TrackingFlags::Flag::Value->match(
+ {tracking_flag_id => $_[0]->flag_id});
}
sub visibility {
- return $_[0]->{'visibility'} ||= Bugzilla::Extension::TrackingFlags::Flag::Visibility->match({
- tracking_flag_id => $_[0]->flag_id
- });
+ return $_[0]->{'visibility'}
+ ||= Bugzilla::Extension::TrackingFlags::Flag::Visibility->match(
+ {tracking_flag_id => $_[0]->flag_id});
}
sub can_set_value {
- my ($self, $new_value, $user) = @_;
- $user ||= Bugzilla->user;
- my $new_value_obj;
- foreach my $value (@{$self->values}) {
- if ($value->value eq $new_value) {
- $new_value_obj = $value;
- last;
- }
+ my ($self, $new_value, $user) = @_;
+ $user ||= Bugzilla->user;
+ my $new_value_obj;
+ foreach my $value (@{$self->values}) {
+ if ($value->value eq $new_value) {
+ $new_value_obj = $value;
+ last;
}
- return $new_value_obj
- && $new_value_obj->setter_group
- && $user->in_group($new_value_obj->setter_group->name)
- ? 1
- : 0;
+ }
+ return
+ $new_value_obj
+ && $new_value_obj->setter_group
+ && $user->in_group($new_value_obj->setter_group->name) ? 1 : 0;
}
sub bug_flag {
- my ($self, $bug_id) = @_;
- # Return the current bug value object if defined unless the passed bug_id does
- # not equal the current bug value objects id.
- if (defined $self->{'bug_flag'}
- && (!$bug_id || $self->{'bug_flag'}->bug->id == $bug_id))
- {
- return $self->{'bug_flag'};
- }
-
- # Flag::Bug->new will return a default bug value object if $params undefined
- my $params = !$bug_id
- ? undef
- : { condition => "tracking_flag_id = ? AND bug_id = ?",
- values => [ $self->flag_id, $bug_id ] };
- return $self->{'bug_flag'} = Bugzilla::Extension::TrackingFlags::Flag::Bug->new($params);
+ my ($self, $bug_id) = @_;
+
+ # Return the current bug value object if defined unless the passed bug_id does
+ # not equal the current bug value objects id.
+ if (defined $self->{'bug_flag'}
+ && (!$bug_id || $self->{'bug_flag'}->bug->id == $bug_id))
+ {
+ return $self->{'bug_flag'};
+ }
+
+ # Flag::Bug->new will return a default bug value object if $params undefined
+ my $params = !$bug_id
+ ? undef
+ : {
+ condition => "tracking_flag_id = ? AND bug_id = ?",
+ values => [$self->flag_id, $bug_id]
+ };
+ return $self->{'bug_flag'}
+ = Bugzilla::Extension::TrackingFlags::Flag::Bug->new($params);
}
sub bug_count {
- my ($self) = @_;
- return $self->{'bug_count'} if defined $self->{'bug_count'};
- my $dbh = Bugzilla->dbh;
- return $self->{'bug_count'} = scalar $dbh->selectrow_array("
+ my ($self) = @_;
+ return $self->{'bug_count'} if defined $self->{'bug_count'};
+ my $dbh = Bugzilla->dbh;
+ return $self->{'bug_count'} = scalar $dbh->selectrow_array("
SELECT COUNT(bug_id)
FROM tracking_flags_bugs
- WHERE tracking_flag_id = ?",
- undef, $self->flag_id);
+ WHERE tracking_flag_id = ?", undef, $self->flag_id);
}
sub activity_count {
- my ($self) = @_;
- return $self->{'activity_count'} if defined $self->{'activity_count'};
- my $dbh = Bugzilla->dbh;
- return $self->{'activity_count'} = scalar $dbh->selectrow_array("
+ my ($self) = @_;
+ return $self->{'activity_count'} if defined $self->{'activity_count'};
+ my $dbh = Bugzilla->dbh;
+ return $self->{'activity_count'} = scalar $dbh->selectrow_array("
SELECT COUNT(bug_id)
FROM bugs_activity
- WHERE fieldid = ?",
- undef, $self->id);
+ WHERE fieldid = ?", undef, $self->id);
}
######################################
@@ -460,25 +464,25 @@ sub activity_count {
# Here we return 'field_id' instead of the real
# id as we want other Bugzilla code to treat this
# as a Bugzilla::Field object in certain places.
-sub id { return $_[0]->{'field_id'}; }
+sub id { return $_[0]->{'field_id'}; }
sub type { return FIELD_TYPE_EXTENSION; }
-sub legal_values { return $_[0]->values; }
-sub custom { return 1; }
-sub in_new_bugmail { return 1; }
+sub legal_values { return $_[0]->values; }
+sub custom { return 1; }
+sub in_new_bugmail { return 1; }
sub obsolete { return $_[0]->is_active ? 0 : 1; }
-sub buglist { return 1; }
-sub is_select { return 1; }
-sub is_abnormal { return 1; }
-sub is_timetracking { return 0; }
+sub buglist { return 1; }
+sub is_select { return 1; }
+sub is_abnormal { return 1; }
+sub is_timetracking { return 0; }
sub visibility_field { return undef; }
sub visibility_values { return undef; }
sub controls_visibility_of { return undef; }
sub value_field { return undef; }
sub controls_values_of { return undef; }
-sub is_visible_on_bug { return 1; }
-sub is_relationship { return 0; }
-sub reverse_desc { return ''; }
-sub is_mandatory { return 0; }
-sub is_numeric { return 0; }
+sub is_visible_on_bug { return 1; }
+sub is_relationship { return 0; }
+sub reverse_desc { return ''; }
+sub is_mandatory { return 0; }
+sub is_numeric { return 0; }
1;
diff --git a/extensions/TrackingFlags/lib/Flag/Bug.pm b/extensions/TrackingFlags/lib/Flag/Bug.pm
index 7be661720..9d9c5ce8c 100644
--- a/extensions/TrackingFlags/lib/Flag/Bug.pm
+++ b/extensions/TrackingFlags/lib/Flag/Bug.pm
@@ -24,32 +24,26 @@ use Scalar::Util qw(blessed);
#### Initialization ####
###############################
-use constant DEFAULT_FLAG_BUG => {
- 'id' => 0,
- 'tracking_flag_id' => 0,
- 'bug_id' => 0,
- 'value' => '---',
-};
+use constant DEFAULT_FLAG_BUG =>
+ {'id' => 0, 'tracking_flag_id' => 0, 'bug_id' => 0, 'value' => '---',};
use constant DB_TABLE => 'tracking_flags_bugs';
use constant DB_COLUMNS => qw(
- id
- tracking_flag_id
- bug_id
- value
+ id
+ tracking_flag_id
+ bug_id
+ value
);
use constant LIST_ORDER => 'id';
use constant UPDATE_COLUMNS => qw(
- value
+ value
);
-use constant VALIDATORS => {
- tracking_flag_id => \&_check_tracking_flag,
- value => \&_check_value,
-};
+use constant VALIDATORS =>
+ {tracking_flag_id => \&_check_tracking_flag, value => \&_check_value,};
use constant AUDIT_CREATES => 0;
use constant AUDIT_UPDATES => 0;
@@ -60,67 +54,67 @@ use constant AUDIT_REMOVES => 0;
###############################
sub new {
- my $invocant = shift;
- my $class = ref($invocant) || $invocant;
- my ($param) = @_;
-
- my $self;
- if ($param) {
- $self = $class->SUPER::new(@_);
- if (!$self) {
- $self = DEFAULT_FLAG_BUG;
- bless($self, $class);
- }
- }
- else {
- $self = DEFAULT_FLAG_BUG;
- bless($self, $class);
+ my $invocant = shift;
+ my $class = ref($invocant) || $invocant;
+ my ($param) = @_;
+
+ my $self;
+ if ($param) {
+ $self = $class->SUPER::new(@_);
+ if (!$self) {
+ $self = DEFAULT_FLAG_BUG;
+ bless($self, $class);
}
+ }
+ else {
+ $self = DEFAULT_FLAG_BUG;
+ bless($self, $class);
+ }
- return $self
+ return $self;
}
sub match {
- my $class = shift;
- my $bug_flags = $class->SUPER::match(@_);
- preload_all_the_things($bug_flags);
- return $bug_flags;
+ my $class = shift;
+ my $bug_flags = $class->SUPER::match(@_);
+ preload_all_the_things($bug_flags);
+ return $bug_flags;
}
sub remove_from_db {
- my ($self) = @_;
- $self->SUPER::remove_from_db();
- $self->{'id'} = $self->{'tracking_flag_id'} = $self->{'bug_id'} = 0;
- $self->{'value'} = '---';
+ my ($self) = @_;
+ $self->SUPER::remove_from_db();
+ $self->{'id'} = $self->{'tracking_flag_id'} = $self->{'bug_id'} = 0;
+ $self->{'value'} = '---';
}
sub preload_all_the_things {
- my ($bug_flags) = @_;
- my $cache = Bugzilla->request_cache;
-
- # Preload tracking flag objects
- my @tracking_flag_ids;
- foreach my $bug_flag (@$bug_flags) {
- if (exists $cache->{'tracking_flags'}
- && $cache->{'tracking_flags'}->{$bug_flag->tracking_flag_id})
- {
- $bug_flag->{'tracking_flag'}
- = $cache->{'tracking_flags'}->{$bug_flag->tracking_flag_id};
- next;
- }
- push(@tracking_flag_ids, $bug_flag->tracking_flag_id);
+ my ($bug_flags) = @_;
+ my $cache = Bugzilla->request_cache;
+
+ # Preload tracking flag objects
+ my @tracking_flag_ids;
+ foreach my $bug_flag (@$bug_flags) {
+ if (exists $cache->{'tracking_flags'}
+ && $cache->{'tracking_flags'}->{$bug_flag->tracking_flag_id})
+ {
+ $bug_flag->{'tracking_flag'}
+ = $cache->{'tracking_flags'}->{$bug_flag->tracking_flag_id};
+ next;
}
+ push(@tracking_flag_ids, $bug_flag->tracking_flag_id);
+ }
- return unless @tracking_flag_ids;
+ return unless @tracking_flag_ids;
- my $tracking_flags
- = Bugzilla::Extension::TrackingFlags::Flag->match({ id => \@tracking_flag_ids });
- my %tracking_flag_hash = map { $_->flag_id => $_ } @$tracking_flags;
+ my $tracking_flags = Bugzilla::Extension::TrackingFlags::Flag->match(
+ {id => \@tracking_flag_ids});
+ my %tracking_flag_hash = map { $_->flag_id => $_ } @$tracking_flags;
- foreach my $bug_flag (@$bug_flags) {
- next if exists $bug_flag->{'tracking_flag'};
- $bug_flag->{'tracking_flag'} = $tracking_flag_hash{$bug_flag->tracking_flag_id};
- }
+ foreach my $bug_flag (@$bug_flags) {
+ next if exists $bug_flag->{'tracking_flag'};
+ $bug_flag->{'tracking_flag'} = $tracking_flag_hash{$bug_flag->tracking_flag_id};
+ }
}
##############################
@@ -128,15 +122,15 @@ sub preload_all_the_things {
##############################
sub update_all_values {
- my ($invocant, $params) = @_;
- my $dbh = Bugzilla->dbh;
- $dbh->do(
- "UPDATE tracking_flags_bugs SET value=? WHERE tracking_flag_id=? AND value=?",
- undef,
- $params->{new_value},
- $params->{value_obj}->tracking_flag_id,
- $params->{old_value},
- );
+ my ($invocant, $params) = @_;
+ my $dbh = Bugzilla->dbh;
+ $dbh->do(
+ "UPDATE tracking_flags_bugs SET value=? WHERE tracking_flag_id=? AND value=?",
+ undef,
+ $params->{new_value},
+ $params->{value_obj}->tracking_flag_id,
+ $params->{old_value},
+ );
}
###############################
@@ -144,19 +138,21 @@ sub update_all_values {
###############################
sub _check_value {
- my ($invocant, $value) = @_;
- $value || ThrowCodeError('param_required', { param => 'value' });
- return $value;
+ my ($invocant, $value) = @_;
+ $value || ThrowCodeError('param_required', {param => 'value'});
+ return $value;
}
sub _check_tracking_flag {
- my ($invocant, $flag) = @_;
- if (blessed $flag) {
- return $flag->flag_id;
- }
- $flag = Bugzilla::Extension::TrackingFlags::Flag->new({ id => $flag, cache => 1 })
- || ThrowCodeError('tracking_flags_invalid_param', { name => 'flag_id', value => $flag });
+ my ($invocant, $flag) = @_;
+ if (blessed $flag) {
return $flag->flag_id;
+ }
+ $flag
+ = Bugzilla::Extension::TrackingFlags::Flag->new({id => $flag, cache => 1})
+ || ThrowCodeError('tracking_flags_invalid_param',
+ {name => 'flag_id', value => $flag});
+ return $flag->flag_id;
}
###############################
@@ -170,19 +166,17 @@ sub set_value { $_[0]->set('value', $_[1]); }
###############################
sub tracking_flag_id { return $_[0]->{'tracking_flag_id'}; }
-sub bug_id { return $_[0]->{'bug_id'}; }
-sub value { return $_[0]->{'value'}; }
+sub bug_id { return $_[0]->{'bug_id'}; }
+sub value { return $_[0]->{'value'}; }
sub bug {
- return $_[0]->{'bug'} ||= Bugzilla::Bug->new({
- id => $_[0]->bug_id, cache => 1
- });
+ return $_[0]->{'bug'} ||= Bugzilla::Bug->new({id => $_[0]->bug_id, cache => 1});
}
sub tracking_flag {
- return $_[0]->{'tracking_flag'} ||= Bugzilla::Extension::TrackingFlags::Flag->new({
- id => $_[0]->tracking_flag_id, cache => 1
- });
+ return $_[0]->{'tracking_flag'}
+ ||= Bugzilla::Extension::TrackingFlags::Flag->new(
+ {id => $_[0]->tracking_flag_id, cache => 1});
}
1;
diff --git a/extensions/TrackingFlags/lib/Flag/Value.pm b/extensions/TrackingFlags/lib/Flag/Value.pm
index 4f2aacc3a..52d63970d 100644
--- a/extensions/TrackingFlags/lib/Flag/Value.pm
+++ b/extensions/TrackingFlags/lib/Flag/Value.pm
@@ -25,32 +25,32 @@ use Scalar::Util qw(blessed weaken);
use constant DB_TABLE => 'tracking_flags_values';
use constant DB_COLUMNS => qw(
- id
- tracking_flag_id
- setter_group_id
- value
- sortkey
- is_active
- comment
+ id
+ tracking_flag_id
+ setter_group_id
+ value
+ sortkey
+ is_active
+ comment
);
use constant LIST_ORDER => 'sortkey';
use constant UPDATE_COLUMNS => qw(
- setter_group_id
- value
- sortkey
- is_active
- comment
+ setter_group_id
+ value
+ sortkey
+ is_active
+ comment
);
use constant VALIDATORS => {
- tracking_flag_id => \&_check_tracking_flag,
- setter_group_id => \&_check_setter_group,
- value => \&_check_value,
- sortkey => \&_check_sortkey,
- is_active => \&Bugzilla::Object::check_boolean,
- comment => \&_check_comment,
+ tracking_flag_id => \&_check_tracking_flag,
+ setter_group_id => \&_check_setter_group,
+ value => \&_check_value,
+ sortkey => \&_check_sortkey,
+ is_active => \&Bugzilla::Object::check_boolean,
+ comment => \&_check_comment,
};
###############################
@@ -58,43 +58,47 @@ use constant VALIDATORS => {
###############################
sub _check_value {
- my ($invocant, $value) = @_;
- defined $value || ThrowCodeError('param_required', { param => 'value' });
- return $value;
+ my ($invocant, $value) = @_;
+ defined $value || ThrowCodeError('param_required', {param => 'value'});
+ return $value;
}
sub _check_tracking_flag {
- my ($invocant, $flag) = @_;
- if (blessed $flag) {
- return $flag->flag_id;
- }
- $flag = Bugzilla::Extension::TrackingFlags::Flag->new({ id => $flag, cache => 1 })
- || ThrowCodeError('tracking_flags_invalid_param', { name => 'flag_id', value => $flag });
+ my ($invocant, $flag) = @_;
+ if (blessed $flag) {
return $flag->flag_id;
+ }
+ $flag
+ = Bugzilla::Extension::TrackingFlags::Flag->new({id => $flag, cache => 1})
+ || ThrowCodeError('tracking_flags_invalid_param',
+ {name => 'flag_id', value => $flag});
+ return $flag->flag_id;
}
sub _check_setter_group {
- my ($invocant, $group) = @_;
- if (blessed $group) {
- return $group->id;
- }
- $group = Bugzilla::Group->new({ id => $group, cache => 1 })
- || ThrowCodeError('tracking_flags_invalid_param', { name => 'setter_group_id', value => $group });
+ my ($invocant, $group) = @_;
+ if (blessed $group) {
return $group->id;
+ }
+ $group
+ = Bugzilla::Group->new({id => $group, cache => 1})
+ || ThrowCodeError('tracking_flags_invalid_param',
+ {name => 'setter_group_id', value => $group});
+ return $group->id;
}
sub _check_sortkey {
- my ($invocant, $sortkey) = @_;
- detaint_natural($sortkey)
- || ThrowUserError('field_invalid_sortkey', { sortkey => $sortkey });
- return $sortkey;
+ my ($invocant, $sortkey) = @_;
+ detaint_natural($sortkey)
+ || ThrowUserError('field_invalid_sortkey', {sortkey => $sortkey});
+ return $sortkey;
}
sub _check_comment {
- my ($invocant, $value) = @_;
- return undef unless defined $value;
- $value = trim($value);
- return $value eq '' ? undef : $value;
+ my ($invocant, $value) = @_;
+ return undef unless defined $value;
+ $value = trim($value);
+ return $value eq '' ? undef : $value;
}
###############################
@@ -102,38 +106,37 @@ sub _check_comment {
###############################
sub set_setter_group_id { $_[0]->set('setter_group_id', $_[1]); }
-sub set_value { $_[0]->set('value', $_[1]); }
-sub set_sortkey { $_[0]->set('sortkey', $_[1]); }
-sub set_is_active { $_[0]->set('is_active', $_[1]); }
-sub set_comment { $_[0]->set('comment', $_[1]); }
+sub set_value { $_[0]->set('value', $_[1]); }
+sub set_sortkey { $_[0]->set('sortkey', $_[1]); }
+sub set_is_active { $_[0]->set('is_active', $_[1]); }
+sub set_comment { $_[0]->set('comment', $_[1]); }
###############################
#### Accessors ####
###############################
sub tracking_flag_id { return $_[0]->{'tracking_flag_id'}; }
-sub setter_group_id { return $_[0]->{'setter_group_id'}; }
-sub value { return $_[0]->{'value'}; }
-sub sortkey { return $_[0]->{'sortkey'}; }
-sub is_active { return $_[0]->{'is_active'}; }
-sub comment { return $_[0]->{'comment'}; }
+sub setter_group_id { return $_[0]->{'setter_group_id'}; }
+sub value { return $_[0]->{'value'}; }
+sub sortkey { return $_[0]->{'sortkey'}; }
+sub is_active { return $_[0]->{'is_active'}; }
+sub comment { return $_[0]->{'comment'}; }
sub tracking_flag {
- return $_[0]->{'tracking_flag'} if $_[0]->{'tracking_flag'};
- my $tf = $_[0]->{'tracking_flag'} = Bugzilla::Extension::TrackingFlags::Flag->new({
- id => $_[0]->tracking_flag_id, cache => 1
- });
- weaken($_[0]->{'tracking_flag'});
- return $tf;
+ return $_[0]->{'tracking_flag'} if $_[0]->{'tracking_flag'};
+ my $tf = $_[0]->{'tracking_flag'}
+ = Bugzilla::Extension::TrackingFlags::Flag->new(
+ {id => $_[0]->tracking_flag_id, cache => 1});
+ weaken($_[0]->{'tracking_flag'});
+ return $tf;
}
sub setter_group {
- if ($_[0]->setter_group_id) {
- $_[0]->{'setter_group'} ||= Bugzilla::Group->new({
- id => $_[0]->setter_group_id, cache => 1
- });
- }
- return $_[0]->{'setter_group'};
+ if ($_[0]->setter_group_id) {
+ $_[0]->{'setter_group'}
+ ||= Bugzilla::Group->new({id => $_[0]->setter_group_id, cache => 1});
+ }
+ return $_[0]->{'setter_group'};
}
########################################
@@ -141,6 +144,6 @@ sub setter_group {
########################################
sub name { return $_[0]->{'value'}; }
-sub is_visible_on_bug { return 1; }
+sub is_visible_on_bug { return 1; }
1;
diff --git a/extensions/TrackingFlags/lib/Flag/Visibility.pm b/extensions/TrackingFlags/lib/Flag/Visibility.pm
index 878c16f99..e90c0bf22 100644
--- a/extensions/TrackingFlags/lib/Flag/Visibility.pm
+++ b/extensions/TrackingFlags/lib/Flag/Visibility.pm
@@ -25,20 +25,20 @@ use Scalar::Util qw(blessed);
use constant DB_TABLE => 'tracking_flags_visibility';
use constant DB_COLUMNS => qw(
- id
- tracking_flag_id
- product_id
- component_id
+ id
+ tracking_flag_id
+ product_id
+ component_id
);
use constant LIST_ORDER => 'id';
-use constant UPDATE_COLUMNS => (); # imutable
+use constant UPDATE_COLUMNS => (); # imutable
use constant VALIDATORS => {
- tracking_flag_id => \&_check_tracking_flag,
- product_id => \&_check_product,
- component_id => \&_check_component,
+ tracking_flag_id => \&_check_tracking_flag,
+ product_id => \&_check_product,
+ component_id => \&_check_component,
};
###############################
@@ -46,66 +46,72 @@ use constant VALIDATORS => {
###############################
sub match {
- my $class= shift;
- my ($params) = @_;
- my $dbh = Bugzilla->dbh;
-
- # Allow matching component and product by name
- # (in addition to matching by ID).
- # Borrowed from Bugzilla::Bug::match
- my %translate_fields = (
- product => 'Bugzilla::Product',
- component => 'Bugzilla::Component',
- );
-
- foreach my $field (keys %translate_fields) {
- my @ids;
- # Convert names to ids. We use "exists" everywhere since people can
- # legally specify "undef" to mean IS NULL
- if (exists $params->{$field}) {
- my $names = $params->{$field};
- my $type = $translate_fields{$field};
- my $objects = Bugzilla::Object::match($type, { name => $names });
- push(@ids, map { $_->id } @$objects);
- }
- # You can also specify ids directly as arguments to this function,
- # so include them in the list if they have been specified.
- if (exists $params->{"${field}_id"}) {
- my $current_ids = $params->{"${field}_id"};
- my @id_array = ref $current_ids ? @$current_ids : ($current_ids);
- push(@ids, @id_array);
- }
- # We do this "or" instead of a "scalar(@ids)" to handle the case
- # when people passed only invalid object names. Otherwise we'd
- # end up with a SUPER::match call with zero criteria (which dies).
- if (exists $params->{$field} or exists $params->{"${field}_id"}) {
- delete $params->{$field};
- $params->{"${field}_id"} = scalar(@ids) == 1 ? [ $ids[0] ] : \@ids;
- }
+ my $class = shift;
+ my ($params) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ # Allow matching component and product by name
+ # (in addition to matching by ID).
+ # Borrowed from Bugzilla::Bug::match
+ my %translate_fields
+ = (product => 'Bugzilla::Product', component => 'Bugzilla::Component',);
+
+ foreach my $field (keys %translate_fields) {
+ my @ids;
+
+ # Convert names to ids. We use "exists" everywhere since people can
+ # legally specify "undef" to mean IS NULL
+ if (exists $params->{$field}) {
+ my $names = $params->{$field};
+ my $type = $translate_fields{$field};
+ my $objects = Bugzilla::Object::match($type, {name => $names});
+ push(@ids, map { $_->id } @$objects);
}
- # If we aren't matching on the product, use the default matching code
- if (!exists $params->{product_id}) {
- return $class->SUPER::match(@_);
+ # You can also specify ids directly as arguments to this function,
+ # so include them in the list if they have been specified.
+ if (exists $params->{"${field}_id"}) {
+ my $current_ids = $params->{"${field}_id"};
+ my @id_array = ref $current_ids ? @$current_ids : ($current_ids);
+ push(@ids, @id_array);
}
- my @criteria = ("1=1");
-
- if ($params->{product_id}) {
- push(@criteria, $dbh->sql_in('product_id', $params->{'product_id'}));
- if ($params->{component_id}) {
- my $component_id = $params->{component_id};
- push(@criteria, "(" . $dbh->sql_in('component_id', $params->{'component_id'}) .
- " OR component_id IS NULL)");
- }
+ # We do this "or" instead of a "scalar(@ids)" to handle the case
+ # when people passed only invalid object names. Otherwise we'd
+ # end up with a SUPER::match call with zero criteria (which dies).
+ if (exists $params->{$field} or exists $params->{"${field}_id"}) {
+ delete $params->{$field};
+ $params->{"${field}_id"} = scalar(@ids) == 1 ? [$ids[0]] : \@ids;
+ }
+ }
+
+ # If we aren't matching on the product, use the default matching code
+ if (!exists $params->{product_id}) {
+ return $class->SUPER::match(@_);
+ }
+
+ my @criteria = ("1=1");
+
+ if ($params->{product_id}) {
+ push(@criteria, $dbh->sql_in('product_id', $params->{'product_id'}));
+ if ($params->{component_id}) {
+ my $component_id = $params->{component_id};
+ push(@criteria,
+ "("
+ . $dbh->sql_in('component_id', $params->{'component_id'})
+ . " OR component_id IS NULL)");
}
+ }
- my $where = join(' AND ', @criteria);
- my $flag_ids = $dbh->selectcol_arrayref("SELECT id
+ my $where = join(' AND ', @criteria);
+ my $flag_ids = $dbh->selectcol_arrayref(
+ "SELECT id
FROM tracking_flags_visibility
- WHERE $where");
+ WHERE $where"
+ );
- return Bugzilla::Extension::TrackingFlags::Flag::Visibility->new_from_list($flag_ids);
+ return Bugzilla::Extension::TrackingFlags::Flag::Visibility->new_from_list(
+ $flag_ids);
}
###############################
@@ -113,34 +119,40 @@ sub match {
###############################
sub _check_tracking_flag {
- my ($invocant, $flag) = @_;
- if (blessed $flag) {
- return $flag->flag_id;
- }
- $flag = Bugzilla::Extension::TrackingFlags::Flag->new($flag)
- || ThrowCodeError('tracking_flags_invalid_param', { name => 'flag_id', value => $flag });
+ my ($invocant, $flag) = @_;
+ if (blessed $flag) {
return $flag->flag_id;
+ }
+ $flag
+ = Bugzilla::Extension::TrackingFlags::Flag->new($flag)
+ || ThrowCodeError('tracking_flags_invalid_param',
+ {name => 'flag_id', value => $flag});
+ return $flag->flag_id;
}
sub _check_product {
- my ($invocant, $product) = @_;
- if (blessed $product) {
- return $product->id;
- }
- $product = Bugzilla::Product->new($product)
- || ThrowCodeError('tracking_flags_invalid_param', { name => 'product_id', value => $product });
+ my ($invocant, $product) = @_;
+ if (blessed $product) {
return $product->id;
+ }
+ $product
+ = Bugzilla::Product->new($product)
+ || ThrowCodeError('tracking_flags_invalid_param',
+ {name => 'product_id', value => $product});
+ return $product->id;
}
sub _check_component {
- my ($invocant, $component) = @_;
- return undef unless defined $component;
- if (blessed $component) {
- return $component->id;
- }
- $component = Bugzilla::Component->new($component)
- || ThrowCodeError('tracking_flags_invalid_param', { name => 'component_id', value => $component });
+ my ($invocant, $component) = @_;
+ return undef unless defined $component;
+ if (blessed $component) {
return $component->id;
+ }
+ $component
+ = Bugzilla::Component->new($component)
+ || ThrowCodeError('tracking_flags_invalid_param',
+ {name => 'component_id', value => $component});
+ return $component->id;
}
###############################
@@ -148,26 +160,27 @@ sub _check_component {
###############################
sub tracking_flag_id { return $_[0]->{'tracking_flag_id'}; }
-sub product_id { return $_[0]->{'product_id'}; }
-sub component_id { return $_[0]->{'component_id'}; }
+sub product_id { return $_[0]->{'product_id'}; }
+sub component_id { return $_[0]->{'component_id'}; }
sub tracking_flag {
- my ($self) = @_;
- $self->{'tracking_flag'} ||= Bugzilla::Extension::TrackingFlags::Flag->new($self->tracking_flag_id);
- return $self->{'tracking_flag'};
+ my ($self) = @_;
+ $self->{'tracking_flag'}
+ ||= Bugzilla::Extension::TrackingFlags::Flag->new($self->tracking_flag_id);
+ return $self->{'tracking_flag'};
}
sub product {
- my ($self) = @_;
- $self->{'product'} ||= Bugzilla::Product->new($self->product_id);
- return $self->{'product'};
+ my ($self) = @_;
+ $self->{'product'} ||= Bugzilla::Product->new($self->product_id);
+ return $self->{'product'};
}
sub component {
- my ($self) = @_;
- return undef unless $self->component_id;
- $self->{'component'} ||= Bugzilla::Component->new($self->component_id);
- return $self->{'component'};
+ my ($self) = @_;
+ return undef unless $self->component_id;
+ $self->{'component'} ||= Bugzilla::Component->new($self->component_id);
+ return $self->{'component'};
}
1;
diff --git a/extensions/TypeSniffer/Config.pm b/extensions/TypeSniffer/Config.pm
index 4545f69f6..336f92c8f 100644
--- a/extensions/TypeSniffer/Config.pm
+++ b/extensions/TypeSniffer/Config.pm
@@ -28,16 +28,8 @@ use warnings;
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 6c34cb169..52170f683 100644
--- a/extensions/TypeSniffer/Extension.pm
+++ b/extensions/TypeSniffer/Extension.pm
@@ -33,9 +33,7 @@ use IO::Scalar;
our $VERSION = '1';
# These extensions override/supplement File::MimeInfo::Magic's detection.
-our %EXTENSION_OVERRIDES = (
- '.lang' => 'text/plain',
-);
+our %EXTENSION_OVERRIDES = ('.lang' => 'text/plain',);
################################################################################
# This extension uses magic to guess MIME types for data where the browser has
@@ -43,62 +41,63 @@ our %EXTENSION_OVERRIDES = (
# 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;
+ 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'} &&
- $params->{'contenttypemethod'} eq 'autodetect' &&
- $attributes->{'mimetype'} eq 'application/octet-stream')
- {
- my $filename = $attributes->{'filename'} . '';
+ # If we have autodetected application/octet-stream from the Content-Type
+ # header, let's have a better go using a sniffer.
+ if ( $params->{'contenttypemethod'}
+ && $params->{'contenttypemethod'} eq 'autodetect'
+ && $attributes->{'mimetype'} eq 'application/octet-stream')
+ {
+ my $filename = $attributes->{'filename'} . '';
- # Check for an override first
- if ($filename =~ /^.+(\..+$)/) {
- my $ext = lc($1);
- if (exists $EXTENSION_OVERRIDES{$ext}) {
- $attributes->{'mimetype'} = $EXTENSION_OVERRIDES{$ext};
- return;
- }
- }
+ # Check for an override first
+ if ($filename =~ /^.+(\..+$)/) {
+ my $ext = lc($1);
+ if (exists $EXTENSION_OVERRIDES{$ext}) {
+ $attributes->{'mimetype'} = $EXTENSION_OVERRIDES{$ext};
+ return;
+ }
+ }
- # Then try file extension detection
- my $mimetype = mimetype($filename);
- if ($mimetype) {
- $attributes->{'mimetype'} = $mimetype;
- return;
- }
+ # Then try file extension detection
+ my $mimetype = mimetype($filename);
+ if ($mimetype) {
+ $attributes->{'mimetype'} = $mimetype;
+ return;
+ }
- # 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';
- }
- }
- }
+ # 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;
- $mimetype = mimetype($fh);
- $fh->seek(0, 0);
- if ($mimetype) {
- $attributes->{'mimetype'} = $mimetype;
+ # ->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';
}
+ }
+ }
+
+ $mimetype = mimetype($fh);
+ $fh->seek(0, 0);
+ if ($mimetype) {
+ $attributes->{'mimetype'} = $mimetype;
}
+ }
}
__PACKAGE__->NAME;
diff --git a/extensions/UserProfile/Config.pm b/extensions/UserProfile/Config.pm
index 99fae1610..83b562163 100644
--- a/extensions/UserProfile/Config.pm
+++ b/extensions/UserProfile/Config.pm
@@ -11,8 +11,8 @@ use 5.10.1;
use strict;
use warnings;
-use constant NAME => 'UserProfile';
-use constant REQUIRED_MODULES => [ ];
-use constant OPTIONAL_MODULES => [ ];
+use constant NAME => 'UserProfile';
+use constant REQUIRED_MODULES => [];
+use constant OPTIONAL_MODULES => [];
__PACKAGE__->NAME;
diff --git a/extensions/UserProfile/Extension.pm b/extensions/UserProfile/Extension.pm
index 9171b942d..cc8be3f1f 100644
--- a/extensions/UserProfile/Extension.pm
+++ b/extensions/UserProfile/Extension.pm
@@ -29,44 +29,43 @@ our $VERSION = '1';
#
BEGIN {
- *Bugzilla::User::last_activity_ts = \&_user_last_activity_ts;
- *Bugzilla::User::set_last_activity_ts = \&_user_set_last_activity_ts;
- *Bugzilla::User::last_statistics_ts = \&_user_last_statistics_ts;
- *Bugzilla::User::clear_last_statistics_ts = \&_user_clear_last_statistics_ts;
- *Bugzilla::User::address = \&_user_address;
+ *Bugzilla::User::last_activity_ts = \&_user_last_activity_ts;
+ *Bugzilla::User::set_last_activity_ts = \&_user_set_last_activity_ts;
+ *Bugzilla::User::last_statistics_ts = \&_user_last_statistics_ts;
+ *Bugzilla::User::clear_last_statistics_ts = \&_user_clear_last_statistics_ts;
+ *Bugzilla::User::address = \&_user_address;
}
-sub _user_last_activity_ts { $_[0]->{last_activity_ts} }
-sub _user_last_statistics_ts { $_[0]->{last_statistics_ts} }
+sub _user_last_activity_ts { $_[0]->{last_activity_ts} }
+sub _user_last_statistics_ts { $_[0]->{last_statistics_ts} }
+
sub _user_address {
- my $mode = Bugzilla->usage_mode;
+ my $mode = Bugzilla->usage_mode;
- Email::Address->disable_cache if any { $mode == $_ } USAGE_MODE_CMDLINE, USAGE_MODE_TEST, USAGE_MODE_EMAIL;
- return Email::Address->new(undef, $_[0]->email);
+ Email::Address->disable_cache
+ if any { $mode == $_ } USAGE_MODE_CMDLINE, USAGE_MODE_TEST, USAGE_MODE_EMAIL;
+ return Email::Address->new(undef, $_[0]->email);
}
-sub _user_set_last_activity_ts {
- my ($self, $value) = @_;
- $self->set('last_activity_ts', $_[1]);
+sub _user_set_last_activity_ts {
+ my ($self, $value) = @_;
+ $self->set('last_activity_ts', $_[1]);
- # we update the database directly to avoid audit_log entries
- Bugzilla->dbh->do(
- "UPDATE profiles SET last_activity_ts = ? WHERE userid = ?",
- undef,
- $value, $self->id);
- Bugzilla->memcached->clear({ table => 'profiles', id => $self->id });
+ # we update the database directly to avoid audit_log entries
+ Bugzilla->dbh->do("UPDATE profiles SET last_activity_ts = ? WHERE userid = ?",
+ undef, $value, $self->id);
+ Bugzilla->memcached->clear({table => 'profiles', id => $self->id});
}
sub _user_clear_last_statistics_ts {
- my ($self) = @_;
- $self->set('last_statistics_ts', undef);
-
- # we update the database directly to avoid audit_log entries
- Bugzilla->dbh->do(
- "UPDATE profiles SET last_statistics_ts = NULL WHERE userid = ?",
- undef,
- $self->id);
- Bugzilla->memcached->clear({ table => 'profiles', id => $self->id });
+ my ($self) = @_;
+ $self->set('last_statistics_ts', undef);
+
+ # we update the database directly to avoid audit_log entries
+ Bugzilla->dbh->do(
+ "UPDATE profiles SET last_statistics_ts = NULL WHERE userid = ?",
+ undef, $self->id);
+ Bugzilla->memcached->clear({table => 'profiles', id => $self->id});
}
#
@@ -76,327 +75,331 @@ sub _user_clear_last_statistics_ts {
sub request_cleanup { Email::Address->purge_cache }
sub bug_after_create {
- my ($self, $args) = @_;
- $self->_bug_touched($args);
+ my ($self, $args) = @_;
+ $self->_bug_touched($args);
}
sub bug_after_update {
- my ($self, $args) = @_;
- $self->_bug_touched($args);
+ my ($self, $args) = @_;
+ $self->_bug_touched($args);
}
sub _bug_touched {
- my ($self, $args) = @_;
- my $bug = $args->{bug};
-
- my $user = Bugzilla->user;
- my ($assigned_to, $qa_contact);
-
- # bug update
- if (exists $args->{changes}) {
- return unless
- scalar(keys %{ $args->{changes} })
- || exists $args->{bug}->{added_comments};
-
- # if the assignee or qa-contact is changed to someone other than the
- # current user, update them
- if (exists $args->{changes}->{assigned_to}
- && $args->{changes}->{assigned_to}->[1] ne $user->login)
- {
- $assigned_to = $bug->assigned_to;
- }
- if (exists $args->{changes}->{qa_contact}
- && ($args->{changes}->{qa_contact}->[1] || '') ne $user->login)
- {
- $qa_contact = $bug->qa_contact;
- }
-
- # if the product is changed, we need to recount everyone involved with
- # this bug
- if (exists $args->{changes}->{product}) {
- tag_for_recount_from_bug($bug->id);
- }
-
+ my ($self, $args) = @_;
+ my $bug = $args->{bug};
+
+ my $user = Bugzilla->user;
+ my ($assigned_to, $qa_contact);
+
+ # bug update
+ if (exists $args->{changes}) {
+ return
+ unless scalar(keys %{$args->{changes}})
+ || exists $args->{bug}->{added_comments};
+
+ # if the assignee or qa-contact is changed to someone other than the
+ # current user, update them
+ if (exists $args->{changes}->{assigned_to}
+ && $args->{changes}->{assigned_to}->[1] ne $user->login)
+ {
+ $assigned_to = $bug->assigned_to;
}
- # new bug
- else {
- # if the assignee or qa-contact is created set to someone other than
- # the current user, update them
- if ($bug->assigned_to->id != $user->id) {
- $assigned_to = $bug->assigned_to;
- }
- if ($bug->qa_contact && $bug->qa_contact->id != $user->id) {
- $qa_contact = $bug->qa_contact;
- }
+ if (exists $args->{changes}->{qa_contact}
+ && ($args->{changes}->{qa_contact}->[1] || '') ne $user->login)
+ {
+ $qa_contact = $bug->qa_contact;
}
- my $dbh = Bugzilla->dbh;
- $dbh->bz_start_transaction();
+ # if the product is changed, we need to recount everyone involved with
+ # this bug
+ if (exists $args->{changes}->{product}) {
+ tag_for_recount_from_bug($bug->id);
+ }
- # update user's last_activity_ts
+ }
+
+ # new bug
+ else {
+ # if the assignee or qa-contact is created set to someone other than
+ # the current user, update them
+ if ($bug->assigned_to->id != $user->id) {
+ $assigned_to = $bug->assigned_to;
+ }
+ if ($bug->qa_contact && $bug->qa_contact->id != $user->id) {
+ $qa_contact = $bug->qa_contact;
+ }
+ }
+
+ my $dbh = Bugzilla->dbh;
+ $dbh->bz_start_transaction();
+
+ # update user's last_activity_ts
+ eval {
+ $user->set_last_activity_ts($args->{timestamp});
+ $self->_recalc_remove($user);
+ };
+ if ($@) {
+ warn $@;
+ $self->_recalc_insert($user);
+ }
+
+ # clear the last_statistics_ts for assignee/qa-contact to force a recount
+ # at the next poll
+ if ($assigned_to) {
eval {
- $user->set_last_activity_ts($args->{timestamp});
- $self->_recalc_remove($user);
+ $assigned_to->clear_last_statistics_ts();
+ $self->_recalc_remove($assigned_to);
};
if ($@) {
- warn $@;
- $self->_recalc_insert($user);
+ warn $@;
+ $self->_recalc_insert($assigned_to);
}
-
- # clear the last_statistics_ts for assignee/qa-contact to force a recount
- # at the next poll
- if ($assigned_to) {
- eval {
- $assigned_to->clear_last_statistics_ts();
- $self->_recalc_remove($assigned_to);
- };
- if ($@) {
- warn $@;
- $self->_recalc_insert($assigned_to);
- }
- }
- if ($qa_contact) {
- eval {
- $qa_contact->clear_last_statistics_ts();
- $self->_recalc_remove($qa_contact);
- };
- if ($@) {
- warn $@;
- $self->_recalc_insert($qa_contact);
- }
+ }
+ if ($qa_contact) {
+ eval {
+ $qa_contact->clear_last_statistics_ts();
+ $self->_recalc_remove($qa_contact);
+ };
+ if ($@) {
+ warn $@;
+ $self->_recalc_insert($qa_contact);
}
+ }
- $dbh->bz_commit_transaction();
+ $dbh->bz_commit_transaction();
}
sub _recalc_insert {
- my ($self, $user) = @_;
- Bugzilla->dbh->do(
- "INSERT IGNORE INTO profiles_statistics_recalc SET user_id=?",
- undef, $user->id
- );
+ my ($self, $user) = @_;
+ Bugzilla->dbh->do("INSERT IGNORE INTO profiles_statistics_recalc SET user_id=?",
+ undef, $user->id);
}
sub _recalc_remove {
- my ($self, $user) = @_;
- Bugzilla->dbh->do(
- "DELETE FROM profiles_statistics_recalc WHERE user_id=?",
- undef, $user->id
- );
+ my ($self, $user) = @_;
+ Bugzilla->dbh->do("DELETE FROM profiles_statistics_recalc WHERE user_id=?",
+ undef, $user->id);
}
sub object_end_of_create {
- my ($self, $args) = @_;
- $self->_object_touched($args);
+ my ($self, $args) = @_;
+ $self->_object_touched($args);
}
sub object_end_of_update {
- my ($self, $args) = @_;
- $self->_object_touched($args);
+ my ($self, $args) = @_;
+ $self->_object_touched($args);
}
sub _object_touched {
- my ($self, $args) = @_;
- my $object = $args->{object}
- or return;
- return if exists $args->{changes} && !scalar(keys %{ $args->{changes} });
-
- if ($object->isa('Bugzilla::Attachment')) {
- # if an attachment is created or updated, that counts as user activity
- my $user = Bugzilla->user;
- my $timestamp = Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
- eval {
- $user->set_last_activity_ts($timestamp);
- $self->_recalc_remove($user);
- };
- if ($@) {
- warn $@;
- $self->_recalc_insert($user);
- }
- }
- elsif ($object->isa('Bugzilla::Product') && exists $args->{changes}->{name}) {
- # if a product is renamed by an admin, rename in the
- # profiles_statistics_products table
- Bugzilla->dbh->do(
- "UPDATE profiles_statistics_products SET product=? where product=?",
- undef,
- $args->{changes}->{name}->[1], $args->{changes}->{name}->[0],
- );
+ my ($self, $args) = @_;
+ my $object = $args->{object} or return;
+ return if exists $args->{changes} && !scalar(keys %{$args->{changes}});
+
+ if ($object->isa('Bugzilla::Attachment')) {
+
+ # if an attachment is created or updated, that counts as user activity
+ my $user = Bugzilla->user;
+ my $timestamp = Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
+ eval {
+ $user->set_last_activity_ts($timestamp);
+ $self->_recalc_remove($user);
+ };
+ if ($@) {
+ warn $@;
+ $self->_recalc_insert($user);
}
+ }
+ elsif ($object->isa('Bugzilla::Product') && exists $args->{changes}->{name}) {
+
+ # if a product is renamed by an admin, rename in the
+ # profiles_statistics_products table
+ Bugzilla->dbh->do(
+ "UPDATE profiles_statistics_products SET product=? where product=?",
+ undef,
+ $args->{changes}->{name}->[1],
+ $args->{changes}->{name}->[0],
+ );
+ }
}
sub reorg_move_bugs {
- my ($self, $args) = @_;
- my $bug_ids = $args->{bug_ids};
- printf "Touching user profile data for %s bugs.\n", scalar(@$bug_ids);
- my $count = 0;
- foreach my $bug_id (@$bug_ids) {
- $count += tag_for_recount_from_bug($bug_id);
- }
- print "Updated $count users.\n";
+ my ($self, $args) = @_;
+ my $bug_ids = $args->{bug_ids};
+ printf "Touching user profile data for %s bugs.\n", scalar(@$bug_ids);
+ my $count = 0;
+ foreach my $bug_id (@$bug_ids) {
+ $count += tag_for_recount_from_bug($bug_id);
+ }
+ print "Updated $count users.\n";
}
sub merge_users_before {
- my ($self, $args) = @_;
- my ($old_id, $new_id) = @$args{qw(old_id new_id)};
- # when users are merged, we have to delete all the statistics for both users
- # we'll recalcuate the stats after the merge
- print "deleting user profile statistics for $old_id and $new_id\n";
- my $dbh = Bugzilla->dbh;
- foreach my $table (qw( profiles_statistics profiles_statistics_status profiles_statistics_products )) {
- $dbh->do("DELETE FROM $table WHERE " . $dbh->sql_in('user_id', [ $old_id, $new_id ]));
- }
+ my ($self, $args) = @_;
+ my ($old_id, $new_id) = @$args{qw(old_id new_id)};
+
+ # when users are merged, we have to delete all the statistics for both users
+ # we'll recalcuate the stats after the merge
+ print "deleting user profile statistics for $old_id and $new_id\n";
+ my $dbh = Bugzilla->dbh;
+ foreach my $table (
+ qw( profiles_statistics profiles_statistics_status profiles_statistics_products )
+ )
+ {
+ $dbh->do(
+ "DELETE FROM $table WHERE " . $dbh->sql_in('user_id', [$old_id, $new_id]));
+ }
}
sub merge_users_after {
- my ($self, $args) = @_;
- my $new_id = $args->{new_id};
- print "generating user profile statistics $new_id\n";
- update_statistics_by_user($new_id);
+ my ($self, $args) = @_;
+ my $new_id = $args->{new_id};
+ print "generating user profile statistics $new_id\n";
+ update_statistics_by_user($new_id);
}
sub webservice_user_get {
- my ($self, $args) = @_;
- my ($service, $users) = @$args{qw(webservice users)};
-
- my $dbh = Bugzilla->dbh;
- my $ids = [
- map { blessed($_->{id}) ? $_->{id}->value : $_->{id} }
- grep { exists $_->{id} }
- @$users
- ];
- return unless @$ids;
- my $timestamps = $dbh->selectall_hashref(
- "SELECT userid,last_activity_ts FROM profiles WHERE " . $dbh->sql_in('userid', $ids),
- 'userid',
- );
- foreach my $user (@$users) {
- my $id = blessed($user->{id}) ? $user->{id}->value : $user->{id};
- $user->{last_activity} = $service->type('dateTime', $timestamps->{$id}->{last_activity_ts});
- }
+ my ($self, $args) = @_;
+ my ($service, $users) = @$args{qw(webservice users)};
+
+ my $dbh = Bugzilla->dbh;
+ my $ids = [map { blessed($_->{id}) ? $_->{id}->value : $_->{id} }
+ grep { exists $_->{id} } @$users];
+ return unless @$ids;
+ my $timestamps = $dbh->selectall_hashref(
+ "SELECT userid,last_activity_ts FROM profiles WHERE "
+ . $dbh->sql_in('userid', $ids),
+ 'userid',
+ );
+ foreach my $user (@$users) {
+ my $id = blessed($user->{id}) ? $user->{id}->value : $user->{id};
+ $user->{last_activity}
+ = $service->type('dateTime', $timestamps->{$id}->{last_activity_ts});
+ }
}
sub template_before_create {
- my ($self, $args) = @_;
- $args->{config}->{FILTERS}->{timeago} = sub {
- my ($time_str) = @_;
- return time_ago(datetime_from($time_str, 'UTC'));
- };
+ my ($self, $args) = @_;
+ $args->{config}->{FILTERS}->{timeago} = sub {
+ my ($time_str) = @_;
+ return time_ago(datetime_from($time_str, 'UTC'));
+ };
}
sub page_before_template {
- my ($self, $args) = @_;
- my ($vars, $page) = @$args{qw(vars page_id)};
- return unless $page eq 'user_profile.html';
- my $user = Bugzilla->user;
-
- # determine user to display
- my ($target, $login);
- my $input = Bugzilla->input_params;
- if (my $user_id = $input->{user_id}) {
- # load from user_id
- $user_id = 0 if $user_id =~ /\D/;
- $target = Bugzilla::User->check({ id => $user_id });
- } else {
- # loading from login name requires authentication
- Bugzilla->login(LOGIN_REQUIRED);
- $login = $input->{login};
- if (!$login) {
- # show current user's profile by default
- $target = $user;
- } else {
- my $limit = Bugzilla->params->{'maxusermatches'} + 1;
- my $users = Bugzilla::User::match($login, $limit, 1);
- if (scalar(@$users) == 1) {
- # always allow singular matches without confirmation
- $target = $users->[0];
- } else {
- Bugzilla::User::match_field({ 'login' => {'type' => 'single'} });
- $target = Bugzilla::User->check($login);
- }
- }
+ my ($self, $args) = @_;
+ my ($vars, $page) = @$args{qw(vars page_id)};
+ return unless $page eq 'user_profile.html';
+ my $user = Bugzilla->user;
+
+ # determine user to display
+ my ($target, $login);
+ my $input = Bugzilla->input_params;
+ if (my $user_id = $input->{user_id}) {
+
+ # load from user_id
+ $user_id = 0 if $user_id =~ /\D/;
+ $target = Bugzilla::User->check({id => $user_id});
+ }
+ else {
+ # loading from login name requires authentication
+ Bugzilla->login(LOGIN_REQUIRED);
+ $login = $input->{login};
+ if (!$login) {
+
+ # show current user's profile by default
+ $target = $user;
+ }
+ else {
+ my $limit = Bugzilla->params->{'maxusermatches'} + 1;
+ my $users = Bugzilla::User::match($login, $limit, 1);
+ if (scalar(@$users) == 1) {
+
+ # always allow singular matches without confirmation
+ $target = $users->[0];
+ }
+ else {
+ Bugzilla::User::match_field({'login' => {'type' => 'single'}});
+ $target = Bugzilla::User->check($login);
+ }
}
- $login ||= $target->login;
+ }
+ $login ||= $target->login;
- # load statistics into $vars
- my $dbh = Bugzilla->switch_to_shadow_db;
+ # load statistics into $vars
+ my $dbh = Bugzilla->switch_to_shadow_db;
- my $stats = $dbh->selectall_hashref(
- "SELECT name, count
+ my $stats = $dbh->selectall_hashref(
+ "SELECT name, count
FROM profiles_statistics
- WHERE user_id = ?",
- "name",
- undef,
- $target->id,
- );
- map { $stats->{$_} = $stats->{$_}->{count} } keys %$stats;
+ WHERE user_id = ?", "name", undef, $target->id,
+ );
+ map { $stats->{$_} = $stats->{$_}->{count} } keys %$stats;
- my $statuses = $dbh->selectall_hashref(
- "SELECT status, count
+ my $statuses = $dbh->selectall_hashref(
+ "SELECT status, count
FROM profiles_statistics_status
- WHERE user_id = ?",
- "status",
- undef,
- $target->id,
- );
- map { $statuses->{$_} = $statuses->{$_}->{count} } keys %$statuses;
+ WHERE user_id = ?", "status", undef, $target->id,
+ );
+ map { $statuses->{$_} = $statuses->{$_}->{count} } keys %$statuses;
- my $products = $dbh->selectall_arrayref(
- "SELECT product, count
+ my $products = $dbh->selectall_arrayref(
+ "SELECT product, count
FROM profiles_statistics_products
WHERE user_id = ?
- ORDER BY product = '', count DESC",
- { Slice => {} },
- $target->id,
- );
-
- # ensure there's always an "other" product entry
- my ($other_product) = grep { $_->{product} eq '' } @$products;
- if (!$other_product) {
- $other_product = { product => '', count => 0 };
- push @$products, $other_product;
+ ORDER BY product = '', count DESC", {Slice => {}}, $target->id,
+ );
+
+ # ensure there's always an "other" product entry
+ my ($other_product) = grep { $_->{product} eq '' } @$products;
+ if (!$other_product) {
+ $other_product = {product => '', count => 0};
+ push @$products, $other_product;
+ }
+
+ # load product objects and validate product visibility
+ foreach my $product (@$products) {
+ next if $product->{product} eq '';
+ my $product_obj = Bugzilla::Product->new({name => $product->{product}});
+ if (!$product_obj || !$user->can_see_product($product_obj->name)) {
+
+ # products not accessible to current user are moved into "other"
+ $other_product->{count} += $product->{count};
+ $product->{count} = 0;
}
-
- # load product objects and validate product visibility
- foreach my $product (@$products) {
- next if $product->{product} eq '';
- my $product_obj = Bugzilla::Product->new({ name => $product->{product} });
- if (!$product_obj || !$user->can_see_product($product_obj->name)) {
- # products not accessible to current user are moved into "other"
- $other_product->{count} += $product->{count};
- $product->{count} = 0;
- } else {
- $product->{product} = $product_obj;
- }
+ else {
+ $product->{product} = $product_obj;
}
+ }
- # set other's name, and remove empty products
- $other_product->{product} = { name => 'Other' };
- $products = [ grep { $_->{count} } @$products ];
+ # set other's name, and remove empty products
+ $other_product->{product} = {name => 'Other'};
+ $products = [grep { $_->{count} } @$products];
- $vars->{stats} = $stats;
- $vars->{statuses} = $statuses;
- $vars->{products} = $products;
- $vars->{login} = $login;
- $vars->{target} = $target;
+ $vars->{stats} = $stats;
+ $vars->{statuses} = $statuses;
+ $vars->{products} = $products;
+ $vars->{login} = $login;
+ $vars->{target} = $target;
}
sub object_columns {
- my ($self, $args) = @_;
- my ($class, $columns) = @$args{qw(class columns)};
- if ($class->isa('Bugzilla::User')) {
- my $dbh = Bugzilla->dbh;
- my @new_columns = qw(last_activity_ts last_statistics_ts);
- push @$columns, grep { $dbh->bz_column_info($class->DB_TABLE, $_) } @new_columns;
- }
+ my ($self, $args) = @_;
+ my ($class, $columns) = @$args{qw(class columns)};
+ if ($class->isa('Bugzilla::User')) {
+ my $dbh = Bugzilla->dbh;
+ my @new_columns = qw(last_activity_ts last_statistics_ts);
+ push @$columns,
+ grep { $dbh->bz_column_info($class->DB_TABLE, $_) } @new_columns;
+ }
}
sub object_update_columns {
- my ($self, $args) = @_;
- my ($object, $columns) = @$args{qw(object columns)};
- if ($object->isa('Bugzilla::User')) {
- push(@$columns, qw(last_activity_ts last_statistics_ts));
- }
+ my ($self, $args) = @_;
+ my ($object, $columns) = @$args{qw(object columns)};
+ if ($object->isa('Bugzilla::User')) {
+ push(@$columns, qw(last_activity_ts last_statistics_ts));
+ }
}
#
@@ -404,161 +407,96 @@ sub object_update_columns {
#
sub db_schema_abstract_schema {
- my ($self, $args) = @_;
- $args->{'schema'}->{'profiles_statistics'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- user_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'CASCADE',
- }
- },
- name => {
- TYPE => 'VARCHAR(30)',
- NOTNULL => 1,
- },
- count => {
- TYPE => 'INT',
- NOTNULL => 1,
- },
- ],
- INDEXES => [
- profiles_statistics_name_idx => {
- FIELDS => [ 'user_id', 'name' ],
- TYPE => 'UNIQUE',
- },
- ],
- };
- $args->{'schema'}->{'profiles_statistics_status'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- user_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'CASCADE',
- }
- },
- status => {
- TYPE => 'VARCHAR(64)',
- NOTNULL => 1,
- },
- count => {
- TYPE => 'INT',
- NOTNULL => 1,
- },
- ],
- INDEXES => [
- profiles_statistics_status_idx => {
- FIELDS => [ 'user_id', 'status' ],
- TYPE => 'UNIQUE',
- },
- ],
- };
- $args->{'schema'}->{'profiles_statistics_products'} = {
- FIELDS => [
- id => {
- TYPE => 'MEDIUMSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- user_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'CASCADE',
- }
- },
- product => {
- TYPE => 'VARCHAR(64)',
- NOTNULL => 1,
- },
- count => {
- TYPE => 'INT',
- NOTNULL => 1,
- },
- ],
- INDEXES => [
- profiles_statistics_products_idx => {
- FIELDS => [ 'user_id', 'product' ],
- TYPE => 'UNIQUE',
- },
- ],
- };
- $args->{'schema'}->{'profiles_statistics_recalc'} = {
- FIELDS => [
- user_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'CASCADE',
- }
- },
- ],
- INDEXES => [
- profiles_statistics_recalc_idx => {
- FIELDS => [ 'user_id' ],
- TYPE => 'UNIQUE',
- },
- ],
- };
- $args->{'schema'}->{'profiles_statistics_recalc'} = {
- FIELDS => [
- user_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'profiles',
- COLUMN => 'userid',
- DELETE => 'CASCADE',
- }
- },
- ],
- INDEXES => [
- profiles_statistics_recalc_idx => {
- FIELDS => [ 'user_id' ],
- TYPE => 'UNIQUE',
- },
- ],
- };
+ my ($self, $args) = @_;
+ $args->{'schema'}->{'profiles_statistics'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ user_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE',}
+ },
+ name => {TYPE => 'VARCHAR(30)', NOTNULL => 1,},
+ count => {TYPE => 'INT', NOTNULL => 1,},
+ ],
+ INDEXES => [
+ profiles_statistics_name_idx =>
+ {FIELDS => ['user_id', 'name'], TYPE => 'UNIQUE',},
+ ],
+ };
+ $args->{'schema'}->{'profiles_statistics_status'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ user_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE',}
+ },
+ status => {TYPE => 'VARCHAR(64)', NOTNULL => 1,},
+ count => {TYPE => 'INT', NOTNULL => 1,},
+ ],
+ INDEXES => [
+ profiles_statistics_status_idx =>
+ {FIELDS => ['user_id', 'status'], TYPE => 'UNIQUE',},
+ ],
+ };
+ $args->{'schema'}->{'profiles_statistics_products'} = {
+ FIELDS => [
+ id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ user_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE',}
+ },
+ product => {TYPE => 'VARCHAR(64)', NOTNULL => 1,},
+ count => {TYPE => 'INT', NOTNULL => 1,},
+ ],
+ INDEXES => [
+ profiles_statistics_products_idx =>
+ {FIELDS => ['user_id', 'product'], TYPE => 'UNIQUE',},
+ ],
+ };
+ $args->{'schema'}->{'profiles_statistics_recalc'} = {
+ FIELDS => [
+ user_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE',}
+ },
+ ],
+ INDEXES => [
+ profiles_statistics_recalc_idx => {FIELDS => ['user_id'], TYPE => 'UNIQUE',},
+ ],
+ };
+ $args->{'schema'}->{'profiles_statistics_recalc'} = {
+ FIELDS => [
+ user_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE',}
+ },
+ ],
+ INDEXES => [
+ profiles_statistics_recalc_idx => {FIELDS => ['user_id'], TYPE => 'UNIQUE',},
+ ],
+ };
}
sub install_update_db {
- my $dbh = Bugzilla->dbh;
- $dbh->bz_add_column('profiles', 'last_activity_ts', { TYPE => 'DATETIME' });
- $dbh->bz_add_column('profiles', 'last_statistics_ts', { TYPE => 'DATETIME' });
+ my $dbh = Bugzilla->dbh;
+ $dbh->bz_add_column('profiles', 'last_activity_ts', {TYPE => 'DATETIME'});
+ $dbh->bz_add_column('profiles', 'last_statistics_ts', {TYPE => 'DATETIME'});
}
sub install_filesystem {
- my ($self, $args) = @_;
- my $files = $args->{'files'};
- my $extensions_dir = bz_locations()->{'extensionsdir'};
- my $script_name = $extensions_dir . "/" . __PACKAGE__->NAME . "/bin/update.pl";
- $files->{$script_name} = {
- perms => Bugzilla::Install::Filesystem::WS_EXECUTE
- };
- $script_name = $extensions_dir . "/" . __PACKAGE__->NAME . "/bin/migrate.pl";
- $files->{$script_name} = {
- perms => Bugzilla::Install::Filesystem::OWNER_EXECUTE
- };
+ my ($self, $args) = @_;
+ my $files = $args->{'files'};
+ my $extensions_dir = bz_locations()->{'extensionsdir'};
+ my $script_name = $extensions_dir . "/" . __PACKAGE__->NAME . "/bin/update.pl";
+ $files->{$script_name} = {perms => Bugzilla::Install::Filesystem::WS_EXECUTE};
+ $script_name = $extensions_dir . "/" . __PACKAGE__->NAME . "/bin/migrate.pl";
+ $files->{$script_name}
+ = {perms => Bugzilla::Install::Filesystem::OWNER_EXECUTE};
}
__PACKAGE__->NAME;
diff --git a/extensions/UserProfile/bin/migrate.pl b/extensions/UserProfile/bin/migrate.pl
index dd257f6bd..08c9f54f4 100755
--- a/extensions/UserProfile/bin/migrate.pl
+++ b/extensions/UserProfile/bin/migrate.pl
@@ -26,7 +26,7 @@ Bugzilla->usage_mode(USAGE_MODE_CMDLINE);
my $dbh = Bugzilla->dbh;
my $user_ids = $dbh->selectcol_arrayref(
- "SELECT userid
+ "SELECT userid
FROM profiles
WHERE last_activity_ts IS NULL
ORDER BY userid"
@@ -34,11 +34,9 @@ my $user_ids = $dbh->selectcol_arrayref(
my ($current, $total) = (1, scalar(@$user_ids));
foreach my $user_id (@$user_ids) {
- indicate_progress({ current => $current++, total => $total, every => 25 });
- my $ts = last_user_activity($user_id);
- next unless $ts;
- $dbh->do(
- "UPDATE profiles SET last_activity_ts = ? WHERE userid = ?",
- undef,
- $ts, $user_id);
+ indicate_progress({current => $current++, total => $total, every => 25});
+ my $ts = last_user_activity($user_id);
+ next unless $ts;
+ $dbh->do("UPDATE profiles SET last_activity_ts = ? WHERE userid = ?",
+ undef, $ts, $user_id);
}
diff --git a/extensions/UserProfile/bin/update.pl b/extensions/UserProfile/bin/update.pl
index 4d704a1f0..bdbcef329 100755
--- a/extensions/UserProfile/bin/update.pl
+++ b/extensions/UserProfile/bin/update.pl
@@ -26,56 +26,50 @@ my $user_ids;
my $verbose = grep { $_ eq '-v' } @ARGV;
$user_ids = $dbh->selectcol_arrayref(
- "SELECT user_id
+ "SELECT user_id
FROM profiles_statistics_recalc
- ORDER BY user_id",
- { Slice => {} }
+ ORDER BY user_id", {Slice => {}}
);
if (@$user_ids) {
- print "recalculating last_user_activity\n";
- my ($count, $total) = (0, scalar(@$user_ids));
- foreach my $user_id (@$user_ids) {
- if ($verbose) {
- $count++;
- my $login = user_id_to_login($user_id);
- print "$count/$total $login ($user_id)\n";
- }
- $dbh->do(
- "UPDATE profiles
- SET last_activity_ts = ?,
- last_statistics_ts = NULL
- WHERE userid = ?",
- undef,
- last_user_activity($user_id),
- $user_id
- );
- Bugzilla->memcached->clear({ table => 'profiles', id => $user_id });
+ print "recalculating last_user_activity\n";
+ my ($count, $total) = (0, scalar(@$user_ids));
+ foreach my $user_id (@$user_ids) {
+ if ($verbose) {
+ $count++;
+ my $login = user_id_to_login($user_id);
+ print "$count/$total $login ($user_id)\n";
}
$dbh->do(
- "DELETE FROM profiles_statistics_recalc WHERE " . $dbh->sql_in('user_id', $user_ids)
+ "UPDATE profiles
+ SET last_activity_ts = ?,
+ last_statistics_ts = NULL
+ WHERE userid = ?", undef, last_user_activity($user_id), $user_id
);
+ Bugzilla->memcached->clear({table => 'profiles', id => $user_id});
+ }
+ $dbh->do("DELETE FROM profiles_statistics_recalc WHERE "
+ . $dbh->sql_in('user_id', $user_ids));
}
$user_ids = $dbh->selectcol_arrayref(
- "SELECT userid
+ "SELECT userid
FROM profiles
WHERE last_activity_ts IS NOT NULL
AND (last_statistics_ts IS NULL
OR last_activity_ts > last_statistics_ts)
- ORDER BY userid",
- { Slice => {} }
+ ORDER BY userid", {Slice => {}}
);
if (@$user_ids) {
- $verbose && print "updating statistics\n";
- my ($count, $total) = (0, scalar(@$user_ids));
- foreach my $user_id (@$user_ids) {
- if ($verbose) {
- $count++;
- my $login = user_id_to_login($user_id);
- print "$count/$total $login ($user_id)\n";
- }
- update_statistics_by_user($user_id);
+ $verbose && print "updating statistics\n";
+ my ($count, $total) = (0, scalar(@$user_ids));
+ foreach my $user_id (@$user_ids) {
+ if ($verbose) {
+ $count++;
+ my $login = user_id_to_login($user_id);
+ print "$count/$total $login ($user_id)\n";
}
+ update_statistics_by_user($user_id);
+ }
}
diff --git a/extensions/UserProfile/lib/Util.pm b/extensions/UserProfile/lib/Util.pm
index 6b2eff098..54a9c2eea 100644
--- a/extensions/UserProfile/lib/Util.pm
+++ b/extensions/UserProfile/lib/Util.pm
@@ -13,44 +13,45 @@ use warnings;
use base qw(Exporter);
our @EXPORT = qw( update_statistics_by_user
- tag_for_recount_from_bug
- last_user_activity );
+ tag_for_recount_from_bug
+ last_user_activity );
use Bugzilla;
sub update_statistics_by_user {
- my ($user_id) = @_;
+ my ($user_id) = @_;
- # run all our queries on the slaves
+ # run all our queries on the slaves
- my $dbh = Bugzilla->switch_to_shadow_db();
+ my $dbh = Bugzilla->switch_to_shadow_db();
- my $now = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
+ my $now = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
- # grab the current values
+ # grab the current values
- my $last_statistics_ts = _get_last_statistics_ts($user_id);
+ my $last_statistics_ts = _get_last_statistics_ts($user_id);
- my $statistics = _get_stats($user_id, 'profiles_statistics', 'name');
- my $by_status = _get_stats($user_id, 'profiles_statistics_status', 'status');
- my $by_product = _get_stats($user_id, 'profiles_statistics_products', 'product');
+ my $statistics = _get_stats($user_id, 'profiles_statistics', 'name');
+ my $by_status = _get_stats($user_id, 'profiles_statistics_status', 'status');
+ my $by_product
+ = _get_stats($user_id, 'profiles_statistics_products', 'product');
- # bugs filed
- _update_statistics($statistics, 'bugs_filed', [ $user_id ], <<EOF);
+ # bugs filed
+ _update_statistics($statistics, 'bugs_filed', [$user_id], <<EOF);
SELECT COUNT(*)
FROM bugs
WHERE bugs.reporter = ?
EOF
- # comments made
- _update_statistics($statistics, 'comments', [ $user_id ], <<EOF);
+ # comments made
+ _update_statistics($statistics, 'comments', [$user_id], <<EOF);
SELECT COUNT(*)
FROM longdescs
WHERE who = ?
EOF
- # commented on
- _update_statistics($statistics, 'commented_on', [ $user_id ], <<EOF);
+ # commented on
+ _update_statistics($statistics, 'commented_on', [$user_id], <<EOF);
SELECT COUNT(*) FROM (
SELECT longdescs.bug_id
FROM longdescs
@@ -59,8 +60,9 @@ EOF
) AS temp
EOF
- # confirmed
- _update_statistics($statistics, 'confirmed', [ $user_id, _field_id('bug_status') ], <<EOF);
+ # confirmed
+ _update_statistics($statistics, 'confirmed',
+ [$user_id, _field_id('bug_status')], <<EOF);
SELECT COUNT(*)
FROM bugs_activity
WHERE who = ?
@@ -69,8 +71,8 @@ EOF
AND added = 'NEW'
EOF
- # patches submitted
- _update_statistics($statistics, 'patches', [ $user_id ], <<EOF);
+ # patches submitted
+ _update_statistics($statistics, 'patches', [$user_id], <<EOF);
SELECT COUNT(*)
FROM attachments
WHERE submitter_id = ?
@@ -80,8 +82,8 @@ EOF
'text/x-phabricator-request'))
EOF
- # patches reviewed
- _update_statistics($statistics, 'reviews', [ $user_id ], <<EOF);
+ # patches reviewed
+ _update_statistics($statistics, 'reviews', [$user_id], <<EOF);
SELECT COUNT(*)
FROM flags
INNER JOIN attachments ON attachments.attach_id = flags.attach_id
@@ -93,22 +95,22 @@ EOF
AND status IN ('+', '-')
EOF
- # assigned to
- _update_statistics($statistics, 'assigned', [ $user_id ], <<EOF);
+ # assigned to
+ _update_statistics($statistics, 'assigned', [$user_id], <<EOF);
SELECT COUNT(*)
FROM bugs
WHERE assigned_to = ?
EOF
- # qa contact
- _update_statistics($statistics, 'qa_contact', [ $user_id ], <<EOF);
+ # qa contact
+ _update_statistics($statistics, 'qa_contact', [$user_id], <<EOF);
SELECT COUNT(*)
FROM bugs
WHERE qa_contact = ?
EOF
- # bugs touched
- _update_statistics($statistics, 'touched', [ $user_id, $user_id], <<EOF);
+ # bugs touched
+ _update_statistics($statistics, 'touched', [$user_id, $user_id], <<EOF);
SELECT COUNT(*) FROM (
SELECT bugs_activity.bug_id
FROM bugs_activity
@@ -122,42 +124,45 @@ EOF
) temp
EOF
- # activity by status/resolution, and product
- _activity_by_status($by_status, $user_id);
- _activity_by_product($by_product, $user_id);
+ # activity by status/resolution, and product
+ _activity_by_status($by_status, $user_id);
+ _activity_by_product($by_product, $user_id);
- # if nothing is dirty, no need to do anything else
- if ($last_statistics_ts) {
- return unless _has_dirty($statistics)
- || _has_dirty($by_status)
- || _has_dirty($by_product);
- }
+ # if nothing is dirty, no need to do anything else
+ if ($last_statistics_ts) {
+ return
+ unless _has_dirty($statistics)
+ || _has_dirty($by_status)
+ || _has_dirty($by_product);
+ }
- # switch back to the main db for updating
+ # switch back to the main db for updating
- $dbh = Bugzilla->switch_to_main_db();
- $dbh->bz_start_transaction();
+ $dbh = Bugzilla->switch_to_main_db();
+ $dbh->bz_start_transaction();
- # commit updated statistics
+ # commit updated statistics
- _set_stats($statistics, $user_id, 'profiles_statistics', 'name')
- if _has_dirty($statistics);
- _set_stats($by_status, $user_id, 'profiles_statistics_status', 'status')
- if _has_dirty($by_status);
- _set_stats($by_product, $user_id, 'profiles_statistics_products', 'product')
- if _has_dirty($by_product);
+ _set_stats($statistics, $user_id, 'profiles_statistics', 'name')
+ if _has_dirty($statistics);
+ _set_stats($by_status, $user_id, 'profiles_statistics_status', 'status')
+ if _has_dirty($by_status);
+ _set_stats($by_product, $user_id, 'profiles_statistics_products', 'product')
+ if _has_dirty($by_product);
- # update the user's last_statistics_ts
- _set_last_statistics_ts($user_id, $now);
+ # update the user's last_statistics_ts
+ _set_last_statistics_ts($user_id, $now);
- $dbh->bz_commit_transaction();
+ $dbh->bz_commit_transaction();
}
sub tag_for_recount_from_bug {
- my ($bug_id) = @_;
- my $dbh = Bugzilla->dbh;
- # get a list of all users associated with this bug
- my $user_ids = $dbh->selectcol_arrayref(<<EOF, undef, $bug_id, _field_id('cc'), $bug_id);
+ my ($bug_id) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ # get a list of all users associated with this bug
+ my $user_ids
+ = $dbh->selectcol_arrayref(<<EOF, undef, $bug_id, _field_id('cc'), $bug_id);
SELECT DISTINCT user_id
FROM (
SELECT DISTINCT who AS user_id
@@ -170,20 +175,22 @@ sub tag_for_recount_from_bug {
WHERE bug_id = ?
) tmp
EOF
- # clear last_statistics_ts
- $dbh->do(
- "UPDATE profiles SET last_statistics_ts=NULL WHERE " . $dbh->sql_in('userid', $user_ids)
- );
- foreach my $id (@$user_ids) {
- Bugzilla->memcached->clear({ table => 'profiles', id => $id });
- }
- return scalar(@$user_ids);
+
+ # clear last_statistics_ts
+ $dbh->do("UPDATE profiles SET last_statistics_ts=NULL WHERE "
+ . $dbh->sql_in('userid', $user_ids));
+ foreach my $id (@$user_ids) {
+ Bugzilla->memcached->clear({table => 'profiles', id => $id});
+ }
+ return scalar(@$user_ids);
}
sub last_user_activity {
- # last comment, or change to a bug (excluding CC changes)
- my ($user_id) = @_;
- return Bugzilla->dbh->selectrow_array(<<EOF, undef, $user_id, $user_id, _field_id('cc'));
+
+ # last comment, or change to a bug (excluding CC changes)
+ my ($user_id) = @_;
+ return Bugzilla->dbh->selectrow_array(
+ <<EOF, undef, $user_id, $user_id, _field_id('cc'));
SELECT MAX(bug_when)
FROM (
SELECT MAX(bug_when) AS bug_when
@@ -201,45 +208,39 @@ EOF
# for performance reasons hit the db directly rather than using the user object
sub _get_last_statistics_ts {
- my ($user_id) = @_;
- return Bugzilla->dbh->selectrow_array(
- "SELECT last_statistics_ts FROM profiles WHERE userid = ?",
- undef, $user_id
- );
+ my ($user_id) = @_;
+ return Bugzilla->dbh->selectrow_array(
+ "SELECT last_statistics_ts FROM profiles WHERE userid = ?",
+ undef, $user_id);
}
sub _set_last_statistics_ts {
- my ($user_id, $timestamp) = @_;
- Bugzilla->dbh->do(
- "UPDATE profiles SET last_statistics_ts = ? WHERE userid = ?",
- undef,
- $timestamp, $user_id,
- );
- Bugzilla->memcached->clear({ table => 'profiles', id => $user_id });
+ my ($user_id, $timestamp) = @_;
+ Bugzilla->dbh->do("UPDATE profiles SET last_statistics_ts = ? WHERE userid = ?",
+ undef, $timestamp, $user_id,);
+ Bugzilla->memcached->clear({table => 'profiles', id => $user_id});
}
sub _update_statistics {
- my ($statistics, $name, $values, $sql) = @_;
- my ($count) = Bugzilla->dbh->selectrow_array($sql, undef, @$values);
- if (!exists $statistics->{$name}) {
- $statistics->{$name} = {
- id => 0,
- count => $count,
- dirty => 1,
- };
- } elsif ($statistics->{$name}->{count} != $count) {
- $statistics->{$name}->{count} = $count;
- $statistics->{$name}->{dirty} = 1;
- };
+ my ($statistics, $name, $values, $sql) = @_;
+ my ($count) = Bugzilla->dbh->selectrow_array($sql, undef, @$values);
+ if (!exists $statistics->{$name}) {
+ $statistics->{$name} = {id => 0, count => $count, dirty => 1,};
+ }
+ elsif ($statistics->{$name}->{count} != $count) {
+ $statistics->{$name}->{count} = $count;
+ $statistics->{$name}->{dirty} = 1;
+ }
}
sub _activity_by_status {
- my ($by_status, $user_id) = @_;
- my $dbh = Bugzilla->dbh;
+ my ($by_status, $user_id) = @_;
+ my $dbh = Bugzilla->dbh;
- # we actually track both status and resolution changes as statuses
- my @values = ($user_id, _field_id('bug_status'), $user_id, _field_id('resolution'));
- my $rows = $dbh->selectall_arrayref(<<EOF, { Slice => {} }, @values);
+ # we actually track both status and resolution changes as statuses
+ my @values
+ = ($user_id, _field_id('bug_status'), $user_id, _field_id('resolution'));
+ my $rows = $dbh->selectall_arrayref(<<EOF, {Slice => {}}, @values);
SELECT added AS status, COUNT(*) AS count
FROM bugs_activity
WHERE who = ?
@@ -254,29 +255,26 @@ sub _activity_by_status {
GROUP BY added
EOF
- foreach my $row (@$rows) {
- my $status = $row->{status};
- if (!exists $by_status->{$status}) {
- $by_status->{$status} = {
- id => 0,
- count => $row->{count},
- dirty => 1,
- };
- } elsif ($by_status->{$status}->{count} != $row->{count}) {
- $by_status->{$status}->{count} = $row->{count};
- $by_status->{$status}->{dirty} = 1;
- }
+ foreach my $row (@$rows) {
+ my $status = $row->{status};
+ if (!exists $by_status->{$status}) {
+ $by_status->{$status} = {id => 0, count => $row->{count}, dirty => 1,};
+ }
+ elsif ($by_status->{$status}->{count} != $row->{count}) {
+ $by_status->{$status}->{count} = $row->{count};
+ $by_status->{$status}->{dirty} = 1;
}
+ }
}
sub _activity_by_product {
- my ($by_product, $user_id) = @_;
- my $dbh = Bugzilla->dbh;
+ my ($by_product, $user_id) = @_;
+ my $dbh = Bugzilla->dbh;
- my %products;
+ my %products;
- # changes
- my $rows = $dbh->selectall_arrayref(<<EOF, { Slice => {} }, $user_id);
+ # changes
+ my $rows = $dbh->selectall_arrayref(<<EOF, {Slice => {}}, $user_id);
SELECT products.name AS product, count(*) AS count
FROM bugs_activity
INNER JOIN bugs ON bugs.bug_id = bugs_activity.bug_id
@@ -284,10 +282,10 @@ sub _activity_by_product {
WHERE who = ?
GROUP BY bugs.product_id
EOF
- map { $products{$_->{product}} += $_->{count} } @$rows;
+ map { $products{$_->{product}} += $_->{count} } @$rows;
- # comments
- $rows = $dbh->selectall_arrayref(<<EOF, { Slice => {} }, $user_id);
+ # comments
+ $rows = $dbh->selectall_arrayref(<<EOF, {Slice => {}}, $user_id);
SELECT products.name AS product, count(*) AS count
FROM longdescs
INNER JOIN bugs ON bugs.bug_id = longdescs.bug_id
@@ -295,96 +293,89 @@ EOF
WHERE who = ?
GROUP BY bugs.product_id
EOF
- map { $products{$_->{product}} += $_->{count} } @$rows;
-
- # store only the top 10 and 'other' (which is an empty string)
- my @sorted = sort { $products{$b} <=> $products{$a} } keys %products;
- my @other;
- @other = splice(@sorted, 10) if scalar(@sorted) > 10;
- map { $products{''} += $products{$_} } @other;
- push @sorted, '' if $products{''};
-
- # update by_product
- foreach my $product (@sorted) {
- if (!exists $by_product->{$product}) {
- $by_product->{$product} = {
- id => 0,
- count => $products{$product},
- dirty => 1,
- };
- } elsif ($by_product->{$product}->{count} != $products{$product}) {
- $by_product->{$product}->{count} = $products{$product};
- $by_product->{$product}->{dirty} = 1;
- }
+ map { $products{$_->{product}} += $_->{count} } @$rows;
+
+ # store only the top 10 and 'other' (which is an empty string)
+ my @sorted = sort { $products{$b} <=> $products{$a} } keys %products;
+ my @other;
+ @other = splice(@sorted, 10) if scalar(@sorted) > 10;
+ map { $products{''} += $products{$_} } @other;
+ push @sorted, '' if $products{''};
+
+ # update by_product
+ foreach my $product (@sorted) {
+ if (!exists $by_product->{$product}) {
+ $by_product->{$product} = {id => 0, count => $products{$product}, dirty => 1,};
}
- foreach my $product (keys %$by_product) {
- if (!grep { $_ eq $product } @sorted) {
- delete $by_product->{$product};
- }
+ elsif ($by_product->{$product}->{count} != $products{$product}) {
+ $by_product->{$product}->{count} = $products{$product};
+ $by_product->{$product}->{dirty} = 1;
}
+ }
+ foreach my $product (keys %$by_product) {
+ if (!grep { $_ eq $product } @sorted) {
+ delete $by_product->{$product};
+ }
+ }
}
our $_field_id_cache;
+
sub _field_id {
- my ($name) = @_;
- if (!$_field_id_cache) {
- my $rows = Bugzilla->dbh->selectall_arrayref("SELECT id, name FROM fielddefs");
- foreach my $row (@$rows) {
- $_field_id_cache->{$row->[1]} = $row->[0];
- }
+ my ($name) = @_;
+ if (!$_field_id_cache) {
+ my $rows = Bugzilla->dbh->selectall_arrayref("SELECT id, name FROM fielddefs");
+ foreach my $row (@$rows) {
+ $_field_id_cache->{$row->[1]} = $row->[0];
}
- return $_field_id_cache->{$name};
+ }
+ return $_field_id_cache->{$name};
}
sub _get_stats {
- my ($user_id, $table, $name_field) = @_;
- my $result = {};
- my $rows = Bugzilla->dbh->selectall_arrayref(
- "SELECT * FROM $table WHERE user_id = ?",
- { Slice => {} },
- $user_id,
- );
- foreach my $row (@$rows) {
- unless (defined $row->{$name_field}) {
- print "$user_id $table $name_field\n";
- die;
- }
- $result->{$row->{$name_field}} = {
- id => $row->{id},
- count => $row->{count},
- dirty => 0,
- }
+ my ($user_id, $table, $name_field) = @_;
+ my $result = {};
+ my $rows
+ = Bugzilla->dbh->selectall_arrayref("SELECT * FROM $table WHERE user_id = ?",
+ {Slice => {}}, $user_id,);
+ foreach my $row (@$rows) {
+ unless (defined $row->{$name_field}) {
+ print "$user_id $table $name_field\n";
+ die;
}
- return $result;
+ $result->{$row->{$name_field}}
+ = {id => $row->{id}, count => $row->{count}, dirty => 0,};
+ }
+ return $result;
}
sub _set_stats {
- my ($statistics, $user_id, $table, $name_field) = @_;
- my $dbh = Bugzilla->dbh;
- foreach my $name (keys %$statistics) {
- next unless $statistics->{$name}->{dirty};
- if ($statistics->{$name}->{id}) {
- $dbh->do(
- "UPDATE $table SET count = ? WHERE user_id = ? AND $name_field = ?",
- undef,
- $statistics->{$name}->{count}, $user_id, $name,
- );
- } else {
- $dbh->do(
- "INSERT INTO $table(user_id, $name_field, count) VALUES (?, ?, ?)",
- undef,
- $user_id, $name, $statistics->{$name}->{count},
- );
- }
+ my ($statistics, $user_id, $table, $name_field) = @_;
+ my $dbh = Bugzilla->dbh;
+ foreach my $name (keys %$statistics) {
+ next unless $statistics->{$name}->{dirty};
+ if ($statistics->{$name}->{id}) {
+ $dbh->do(
+ "UPDATE $table SET count = ? WHERE user_id = ? AND $name_field = ?",
+ undef, $statistics->{$name}->{count},
+ $user_id, $name,
+ );
+ }
+ else {
+ $dbh->do(
+ "INSERT INTO $table(user_id, $name_field, count) VALUES (?, ?, ?)",
+ undef, $user_id, $name, $statistics->{$name}->{count},
+ );
}
+ }
}
sub _has_dirty {
- my ($statistics) = @_;
- foreach my $name (keys %$statistics) {
- return 1 if $statistics->{$name}->{dirty};
- }
- return 0;
+ my ($statistics) = @_;
+ foreach my $name (keys %$statistics) {
+ return 1 if $statistics->{$name}->{dirty};
+ }
+ return 0;
}
1;
diff --git a/extensions/UserStory/Config.pm b/extensions/UserStory/Config.pm
index 8668deaa7..baf1776d3 100644
--- a/extensions/UserStory/Config.pm
+++ b/extensions/UserStory/Config.pm
@@ -12,13 +12,8 @@ use strict;
use warnings;
use constant NAME => 'UserStory';
-use constant REQUIRED_MODULES => [
- {
- package => 'Text-Diff',
- module => 'Text::Diff',
- version => 0,
- },
-];
+use constant REQUIRED_MODULES =>
+ [{package => 'Text-Diff', module => 'Text::Diff', version => 0,},];
use constant OPTIONAL_MODULES => [];
__PACKAGE__->NAME;
diff --git a/extensions/UserStory/Extension.pm b/extensions/UserStory/Extension.pm
index f9fdb3f29..0be43173b 100644
--- a/extensions/UserStory/Extension.pm
+++ b/extensions/UserStory/Extension.pm
@@ -23,82 +23,82 @@ use Bugzilla::Extension::BMO::FakeBug;
use Text::Diff;
BEGIN {
- *Bugzilla::Bug::user_story_visible = \&_bug_user_story_visible;
- *Bugzilla::Extension::BMO::FakeBug::user_story_visible = \&_bug_user_story_visible;
+ *Bugzilla::Bug::user_story_visible = \&_bug_user_story_visible;
+ *Bugzilla::Extension::BMO::FakeBug::user_story_visible
+ = \&_bug_user_story_visible;
}
sub _bug_user_story_visible {
- my ($self) = @_;
- if (!exists $self->{user_story_visible}) {
- # Visible by default
- $self->{user_story_visible} = 1;
- my ($product, $component) = ($self->product, $self->component);
- my $exclude_components = [];
- if (exists USER_STORY_EXCLUDE->{$product}) {
- $exclude_components = USER_STORY_EXCLUDE->{$product};
- if (scalar(@$exclude_components) == 0
- || ($component && grep { $_ eq $component } @$exclude_components))
- {
- $self->{user_story_visible} = 0;
- }
- }
- $self->{user_story_exclude_components} = $exclude_components;
+ my ($self) = @_;
+ if (!exists $self->{user_story_visible}) {
+
+ # Visible by default
+ $self->{user_story_visible} = 1;
+ my ($product, $component) = ($self->product, $self->component);
+ my $exclude_components = [];
+ if (exists USER_STORY_EXCLUDE->{$product}) {
+ $exclude_components = USER_STORY_EXCLUDE->{$product};
+ if (scalar(@$exclude_components) == 0
+ || ($component && grep { $_ eq $component } @$exclude_components))
+ {
+ $self->{user_story_visible} = 0;
+ }
}
- return ($self->{user_story_visible}, $self->{user_story_exclude_components});
+ $self->{user_story_exclude_components} = $exclude_components;
+ }
+ return ($self->{user_story_visible}, $self->{user_story_exclude_components});
}
# ensure user is allowed to edit the story
sub bug_check_can_change_field {
- my ($self, $args) = @_;
- my ($bug, $field, $priv_results) = @$args{qw(bug field priv_results)};
- return unless $field eq 'cf_user_story';
- if (!Bugzilla->user->in_group(USER_STORY_GROUP)) {
- push (@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
- }
+ my ($self, $args) = @_;
+ my ($bug, $field, $priv_results) = @$args{qw(bug field priv_results)};
+ return unless $field eq 'cf_user_story';
+ if (!Bugzilla->user->in_group(USER_STORY_GROUP)) {
+ push(@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
+ }
}
# store just a diff of the changes in the bugs_activity table
sub bug_update_before_logging {
- my ($self, $args) = @_;
- my $changes = $args->{changes};
- return unless exists $changes->{cf_user_story};
- my $diff = diff(
- \$changes->{cf_user_story}->[0],
- \$changes->{cf_user_story}->[1],
- {
- CONTEXT => 0,
- },
- );
- $changes->{cf_user_story} = [ '', $diff ];
+ my ($self, $args) = @_;
+ my $changes = $args->{changes};
+ return unless exists $changes->{cf_user_story};
+ my $diff = diff(
+ \$changes->{cf_user_story}->[0],
+ \$changes->{cf_user_story}->[1],
+ {CONTEXT => 0,},
+ );
+ $changes->{cf_user_story} = ['', $diff];
}
# stop inline-history from displaying changes to the user story
sub inline_history_activtiy {
- my ($self, $args) = @_;
- foreach my $activity (@{ $args->{activity} }) {
- foreach my $change (@{ $activity->{changes} }) {
- if ($change->{fieldname} eq 'cf_user_story') {
- $change->{removed} = '';
- $change->{added} = '(updated)';
- }
- }
+ my ($self, $args) = @_;
+ foreach my $activity (@{$args->{activity}}) {
+ foreach my $change (@{$activity->{changes}}) {
+ if ($change->{fieldname} eq 'cf_user_story') {
+ $change->{removed} = '';
+ $change->{added} = '(updated)';
+ }
}
+ }
}
# create cf_user_story field
sub install_update_db {
- my ($self, $args) = @_;
- return if Bugzilla::Field->new({ name => 'cf_user_story'});
- Bugzilla::Field->create({
- name => 'cf_user_story',
- description => 'User Story',
- type => FIELD_TYPE_TEXTAREA,
- mailhead => 0,
- enter_bug => 0,
- obsolete => 0,
- custom => 1,
- buglist => 0,
- });
+ my ($self, $args) = @_;
+ return if Bugzilla::Field->new({name => 'cf_user_story'});
+ Bugzilla::Field->create({
+ name => 'cf_user_story',
+ description => 'User Story',
+ type => FIELD_TYPE_TEXTAREA,
+ mailhead => 0,
+ enter_bug => 0,
+ obsolete => 0,
+ custom => 1,
+ buglist => 0,
+ });
}
__PACKAGE__->NAME;
diff --git a/extensions/UserStory/lib/Constants.pm b/extensions/UserStory/lib/Constants.pm
index 6a1c0c449..49f6b0d22 100644
--- a/extensions/UserStory/lib/Constants.pm
+++ b/extensions/UserStory/lib/Constants.pm
@@ -25,6 +25,6 @@ use constant USER_STORY_GROUP => 'editbugs';
# Don't show User Story on Developer Tools component, visible on all other
# Firefox components
# 'Firefox' => ['Developer Tools'],
-use constant USER_STORY_EXCLUDE => { };
+use constant USER_STORY_EXCLUDE => {};
1;
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 198b065fa..523ee653d 100644
--- a/extensions/Voting/Extension.pm
+++ b/extensions/Voting/Extension.pm
@@ -23,15 +23,16 @@ 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;
BEGIN {
- *Bugzilla::Bug::user_votes = \&_bug_user_votes;
+ *Bugzilla::Bug::user_votes = \&_bug_user_votes;
}
################
@@ -39,70 +40,71 @@ BEGIN {
################
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});
+ }
}
###########
@@ -110,102 +112,108 @@ sub install_update_db {
###########
sub _bug_user_votes {
- my ($self) = @_;
- return $self->{'user_votes'} if exists $self->{'user_votes'};
- $self->{'user_votes'} = Bugzilla->dbh->selectrow_array(
- "SELECT vote_count FROM votes WHERE bug_id = ? AND who = ?",
- undef, $self->id, Bugzilla->user->id);
- return $self->{'user_votes'};
+ my ($self) = @_;
+ return $self->{'user_votes'} if exists $self->{'user_votes'};
+ $self->{'user_votes'}
+ = Bugzilla->dbh->selectrow_array(
+ "SELECT vote_count FROM votes WHERE bug_id = ? AND who = ?",
+ undef, $self->id, Bugzilla->user->id);
+ return $self->{'user_votes'};
}
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);
}
+ }
}
#############
@@ -213,27 +221,28 @@ sub bug_end_of_update {
#############
sub template_before_create {
- my ($self, $args) = @_;
- my $config = $args->{config};
- my $constants = $config->{VARIABLES}{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->{VARIABLES}{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;
+ }
+ }
}
###########
@@ -241,19 +250,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';
}
###############
@@ -261,59 +270,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');
}
@@ -322,35 +331,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]});
}
- return $votes;
+ else {
+ $votes = $default;
+ }
+ }
+ return $votes;
}
#########
@@ -358,291 +368,322 @@ 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.
+ # If a bug_id is given, and we're editing, we'll add it to the votes list.
- my $bug_id = $input->{bug_id};
- $bug_id = $bug_id->[0] if ref($bug_id) eq 'ARRAY';
- my $bug = Bugzilla::Bug->check({ id => $bug_id, cache => 1 }) if $bug_id;
- my $who_id = $input->{user_id} || $user->id;
+ my $bug_id = $input->{bug_id};
+ $bug_id = $bug_id->[0] if ref($bug_id) eq 'ARRAY';
+ my $bug = Bugzilla::Bug->check({id => $bug_id, cache => 1}) if $bug_id;
+ my $who_id = $input->{user_id} || $user->id;
- # Logged-out users must specify a user_id.
- Bugzilla->login(LOGIN_REQUIRED) if !$who_id;
+ # Logged-out users must specify a user_id.
+ Bugzilla->login(LOGIN_REQUIRED) if !$who_id;
- my $who = Bugzilla::User->check({ id => $who_id });
+ my $who = Bugzilla::User->check({id => $who_id});
- my $canedit = $user->id == $who->id;
+ my $canedit = $user->id == $who->id;
- $dbh->bz_start_transaction();
+ $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));
- }
+ 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->{'votes_recorded'} = 1;
+ 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->{'votes_recorded'} = 1;
}
######################
@@ -650,234 +691,246 @@ 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}));
+ AND votes.vote_count > ?', undef,
+ ($product->id, $product->{maxvotesperbug})
+ );
+
+ foreach my $vote (@$votes) {
+ my ($who, $id) = (@$vote);
- 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 = user_id_to_login($who);
+ # 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 = user_id_to_login($who);
- push(@toomanyvotes_list, {id => $id, name => $name});
- }
+ 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);
-
- 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'));
- my $name = user_id_to_login($who);
-
- 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, $product);
- 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
+ );
+
+ 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'));
+ my $name = user_id_to_login($who);
+
+ 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, $product);
+ 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, $product) = @_;
- my $bug = ref $id ? $id : new Bugzilla::Bug({ id => $id, cache => 1 });
- $product ||= $bug->product_obj;
-
- my $ret = 0;
- if (!$bug->everconfirmed
- and $product->{votestoconfirm}
- and $bug->votes >= $product->{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, $product) = @_;
+ my $bug = ref $id ? $id : new Bugzilla::Bug({id => $id, cache => 1});
+ $product ||= $bug->product_obj;
+
+ my $ret = 0;
+ if ( !$bug->everconfirmed
+ and $product->{votestoconfirm}
+ and $bug->votes >= $product->{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/ZPushNotify/Config.pm b/extensions/ZPushNotify/Config.pm
index 1a31285e2..04c607974 100644
--- a/extensions/ZPushNotify/Config.pm
+++ b/extensions/ZPushNotify/Config.pm
@@ -11,7 +11,7 @@ use 5.10.1;
use strict;
use warnings;
-use constant NAME => 'ZPushNotify';
+use constant NAME => 'ZPushNotify';
use constant REQUIRED_MODULES => [];
use constant OPTIONAL_MODULES => [];
diff --git a/extensions/ZPushNotify/Extension.pm b/extensions/ZPushNotify/Extension.pm
index df3ab8f46..7c3b6cbf0 100644
--- a/extensions/ZPushNotify/Extension.pm
+++ b/extensions/ZPushNotify/Extension.pm
@@ -21,46 +21,38 @@ use Bugzilla;
#
sub _notify {
- my ($bug_id, $delta_ts) = @_;
- # beacuse the push_notify table is hot, we defer updating it until the
- # request has completed. this ensures we are outside the scope of any
- # transaction blocks.
+ my ($bug_id, $delta_ts) = @_;
- my $stash = Bugzilla->request_cache->{ZPushNotify_stash} ||= [];
- push @$stash, { bug_id => $bug_id, delta_ts => $delta_ts };
+ # beacuse the push_notify table is hot, we defer updating it until the
+ # request has completed. this ensures we are outside the scope of any
+ # transaction blocks.
+
+ my $stash = Bugzilla->request_cache->{ZPushNotify_stash} ||= [];
+ push @$stash, {bug_id => $bug_id, delta_ts => $delta_ts};
}
sub request_cleanup {
- my $stash = Bugzilla->request_cache->{ZPushNotify_stash}
- || return;
-
- my $dbh = Bugzilla->dbh;
- foreach my $rh (@$stash) {
- # using REPLACE INTO or INSERT .. ON DUPLICATE KEY UPDATE results in a
- # lock on the bugs table due to the FK. this way is more verbose but
- # only locks the push_notify table.
- $dbh->bz_start_transaction();
- my ($id) = $dbh->selectrow_array(
- "SELECT id FROM push_notify WHERE bug_id=?",
- undef,
- $rh->{bug_id}
- );
- if ($id) {
- $dbh->do(
- "UPDATE push_notify SET delta_ts=? WHERE id=?",
- undef,
- $rh->{delta_ts}, $id
- );
- }
- else {
- $dbh->do(
- "INSERT INTO push_notify (bug_id, delta_ts) VALUES (?, ?)",
- undef,
- $rh->{bug_id}, $rh->{delta_ts}
- );
- }
- $dbh->bz_commit_transaction();
+ my $stash = Bugzilla->request_cache->{ZPushNotify_stash} || return;
+
+ my $dbh = Bugzilla->dbh;
+ foreach my $rh (@$stash) {
+
+ # using REPLACE INTO or INSERT .. ON DUPLICATE KEY UPDATE results in a
+ # lock on the bugs table due to the FK. this way is more verbose but
+ # only locks the push_notify table.
+ $dbh->bz_start_transaction();
+ my ($id) = $dbh->selectrow_array("SELECT id FROM push_notify WHERE bug_id=?",
+ undef, $rh->{bug_id});
+ if ($id) {
+ $dbh->do("UPDATE push_notify SET delta_ts=? WHERE id=?",
+ undef, $rh->{delta_ts}, $id);
+ }
+ else {
+ $dbh->do("INSERT INTO push_notify (bug_id, delta_ts) VALUES (?, ?)",
+ undef, $rh->{bug_id}, $rh->{delta_ts});
}
+ $dbh->bz_commit_transaction();
+ }
}
#
@@ -68,59 +60,59 @@ sub request_cleanup {
#
sub object_end_of_create {
- my ($self, $args) = @_;
- my $object = $args->{object};
- return unless Bugzilla->params->{enable_simple_push};
- return unless $object->isa('Bugzilla::Flag');
- _notify($object->bug->id, $object->creation_date);
+ my ($self, $args) = @_;
+ my $object = $args->{object};
+ return unless Bugzilla->params->{enable_simple_push};
+ return unless $object->isa('Bugzilla::Flag');
+ _notify($object->bug->id, $object->creation_date);
}
sub flag_updated {
- my ($self, $args) = @_;
- my $flag = $args->{flag};
- my $timestamp = $args->{timestamp};
- my $changes = $args->{changes};
- return unless Bugzilla->params->{enable_simple_push};
- return unless scalar(keys %$changes);
- _notify($flag->bug->id, $timestamp);
+ my ($self, $args) = @_;
+ my $flag = $args->{flag};
+ my $timestamp = $args->{timestamp};
+ my $changes = $args->{changes};
+ return unless Bugzilla->params->{enable_simple_push};
+ return unless scalar(keys %$changes);
+ _notify($flag->bug->id, $timestamp);
}
sub flag_deleted {
- my ($self, $args) = @_;
- my $flag = $args->{flag};
- my $timestamp = $args->{timestamp};
- return unless Bugzilla->params->{enable_simple_push};
- _notify($flag->bug->id, $timestamp);
+ my ($self, $args) = @_;
+ my $flag = $args->{flag};
+ my $timestamp = $args->{timestamp};
+ return unless Bugzilla->params->{enable_simple_push};
+ _notify($flag->bug->id, $timestamp);
}
sub attachment_end_of_update {
- my ($self, $args) = @_;
- return unless Bugzilla->params->{enable_simple_push};
- return unless scalar keys %{ $args->{changes} };
- return unless my $object = $args->{object};
- _notify($object->bug->id, $object->modification_time);
+ my ($self, $args) = @_;
+ return unless Bugzilla->params->{enable_simple_push};
+ return unless scalar keys %{$args->{changes}};
+ return unless my $object = $args->{object};
+ _notify($object->bug->id, $object->modification_time);
}
sub object_before_delete {
- my ($self, $args) = @_;
- return unless Bugzilla->params->{enable_simple_push};
- return unless my $object = $args->{object};
- if ($object->isa('Bugzilla::Attachment')) {
- my $timestamp = Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
- _notify($object->bug->id, $timestamp);
- }
+ my ($self, $args) = @_;
+ return unless Bugzilla->params->{enable_simple_push};
+ return unless my $object = $args->{object};
+ if ($object->isa('Bugzilla::Attachment')) {
+ my $timestamp = Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
+ _notify($object->bug->id, $timestamp);
+ }
}
sub bug_end_of_update_delta_ts {
- my ($self, $args) = @_;
- return unless Bugzilla->params->{enable_simple_push};
- _notify($args->{bug_id}, $args->{timestamp});
+ my ($self, $args) = @_;
+ return unless Bugzilla->params->{enable_simple_push};
+ _notify($args->{bug_id}, $args->{timestamp});
}
sub bug_end_of_create {
- my ($self, $args) = @_;
- return unless Bugzilla->params->{enable_simple_push};
- _notify($args->{bug}->id, $args->{timestamp});
+ my ($self, $args) = @_;
+ return unless Bugzilla->params->{enable_simple_push};
+ _notify($args->{bug}->id, $args->{timestamp});
}
#
@@ -128,44 +120,25 @@ sub bug_end_of_create {
#
sub db_schema_abstract_schema {
- my ($self, $args) = @_;
- $args->{'schema'}->{'push_notify'} = {
- FIELDS => [
- id => {
- TYPE => 'INTSERIAL',
- NOTNULL => 1,
- PRIMARYKEY => 1,
- },
- bug_id => {
- TYPE => 'INT3',
- NOTNULL => 1,
- REFERENCES => {
- TABLE => 'bugs',
- COLUMN => 'bug_id',
- DELETE => 'CASCADE'
- },
- },
- delta_ts => {
- TYPE => 'DATETIME',
- NOTNULL => 1,
- },
- ],
- INDEXES => [
- push_notify_idx => {
- FIELDS => [ 'bug_id' ],
- TYPE => 'UNIQUE',
- },
- ],
- };
+ my ($self, $args) = @_;
+ $args->{'schema'}->{'push_notify'} = {
+ FIELDS => [
+ id => {TYPE => 'INTSERIAL', NOTNULL => 1, PRIMARYKEY => 1,},
+ bug_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {TABLE => 'bugs', COLUMN => 'bug_id', DELETE => 'CASCADE'},
+ },
+ delta_ts => {TYPE => 'DATETIME', NOTNULL => 1,},
+ ],
+ INDEXES => [push_notify_idx => {FIELDS => ['bug_id'], TYPE => 'UNIQUE',},],
+ };
}
sub config_modify_panels {
- my ($self, $args) = @_;
- push @{ $args->{panels}->{advanced}->{params} }, {
- name => 'enable_simple_push',
- type => 'b',
- default => 0,
- };
+ my ($self, $args) = @_;
+ push @{$args->{panels}->{advanced}->{params}},
+ {name => 'enable_simple_push', type => 'b', default => 0,};
}
__PACKAGE__->NAME;
diff --git a/extensions/create.pl b/extensions/create.pl
index 8927c172c..3d3bfaa93 100755
--- a/extensions/create.pl
+++ b/extensions/create.pl
@@ -27,7 +27,7 @@ my $base_dir = bz_locations()->{'extensionsdir'};
my $name = $ARGV[0] or ThrowUserError('extension_create_no_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";
@@ -35,33 +35,33 @@ mkpath($extension_dir)
|| die "$extension_dir already exists or cannot be created.\n";
my $lcname = lc($name);
-foreach my $path (qw(lib web template/en/default/hook),
- "template/en/default/$lcname")
+foreach
+ my $path (qw(lib web template/en/default/hook), "template/en/default/$lcname")
{
- mkpath("$extension_dir/$path") || die "$extension_dir/$path: $!";
+ mkpath("$extension_dir/$path") || die "$extension_dir/$path: $!";
}
my $year = DateTime->now()->year;
-my $template = Bugzilla->template;
-my $vars = { year => $year, name => $name, path => $extension_dir };
+my $template = Bugzilla->template;
+my $vars = {year => $year, 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",
+ '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",
);
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");
- 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");
+ print $fh $output;
+ close($fh);
}
print get_text('extension_created', $vars), "\n";