notification.py
230 lines
| 9.4 KiB
| text/x-python
|
PythonLexer
Bradley M. Kuhn
|
r4187 | # -*- coding: utf-8 -*- | ||
# This program is free software: you can redistribute it and/or modify | ||||
# it under the terms of the GNU General Public License as published by | ||||
# the Free Software Foundation, either version 3 of the License, or | ||||
# (at your option) any later version. | ||||
# | ||||
# This program is distributed in the hope that it will be useful, | ||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
# GNU General Public License for more details. | ||||
# | ||||
# You should have received a copy of the GNU General Public License | ||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
""" | ||||
kallithea.model.notification | ||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
Model for notifications | ||||
Bradley M. Kuhn
|
r4211 | This file was forked by the Kallithea project in July 2014. | ||
Original author and date, and relevant copyright and licensing information is below: | ||||
Bradley M. Kuhn
|
r4187 | :created_on: Nov 20, 2011 | ||
:author: marcink | ||||
Bradley M. Kuhn
|
r4211 | :copyright: (c) 2013 RhodeCode GmbH, and others. | ||
Bradley M. Kuhn
|
r4208 | :license: GPLv3, see LICENSE.md for more details. | ||
Bradley M. Kuhn
|
r4187 | """ | ||
Thomas De Schampheleire
|
r7367 | import datetime | ||
Bradley M. Kuhn
|
r4187 | import logging | ||
Mads Kiilerich
|
r7718 | from tg import app_globals | ||
from tg import tmpl_context as c | ||||
Mads Kiilerich
|
r6508 | from tg.i18n import ugettext as _ | ||
Bradley M. Kuhn
|
r4187 | |||
import kallithea | ||||
from kallithea.lib import helpers as h | ||||
Thomas De Schampheleire
|
r7368 | from kallithea.model.db import User | ||
Bradley M. Kuhn
|
r4187 | |||
Mads Kiilerich
|
r7718 | |||
Bradley M. Kuhn
|
r4187 | log = logging.getLogger(__name__) | ||
Søren Løvborg
|
r6483 | class NotificationModel(object): | ||
Bradley M. Kuhn
|
r4187 | |||
Thomas De Schampheleire
|
r7368 | TYPE_CHANGESET_COMMENT = u'cs_comment' | ||
TYPE_MESSAGE = u'message' | ||||
TYPE_MENTION = u'mention' # not used | ||||
TYPE_REGISTRATION = u'registration' | ||||
TYPE_PULL_REQUEST = u'pull_request' | ||||
TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment' | ||||
Bradley M. Kuhn
|
r4187 | def create(self, created_by, subject, body, recipients=None, | ||
Thomas De Schampheleire
|
r7368 | type_=TYPE_MESSAGE, with_email=True, | ||
Mads Kiilerich
|
r5643 | email_kwargs=None, repo_name=None): | ||
Bradley M. Kuhn
|
r4187 | """ | ||
Creates notification of given type | ||||
:param created_by: int, str or User instance. User who created this | ||||
notification | ||||
:param subject: | ||||
:param body: | ||||
:param recipients: list of int, str or User objects, when None | ||||
is given send to all admins | ||||
:param type_: type of notification | ||||
:param with_email: send email with this notification | ||||
:param email_kwargs: additional dict to pass as args to email template | ||||
""" | ||||
Mads Kiilerich
|
r6133 | from kallithea.lib.celerylib import tasks | ||
Jiřà Suchan
|
r5555 | email_kwargs = email_kwargs or {} | ||
Bradley M. Kuhn
|
r4187 | if recipients and not getattr(recipients, '__iter__', False): | ||
raise Exception('recipients must be a list or iterable') | ||||
Søren Løvborg
|
r6423 | created_by_obj = User.guess_instance(created_by) | ||
Bradley M. Kuhn
|
r4187 | |||
Thomas De Schampheleire
|
r7361 | recipients_objs = set() | ||
Bradley M. Kuhn
|
r4187 | if recipients: | ||
for u in recipients: | ||||
Søren Løvborg
|
r6423 | obj = User.guess_instance(u) | ||
Mads Kiilerich
|
r5306 | if obj is not None: | ||
Thomas De Schampheleire
|
r7361 | recipients_objs.add(obj) | ||
Bradley M. Kuhn
|
r4187 | else: | ||
# TODO: inform user that requested operation couldn't be completed | ||||
log.error('cannot email unknown user %r', u) | ||||
Mads Kiilerich
|
r5375 | log.debug('sending notifications %s to %s', | ||
type_, recipients_objs | ||||
Bradley M. Kuhn
|
r4187 | ) | ||
Mads Kiilerich
|
r4332 | elif recipients is None: | ||
Bradley M. Kuhn
|
r4187 | # empty recipients means to all admins | ||
recipients_objs = User.query().filter(User.admin == True).all() | ||||
Mads Kiilerich
|
r5375 | log.debug('sending notifications %s to admins: %s', | ||
type_, recipients_objs | ||||
Bradley M. Kuhn
|
r4187 | ) | ||
Mads Kiilerich
|
r4332 | #else: silently skip notification mails? | ||
Bradley M. Kuhn
|
r4187 | if not with_email: | ||
Thomas De Schampheleire
|
r7367 | return | ||
Bradley M. Kuhn
|
r4187 | |||
Thomas De Schampheleire
|
r6238 | headers = {} | ||
headers['X-Kallithea-Notification-Type'] = type_ | ||||
Mads Kiilerich
|
r4384 | if 'threading' in email_kwargs: | ||
Thomas De Schampheleire
|
r6238 | headers['References'] = ' '.join('<%s>' % x for x in email_kwargs['threading']) | ||
Mads Kiilerich
|
r4384 | |||
Thomas De Schampheleire
|
r7298 | # this is passed into template | ||
Thomas De Schampheleire
|
r7367 | created_on = h.fmt_date(datetime.datetime.now()) | ||
Thomas De Schampheleire
|
r7298 | html_kwargs = { | ||
'subject': subject, | ||||
'body': h.render_w_mentions(body, repo_name), | ||||
Thomas De Schampheleire
|
r7367 | 'when': created_on, | ||
'user': created_by_obj.username, | ||||
Thomas De Schampheleire
|
r7298 | } | ||
txt_kwargs = { | ||||
'subject': subject, | ||||
'body': body, | ||||
Thomas De Schampheleire
|
r7367 | 'when': created_on, | ||
'user': created_by_obj.username, | ||||
Thomas De Schampheleire
|
r7298 | } | ||
html_kwargs.update(email_kwargs) | ||||
txt_kwargs.update(email_kwargs) | ||||
email_subject = EmailNotificationModel() \ | ||||
.get_email_description(type_, **txt_kwargs) | ||||
email_txt_body = EmailNotificationModel() \ | ||||
.get_email_tmpl(type_, 'txt', **txt_kwargs) | ||||
email_html_body = EmailNotificationModel() \ | ||||
.get_email_tmpl(type_, 'html', **html_kwargs) | ||||
# don't send email to person who created this comment | ||||
rec_objs = set(recipients_objs).difference(set([created_by_obj])) | ||||
Bradley M. Kuhn
|
r4187 | # send email with notification to all other participants | ||
for rec in rec_objs: | ||||
Mads Kiilerich
|
r6133 | tasks.send_email([rec.email], email_subject, email_txt_body, | ||
Thomas De Schampheleire
|
r5455 | email_html_body, headers, author=created_by_obj) | ||
Bradley M. Kuhn
|
r4187 | |||
Søren Løvborg
|
r6483 | class EmailNotificationModel(object): | ||
Bradley M. Kuhn
|
r4187 | |||
Thomas De Schampheleire
|
r7368 | TYPE_CHANGESET_COMMENT = NotificationModel.TYPE_CHANGESET_COMMENT | ||
TYPE_MESSAGE = NotificationModel.TYPE_MESSAGE # only used for testing | ||||
# NotificationModel.TYPE_MENTION is not used | ||||
Bradley M. Kuhn
|
r4187 | TYPE_PASSWORD_RESET = 'password_link' | ||
Thomas De Schampheleire
|
r7368 | TYPE_REGISTRATION = NotificationModel.TYPE_REGISTRATION | ||
TYPE_PULL_REQUEST = NotificationModel.TYPE_PULL_REQUEST | ||||
TYPE_PULL_REQUEST_COMMENT = NotificationModel.TYPE_PULL_REQUEST_COMMENT | ||||
Bradley M. Kuhn
|
r4187 | TYPE_DEFAULT = 'default' | ||
def __init__(self): | ||||
super(EmailNotificationModel, self).__init__() | ||||
Alessandro Molina
|
r6522 | self._template_root = kallithea.CONFIG['paths']['templates'][0] | ||
self._tmpl_lookup = app_globals.mako_lookup | ||||
Bradley M. Kuhn
|
r4187 | self.email_types = { | ||
Andrew Shadura
|
r4561 | 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', | ||||
Bradley M. Kuhn
|
r4187 | } | ||
Mads Kiilerich
|
r4381 | self._subj_map = { | ||
Thomas De Schampheleire <thomas.de.schampheleire at gmail.com>
|
r6021 | self.TYPE_CHANGESET_COMMENT: _('[Comment] %(repo_name)s changeset %(short_id)s "%(message_short)s" on %(branch)s'), | ||
Mads Kiilerich
|
r4381 | self.TYPE_MESSAGE: 'Test Message', | ||
# self.TYPE_PASSWORD_RESET | ||||
self.TYPE_REGISTRATION: _('New user %(new_username)s registered'), | ||||
# self.TYPE_DEFAULT | ||||
Mads Kiilerich
|
r6022 | self.TYPE_PULL_REQUEST: _('[Review] %(repo_name)s PR %(pr_nice_id)s "%(pr_title_short)s" from %(pr_source_branch)s by %(pr_owner_username)s'), | ||
self.TYPE_PULL_REQUEST_COMMENT: _('[Comment] %(repo_name)s PR %(pr_nice_id)s "%(pr_title_short)s" from %(pr_source_branch)s by %(pr_owner_username)s'), | ||||
Mads Kiilerich
|
r4381 | } | ||
Bradley M. Kuhn
|
r4187 | |||
Mads Kiilerich
|
r4381 | def get_email_description(self, type_, **kwargs): | ||
""" | ||||
return subject for email based on given type | ||||
""" | ||||
tmpl = self._subj_map[type_] | ||||
try: | ||||
Mads Kiilerich
|
r4382 | subj = tmpl % kwargs | ||
Mads Kiilerich
|
r5374 | except KeyError as e: | ||
Mads Kiilerich
|
r7956 | log.error('error generating email subject for %r from %s: %s', type_, ', '.join(self._subj_map), e) | ||
Mads Kiilerich
|
r4381 | raise | ||
Mads Kiilerich
|
r7950 | # gmail doesn't do proper threading but will ignore leading square | ||
# bracket content ... so that is where we put status info | ||||
bracket_tags = [] | ||||
status_change = kwargs.get('status_change') | ||||
if status_change: | ||||
Mads Kiilerich
|
r8081 | bracket_tags.append(str(status_change)) # apply str to evaluate LazyString before .join | ||
Mads Kiilerich
|
r7950 | if kwargs.get('closing_pr'): | ||
bracket_tags.append(_('Closing')) | ||||
if bracket_tags: | ||||
Mads Kiilerich
|
r5111 | if subj.startswith('['): | ||
Mads Kiilerich
|
r7950 | subj = '[' + ', '.join(bracket_tags) + ': ' + subj[1:] | ||
Mads Kiilerich
|
r5111 | else: | ||
Mads Kiilerich
|
r7950 | subj = '[' + ', '.join(bracket_tags) + '] ' + subj | ||
Mads Kiilerich
|
r4382 | return subj | ||
Bradley M. Kuhn
|
r4187 | |||
Andrew Shadura
|
r4561 | def get_email_tmpl(self, type_, content_type, **kwargs): | ||
Bradley M. Kuhn
|
r4187 | """ | ||
return generated template for email based on given type | ||||
""" | ||||
Andrew Shadura
|
r4561 | base = 'email_templates/' + self.email_types.get(type_, self.email_types[self.TYPE_DEFAULT]) + '.' + content_type | ||
Bradley M. Kuhn
|
r4187 | email_template = self._tmpl_lookup.get_template(base) | ||
# translator and helpers inject | ||||
_kwargs = {'_': _, | ||||
'h': h, | ||||
'c': c} | ||||
_kwargs.update(kwargs) | ||||
Kateryna Musina
|
r6099 | if content_type == 'html': | ||
_kwargs.update({ | ||||
"color_text": "#202020", | ||||
"color_emph": "#395fa0", | ||||
"color_link": "#395fa0", | ||||
"color_border": "#ddd", | ||||
"color_background_grey": "#f9f9f9", | ||||
"color_button": "#395fa0", | ||||
"monospace_style": "font-family:Lucida Console,Consolas,Monaco,Inconsolata,Liberation Mono,monospace", | ||||
"sans_style": "font-family:Helvetica,Arial,sans-serif", | ||||
}) | ||||
_kwargs.update({ | ||||
"default_style": "%(sans_style)s;font-weight:200;font-size:14px;line-height:17px;color:%(color_text)s" % _kwargs, | ||||
"comment_style": "%(monospace_style)s;white-space:pre-wrap" % _kwargs, | ||||
Thomas De Schampheleire
|
r7087 | "data_style": "border:%(color_border)s 1px solid;background:%(color_background_grey)s" % _kwargs, | ||
Mads Kiilerich
|
r6405 | "emph_style": "font-weight:600;color:%(color_emph)s" % _kwargs, | ||
Thomas De Schampheleire
|
r7085 | "link_style": "color:%(color_link)s;text-decoration:none" % _kwargs, | ||
Thomas De Schampheleire
|
r7088 | "link_text_style": "color:%(color_text)s;text-decoration:none;border:%(color_border)s 1px solid;background:%(color_background_grey)s" % _kwargs, | ||
Kateryna Musina
|
r6099 | }) | ||
Mads Kiilerich
|
r5375 | log.debug('rendering tmpl %s with kwargs %s', base, _kwargs) | ||
Mads Kiilerich
|
r6600 | return email_template.render_unicode(**_kwargs) | ||