def testReplyInvitation(self): """We include a footer about replying that is appropriate for that user.""" email_task = notify_helpers._MakeEmailWorkItem( notify_reasons.AddrPerm(True, '*****@*****.**', self.member, REPLY_NOT_ALLOWED), ['reason'], self.issue, 'body non', 'body mem', self.project, 'example.com', self.commenter_view, self.detail_url) self.assertEqual(emailfmt.NoReplyAddress(), email_task['reply_to']) self.assertNotIn('Reply to this email', email_task['body']) email_task = notify_helpers._MakeEmailWorkItem( notify_reasons.AddrPerm(True, '*****@*****.**', self.member, REPLY_MAY_COMMENT), ['reason'], self.issue, 'body non', 'body mem', self.project, 'example.com', self.commenter_view, self.detail_url) self.assertEqual( '%s@%s' % (self.project.project_name, emailfmt.MailDomain()), email_task['reply_to']) self.assertIn('Reply to this email to add a comment', email_task['body']) self.assertNotIn('make changes', email_task['body']) email_task = notify_helpers._MakeEmailWorkItem( notify_reasons.AddrPerm(True, '*****@*****.**', self.member, REPLY_MAY_UPDATE), ['reason'], self.issue, 'body non', 'body mem', self.project, 'example.com', self.commenter_view, self.detail_url) self.assertEqual( '%s@%s' % (self.project.project_name, emailfmt.MailDomain()), email_task['reply_to']) self.assertIn('Reply to this email to add a comment', email_task['body']) self.assertIn('make updates', email_task['body'])
def testComputeGroupReasonList_Subscribers(self): """Bob subscribed.""" sq = tracker_bizobj.MakeSavedQuery( 1, 'freds issues', 1, 'owner:[email protected]', subscription_mode='immediate', executes_in_project_ids=[789]) self.services.features.UpdateUserSavedQueries( 'cnxn', self.bob.user_id, [sq]) actual = notify_reasons.ComputeGroupReasonList( 'cnxn', self.services, self.project, self.issue, self.config, self.users_by_id, [], True) self.CheckGroupReasonList( actual, owner_apl=[notify_reasons.AddrPerm( False, self.fred.email, self.fred, REPLY_NOT_ALLOWED)], subscriber_apl=[notify_reasons.AddrPerm( False, self.bob.email, self.bob, REPLY_NOT_ALLOWED)]) # Now with subscriber notifications disabled. actual = notify_reasons.ComputeGroupReasonList( 'cnxn', self.services, self.project, self.issue, self.config, self.users_by_id, [], True, include_subscribers=False) self.CheckGroupReasonList( actual, owner_apl=[notify_reasons.AddrPerm( False, self.fred.email, self.fred, REPLY_NOT_ALLOWED)])
def testComputeGroupReasonList_NotifyAll(self): """Project is configured to always notify [email protected].""" self.project.issue_notify_address = '*****@*****.**' actual = notify_reasons.ComputeGroupReasonList( 'cnxn', self.services, self.project, self.issue, self.config, self.users_by_id, [], True) self.CheckGroupReasonList( actual, owner_apl=[notify_reasons.AddrPerm( False, self.fred.email, self.fred, REPLY_NOT_ALLOWED)], all_notifications_apl=[notify_reasons.AddrPerm( False, '*****@*****.**', None, REPLY_NOT_ALLOWED)]) # We don't use the notify-all address when the issue is not public. actual = notify_reasons.ComputeGroupReasonList( 'cnxn', self.services, self.project, self.issue, self.config, self.users_by_id, [], False) self.CheckGroupReasonList( actual, owner_apl=[notify_reasons.AddrPerm( False, self.fred.email, self.fred, REPLY_NOT_ALLOWED)]) # Now with the notify-all address disabled. actual = notify_reasons.ComputeGroupReasonList( 'cnxn', self.services, self.project, self.issue, self.config, self.users_by_id, [], True, include_notify_all=False) self.CheckGroupReasonList( actual, owner_apl=[notify_reasons.AddrPerm( False, self.fred.email, self.fred, REPLY_NOT_ALLOWED)])
def testComputeGroupReasonList_OwnerAndCC(self): """Fred owns the issue, Alice is CC'd.""" self.issue.cc_ids = [self.alice.user_id] actual = notify_reasons.ComputeGroupReasonList( 'cnxn', self.services, self.project, self.issue, self.config, self.users_by_id, [], True) self.CheckGroupReasonList( actual, owner_apl=[notify_reasons.AddrPerm( False, self.fred.email, self.fred, REPLY_NOT_ALLOWED)], ccd_apl=[notify_reasons.AddrPerm( False, self.alice.email, self.alice, REPLY_NOT_ALLOWED)])
def testComputeGroupReasonList_Starrers(self): """Bob and Alice starred it, but Alice opts out of notifications.""" self.alice.notify_starred_issue_change = False actual = notify_reasons.ComputeGroupReasonList( 'cnxn', self.services, self.project, self.issue, self.config, self.users_by_id, [], True, starrer_ids=[self.alice.user_id, self.bob.user_id]) self.CheckGroupReasonList( actual, owner_apl=[notify_reasons.AddrPerm( False, self.fred.email, self.fred, REPLY_NOT_ALLOWED)], starrer_apl=[notify_reasons.AddrPerm( False, self.bob.email, self.bob, REPLY_NOT_ALLOWED)])
def testRecipientIsMember(self): cnxn = 'fake cnxn' ids_to_consider = [111, 222, 999] addr_perm_list = notify_reasons.ComputeIssueChangeAddressPermList( cnxn, ids_to_consider, self.project, self.issue, self.services, set(), self.users_by_id, pref_check_function=lambda *args: True) self.assertEqual( [notify_reasons.AddrPerm( True, '*****@*****.**', self.owner, REPLY_MAY_UPDATE), notify_reasons.AddrPerm( True, '*****@*****.**', self.member, REPLY_MAY_UPDATE), notify_reasons.AddrPerm( False, '*****@*****.**', self.visitor, REPLY_MAY_COMMENT)], addr_perm_list)
def testFormatBulkIssues_LinkOnly_Multiple(self): """A user may not see full notification details for some changed issue.""" self.issue1.summary = 'one summary' self.issue1.labels = ['Restrict-View-Google'] self.issue2.summary = 'two summary' task = notify.NotifyBulkChangeTask(request=None, response=None, services=self.services) users_by_id = {} commenter_view = None config = self.services.config.GetProjectConfig('cnxn', 12345) addrperm = notify_reasons.AddrPerm(False, '*****@*****.**', self.nonmember, notify_reasons.REPLY_NOT_ALLOWED, None) subject, body = task._FormatBulkIssues([self.issue1, self.issue2], users_by_id, commenter_view, 'localhost:8080', 'test comment', [], config, addrperm) self.assertIn('2 issues', subject) self.assertNotIn('summary', subject) self.assertNotIn('one summary', body) self.assertIn('two summary', body) self.assertNotIn('test comment', body)
def testHtmlBody_WithEscapedHtml(self): """"An html body is sent with html content escaped.""" body_with_html_content = ( '<a href="http://www.google.com">test</a> \'something\'') email_task = notify_helpers._MakeEmailWorkItem( notify_reasons.AddrPerm(False, '*****@*****.**', self.member, REPLY_NOT_ALLOWED, user_pb2.UserPrefs()), ['reason'], self.issue, 'body link-only', body_with_html_content, 'unused body mem', self.project, 'example.com', self.commenter_view, self.detail_url) escaped_body_with_html_content = ( '<a href="http://www.google.com">test</a> ' ''something'') notify_helpers._MakeNotificationFooter(['reason'], REPLY_NOT_ALLOWED, 'example.com') expected_html_body = ( notify_helpers.HTML_BODY_WITH_GMAIL_ACTION_TEMPLATE % { 'url': self.detail_url, 'body': '%s-- <br/>%s' % (escaped_body_with_html_content, self.expected_html_footer) }) self.assertEquals(expected_html_body, email_task['html_body'])
def testNotifyAddress(self): # No mailing list or filter rules are defined addr_perm_list = notify_reasons.ComputeProjectNotificationAddrList( self.cnxn, self.services, self.project, True, set()) self.assertListEqual([], addr_perm_list) # Only mailing list is notified. self.project.issue_notify_address = '*****@*****.**' addr_perm_list = notify_reasons.ComputeProjectNotificationAddrList( self.cnxn, self.services, self.project, True, set()) self.assertListEqual([ notify_reasons.AddrPerm(False, '*****@*****.**', None, REPLY_NOT_ALLOWED, user_pb2.UserPrefs()) ], addr_perm_list) # No one is notified because mailing list was already notified. omit_addrs = {'*****@*****.**'} addr_perm_list = notify_reasons.ComputeProjectNotificationAddrList( self.cnxn, self.services, self.project, False, omit_addrs) self.assertListEqual([], addr_perm_list) # No one is notified because anon users cannot view. addr_perm_list = notify_reasons.ComputeProjectNotificationAddrList( self.cnxn, self.services, self.project, False, set()) self.assertListEqual([], addr_perm_list)
def testBodySelection_Member(self): """We send members the email body that is indented for members.""" email_task = notify_helpers._MakeEmailWorkItem( notify_reasons.AddrPerm(True, '*****@*****.**', self.member, REPLY_NOT_ALLOWED, user_pb2.UserPrefs()), ['reason'], self.issue, 'body link-only', 'body mem', 'body mem', self.project, 'example.com', self.commenter_view, self.detail_url) self.assertIn('body mem', email_task['body'])
def testInboundEmailDisabled(self): """We don't invite replies if they are disabled for this project.""" self.project.process_inbound_email = False email_task = notify_helpers._MakeEmailWorkItem( notify_reasons.AddrPerm(True, '*****@*****.**', self.member, REPLY_MAY_UPDATE), ['reason'], self.issue, 'body non', 'body mem', self.project, 'example.com', self.commenter_view, self.detail_url) self.assertEqual(emailfmt.NoReplyAddress(), email_task['reply_to'])
def testReasons(self): """The footer lists reasons why that email was sent to that user.""" email_task = notify_helpers._MakeEmailWorkItem( notify_reasons.AddrPerm(True, '*****@*****.**', self.member, REPLY_MAY_UPDATE), ['Funny', 'Caring', 'Near'], self.issue, 'body', 'body', self.project, 'example.com', self.commenter_view, self.detail_url) self.assertIn('because:', email_task['body']) self.assertIn('1. Funny', email_task['body']) self.assertIn('2. Caring', email_task['body']) self.assertIn('3. Near', email_task['body']) email_task = notify_helpers._MakeEmailWorkItem( notify_reasons.AddrPerm(True, '*****@*****.**', self.member, REPLY_MAY_UPDATE), [], self.issue, 'body', 'body', self.project, 'example.com', self.commenter_view, self.detail_url) self.assertNotIn('because', email_task['body'])
def testBodySelection_LinkOnly(self, mock_sulo): """We send a link-only body when ShouldUseLinkOnly() is true.""" mock_sulo.return_value = True email_task = notify_helpers._MakeEmailWorkItem( notify_reasons.AddrPerm(True, '*****@*****.**', self.member, REPLY_NOT_ALLOWED, user_pb2.UserPrefs()), ['reason'], self.issue, 'body link-only', 'body mem', 'body mem', self.project, 'example.com', self.commenter_view, self.detail_url) self.assertIn('body link-only', email_task['body'])
def setUp(self): parent = user_pb2.User(user_id=111, email='*****@*****.**', linked_child_ids=[222]) child = user_pb2.User(user_id=222, email='*****@*****.**', linked_parent_id=111) user_3 = user_pb2.User(user_id=333, email='*****@*****.**') user_4 = user_pb2.User(user_id=444, email='*****@*****.**') self.addr_perm_parent = notify_reasons.AddrPerm( False, parent.email, parent, notify_reasons.REPLY_NOT_ALLOWED) self.addr_perm_child = notify_reasons.AddrPerm( False, child.email, child, notify_reasons.REPLY_NOT_ALLOWED) self.addr_perm_3 = notify_reasons.AddrPerm( False, user_3.email, user_3, notify_reasons.REPLY_NOT_ALLOWED) self.addr_perm_4 = notify_reasons.AddrPerm( False, user_4.email, user_4, notify_reasons.REPLY_NOT_ALLOWED) self.addr_perm_5 = notify_reasons.AddrPerm( False, '*****@*****.**', None, notify_reasons.REPLY_NOT_ALLOWED)
def testHtmlBody(self): """"An html body is sent if a detail_url is specified.""" email_task = notify_helpers._MakeEmailWorkItem( notify_reasons.AddrPerm(False, '*****@*****.**', self.member, REPLY_NOT_ALLOWED), ['reason'], self.issue, 'body non', 'body mem', self.project, 'example.com', self.commenter_view, self.detail_url) expected_html_body = ( notify_helpers.HTML_BODY_WITH_GMAIL_ACTION_TEMPLATE % { 'url': self.detail_url, 'body': 'body non-- <br/>%s' % self.expected_html_footer }) self.assertEquals(expected_html_body, email_task['html_body'])
def testBodySelection(self): """We send non-members the email body that is indented for non-members.""" email_task = notify_helpers._MakeEmailWorkItem( notify_reasons.AddrPerm(False, '*****@*****.**', self.member, REPLY_NOT_ALLOWED), ['reason'], self.issue, 'body non', 'body mem', self.project, 'example.com', self.commenter_view, self.detail_url) self.assertEqual('*****@*****.**', email_task['to']) self.assertEqual('Issue 1234 in proj1: summary', email_task['subject']) self.assertIn('body non', email_task['body']) self.assertEqual( emailfmt.FormatFromAddr(self.project, commenter_view=self.commenter_view, can_reply_to=False), email_task['from_addr']) self.assertEqual(emailfmt.NoReplyAddress(), email_task['reply_to']) email_task = notify_helpers._MakeEmailWorkItem( notify_reasons.AddrPerm(True, '*****@*****.**', self.member, REPLY_NOT_ALLOWED), ['reason'], self.issue, 'body mem', 'body mem', self.project, 'example.com', self.commenter_view, self.detail_url) self.assertIn('body mem', email_task['body'])
def testHtmlBody_LinkWithinTags(self): """"An html body is sent with correct <a href>s.""" email_task = notify_helpers._MakeEmailWorkItem( notify_reasons.AddrPerm(False, '*****@*****.**', self.member, REPLY_NOT_ALLOWED), ['reason'], self.issue, 'a <http://google.com> z', 'unused body', self.project, 'example.com', self.commenter_view, self.detail_url) expected_html_body = ( notify_helpers.HTML_BODY_WITH_GMAIL_ACTION_TEMPLATE % { 'url': self.detail_url, 'body': ('a <<a href="http://google.com">http://google.com</a>> ' 'z-- <br/>%s' % self.expected_html_footer) }) self.assertEquals(expected_html_body, email_task['html_body'])
def testFilterRuleNotifyAddresses(self): issue = fake.MakeTestIssue(self.project.project_id, 1, 'summary', 'New', 555) issue.derived_notify_addrs.extend(['*****@*****.**']) addr_perm_list = notify_reasons.ComputeIssueNotificationAddrList( self.cnxn, self.services, issue, set()) self.assertListEqual([ notify_reasons.AddrPerm(False, '*****@*****.**', None, REPLY_NOT_ALLOWED, user_pb2.UserPrefs()) ], addr_perm_list) # Also-notify addresses can be omitted (e.g., if it is the same as # the email address of the user who made the change). addr_perm_list = notify_reasons.ComputeIssueNotificationAddrList( self.cnxn, self.services, issue, {'*****@*****.**'}) self.assertListEqual([], addr_perm_list)
def testHtmlBody_WithLinks(self): """"An html body is sent if a detail_url is specified.""" email_task = notify_helpers._MakeEmailWorkItem( notify_reasons.AddrPerm(False, '*****@*****.**', self.member, REPLY_NOT_ALLOWED), ['reason'], self.issue, 'test google.com test', 'unused body mem', self.project, 'example.com', self.commenter_view, self.detail_url) expected_html_body = ( notify_helpers.HTML_BODY_WITH_GMAIL_ACTION_TEMPLATE % { 'url': self.detail_url, 'body': ('test <a href="http://google.com">google.com</a> test-- <br/>%s' % (self.expected_html_footer)) }) self.assertEquals(expected_html_body, email_task['html_body'])
def testHtmlBody_WithUnicodeChars(self): """"An html body is sent if a detail_url is specified.""" unicode_content = '\xe2\x9d\xa4 â â' email_task = notify_helpers._MakeEmailWorkItem( notify_reasons.AddrPerm(False, '*****@*****.**', self.member, REPLY_NOT_ALLOWED), ['reason'], self.issue, unicode_content, 'unused body mem', self.project, 'example.com', self.commenter_view, self.detail_url) expected_html_body = ( notify_helpers.HTML_BODY_WITH_GMAIL_ACTION_TEMPLATE % { 'url': self.detail_url, 'body': '%s-- <br/>%s' % (unicode_content.decode('utf-8'), self.expected_html_footer) }) self.assertEquals(expected_html_body, email_task['html_body'])
def testHtmlBody_EmailWithinTags(self): """"An html body is sent with correct <a href>s.""" email_task = notify_helpers._MakeEmailWorkItem( notify_reasons.AddrPerm(False, '*****@*****.**', self.member, REPLY_NOT_ALLOWED, user_pb2.UserPrefs()), ['reason'], self.issue, 'body link-only', 'a <*****@*****.**> <*****@*****.**> z', 'unused body mem', self.project, 'example.com', self.commenter_view, self.detail_url) expected_html_body = ( notify_helpers.HTML_BODY_WITH_GMAIL_ACTION_TEMPLATE % { 'url': self.detail_url, 'body': ('a <<a href="mailto:[email protected]">[email protected]</a>>' ' <<a href="mailto:[email protected]">[email protected]</a>> ' 'z-- <br/>%s' % self.expected_html_footer) }) self.assertEquals(expected_html_body, email_task['html_body'])
def setUp(self): self.user_prefs = user_pb2.UserPrefs() self.user = user_pb2.User() self.issue = fake.MakeTestIssue(789, 1, 'summary one', 'New', 111) self.rvg_issue = fake.MakeTestIssue(789, 2, 'summary two', 'New', 111, labels=['Restrict-View-Google']) self.more_restricted_issue = fake.MakeTestIssue( 789, 3, 'summary three', 'New', 111, labels=['Restrict-View-Core']) self.both_restricted_issue = fake.MakeTestIssue( 789, 4, 'summary four', 'New', 111, labels=['Restrict-View-Google', 'Restrict-View-Core']) self.addr_perm = notify_reasons.AddrPerm( False, '*****@*****.**', self.user, notify_reasons.REPLY_MAY_COMMENT, self.user_prefs)
def testFormatBulkIssues_Normal_Single(self): """A user may see full notification details for all changed issues.""" self.issue1.summary = 'one summary' task = notify.NotifyBulkChangeTask(request=None, response=None, services=self.services) users_by_id = {} commenter_view = None config = self.services.config.GetProjectConfig('cnxn', 12345) addrperm = notify_reasons.AddrPerm(False, '*****@*****.**', self.nonmember, notify_reasons.REPLY_NOT_ALLOWED, None) subject, body = task._FormatBulkIssues([self.issue1], users_by_id, commenter_view, 'localhost:8080', 'test comment', [], config, addrperm) self.assertIn('one summary', subject) self.assertIn('one summary', body) self.assertIn('test comment', body)
def _BulkEditEmailTasks( self, cnxn, issues, old_owner_ids, omit_addrs, project, non_private_issues, users_by_id, ids_in_issues, starrers, commenter_view, hostport, comment_text, amendments, config): """Generate Email PBs to notify interested users after a bulk edit.""" # 1. Get the user IDs of everyone who could be notified, # and make all their user proxies. Also, build a dictionary # of all the users to notify and the issues that they are # interested in. Also, build a dictionary of additional email # addresses to notify and the issues to notify them of. users_by_id = {} ids_to_notify_of_issue = {} additional_addrs_to_notify_of_issue = collections.defaultdict(list) users_to_queries = notify_reasons.GetNonOmittedSubscriptions( cnxn, self.services, [project.project_id], {}) config = self.services.config.GetProjectConfig( cnxn, project.project_id) for issue, old_owner_id in zip(issues, old_owner_ids): issue_participants = set( [tracker_bizobj.GetOwnerId(issue), old_owner_id] + tracker_bizobj.GetCcIds(issue)) # users named in user-value fields that notify. for fd in config.field_defs: issue_participants.update( notify_reasons.ComputeNamedUserIDsToNotify(issue.field_values, fd)) for user_id in ids_in_issues[issue.local_id]: # TODO(jrobbins): implement batch GetUser() for speed. if not user_id: continue auth = authdata.AuthData.FromUserID( cnxn, user_id, self.services) if (auth.user_pb.notify_issue_change and not auth.effective_ids.isdisjoint(issue_participants)): ids_to_notify_of_issue.setdefault(user_id, []).append(issue) elif (auth.user_pb.notify_starred_issue_change and user_id in starrers[issue.local_id]): # Skip users who have starred issues that they can no longer view. starrer_perms = permissions.GetPermissions( auth.user_pb, auth.effective_ids, project) granted_perms = tracker_bizobj.GetGrantedPerms( issue, auth.effective_ids, config) starrer_can_view = permissions.CanViewIssue( auth.effective_ids, starrer_perms, project, issue, granted_perms=granted_perms) if starrer_can_view: ids_to_notify_of_issue.setdefault(user_id, []).append(issue) logging.info( 'ids_to_notify_of_issue[%s] = %s', user_id, [i.local_id for i in ids_to_notify_of_issue.get(user_id, [])]) # Find all subscribers that should be notified. subscribers_to_consider = notify_reasons.EvaluateSubscriptions( cnxn, issue, users_to_queries, self.services, config) for sub_id in subscribers_to_consider: auth = authdata.AuthData.FromUserID(cnxn, sub_id, self.services) sub_perms = permissions.GetPermissions( auth.user_pb, auth.effective_ids, project) granted_perms = tracker_bizobj.GetGrantedPerms( issue, auth.effective_ids, config) sub_can_view = permissions.CanViewIssue( auth.effective_ids, sub_perms, project, issue, granted_perms=granted_perms) if sub_can_view: ids_to_notify_of_issue.setdefault(sub_id, []) if issue not in ids_to_notify_of_issue[sub_id]: ids_to_notify_of_issue[sub_id].append(issue) if issue in non_private_issues: for notify_addr in issue.derived_notify_addrs: additional_addrs_to_notify_of_issue[notify_addr].append(issue) # 2. Compose an email specifically for each user, and one email to each # notify_addr with all the issues that it. # Start from non-members first, then members to reveal email addresses. email_tasks = [] needed_user_view_ids = [uid for uid in ids_to_notify_of_issue if uid not in users_by_id] users_by_id.update(framework_views.MakeAllUserViews( cnxn, self.services.user, needed_user_view_ids)) member_ids_to_notify_of_issue = {} non_member_ids_to_notify_of_issue = {} member_additional_addrs = {} non_member_additional_addrs = {} addr_to_addrperm = {} # {email_address: AddrPerm object} all_user_prefs = self.services.user.GetUsersPrefs( cnxn, ids_to_notify_of_issue) # TODO(jrobbins): Merge ids_to_notify_of_issue entries for linked accounts. for user_id in ids_to_notify_of_issue: if not user_id: continue # Don't try to notify NO_USER_SPECIFIED if users_by_id[user_id].email in omit_addrs: logging.info('Omitting %s', user_id) continue user_issues = ids_to_notify_of_issue[user_id] if not user_issues: continue # user's prefs indicate they don't want these notifications auth = authdata.AuthData.FromUserID( cnxn, user_id, self.services) is_member = bool(framework_bizobj.UserIsInProject( project, auth.effective_ids)) if is_member: member_ids_to_notify_of_issue[user_id] = user_issues else: non_member_ids_to_notify_of_issue[user_id] = user_issues addr = users_by_id[user_id].email omit_addrs.add(addr) addr_to_addrperm[addr] = notify_reasons.AddrPerm( is_member, addr, users_by_id[user_id].user, notify_reasons.REPLY_NOT_ALLOWED, all_user_prefs[user_id]) for addr, addr_issues in additional_addrs_to_notify_of_issue.items(): auth = None try: auth = authdata.AuthData.FromEmail(cnxn, addr, self.services) except: # pylint: disable=bare-except logging.warning('Cannot find user of email %s ', addr) if auth: is_member = bool(framework_bizobj.UserIsInProject( project, auth.effective_ids)) else: is_member = False if is_member: member_additional_addrs[addr] = addr_issues else: non_member_additional_addrs[addr] = addr_issues omit_addrs.add(addr) addr_to_addrperm[addr] = notify_reasons.AddrPerm( is_member, addr, None, notify_reasons.REPLY_NOT_ALLOWED, None) for user_id, user_issues in non_member_ids_to_notify_of_issue.items(): addr = users_by_id[user_id].email email = self._FormatBulkIssuesEmail( addr_to_addrperm[addr], user_issues, users_by_id, commenter_view, hostport, comment_text, amendments, config, project) email_tasks.append(email) logging.info('about to bulk notify non-member %s (%s) of %s', users_by_id[user_id].email, user_id, [issue.local_id for issue in user_issues]) for addr, addr_issues in non_member_additional_addrs.items(): email = self._FormatBulkIssuesEmail( addr_to_addrperm[addr], addr_issues, users_by_id, commenter_view, hostport, comment_text, amendments, config, project) email_tasks.append(email) logging.info('about to bulk notify non-member additional addr %s of %s', addr, [addr_issue.local_id for addr_issue in addr_issues]) framework_views.RevealAllEmails(users_by_id) commenter_view.RevealEmail() for user_id, user_issues in member_ids_to_notify_of_issue.items(): addr = users_by_id[user_id].email email = self._FormatBulkIssuesEmail( addr_to_addrperm[addr], user_issues, users_by_id, commenter_view, hostport, comment_text, amendments, config, project) email_tasks.append(email) logging.info('about to bulk notify member %s (%s) of %s', addr, user_id, [issue.local_id for issue in user_issues]) for addr, addr_issues in member_additional_addrs.items(): email = self._FormatBulkIssuesEmail( addr_to_addrperm[addr], addr_issues, users_by_id, commenter_view, hostport, comment_text, amendments, config, project) email_tasks.append(email) logging.info('about to bulk notify member additional addr %s of %s', addr, [addr_issue.local_id for addr_issue in addr_issues]) # 4. Add in the project's issue_notify_address. This happens even if it # is the same as the commenter's email address (which would be an unusual # but valid project configuration). Only issues that any contributor could # view are included in emails to the all-issue-activity mailing lists. if (project.issue_notify_address and project.issue_notify_address not in omit_addrs): non_private_issues_live = [] for issue in issues: contributor_could_view = permissions.CanViewIssue( set(), permissions.CONTRIBUTOR_ACTIVE_PERMISSIONSET, project, issue) if contributor_could_view: non_private_issues_live.append(issue) if non_private_issues_live: project_notify_addrperm = notify_reasons.AddrPerm( True, project.issue_notify_address, None, notify_reasons.REPLY_NOT_ALLOWED, None) email = self._FormatBulkIssuesEmail( project_notify_addrperm, non_private_issues_live, users_by_id, commenter_view, hostport, comment_text, amendments, config, project) email_tasks.append(email) omit_addrs.add(project.issue_notify_address) logging.info('about to bulk notify all-issues %s of %s', project.issue_notify_address, [issue.local_id for issue in non_private_issues]) return email_tasks