##// END OF EJS Templates
emails: set References header for threading in mail user agents even with different subjects...
marcink -
r4447:ae62a3cc default
parent child Browse files
Show More
@@ -578,8 +578,7 b' class AdminSettingsView(BaseAppView):'
578 578 'user': self._rhodecode_db_user
579 579 }
580 580
581 (subject, headers, email_body,
582 email_body_plaintext) = EmailNotificationModel().render_email(
581 (subject, email_body, email_body_plaintext) = EmailNotificationModel().render_email(
583 582 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
584 583
585 584 recipients = [test_email] if test_email else None
@@ -376,8 +376,7 b' users: description edit fixes'
376 376 }
377 377
378 378 template_type = email_id.split('+')[0]
379 (c.subject, c.headers, c.email_body,
380 c.email_body_plaintext) = EmailNotificationModel().render_email(
379 (c.subject, c.email_body, c.email_body_plaintext) = EmailNotificationModel().render_email(
381 380 template_type, **email_kwargs.get(email_id, {}))
382 381
383 382 test_email = self.request.GET.get('email')
@@ -29,6 +29,7 b' import time'
29 29 from pyramid import compat
30 30 from pyramid_mailer.mailer import Mailer
31 31 from pyramid_mailer.message import Message
32 from email.utils import formatdate
32 33
33 34 import rhodecode
34 35 from rhodecode.lib import audit_logger
@@ -40,7 +41,8 b' from rhodecode.model.db import ('
40 41
41 42
42 43 @async_task(ignore_result=True, base=RequestContextTask)
43 def send_email(recipients, subject, body='', html_body='', email_config=None):
44 def send_email(recipients, subject, body='', html_body='', email_config=None,
45 extra_headers=None):
44 46 """
45 47 Sends an email with defined parameters from the .ini files.
46 48
@@ -50,6 +52,7 b' def send_email(recipients, subject, body'
50 52 :param body: body of the mail
51 53 :param html_body: html version of body
52 54 :param email_config: specify custom configuration for mailer
55 :param extra_headers: specify custom headers
53 56 """
54 57 log = get_logger(send_email)
55 58
@@ -108,13 +111,23 b' def send_email(recipients, subject, body'
108 111 # sendmail_template='',
109 112 )
110 113
114 if extra_headers is None:
115 extra_headers = {}
116
117 extra_headers.setdefault('Date', formatdate(time.time()))
118
119 if 'thread_ids' in extra_headers:
120 thread_ids = extra_headers.pop('thread_ids')
121 extra_headers['References'] = ' '.join('<{}>'.format(t) for t in thread_ids)
122
111 123 try:
112 124 mailer = Mailer(**email_conf)
113 125
114 126 message = Message(subject=subject,
115 127 sender=email_conf['default_sender'],
116 128 recipients=recipients,
117 body=body, html=html_body)
129 body=body, html=html_body,
130 extra_headers=extra_headers)
118 131 mailer.send_immediately(message)
119 132
120 133 except Exception:
@@ -143,8 +143,7 b' def send_exc_email(request, exc_id, exc_'
143 143 'exc_traceback': read_exception(exc_id, prefix=None),
144 144 }
145 145
146 (subject, headers, email_body,
147 email_body_plaintext) = EmailNotificationModel().render_email(
146 (subject, email_body, email_body_plaintext) = EmailNotificationModel().render_email(
148 147 EmailNotificationModel.TYPE_EMAIL_EXCEPTION, **email_kwargs)
149 148
150 149 run_task(tasks.send_email, recipients, subject,
@@ -370,13 +370,18 b' class CommentsModel(BaseModel):'
370 370 repo.repo_name,
371 371 h.route_url('repo_summary', repo_name=repo.repo_name))
372 372
373 commit_url = h.route_url('repo_commit', repo_name=repo.repo_name,
374 commit_id=commit_id)
375
373 376 # commit specifics
374 377 kwargs.update({
375 378 'commit': commit_obj,
376 379 'commit_message': commit_obj.message,
377 380 'commit_target_repo_url': target_repo_url,
378 381 'commit_comment_url': commit_comment_url,
379 'commit_comment_reply_url': commit_comment_reply_url
382 'commit_comment_reply_url': commit_comment_reply_url,
383 'commit_url': commit_url,
384 'thread_ids': [commit_url, commit_comment_url],
380 385 })
381 386
382 387 elif pull_request_obj:
@@ -421,15 +426,14 b' class CommentsModel(BaseModel):'
421 426 'pr_comment_url': pr_comment_url,
422 427 'pr_comment_reply_url': pr_comment_reply_url,
423 428 'pr_closing': closing_pr,
429 'thread_ids': [pr_url, pr_comment_url],
424 430 })
425 431
426 432 recipients += [self._get_user(u) for u in (extra_recipients or [])]
427 433
428 434 if send_email:
429 435 # pre-generate the subject for notification itself
430 (subject,
431 _h, _e, # we don't care about those
432 body_plaintext) = EmailNotificationModel().render_email(
436 (subject, _e, body_plaintext) = EmailNotificationModel().render_email(
433 437 notification_type, **kwargs)
434 438
435 439 mention_recipients = set(
@@ -131,15 +131,17 b' class NotificationModel(BaseModel):'
131 131 # inject current recipient
132 132 email_kwargs['recipient'] = recipient
133 133 email_kwargs['mention'] = recipient in mention_recipients
134 (subject, headers, email_body,
135 email_body_plaintext) = EmailNotificationModel().render_email(
134 (subject, email_body, email_body_plaintext) = EmailNotificationModel().render_email(
136 135 notification_type, **email_kwargs)
137 136
138 log.debug(
139 'Creating notification email task for user:`%s`', recipient)
137 extra_headers = None
138 if 'thread_ids' in email_kwargs:
139 extra_headers = {'thread_ids': email_kwargs.pop('thread_ids')}
140
141 log.debug('Creating notification email task for user:`%s`', recipient)
140 142 task = run_task(
141 143 tasks.send_email, recipient.email, subject,
142 email_body_plaintext, email_body)
144 email_body_plaintext, email_body, extra_headers=extra_headers)
143 145 log.debug('Created email task: %s', task)
144 146
145 147 return notification
@@ -342,8 +344,7 b' class EmailNotificationModel(BaseModel):'
342 344 """
343 345 Example usage::
344 346
345 (subject, headers, email_body,
346 email_body_plaintext) = EmailNotificationModel().render_email(
347 (subject, email_body, email_body_plaintext) = EmailNotificationModel().render_email(
347 348 EmailNotificationModel.TYPE_TEST, **email_kwargs)
348 349
349 350 """
@@ -387,12 +388,6 b' class EmailNotificationModel(BaseModel):'
387 388 subject = email_template.render('subject', **_kwargs)
388 389
389 390 try:
390 headers = email_template.render('headers', **_kwargs)
391 except AttributeError:
392 # it's not defined in template, ok we can skip it
393 headers = ''
394
395 try:
396 391 body_plaintext = email_template.render('body_plaintext', **_kwargs)
397 392 except AttributeError:
398 393 # it's not defined in template, ok we can skip it
@@ -408,4 +403,4 b' class EmailNotificationModel(BaseModel):'
408 403 log.exception('Failed to parse body with premailer')
409 404 pass
410 405
411 return subject, headers, body, body_plaintext
406 return subject, body, body_plaintext
@@ -1344,12 +1344,11 b' class PullRequestModel(BaseModel):'
1344 1344 'pull_request_source_repo_url': pr_source_repo_url,
1345 1345
1346 1346 'pull_request_url': pr_url,
1347 'thread_ids': [pr_url],
1347 1348 }
1348 1349
1349 1350 # pre-generate the subject for notification itself
1350 (subject,
1351 _h, _e, # we don't care about those
1352 body_plaintext) = EmailNotificationModel().render_email(
1351 (subject, _e, body_plaintext) = EmailNotificationModel().render_email(
1353 1352 notification_type, **kwargs)
1354 1353
1355 1354 # create notification objects, and emails
@@ -1414,11 +1413,10 b' class PullRequestModel(BaseModel):'
1414 1413 'added_files': file_changes.added,
1415 1414 'modified_files': file_changes.modified,
1416 1415 'removed_files': file_changes.removed,
1416 'thread_ids': [pr_url],
1417 1417 }
1418 1418
1419 (subject,
1420 _h, _e, # we don't care about those
1421 body_plaintext) = EmailNotificationModel().render_email(
1419 (subject, _e, body_plaintext) = EmailNotificationModel().render_email(
1422 1420 EmailNotificationModel.TYPE_PULL_REQUEST_UPDATE, **email_kwargs)
1423 1421
1424 1422 # create notification objects, and emails
@@ -422,9 +422,7 b' class UserModel(BaseModel):'
422 422 }
423 423 notification_type = EmailNotificationModel.TYPE_REGISTRATION
424 424 # pre-generate the subject for notification itself
425 (subject,
426 _h, _e, # we don't care about those
427 body_plaintext) = EmailNotificationModel().render_email(
425 (subject, _e, body_plaintext) = EmailNotificationModel().render_email(
428 426 notification_type, **kwargs)
429 427
430 428 # create notification objects, and emails
@@ -659,8 +657,7 b' class UserModel(BaseModel):'
659 657 'first_admin_email': User.get_first_super_admin().email
660 658 }
661 659
662 (subject, headers, email_body,
663 email_body_plaintext) = EmailNotificationModel().render_email(
660 (subject, email_body, email_body_plaintext) = EmailNotificationModel().render_email(
664 661 EmailNotificationModel.TYPE_PASSWORD_RESET, **email_kwargs)
665 662
666 663 recipients = [user_email]
@@ -718,8 +715,7 b' class UserModel(BaseModel):'
718 715 'first_admin_email': User.get_first_super_admin().email
719 716 }
720 717
721 (subject, headers, email_body,
722 email_body_plaintext) = EmailNotificationModel().render_email(
718 (subject, email_body, email_body_plaintext) = EmailNotificationModel().render_email(
723 719 EmailNotificationModel.TYPE_PASSWORD_RESET_CONFIRMATION,
724 720 **email_kwargs)
725 721
@@ -8,11 +8,6 b''
8 8 SUBJECT:
9 9 <pre>${c.subject}</pre>
10 10
11 HEADERS:
12 <pre>
13 ${c.headers}
14 </pre>
15
16 11 PLAINTEXT:
17 12 <pre>
18 13 ${c.email_body_plaintext|n}
@@ -68,9 +68,6 b' text_monospace = "\'Menlo\', \'Liberation M'
68 68
69 69 %>
70 70
71 ## headers we additionally can set for email
72 <%def name="headers()" filter="n,trim"></%def>
73
74 71 <%def name="plaintext_footer()" filter="trim">
75 72 ${_('This is a notification from RhodeCode.')} ${instance_url}
76 73 </%def>
@@ -15,17 +15,18 b' data = {'
15 15 'comment_id': comment_id,
16 16
17 17 'commit_id': h.show_id(commit),
18 'mention_prefix': '[mention] ' if mention else '',
18 19 }
19 20 %>
20 21
21 22
22 23 % if comment_file:
23 ${(_('[mention]') if mention else '')} ${_('{user} left a {comment_type} on file `{comment_file}` in commit `{commit_id}`').format(**data)} ${_('in the `{repo_name}` repository').format(**data) |n}
24 ${_('{mention_prefix}{user} left a {comment_type} on file `{comment_file}` in commit `{commit_id}`').format(**data)} ${_('in the `{repo_name}` repository').format(**data) |n}
24 25 % else:
25 26 % if status_change:
26 ${(_('[mention]') if mention else '')} ${_('[status: {status}] {user} left a {comment_type} on commit `{commit_id}`').format(**data) |n} ${_('in the `{repo_name}` repository').format(**data) |n}
27 ${_('{mention_prefix}[status: {status}] {user} left a {comment_type} on commit `{commit_id}`').format(**data) |n} ${_('in the `{repo_name}` repository').format(**data) |n}
27 28 % else:
28 ${(_('[mention]') if mention else '')} ${_('{user} left a {comment_type} on commit `{commit_id}`').format(**data) |n} ${_('in the `{repo_name}` repository').format(**data) |n}
29 ${_('{mention_prefix}{user} left a {comment_type} on commit `{commit_id}`').format(**data) |n} ${_('in the `{repo_name}` repository').format(**data) |n}
29 30 % endif
30 31 % endif
31 32
@@ -16,17 +16,18 b' data = {'
16 16
17 17 'pr_title': pull_request.title,
18 18 'pr_id': pull_request.pull_request_id,
19 'mention_prefix': '[mention] ' if mention else '',
19 20 }
20 21 %>
21 22
22 23
23 24 % if comment_file:
24 ${(_('[mention]') if mention else '')} ${_('{user} left a {comment_type} on file `{comment_file}` in pull request !{pr_id}: "{pr_title}"').format(**data) |n}
25 ${_('{mention_prefix}{user} left a {comment_type} on file `{comment_file}` in pull request !{pr_id}: "{pr_title}"').format(**data) |n}
25 26 % else:
26 27 % if status_change:
27 ${(_('[mention]') if mention else '')} ${_('[status: {status}] {user} left a {comment_type} on pull request !{pr_id}: "{pr_title}"').format(**data) |n}
28 ${_('{mention_prefix}[status: {status}] {user} left a {comment_type} on pull request !{pr_id}: "{pr_title}"').format(**data) |n}
28 29 % else:
29 ${(_('[mention]') if mention else '')} ${_('{user} left a {comment_type} on pull request !{pr_id}: "{pr_title}"').format(**data) |n}
30 ${_('{mention_prefix}{user} left a {comment_type} on pull request !{pr_id}: "{pr_title}"').format(**data) |n}
30 31 % endif
31 32 % endif
32 33
@@ -5,10 +5,6 b''
5 5 Test "Subject" ${_('hello "world"')|n}
6 6 </%def>
7 7
8 <%def name="headers()" filter="n,trim">
9 X=Y
10 </%def>
11
12 8 ## plain text version of the email. Empty by default
13 9 <%def name="body_plaintext()" filter="n,trim">
14 10 Email Plaintext Body
@@ -35,15 +35,12 b' def test_get_template_obj(app, request_s'
35 35
36 36 def test_render_email(app, http_host_only_stub):
37 37 kwargs = {}
38 subject, headers, body, body_plaintext = EmailNotificationModel().render_email(
38 subject, body, body_plaintext = EmailNotificationModel().render_email(
39 39 EmailNotificationModel.TYPE_TEST, **kwargs)
40 40
41 41 # subject
42 42 assert subject == 'Test "Subject" hello "world"'
43 43
44 # headers
45 assert headers == 'X=Y'
46
47 44 # body plaintext
48 45 assert body_plaintext == 'Email Plaintext Body'
49 46
@@ -80,7 +77,7 b' def test_render_pr_email(app, user_admin'
80 77 'pull_request_url': 'http://localhost/pr1',
81 78 }
82 79
83 subject, headers, body, body_plaintext = EmailNotificationModel().render_email(
80 subject, body, body_plaintext = EmailNotificationModel().render_email(
84 81 EmailNotificationModel.TYPE_PULL_REQUEST, **kwargs)
85 82
86 83 # subject
@@ -133,7 +130,7 b' def test_render_pr_update_email(app, use'
133 130 'removed_files': file_changes.removed,
134 131 }
135 132
136 subject, headers, body, body_plaintext = EmailNotificationModel().render_email(
133 subject, body, body_plaintext = EmailNotificationModel().render_email(
137 134 EmailNotificationModel.TYPE_PULL_REQUEST_UPDATE, **kwargs)
138 135
139 136 # subject
@@ -188,7 +185,6 b' def test_render_comment_subject_no_newli'
188 185
189 186 'pull_request_url': 'http://code.rc.com/_pr/123'
190 187 }
191 subject, headers, body, body_plaintext = EmailNotificationModel().render_email(
192 email_type, **kwargs)
188 subject, body, body_plaintext = EmailNotificationModel().render_email(email_type, **kwargs)
193 189
194 190 assert '\n' not in subject
General Comments 0
You need to be logged in to leave comments. Login now