diff --git a/rhodecode/apps/admin/views/settings.py b/rhodecode/apps/admin/views/settings.py --- a/rhodecode/apps/admin/views/settings.py +++ b/rhodecode/apps/admin/views/settings.py @@ -578,8 +578,7 @@ class AdminSettingsView(BaseAppView): 'user': self._rhodecode_db_user } - (subject, headers, email_body, - email_body_plaintext) = EmailNotificationModel().render_email( + (subject, email_body, email_body_plaintext) = EmailNotificationModel().render_email( EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs) recipients = [test_email] if test_email else None diff --git a/rhodecode/apps/debug_style/views.py b/rhodecode/apps/debug_style/views.py --- a/rhodecode/apps/debug_style/views.py +++ b/rhodecode/apps/debug_style/views.py @@ -376,8 +376,7 @@ users: description edit fixes } template_type = email_id.split('+')[0] - (c.subject, c.headers, c.email_body, - c.email_body_plaintext) = EmailNotificationModel().render_email( + (c.subject, c.email_body, c.email_body_plaintext) = EmailNotificationModel().render_email( template_type, **email_kwargs.get(email_id, {})) test_email = self.request.GET.get('email') diff --git a/rhodecode/lib/celerylib/tasks.py b/rhodecode/lib/celerylib/tasks.py --- a/rhodecode/lib/celerylib/tasks.py +++ b/rhodecode/lib/celerylib/tasks.py @@ -29,6 +29,7 @@ import time from pyramid import compat from pyramid_mailer.mailer import Mailer from pyramid_mailer.message import Message +from email.utils import formatdate import rhodecode from rhodecode.lib import audit_logger @@ -40,7 +41,8 @@ from rhodecode.model.db import ( @async_task(ignore_result=True, base=RequestContextTask) -def send_email(recipients, subject, body='', html_body='', email_config=None): +def send_email(recipients, subject, body='', html_body='', email_config=None, + extra_headers=None): """ Sends an email with defined parameters from the .ini files. @@ -50,6 +52,7 @@ def send_email(recipients, subject, body :param body: body of the mail :param html_body: html version of body :param email_config: specify custom configuration for mailer + :param extra_headers: specify custom headers """ log = get_logger(send_email) @@ -108,13 +111,23 @@ def send_email(recipients, subject, body # sendmail_template='', ) + if extra_headers is None: + extra_headers = {} + + extra_headers.setdefault('Date', formatdate(time.time())) + + if 'thread_ids' in extra_headers: + thread_ids = extra_headers.pop('thread_ids') + extra_headers['References'] = ' '.join('<{}>'.format(t) for t in thread_ids) + try: mailer = Mailer(**email_conf) message = Message(subject=subject, sender=email_conf['default_sender'], recipients=recipients, - body=body, html=html_body) + body=body, html=html_body, + extra_headers=extra_headers) mailer.send_immediately(message) except Exception: diff --git a/rhodecode/lib/exc_tracking.py b/rhodecode/lib/exc_tracking.py --- a/rhodecode/lib/exc_tracking.py +++ b/rhodecode/lib/exc_tracking.py @@ -143,8 +143,7 @@ def send_exc_email(request, exc_id, exc_ 'exc_traceback': read_exception(exc_id, prefix=None), } - (subject, headers, email_body, - email_body_plaintext) = EmailNotificationModel().render_email( + (subject, email_body, email_body_plaintext) = EmailNotificationModel().render_email( EmailNotificationModel.TYPE_EMAIL_EXCEPTION, **email_kwargs) run_task(tasks.send_email, recipients, subject, diff --git a/rhodecode/model/comment.py b/rhodecode/model/comment.py --- a/rhodecode/model/comment.py +++ b/rhodecode/model/comment.py @@ -370,13 +370,18 @@ class CommentsModel(BaseModel): repo.repo_name, h.route_url('repo_summary', repo_name=repo.repo_name)) + commit_url = h.route_url('repo_commit', repo_name=repo.repo_name, + commit_id=commit_id) + # commit specifics kwargs.update({ 'commit': commit_obj, 'commit_message': commit_obj.message, 'commit_target_repo_url': target_repo_url, 'commit_comment_url': commit_comment_url, - 'commit_comment_reply_url': commit_comment_reply_url + 'commit_comment_reply_url': commit_comment_reply_url, + 'commit_url': commit_url, + 'thread_ids': [commit_url, commit_comment_url], }) elif pull_request_obj: @@ -421,15 +426,14 @@ class CommentsModel(BaseModel): 'pr_comment_url': pr_comment_url, 'pr_comment_reply_url': pr_comment_reply_url, 'pr_closing': closing_pr, + 'thread_ids': [pr_url, pr_comment_url], }) recipients += [self._get_user(u) for u in (extra_recipients or [])] if send_email: # pre-generate the subject for notification itself - (subject, - _h, _e, # we don't care about those - body_plaintext) = EmailNotificationModel().render_email( + (subject, _e, body_plaintext) = EmailNotificationModel().render_email( notification_type, **kwargs) mention_recipients = set( diff --git a/rhodecode/model/notification.py b/rhodecode/model/notification.py --- a/rhodecode/model/notification.py +++ b/rhodecode/model/notification.py @@ -131,15 +131,17 @@ class NotificationModel(BaseModel): # inject current recipient email_kwargs['recipient'] = recipient email_kwargs['mention'] = recipient in mention_recipients - (subject, headers, email_body, - email_body_plaintext) = EmailNotificationModel().render_email( + (subject, email_body, email_body_plaintext) = EmailNotificationModel().render_email( notification_type, **email_kwargs) - log.debug( - 'Creating notification email task for user:`%s`', recipient) + extra_headers = None + if 'thread_ids' in email_kwargs: + extra_headers = {'thread_ids': email_kwargs.pop('thread_ids')} + + log.debug('Creating notification email task for user:`%s`', recipient) task = run_task( tasks.send_email, recipient.email, subject, - email_body_plaintext, email_body) + email_body_plaintext, email_body, extra_headers=extra_headers) log.debug('Created email task: %s', task) return notification @@ -342,8 +344,7 @@ class EmailNotificationModel(BaseModel): """ Example usage:: - (subject, headers, email_body, - email_body_plaintext) = EmailNotificationModel().render_email( + (subject, email_body, email_body_plaintext) = EmailNotificationModel().render_email( EmailNotificationModel.TYPE_TEST, **email_kwargs) """ @@ -387,12 +388,6 @@ class EmailNotificationModel(BaseModel): subject = email_template.render('subject', **_kwargs) try: - headers = email_template.render('headers', **_kwargs) - except AttributeError: - # it's not defined in template, ok we can skip it - headers = '' - - try: body_plaintext = email_template.render('body_plaintext', **_kwargs) except AttributeError: # it's not defined in template, ok we can skip it @@ -408,4 +403,4 @@ class EmailNotificationModel(BaseModel): log.exception('Failed to parse body with premailer') pass - return subject, headers, body, body_plaintext + return subject, body, body_plaintext diff --git a/rhodecode/model/pull_request.py b/rhodecode/model/pull_request.py --- a/rhodecode/model/pull_request.py +++ b/rhodecode/model/pull_request.py @@ -1344,12 +1344,11 @@ class PullRequestModel(BaseModel): 'pull_request_source_repo_url': pr_source_repo_url, 'pull_request_url': pr_url, + 'thread_ids': [pr_url], } # pre-generate the subject for notification itself - (subject, - _h, _e, # we don't care about those - body_plaintext) = EmailNotificationModel().render_email( + (subject, _e, body_plaintext) = EmailNotificationModel().render_email( notification_type, **kwargs) # create notification objects, and emails @@ -1414,11 +1413,10 @@ class PullRequestModel(BaseModel): 'added_files': file_changes.added, 'modified_files': file_changes.modified, 'removed_files': file_changes.removed, + 'thread_ids': [pr_url], } - (subject, - _h, _e, # we don't care about those - body_plaintext) = EmailNotificationModel().render_email( + (subject, _e, body_plaintext) = EmailNotificationModel().render_email( EmailNotificationModel.TYPE_PULL_REQUEST_UPDATE, **email_kwargs) # create notification objects, and emails diff --git a/rhodecode/model/user.py b/rhodecode/model/user.py --- a/rhodecode/model/user.py +++ b/rhodecode/model/user.py @@ -422,9 +422,7 @@ class UserModel(BaseModel): } notification_type = EmailNotificationModel.TYPE_REGISTRATION # pre-generate the subject for notification itself - (subject, - _h, _e, # we don't care about those - body_plaintext) = EmailNotificationModel().render_email( + (subject, _e, body_plaintext) = EmailNotificationModel().render_email( notification_type, **kwargs) # create notification objects, and emails @@ -659,8 +657,7 @@ class UserModel(BaseModel): 'first_admin_email': User.get_first_super_admin().email } - (subject, headers, email_body, - email_body_plaintext) = EmailNotificationModel().render_email( + (subject, email_body, email_body_plaintext) = EmailNotificationModel().render_email( EmailNotificationModel.TYPE_PASSWORD_RESET, **email_kwargs) recipients = [user_email] @@ -718,8 +715,7 @@ class UserModel(BaseModel): 'first_admin_email': User.get_first_super_admin().email } - (subject, headers, email_body, - email_body_plaintext) = EmailNotificationModel().render_email( + (subject, email_body, email_body_plaintext) = EmailNotificationModel().render_email( EmailNotificationModel.TYPE_PASSWORD_RESET_CONFIRMATION, **email_kwargs) diff --git a/rhodecode/templates/debug_style/email.mako b/rhodecode/templates/debug_style/email.mako --- a/rhodecode/templates/debug_style/email.mako +++ b/rhodecode/templates/debug_style/email.mako @@ -8,11 +8,6 @@ SUBJECT:
${c.subject}-HEADERS: -
-${c.headers} -- PLAINTEXT:
${c.email_body_plaintext|n} diff --git a/rhodecode/templates/email_templates/base.mako b/rhodecode/templates/email_templates/base.mako --- a/rhodecode/templates/email_templates/base.mako +++ b/rhodecode/templates/email_templates/base.mako @@ -68,9 +68,6 @@ text_monospace = "'Menlo', 'Liberation M %> -## headers we additionally can set for email -<%def name="headers()" filter="n,trim">%def> - <%def name="plaintext_footer()" filter="trim"> ${_('This is a notification from RhodeCode.')} ${instance_url} %def> diff --git a/rhodecode/templates/email_templates/commit_comment.mako b/rhodecode/templates/email_templates/commit_comment.mako --- a/rhodecode/templates/email_templates/commit_comment.mako +++ b/rhodecode/templates/email_templates/commit_comment.mako @@ -15,17 +15,18 @@ data = { 'comment_id': comment_id, 'commit_id': h.show_id(commit), + 'mention_prefix': '[mention] ' if mention else '', } %> % if comment_file: - ${(_('[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} + ${_('{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} % else: % if status_change: - ${(_('[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} + ${_('{mention_prefix}[status: {status}] {user} left a {comment_type} on commit `{commit_id}`').format(**data) |n} ${_('in the `{repo_name}` repository').format(**data) |n} % else: - ${(_('[mention]') if mention else '')} ${_('{user} left a {comment_type} on commit `{commit_id}`').format(**data) |n} ${_('in the `{repo_name}` repository').format(**data) |n} + ${_('{mention_prefix}{user} left a {comment_type} on commit `{commit_id}`').format(**data) |n} ${_('in the `{repo_name}` repository').format(**data) |n} % endif % endif diff --git a/rhodecode/templates/email_templates/pull_request_comment.mako b/rhodecode/templates/email_templates/pull_request_comment.mako --- a/rhodecode/templates/email_templates/pull_request_comment.mako +++ b/rhodecode/templates/email_templates/pull_request_comment.mako @@ -16,17 +16,18 @@ data = { 'pr_title': pull_request.title, 'pr_id': pull_request.pull_request_id, + 'mention_prefix': '[mention] ' if mention else '', } %> % if comment_file: - ${(_('[mention]') if mention else '')} ${_('{user} left a {comment_type} on file `{comment_file}` in pull request !{pr_id}: "{pr_title}"').format(**data) |n} + ${_('{mention_prefix}{user} left a {comment_type} on file `{comment_file}` in pull request !{pr_id}: "{pr_title}"').format(**data) |n} % else: % if status_change: - ${(_('[mention]') if mention else '')} ${_('[status: {status}] {user} left a {comment_type} on pull request !{pr_id}: "{pr_title}"').format(**data) |n} + ${_('{mention_prefix}[status: {status}] {user} left a {comment_type} on pull request !{pr_id}: "{pr_title}"').format(**data) |n} % else: - ${(_('[mention]') if mention else '')} ${_('{user} left a {comment_type} on pull request !{pr_id}: "{pr_title}"').format(**data) |n} + ${_('{mention_prefix}{user} left a {comment_type} on pull request !{pr_id}: "{pr_title}"').format(**data) |n} % endif % endif diff --git a/rhodecode/templates/email_templates/test.mako b/rhodecode/templates/email_templates/test.mako --- a/rhodecode/templates/email_templates/test.mako +++ b/rhodecode/templates/email_templates/test.mako @@ -5,10 +5,6 @@ Test "Subject" ${_('hello "world"')|n} %def> -<%def name="headers()" filter="n,trim"> -X=Y -%def> - ## plain text version of the email. Empty by default <%def name="body_plaintext()" filter="n,trim"> Email Plaintext Body diff --git a/rhodecode/tests/lib/test_mako_emails.py b/rhodecode/tests/lib/test_mako_emails.py --- a/rhodecode/tests/lib/test_mako_emails.py +++ b/rhodecode/tests/lib/test_mako_emails.py @@ -35,15 +35,12 @@ def test_get_template_obj(app, request_s def test_render_email(app, http_host_only_stub): kwargs = {} - subject, headers, body, body_plaintext = EmailNotificationModel().render_email( + subject, body, body_plaintext = EmailNotificationModel().render_email( EmailNotificationModel.TYPE_TEST, **kwargs) # subject assert subject == 'Test "Subject" hello "world"' - # headers - assert headers == 'X=Y' - # body plaintext assert body_plaintext == 'Email Plaintext Body' @@ -80,7 +77,7 @@ def test_render_pr_email(app, user_admin 'pull_request_url': 'http://localhost/pr1', } - subject, headers, body, body_plaintext = EmailNotificationModel().render_email( + subject, body, body_plaintext = EmailNotificationModel().render_email( EmailNotificationModel.TYPE_PULL_REQUEST, **kwargs) # subject @@ -133,7 +130,7 @@ def test_render_pr_update_email(app, use 'removed_files': file_changes.removed, } - subject, headers, body, body_plaintext = EmailNotificationModel().render_email( + subject, body, body_plaintext = EmailNotificationModel().render_email( EmailNotificationModel.TYPE_PULL_REQUEST_UPDATE, **kwargs) # subject @@ -188,7 +185,6 @@ def test_render_comment_subject_no_newli 'pull_request_url': 'http://code.rc.com/_pr/123' } - subject, headers, body, body_plaintext = EmailNotificationModel().render_email( - email_type, **kwargs) + subject, body, body_plaintext = EmailNotificationModel().render_email(email_type, **kwargs) assert '\n' not in subject