##// END OF EJS Templates
Don't send emails to person who comment on changeset
marcink -
r2387:7d517a35 beta
parent child Browse files
Show More
@@ -1,153 +1,154 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.comment
3 rhodecode.model.comment
4 ~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 comments model for RhodeCode
6 comments model for RhodeCode
7
7
8 :created_on: Nov 11, 2011
8 :created_on: Nov 11, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28
28
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30 from sqlalchemy.util.compat import defaultdict
30 from sqlalchemy.util.compat import defaultdict
31
31
32 from rhodecode.lib.utils2 import extract_mentioned_users, safe_unicode
32 from rhodecode.lib.utils2 import extract_mentioned_users, safe_unicode
33 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
34 from rhodecode.model import BaseModel
34 from rhodecode.model import BaseModel
35 from rhodecode.model.db import ChangesetComment, User, Repository, Notification
35 from rhodecode.model.db import ChangesetComment, User, Repository, Notification
36 from rhodecode.model.notification import NotificationModel
36 from rhodecode.model.notification import NotificationModel
37
37
38 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
39
39
40
40
41 class ChangesetCommentsModel(BaseModel):
41 class ChangesetCommentsModel(BaseModel):
42
42
43 def __get_changeset_comment(self, changeset_comment):
43 def __get_changeset_comment(self, changeset_comment):
44 return self._get_instance(ChangesetComment, changeset_comment)
44 return self._get_instance(ChangesetComment, changeset_comment)
45
45
46 def _extract_mentions(self, s):
46 def _extract_mentions(self, s):
47 user_objects = []
47 user_objects = []
48 for username in extract_mentioned_users(s):
48 for username in extract_mentioned_users(s):
49 user_obj = User.get_by_username(username, case_insensitive=True)
49 user_obj = User.get_by_username(username, case_insensitive=True)
50 if user_obj:
50 if user_obj:
51 user_objects.append(user_obj)
51 user_objects.append(user_obj)
52 return user_objects
52 return user_objects
53
53
54 def create(self, text, repo_id, user_id, revision, f_path=None,
54 def create(self, text, repo_id, user_id, revision, f_path=None,
55 line_no=None):
55 line_no=None):
56 """
56 """
57 Creates new comment for changeset
57 Creates new comment for changeset
58
58
59 :param text:
59 :param text:
60 :param repo_id:
60 :param repo_id:
61 :param user_id:
61 :param user_id:
62 :param revision:
62 :param revision:
63 :param f_path:
63 :param f_path:
64 :param line_no:
64 :param line_no:
65 """
65 """
66
66
67 if text:
67 if text:
68 repo = Repository.get(repo_id)
68 repo = Repository.get(repo_id)
69 cs = repo.scm_instance.get_changeset(revision)
69 cs = repo.scm_instance.get_changeset(revision)
70 desc = "%s - %s" % (cs.short_id, h.shorter(cs.message, 256))
70 desc = "%s - %s" % (cs.short_id, h.shorter(cs.message, 256))
71 author_email = cs.author_email
71 author_email = cs.author_email
72 comment = ChangesetComment()
72 comment = ChangesetComment()
73 comment.repo = repo
73 comment.repo = repo
74 comment.user_id = user_id
74 comment.user_id = user_id
75 comment.revision = revision
75 comment.revision = revision
76 comment.text = text
76 comment.text = text
77 comment.f_path = f_path
77 comment.f_path = f_path
78 comment.line_no = line_no
78 comment.line_no = line_no
79
79
80 self.sa.add(comment)
80 self.sa.add(comment)
81 self.sa.flush()
81 self.sa.flush()
82 # make notification
82 # make notification
83 line = ''
83 line = ''
84 if line_no:
84 if line_no:
85 line = _('on line %s') % line_no
85 line = _('on line %s') % line_no
86 subj = safe_unicode(
86 subj = safe_unicode(
87 h.link_to('Re commit: %(commit_desc)s %(line)s' % \
87 h.link_to('Re commit: %(commit_desc)s %(line)s' % \
88 {'commit_desc': desc, 'line': line},
88 {'commit_desc': desc, 'line': line},
89 h.url('changeset_home', repo_name=repo.repo_name,
89 h.url('changeset_home', repo_name=repo.repo_name,
90 revision=revision,
90 revision=revision,
91 anchor='comment-%s' % comment.comment_id,
91 anchor='comment-%s' % comment.comment_id,
92 qualified=True,
92 qualified=True,
93 )
93 )
94 )
94 )
95 )
95 )
96
96
97 body = text
97 body = text
98
98
99 # get the current participants of this changeset
99 # get the current participants of this changeset
100 recipients = ChangesetComment.get_users(revision=revision)
100 recipients = ChangesetComment.get_users(revision=revision)
101
101
102 # add changeset author if it's in rhodecode system
102 # add changeset author if it's in rhodecode system
103 recipients += [User.get_by_email(author_email)]
103 recipients += [User.get_by_email(author_email)]
104
104
105 # create notification objects, and emails
105 NotificationModel().create(
106 NotificationModel().create(
106 created_by=user_id, subject=subj, body=body,
107 created_by=user_id, subject=subj, body=body,
107 recipients=recipients, type_=Notification.TYPE_CHANGESET_COMMENT
108 recipients=recipients, type_=Notification.TYPE_CHANGESET_COMMENT
108 )
109 )
109
110
110 mention_recipients = set(self._extract_mentions(body))\
111 mention_recipients = set(self._extract_mentions(body))\
111 .difference(recipients)
112 .difference(recipients)
112 if mention_recipients:
113 if mention_recipients:
113 subj = _('[Mention]') + ' ' + subj
114 subj = _('[Mention]') + ' ' + subj
114 NotificationModel().create(
115 NotificationModel().create(
115 created_by=user_id, subject=subj, body=body,
116 created_by=user_id, subject=subj, body=body,
116 recipients=mention_recipients,
117 recipients=mention_recipients,
117 type_=Notification.TYPE_CHANGESET_COMMENT
118 type_=Notification.TYPE_CHANGESET_COMMENT
118 )
119 )
119
120
120 return comment
121 return comment
121
122
122 def delete(self, comment):
123 def delete(self, comment):
123 """
124 """
124 Deletes given comment
125 Deletes given comment
125
126
126 :param comment_id:
127 :param comment_id:
127 """
128 """
128 comment = self.__get_changeset_comment(comment)
129 comment = self.__get_changeset_comment(comment)
129 self.sa.delete(comment)
130 self.sa.delete(comment)
130
131
131 return comment
132 return comment
132
133
133 def get_comments(self, repo_id, revision):
134 def get_comments(self, repo_id, revision):
134 return ChangesetComment.query()\
135 return ChangesetComment.query()\
135 .filter(ChangesetComment.repo_id == repo_id)\
136 .filter(ChangesetComment.repo_id == repo_id)\
136 .filter(ChangesetComment.revision == revision)\
137 .filter(ChangesetComment.revision == revision)\
137 .filter(ChangesetComment.line_no == None)\
138 .filter(ChangesetComment.line_no == None)\
138 .filter(ChangesetComment.f_path == None).all()
139 .filter(ChangesetComment.f_path == None).all()
139
140
140 def get_inline_comments(self, repo_id, revision):
141 def get_inline_comments(self, repo_id, revision):
141 comments = self.sa.query(ChangesetComment)\
142 comments = self.sa.query(ChangesetComment)\
142 .filter(ChangesetComment.repo_id == repo_id)\
143 .filter(ChangesetComment.repo_id == repo_id)\
143 .filter(ChangesetComment.revision == revision)\
144 .filter(ChangesetComment.revision == revision)\
144 .filter(ChangesetComment.line_no != None)\
145 .filter(ChangesetComment.line_no != None)\
145 .filter(ChangesetComment.f_path != None)\
146 .filter(ChangesetComment.f_path != None)\
146 .order_by(ChangesetComment.comment_id.asc())\
147 .order_by(ChangesetComment.comment_id.asc())\
147 .all()
148 .all()
148
149
149 paths = defaultdict(lambda: defaultdict(list))
150 paths = defaultdict(lambda: defaultdict(list))
150
151
151 for co in comments:
152 for co in comments:
152 paths[co.f_path][co.line_no].append(co)
153 paths[co.f_path][co.line_no].append(co)
153 return paths.items()
154 return paths.items()
@@ -1,226 +1,229 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.notification
3 rhodecode.model.notification
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Model for notifications
6 Model for notifications
7
7
8
8
9 :created_on: Nov 20, 2011
9 :created_on: Nov 20, 2011
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26
26
27 import os
27 import os
28 import logging
28 import logging
29 import traceback
29 import traceback
30 import datetime
30 import datetime
31
31
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33
33
34 import rhodecode
34 import rhodecode
35 from rhodecode.config.conf import DATETIME_FORMAT
35 from rhodecode.config.conf import DATETIME_FORMAT
36 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
37 from rhodecode.model import BaseModel
37 from rhodecode.model import BaseModel
38 from rhodecode.model.db import Notification, User, UserNotification
38 from rhodecode.model.db import Notification, User, UserNotification
39
39
40 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
41
41
42
42
43 class NotificationModel(BaseModel):
43 class NotificationModel(BaseModel):
44
44
45 def __get_user(self, user):
45 def __get_user(self, user):
46 return self._get_instance(User, user, callback=User.get_by_username)
46 return self._get_instance(User, user, callback=User.get_by_username)
47
47
48 def __get_notification(self, notification):
48 def __get_notification(self, notification):
49 if isinstance(notification, Notification):
49 if isinstance(notification, Notification):
50 return notification
50 return notification
51 elif isinstance(notification, (int, long)):
51 elif isinstance(notification, (int, long)):
52 return Notification.get(notification)
52 return Notification.get(notification)
53 else:
53 else:
54 if notification:
54 if notification:
55 raise Exception('notification must be int, long or Instance'
55 raise Exception('notification must be int, long or Instance'
56 ' of Notification got %s' % type(notification))
56 ' of Notification got %s' % type(notification))
57
57
58 def create(self, created_by, subject, body, recipients=None,
58 def create(self, created_by, subject, body, recipients=None,
59 type_=Notification.TYPE_MESSAGE, with_email=True,
59 type_=Notification.TYPE_MESSAGE, with_email=True,
60 email_kwargs={}):
60 email_kwargs={}):
61 """
61 """
62
62
63 Creates notification of given type
63 Creates notification of given type
64
64
65 :param created_by: int, str or User instance. User who created this
65 :param created_by: int, str or User instance. User who created this
66 notification
66 notification
67 :param subject:
67 :param subject:
68 :param body:
68 :param body:
69 :param recipients: list of int, str or User objects, when None
69 :param recipients: list of int, str or User objects, when None
70 is given send to all admins
70 is given send to all admins
71 :param type_: type of notification
71 :param type_: type of notification
72 :param with_email: send email with this notification
72 :param with_email: send email with this notification
73 :param email_kwargs: additional dict to pass as args to email template
73 :param email_kwargs: additional dict to pass as args to email template
74 """
74 """
75 from rhodecode.lib.celerylib import tasks, run_task
75 from rhodecode.lib.celerylib import tasks, run_task
76
76
77 if recipients and not getattr(recipients, '__iter__', False):
77 if recipients and not getattr(recipients, '__iter__', False):
78 raise Exception('recipients must be a list of iterable')
78 raise Exception('recipients must be a list of iterable')
79
79
80 created_by_obj = self.__get_user(created_by)
80 created_by_obj = self.__get_user(created_by)
81
81
82 if recipients:
82 if recipients:
83 recipients_objs = []
83 recipients_objs = []
84 for u in recipients:
84 for u in recipients:
85 obj = self.__get_user(u)
85 obj = self.__get_user(u)
86 if obj:
86 if obj:
87 recipients_objs.append(obj)
87 recipients_objs.append(obj)
88 recipients_objs = set(recipients_objs)
88 recipients_objs = set(recipients_objs)
89 log.debug('sending notifications %s to %s' % (
89 log.debug('sending notifications %s to %s' % (
90 type_, recipients_objs)
90 type_, recipients_objs)
91 )
91 )
92 else:
92 else:
93 # empty recipients means to all admins
93 # empty recipients means to all admins
94 recipients_objs = User.query().filter(User.admin == True).all()
94 recipients_objs = User.query().filter(User.admin == True).all()
95 log.debug('sending notifications %s to admins: %s' % (
95 log.debug('sending notifications %s to admins: %s' % (
96 type_, recipients_objs)
96 type_, recipients_objs)
97 )
97 )
98 notif = Notification.create(
98 notif = Notification.create(
99 created_by=created_by_obj, subject=subject,
99 created_by=created_by_obj, subject=subject,
100 body=body, recipients=recipients_objs, type_=type_
100 body=body, recipients=recipients_objs, type_=type_
101 )
101 )
102
102
103 if with_email is False:
103 if with_email is False:
104 return notif
104 return notif
105
105
106 # send email with notification
106 #don't send email to person who created this comment
107 for rec in recipients_objs:
107 rec_objs = set(recipients_objs).difference(set([created_by_obj]))
108
109 # send email with notification to all other participants
110 for rec in rec_objs:
108 email_subject = NotificationModel().make_description(notif, False)
111 email_subject = NotificationModel().make_description(notif, False)
109 type_ = type_
112 type_ = type_
110 email_body = body
113 email_body = body
111 kwargs = {'subject': subject, 'body': h.rst_w_mentions(body)}
114 kwargs = {'subject': subject, 'body': h.rst_w_mentions(body)}
112 kwargs.update(email_kwargs)
115 kwargs.update(email_kwargs)
113 email_body_html = EmailNotificationModel()\
116 email_body_html = EmailNotificationModel()\
114 .get_email_tmpl(type_, **kwargs)
117 .get_email_tmpl(type_, **kwargs)
115
118
116 run_task(tasks.send_email, rec.email, email_subject, email_body,
119 run_task(tasks.send_email, rec.email, email_subject, email_body,
117 email_body_html)
120 email_body_html)
118
121
119 return notif
122 return notif
120
123
121 def delete(self, user, notification):
124 def delete(self, user, notification):
122 # we don't want to remove actual notification just the assignment
125 # we don't want to remove actual notification just the assignment
123 try:
126 try:
124 notification = self.__get_notification(notification)
127 notification = self.__get_notification(notification)
125 user = self.__get_user(user)
128 user = self.__get_user(user)
126 if notification and user:
129 if notification and user:
127 obj = UserNotification.query()\
130 obj = UserNotification.query()\
128 .filter(UserNotification.user == user)\
131 .filter(UserNotification.user == user)\
129 .filter(UserNotification.notification
132 .filter(UserNotification.notification
130 == notification)\
133 == notification)\
131 .one()
134 .one()
132 self.sa.delete(obj)
135 self.sa.delete(obj)
133 return True
136 return True
134 except Exception:
137 except Exception:
135 log.error(traceback.format_exc())
138 log.error(traceback.format_exc())
136 raise
139 raise
137
140
138 def get_for_user(self, user):
141 def get_for_user(self, user):
139 user = self.__get_user(user)
142 user = self.__get_user(user)
140 return user.notifications
143 return user.notifications
141
144
142 def mark_all_read_for_user(self, user):
145 def mark_all_read_for_user(self, user):
143 user = self.__get_user(user)
146 user = self.__get_user(user)
144 UserNotification.query()\
147 UserNotification.query()\
145 .filter(UserNotification.read==False)\
148 .filter(UserNotification.read==False)\
146 .update({'read': True})
149 .update({'read': True})
147
150
148 def get_unread_cnt_for_user(self, user):
151 def get_unread_cnt_for_user(self, user):
149 user = self.__get_user(user)
152 user = self.__get_user(user)
150 return UserNotification.query()\
153 return UserNotification.query()\
151 .filter(UserNotification.read == False)\
154 .filter(UserNotification.read == False)\
152 .filter(UserNotification.user == user).count()
155 .filter(UserNotification.user == user).count()
153
156
154 def get_unread_for_user(self, user):
157 def get_unread_for_user(self, user):
155 user = self.__get_user(user)
158 user = self.__get_user(user)
156 return [x.notification for x in UserNotification.query()\
159 return [x.notification for x in UserNotification.query()\
157 .filter(UserNotification.read == False)\
160 .filter(UserNotification.read == False)\
158 .filter(UserNotification.user == user).all()]
161 .filter(UserNotification.user == user).all()]
159
162
160 def get_user_notification(self, user, notification):
163 def get_user_notification(self, user, notification):
161 user = self.__get_user(user)
164 user = self.__get_user(user)
162 notification = self.__get_notification(notification)
165 notification = self.__get_notification(notification)
163
166
164 return UserNotification.query()\
167 return UserNotification.query()\
165 .filter(UserNotification.notification == notification)\
168 .filter(UserNotification.notification == notification)\
166 .filter(UserNotification.user == user).scalar()
169 .filter(UserNotification.user == user).scalar()
167
170
168 def make_description(self, notification, show_age=True):
171 def make_description(self, notification, show_age=True):
169 """
172 """
170 Creates a human readable description based on properties
173 Creates a human readable description based on properties
171 of notification object
174 of notification object
172 """
175 """
173
176
174 _map = {
177 _map = {
175 notification.TYPE_CHANGESET_COMMENT: _('commented on commit'),
178 notification.TYPE_CHANGESET_COMMENT: _('commented on commit'),
176 notification.TYPE_MESSAGE: _('sent message'),
179 notification.TYPE_MESSAGE: _('sent message'),
177 notification.TYPE_MENTION: _('mentioned you'),
180 notification.TYPE_MENTION: _('mentioned you'),
178 notification.TYPE_REGISTRATION: _('registered in RhodeCode')
181 notification.TYPE_REGISTRATION: _('registered in RhodeCode')
179 }
182 }
180
183
181 tmpl = "%(user)s %(action)s %(when)s"
184 tmpl = "%(user)s %(action)s %(when)s"
182 if show_age:
185 if show_age:
183 when = h.age(notification.created_on)
186 when = h.age(notification.created_on)
184 else:
187 else:
185 DTF = lambda d: datetime.datetime.strftime(d, DATETIME_FORMAT)
188 DTF = lambda d: datetime.datetime.strftime(d, DATETIME_FORMAT)
186 when = DTF(notification.created_on)
189 when = DTF(notification.created_on)
187
190
188 data = dict(
191 data = dict(
189 user=notification.created_by_user.username,
192 user=notification.created_by_user.username,
190 action=_map[notification.type_], when=when,
193 action=_map[notification.type_], when=when,
191 )
194 )
192 return tmpl % data
195 return tmpl % data
193
196
194
197
195 class EmailNotificationModel(BaseModel):
198 class EmailNotificationModel(BaseModel):
196
199
197 TYPE_CHANGESET_COMMENT = Notification.TYPE_CHANGESET_COMMENT
200 TYPE_CHANGESET_COMMENT = Notification.TYPE_CHANGESET_COMMENT
198 TYPE_PASSWORD_RESET = 'passoword_link'
201 TYPE_PASSWORD_RESET = 'passoword_link'
199 TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
202 TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
200 TYPE_DEFAULT = 'default'
203 TYPE_DEFAULT = 'default'
201
204
202 def __init__(self):
205 def __init__(self):
203 self._template_root = rhodecode.CONFIG['pylons.paths']['templates'][0]
206 self._template_root = rhodecode.CONFIG['pylons.paths']['templates'][0]
204 self._tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
207 self._tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
205
208
206 self.email_types = {
209 self.email_types = {
207 self.TYPE_CHANGESET_COMMENT: 'email_templates/changeset_comment.html',
210 self.TYPE_CHANGESET_COMMENT: 'email_templates/changeset_comment.html',
208 self.TYPE_PASSWORD_RESET: 'email_templates/password_reset.html',
211 self.TYPE_PASSWORD_RESET: 'email_templates/password_reset.html',
209 self.TYPE_REGISTRATION: 'email_templates/registration.html',
212 self.TYPE_REGISTRATION: 'email_templates/registration.html',
210 self.TYPE_DEFAULT: 'email_templates/default.html'
213 self.TYPE_DEFAULT: 'email_templates/default.html'
211 }
214 }
212
215
213 def get_email_tmpl(self, type_, **kwargs):
216 def get_email_tmpl(self, type_, **kwargs):
214 """
217 """
215 return generated template for email based on given type
218 return generated template for email based on given type
216
219
217 :param type_:
220 :param type_:
218 """
221 """
219
222
220 base = self.email_types.get(type_, self.email_types[self.TYPE_DEFAULT])
223 base = self.email_types.get(type_, self.email_types[self.TYPE_DEFAULT])
221 email_template = self._tmpl_lookup.get_template(base)
224 email_template = self._tmpl_lookup.get_template(base)
222 # translator inject
225 # translator inject
223 _kwargs = {'_': _}
226 _kwargs = {'_': _}
224 _kwargs.update(kwargs)
227 _kwargs.update(kwargs)
225 log.debug('rendering tmpl %s with kwargs %s' % (base, _kwargs))
228 log.debug('rendering tmpl %s with kwargs %s' % (base, _kwargs))
226 return email_template.render(**_kwargs)
229 return email_template.render(**_kwargs)
General Comments 0
You need to be logged in to leave comments. Login now