Changeset - 3397e3457f9c
[Not reviewed]
default
0 4 7
Andrew Shadura - 11 years ago 2014-10-26 09:53:22
andrew@shadura.me
email templates: send text/plain part as well

This change adds text parts to the email templates, as
HTML and text templates may be way to different to be
handled automatically. Also, use proper dash-dash-space
signature separator, so the email clients recognise it
for sure.
11 files changed with 65 insertions and 56 deletions:
0 comments (0 inline, 0 general)
kallithea/controllers/admin/settings.py
Show inline comments
 
@@ -329,62 +329,65 @@ class SettingsController(BaseController)
 
                log.error(traceback.format_exc())
 
                h.flash(_('Error occurred during updating '
 
                          'visualisation settings'),
 
                        category='error')
 

	
 
            return redirect(url('admin_settings_visual'))
 

	
 
        defaults = Setting.get_app_settings()
 
        defaults.update(self._get_hg_ui_settings())
 

	
 
        return htmlfill.render(
 
            render('admin/settings/settings.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def settings_email(self):
 
        """GET /admin/settings/email: All items in the collection"""
 
        # url('admin_settings_email')
 
        c.active = 'email'
 
        if request.POST:
 
            test_email = request.POST.get('test_email')
 
            test_email_subj = 'Kallithea test email'
 
            test_email_body = ('Kallithea Email test, '
 
            test_body = ('Kallithea Email test, '
 
                               'Kallithea version: %s' % c.kallithea_version)
 
            if not test_email:
 
                h.flash(_('Please enter email address'), category='error')
 
                return redirect(url('admin_settings_email'))
 

	
 
            test_email_txt_body = EmailNotificationModel()\
 
                .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
 
                                'txt', body=test_body)
 
            test_email_html_body = EmailNotificationModel()\
 
                .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
 
                                body=test_email_body)
 
                                'html', body=test_body)
 

	
 
            recipients = [test_email] if test_email else None
 

	
 
            run_task(tasks.send_email, recipients, test_email_subj,
 
                     test_email_body, test_email_html_body)
 
                     test_email_txt_body, test_email_html_body)
 

	
 
            h.flash(_('Send email task created'), category='success')
 
            return redirect(url('admin_settings_email'))
 

	
 
        defaults = Setting.get_app_settings()
 
        defaults.update(self._get_hg_ui_settings())
 

	
 
        import kallithea
 
        c.ini = kallithea.CONFIG
 

	
 
        return htmlfill.render(
 
            render('admin/settings/settings.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def settings_hooks(self):
 
        """GET /admin/settings/hooks: All items in the collection"""
 
        # url('admin_settings_hooks')
 
        c.active = 'hooks'
 
        if request.POST:
 
            if c.visual.allow_custom_hooks_settings:
 
                ui_key = request.POST.get('new_hook_ui_key')
kallithea/model/notification.py
Show inline comments
 
@@ -97,64 +97,65 @@ class NotificationModel(BaseModel):
 
            recipients_objs = User.query().filter(User.admin == True).all()
 
            log.debug('sending notifications %s to admins: %s' % (
 
                type_, recipients_objs)
 
            )
 
        #else: silently skip notification mails?
 

	
 
        # TODO: inform user who are notified
 
        notif = Notification.create(
 
            created_by=created_by_obj, subject=subject,
 
            body=body, recipients=recipients_objs, type_=type_
 
        )
 

	
 
        if not with_email:
 
            return notif
 

	
 
        #don't send email to person who created this comment
 
        rec_objs = set(recipients_objs).difference(set([created_by_obj]))
 

	
 
        headers = None
 
        if 'threading' in email_kwargs:
 
            headers = {'References': ' '.join('<%s>' % x for x in email_kwargs['threading'])}
 

	
 
        # send email with notification to all other participants
 
        for rec in rec_objs:
 
            email_body = None  # we set body to none, we just send HTML emails
 
            ## this is passed into template
 
            kwargs = {'subject': subject,
 
                      'body': h.rst_w_mentions(body),
 
                      'when': h.fmt_date(notif.created_on),
 
                      'user': notif.created_by_user.username,
 
                      }
 

	
 
            kwargs.update(email_kwargs)
 
            email_subject = EmailNotificationModel()\
 
                                .get_email_description(type_, **kwargs)
 
            email_body_html = EmailNotificationModel()\
 
                                .get_email_tmpl(type_, **kwargs)
 
            email_txt_body = EmailNotificationModel()\
 
                                .get_email_tmpl(type_, 'txt', **kwargs)
 
            email_html_body = EmailNotificationModel()\
 
                                .get_email_tmpl(type_, 'html', **kwargs)
 

	
 
            run_task(tasks.send_email, [rec.email], email_subject, email_body,
 
                     email_body_html, headers)
 
            run_task(tasks.send_email, [rec.email], email_subject, email_txt_body,
 
                     email_html_body, headers)
 

	
 
        return notif
 

	
 
    def delete(self, user, notification):
 
        # we don't want to remove actual notification just the assignment
 
        try:
 
            notification = self.__get_notification(notification)
 
            user = self._get_user(user)
 
            if notification and user:
 
                obj = UserNotification.query()\
 
                        .filter(UserNotification.user == user)\
 
                        .filter(UserNotification.notification
 
                                == notification)\
 
                        .one()
 
                Session().delete(obj)
 
                return True
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            raise
 

	
 
    def get_for_user(self, user, filter_=None):
 
        """
 
        Get notifications for given user, filter them if filter dict is given
 

	
 
@@ -249,70 +250,70 @@ class NotificationModel(BaseModel):
 
            when = h.fmt_date(notification.created_on)
 

	
 
        return tmpl % dict(
 
            user=notification.created_by_user.username,
 
            when=when,
 
            )
 

	
 

	
 
class EmailNotificationModel(BaseModel):
 

	
 
    TYPE_CHANGESET_COMMENT = Notification.TYPE_CHANGESET_COMMENT
 
    TYPE_MESSAGE = Notification.TYPE_MESSAGE # only used for testing
 
    # Notification.TYPE_MENTION is not used
 
    TYPE_PASSWORD_RESET = 'password_link'
 
    TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
 
    TYPE_PULL_REQUEST = Notification.TYPE_PULL_REQUEST
 
    TYPE_PULL_REQUEST_COMMENT = Notification.TYPE_PULL_REQUEST_COMMENT
 
    TYPE_DEFAULT = 'default'
 

	
 
    def __init__(self):
 
        super(EmailNotificationModel, self).__init__()
 
        self._template_root = kallithea.CONFIG['pylons.paths']['templates'][0]
 
        self._tmpl_lookup = kallithea.CONFIG['pylons.app_globals'].mako_lookup
 
        self.email_types = {
 
            self.TYPE_CHANGESET_COMMENT: 'email_templates/changeset_comment.html',
 
            self.TYPE_PASSWORD_RESET: 'email_templates/password_reset.html',
 
            self.TYPE_REGISTRATION: 'email_templates/registration.html',
 
            self.TYPE_DEFAULT: 'email_templates/default.html',
 
            self.TYPE_PULL_REQUEST: 'email_templates/pull_request.html',
 
            self.TYPE_PULL_REQUEST_COMMENT: 'email_templates/pull_request_comment.html',
 
            self.TYPE_CHANGESET_COMMENT: 'changeset_comment',
 
            self.TYPE_PASSWORD_RESET: 'password_reset',
 
            self.TYPE_REGISTRATION: 'registration',
 
            self.TYPE_DEFAULT: 'default',
 
            self.TYPE_PULL_REQUEST: 'pull_request',
 
            self.TYPE_PULL_REQUEST_COMMENT: 'pull_request_comment',
 
        }
 
        self._subj_map = {
 
            self.TYPE_CHANGESET_COMMENT: _('Comment on %(repo_name)s changeset %(short_id)s on %(branch)s by %(comment_username)s'),
 
            self.TYPE_MESSAGE: 'Test Message',
 
            # self.TYPE_PASSWORD_RESET
 
            self.TYPE_REGISTRATION: _('New user %(new_username)s registered'),
 
            # self.TYPE_DEFAULT
 
            self.TYPE_PULL_REQUEST: _('Review request on %(repo_name)s pull request #%(pr_id)s from %(ref)s by %(pr_username)s'),
 
            self.TYPE_PULL_REQUEST_COMMENT: _('Comment on %(repo_name)s pull request #%(pr_id)s from %(ref)s by %(comment_username)s'),
 
        }
 

	
 
    def get_email_description(self, type_, **kwargs):
 
        """
 
        return subject for email based on given type
 
        """
 
        tmpl = self._subj_map[type_]
 
        try:
 
            subj = tmpl % kwargs
 
        except KeyError, e:
 
            log.error('error generating email subject for %r from %s: %s', type_, ','.join(self._subj_map.keys()), e)
 
            raise
 
        l = [str(x) for x in [kwargs.get('status_change'), kwargs.get('closing_pr') and _('Closing')] if x]
 
        if l:
 
            subj += ' (%s)' % (', '.join(l))
 
        return subj
 

	
 
    def get_email_tmpl(self, type_, **kwargs):
 
    def get_email_tmpl(self, type_, content_type, **kwargs):
 
        """
 
        return generated template for email based on given type
 
        """
 

	
 
        base = self.email_types.get(type_, self.email_types[self.TYPE_DEFAULT])
 
        base = 'email_templates/' + self.email_types.get(type_, self.email_types[self.TYPE_DEFAULT]) + '.' + content_type
 
        email_template = self._tmpl_lookup.get_template(base)
 
        # translator and helpers inject
 
        _kwargs = {'_': _,
 
                   'h': h,
 
                   'c': c}
 
        _kwargs.update(kwargs)
 
        log.debug('rendering tmpl %s with kwargs %s' % (base, _kwargs))
 
        return email_template.render(**_kwargs)
kallithea/model/user.py
Show inline comments
 
@@ -276,53 +276,58 @@ class UserModel(BaseModel):
 
                      'removed. Switch owners or remove those repositories. %s')
 
                    % (user.username, len(repos), ', '.join(repos))
 
                )
 
            self.sa.delete(user)
 

	
 
            from kallithea.lib.hooks import log_delete_user
 
            log_delete_user(user.get_dict(), cur_user)
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            raise
 

	
 
    def reset_password_link(self, data):
 
        from kallithea.lib.celerylib import tasks, run_task
 
        from kallithea.model.notification import EmailNotificationModel
 
        import kallithea.lib.helpers as h
 

	
 
        user_email = data['email']
 
        try:
 
            user = User.get_by_email(user_email)
 
            if user:
 
                log.debug('password reset user found %s' % user)
 
                link = h.canonical_url('reset_password_confirmation', key=user.api_key)
 
                reg_type = EmailNotificationModel.TYPE_PASSWORD_RESET
 
                body = EmailNotificationModel().get_email_tmpl(reg_type,
 
                                                               'txt',
 
                                                               user=user.short_contact,
 
                                                               reset_url=link)
 
                html_body = EmailNotificationModel().get_email_tmpl(reg_type,
 
                                                               'html',
 
                                                               user=user.short_contact,
 
                                                               reset_url=link)
 
                log.debug('sending email')
 
                run_task(tasks.send_email, [user_email],
 
                         _("Password reset link"), body, body)
 
                         _("Password reset link"), body, html_body)
 
                log.info('send new password mail to %s' % user_email)
 
            else:
 
                log.debug("password reset email %s not found" % user_email)
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            return False
 

	
 
        return True
 

	
 
    def reset_password(self, data):
 
        from kallithea.lib.celerylib import tasks, run_task
 
        from kallithea.lib import auth
 
        user_email = data['email']
 
        pre_db = True
 
        try:
 
            user = User.get_by_email(user_email)
 
            new_passwd = auth.PasswordGenerator().gen_password(8,
 
                            auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
 
            if user:
 
                user.password = auth.get_crypt_password(new_passwd)
 
                Session().add(user)
 
                Session().commit()
 
                log.info('change password for %s' % user_email)
 
            if new_passwd is None:
kallithea/templates/email_templates/changeset_comment.txt
Show inline comments
 
file copied from kallithea/templates/email_templates/changeset_comment.html to kallithea/templates/email_templates/changeset_comment.txt
 
## -*- coding: utf-8 -*-
 
<%inherit file="main.html"/>
 
<%inherit file="main.txt"/>
 

	
 
%if is_mention:
 
<p>${_('Comment from %s on %s changeset %s mentioned you') % (cs_comment_user, cs_target_repo, h.short_id(raw_id))}:</p>
 
${_('Comment from %s on %s changeset %s mentioned you') % (cs_comment_user, cs_target_repo, h.short_id(raw_id))}:
 
%else:
 
<p>${_('Comment from %s on %s changeset %s') % (cs_comment_user, cs_target_repo, h.short_id(raw_id))}:</p>
 
${_('Comment from %s on %s changeset %s') % (cs_comment_user, cs_target_repo, h.short_id(raw_id))}:
 
%endif
 
<p>${body}</p>
 
${body}
 

	
 
%if status_change:
 
    <p>${_('The changeset status was changed to')}: <b>${status_change}</b></p>
 
${_('The changeset status was changed to')}: ${status_change}
 
%endif
 

	
 
<p>${_('URL')}: <a href="${cs_comment_url}">${cs_comment_url}</a></p>
 
${_('URL')}: ${cs_comment_url}
 

	
 
<p>${_('Changeset')}: ${h.short_id(raw_id)}</p>
 
<p>${_('Description')}:<br/>
 
${_('Changeset')}: ${h.short_id(raw_id)}
 
${_('Description')}:
 
${h.shorter(message, 256)}
 
</p>
kallithea/templates/email_templates/default.txt
Show inline comments
 
file copied from kallithea/templates/email_templates/default.html to kallithea/templates/email_templates/default.txt
 
## -*- coding: utf-8 -*-
 
<%inherit file="main.html"/>
 
<%inherit file="main.txt"/>
 

	
 
${body}
kallithea/templates/email_templates/main.html
Show inline comments
 
${self.body()}
 

	
 
<br/>
 
<br/>
 
-- <br/>
 
${_("This is an automatic notification - don't reply to this mail.")}
 
${_("This is an automatic notification. Don't reply to this mail.")}
kallithea/templates/email_templates/main.txt
Show inline comments
 
file copied from kallithea/templates/email_templates/main.html to kallithea/templates/email_templates/main.txt
 
${self.body()}
 
<br/>
 
-- <br/>
 
${_("This is an automatic notification - don't reply to this mail.")}
 

	
 
-- 
 
${_("This is an automatic notification. Don't reply to this mail.")}
kallithea/templates/email_templates/password_reset.txt
Show inline comments
 
file copied from kallithea/templates/email_templates/password_reset.html to kallithea/templates/email_templates/password_reset.txt
 
## -*- coding: utf-8 -*-
 
<%inherit file="main.html"/>
 
<%inherit file="main.txt"/>
 

	
 
<h4>${_('Hello %s') % user}</h4>
 
${_('Hello %s') % user}
 

	
 
<p>${_('We received a request to create a new password for your account.')}</p>
 
<p>${_('You can generate it by clicking following URL')}:</p>
 
<p><a href="${reset_url}">${reset_url}</a></p>
 
${_('We received a request to create a new password for your account.')}
 
${_('You can generate it by clicking following URL')}:
 

	
 
<p>${_("Please ignore this email if you did not request a new password .")}</p>
 
${reset_url}
 

	
 
${_("Please ignore this email if you did not request a new password .")}
kallithea/templates/email_templates/pull_request.txt
Show inline comments
 
file copied from kallithea/templates/email_templates/pull_request.html to kallithea/templates/email_templates/pull_request.txt
 
## -*- coding: utf-8 -*-
 
<%inherit file="main.html"/>
 
<%inherit file="main.txt"/>
 

	
 
%if is_mention:
 
<p>${_('%s mentioned you on %s pull request "%s"') % (pr_user_created, repo_name, pr_title)}</p>
 
${_('%s mentioned you on %s pull request "%s"') % (pr_user_created, repo_name, pr_title)}
 
%else:
 
<p>${_('%s requested your review of %s pull request "%s"') % (pr_user_created, repo_name, pr_title)}</p>
 
${_('%s requested your review of %s pull request "%s"') % (pr_user_created, repo_name, pr_title)}
 
%endif
 

	
 
<p>${_('URL')}: <a href="${pr_url}">${pr_url}</a></p>
 

	
 
<p>${_('Description')}:</p>
 
<p style="white-space: pre-wrap;">${body}</p>
 
${_('URL')}: ${pr_url}
 

	
 
<p>${_('Changesets')}:</p>
 
<p style="white-space: pre-wrap;">
 
${_('Description')}:
 
${body}
 

	
 
${_('Changesets')}:
 
%for r,r_msg in pr_revisions:
 
<i>${h.short_id(r)}</i>:
 
${h.short_id(r)}:
 
${h.shorter(r_msg, 256)}
 

	
 
%endfor
 
</p>
kallithea/templates/email_templates/pull_request_comment.txt
Show inline comments
 
file copied from kallithea/templates/email_templates/pull_request_comment.html to kallithea/templates/email_templates/pull_request_comment.txt
 
## -*- coding: utf-8 -*-
 
<%inherit file="main.html"/>
 
<%inherit file="main.txt"/>
 

	
 
<p>${_('Comment from %s on %s pull request "%s"') % (pr_comment_user, repo_name, pr_title)}:</p>
 
<p>${body}</p>
 
${_('Comment from %s on %s pull request "%s"') % (pr_comment_user, repo_name, pr_title)}:
 
${body}
 

	
 
%if status_change:
 
    %if closing_pr:
 
       <p>${_('The comment closed the pull request with status')}: <b>${status_change}</b></p>
 
${_('The comment closed the pull request with status')}: ${status_change}
 
    %else:
 
       <p>${_('The comment was made with status')}: <b>${status_change}</b></p>
 
${_('The comment was made with status')}: ${status_change}
 
    %endif
 
%endif
 

	
 
<p>${_('URL')}: <a href="${pr_comment_url}">${pr_comment_url}</a></p>
 
${_('URL')}: ${pr_comment_url}
kallithea/templates/email_templates/registration.txt
Show inline comments
 
file copied from kallithea/templates/email_templates/registration.html to kallithea/templates/email_templates/registration.txt
 
## -*- coding: utf-8 -*-
 
<%inherit file="main.html"/>
 
<%inherit file="main.txt"/>
 

	
 
${body}
 

	
 
${_('View this user here')}: <a href="${registered_user_url}">${registered_user_url}</a>
 
${_('View this user here')}: ${registered_user_url}
0 comments (0 inline, 0 general)