From 70b298ecbd3aba8f24bf6d0147bece13b81d6040 Mon Sep 17 00:00:00 2001 From: Byron Jones Date: Tue, 19 May 2015 20:46:40 +0800 Subject: Bug 1163868: Include requests from others in RequestNagger --- extensions/RequestNagger/bin/send-request-nags.pl | 316 ++++++++++++---------- 1 file changed, 180 insertions(+), 136 deletions(-) (limited to 'extensions/RequestNagger/bin') diff --git a/extensions/RequestNagger/bin/send-request-nags.pl b/extensions/RequestNagger/bin/send-request-nags.pl index 93265b9ee..1673d2208 100755 --- a/extensions/RequestNagger/bin/send-request-nags.pl +++ b/extensions/RequestNagger/bin/send-request-nags.pl @@ -19,6 +19,7 @@ BEGIN { Bugzilla->extensions() } use Bugzilla::Attachment; use Bugzilla::Bug; use Bugzilla::Constants; +use Bugzilla::Hook; use Bugzilla::Error; use Bugzilla::Extension::RequestNagger::Constants; use Bugzilla::Extension::RequestNagger::Bug; @@ -26,7 +27,7 @@ use Bugzilla::Mailer; use Bugzilla::User; use Bugzilla::Util qw(format_time); use Email::MIME; -use Sys::Hostname; +use Sys::Hostname qw(hostname); Bugzilla->usage_mode(USAGE_MODE_CMDLINE); @@ -42,168 +43,211 @@ Bugzilla->switch_to_shadow_db(); # send nags to requestees send_nags( - sql => REQUESTEE_NAG_SQL, - template => 'requestee', - recipient_field => 'requestee_id', - date => $date, + requestee_sql => REQUESTEE_NAG_SQL, + setter_sql => SETTER_NAG_SQL, + template => 'user', + date => $date, ); # send nags to watchers send_nags( - sql => WATCHING_NAG_SQL, - template => 'watching', - recipient_field => 'watcher_id', - date => $date, + requestee_sql => WATCHING_REQUESTEE_NAG_SQL, + setter_sql => WATCHING_SETTER_NAG_SQL, + template => 'watching', + date => $date, ); sub send_nags { my (%args) = @_; - my $rows = $dbh->selectall_arrayref($args{sql}, { Slice => {} }); - - # iterate over rows, sending email when the current recipient changes - my $requests = []; - my $current_recipient; - foreach my $request (@$rows) { - # send previous user's requests - if (!$current_recipient || $request->{$args{recipient_field}} != $current_recipient->id) { - send_email(%args, recipient => $current_recipient, requests => $requests); - $current_recipient = Bugzilla::User->new({ id => $request->{$args{recipient_field}}, cache => 1 }); - $requests = []; + + my @reports = qw( requestee setter ); + my $securemail = Bugzilla::User->can('public_key'); + my $requests = {}; + + # get requests + + foreach my $report (@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}; } - # check group membership - $request->{requestee} = Bugzilla::User->new({ id => $request->{requestee_id}, cache => 1 }); - my $group; - foreach my $type (FLAG_TYPES) { - next unless $type->{type} eq $request->{flag_type}; - $group = $type->{group}; - last; + # 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} } + ); + } } - next unless $request->{requestee}->in_group($group); - - # check bug visibility - next unless $current_recipient->can_see_bug($request->{bug_id}); - - # create objects - $request->{bug} = Bugzilla::Bug->new({ id => $request->{bug_id}, cache => 1 }); - $request->{requester} = Bugzilla::User->new({ id => $request->{requester_id}, cache => 1 }); - $request->{flag} = Bugzilla::Flag->new({ id => $request->{flag_id}, cache => 1 }); - if ($request->{attach_id}) { - $request->{attachment} = Bugzilla::Attachment->new({ id => $request->{attach_id}, cache => 1 }); - # check attachment visibility - next if $request->{attachment}->isprivate && !$current_recipient->is_insider; + } + + # send emails + + foreach my $recipient_id (sort keys %$requests) { + my $recipient = Bugzilla::User->new({ id => $recipient_id, cache => 1 }); + my $has_key = $securemail && $recipient->public_key; + my $has_private_bug = 0; + + foreach my $target_login (keys %{ $requests->{$recipient_id} }) { + my $rh = $requests->{$recipient_id}->{$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; + } + } + } + } } - if (exists $request->{watcher_id}) { - $request->{watcher} = Bugzilla::User->new({ id => $request->{watcher_id}, cache => 1 }); + my $encrypt = $securemail && $has_private_bug && $has_key; + + # generate email + my $template = Bugzilla->template_inner($recipient->setting('lang')); + my $template_file = $args{template}; + my $vars = { + recipient => $recipient, + requests => $requests->{$recipient_id}, + date => $args{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" }, + body => $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" }, + body => $html, + ); } - # add this request to the current user's list - push(@$requests, $request); - } - send_email(%args, recipient => $current_recipient, requests => $requests); -} + 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'); + } -sub send_email { - my (%vars) = @_; - my $vars = \%vars; - return unless $vars->{recipient} && @{ $vars->{requests} }; - - my $request_list = delete $vars->{requests}; - - # if securemail is installed, we need to encrypt or censor emails which - # contain non-public bugs - my $default_user = Bugzilla::User->new(); - my $securemail = $vars->{recipient}->can('public_key'); - my $has_key = $securemail && $vars->{recipient}->public_key; - # have to do this each time as objects are shared between requests - my $has_private_bug = 0; - foreach my $request (@{ $request_list }) { - # rebless bug objects into our subclass - bless($request->{bug}, 'Bugzilla::Extension::RequestNagger::Bug'); - # and tell that object to hide the summary if required - if ($securemail && !$default_user->can_see_bug($request->{bug})) { - $has_private_bug = 1; - $request->{bug}->{secure_bug} = !$has_key; + # 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 { - $request->{bug}->{secure_bug} = 0; + MessageToMTA($email); } + + # nuke objects to avoid excessive memory usage + $requests->{$recipient_id} = undef; + Bugzilla->clear_request_cache(); } - my $encrypt = $securemail && $has_private_bug && $has_key; - - # restructure the list to group by requestee then flag type - my $requests = {}; - my %seen_types; - foreach my $request (@{ $request_list }) { - # by requestee - my $requestee_login = $request->{requestee}->login; - $requests->{$requestee_login} ||= { - requestee => $request->{requestee}, - types => {}, - typelist => [], - }; +} - # by flag type - my $types = $requests->{$requestee_login}->{types}; - my $flag_type = $request->{flag_type}; - $types->{$flag_type} ||= []; +sub _include_request { + my ($request, $report) = @_; - push @{ $types->{$flag_type} }, $request; - $seen_types{$requestee_login}{$flag_type} = 1; - } - foreach my $requestee_login (keys %seen_types) { - my @flag_types; - foreach my $flag_type (map { $_->{type} } FLAG_TYPES) { - push @flag_types, $flag_type if $seen_types{$requestee_login}{$flag_type}; + 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; } - $requests->{$requestee_login}->{typelist} = \@flag_types; + return 0 unless $recipient->in_group($group); } - $vars->{requests} = $requests; - - # generate email - my $template = Bugzilla->template_inner($vars->{recipient}->setting('lang')); - my $template_file = $vars->{template}; - - 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" }, - body => $text, - ) - ); - if ($vars->{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" }, - body => $html, - ); + + # 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; } - 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'); + return 1; +} + +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 }); } - $email->parts_set(\@parts); - if ($encrypt) { - $email->header_set('X-Bugzilla-Encrypt' => '1'); + if (exists $request->{watcher_id}) { + $request->{watcher} = Bugzilla::User->new({ id => $request->{watcher_id}, cache => 1 }); } - # send - if ($DO_NOT_NAG) { - print $email->as_string, "\n"; - } else { - MessageToMTA($email); + $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 }); } } - -- cgit v1.2.3-24-g4f1b