##// END OF EJS Templates
added template context into Notification templates
marcink -
r3423:043d3827 beta
parent child Browse files
Show More
@@ -1,278 +1,280 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
30
31 from pylons import tmpl_context as c
31 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
32
33
33 import rhodecode
34 import rhodecode
34 from rhodecode.lib import helpers as h
35 from rhodecode.lib import helpers as h
35 from rhodecode.model import BaseModel
36 from rhodecode.model import BaseModel
36 from rhodecode.model.db import Notification, User, UserNotification
37 from rhodecode.model.db import Notification, User, UserNotification
37
38
38 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
39
40
40
41
41 class NotificationModel(BaseModel):
42 class NotificationModel(BaseModel):
42
43
43 cls = Notification
44 cls = Notification
44
45
45 def __get_notification(self, notification):
46 def __get_notification(self, notification):
46 if isinstance(notification, Notification):
47 if isinstance(notification, Notification):
47 return notification
48 return notification
48 elif isinstance(notification, (int, long)):
49 elif isinstance(notification, (int, long)):
49 return Notification.get(notification)
50 return Notification.get(notification)
50 else:
51 else:
51 if notification:
52 if notification:
52 raise Exception('notification must be int, long or Instance'
53 raise Exception('notification must be int, long or Instance'
53 ' of Notification got %s' % type(notification))
54 ' of Notification got %s' % type(notification))
54
55
55 def create(self, created_by, subject, body, recipients=None,
56 def create(self, created_by, subject, body, recipients=None,
56 type_=Notification.TYPE_MESSAGE, with_email=True,
57 type_=Notification.TYPE_MESSAGE, with_email=True,
57 email_kwargs={}):
58 email_kwargs={}):
58 """
59 """
59
60
60 Creates notification of given type
61 Creates notification of given type
61
62
62 :param created_by: int, str or User instance. User who created this
63 :param created_by: int, str or User instance. User who created this
63 notification
64 notification
64 :param subject:
65 :param subject:
65 :param body:
66 :param body:
66 :param recipients: list of int, str or User objects, when None
67 :param recipients: list of int, str or User objects, when None
67 is given send to all admins
68 is given send to all admins
68 :param type_: type of notification
69 :param type_: type of notification
69 :param with_email: send email with this notification
70 :param with_email: send email with this notification
70 :param email_kwargs: additional dict to pass as args to email template
71 :param email_kwargs: additional dict to pass as args to email template
71 """
72 """
72 from rhodecode.lib.celerylib import tasks, run_task
73 from rhodecode.lib.celerylib import tasks, run_task
73
74
74 if recipients and not getattr(recipients, '__iter__', False):
75 if recipients and not getattr(recipients, '__iter__', False):
75 raise Exception('recipients must be a list or iterable')
76 raise Exception('recipients must be a list or iterable')
76
77
77 created_by_obj = self._get_user(created_by)
78 created_by_obj = self._get_user(created_by)
78
79
79 if recipients:
80 if recipients:
80 recipients_objs = []
81 recipients_objs = []
81 for u in recipients:
82 for u in recipients:
82 obj = self._get_user(u)
83 obj = self._get_user(u)
83 if obj:
84 if obj:
84 recipients_objs.append(obj)
85 recipients_objs.append(obj)
85 recipients_objs = set(recipients_objs)
86 recipients_objs = set(recipients_objs)
86 log.debug('sending notifications %s to %s' % (
87 log.debug('sending notifications %s to %s' % (
87 type_, recipients_objs)
88 type_, recipients_objs)
88 )
89 )
89 else:
90 else:
90 # empty recipients means to all admins
91 # empty recipients means to all admins
91 recipients_objs = User.query().filter(User.admin == True).all()
92 recipients_objs = User.query().filter(User.admin == True).all()
92 log.debug('sending notifications %s to admins: %s' % (
93 log.debug('sending notifications %s to admins: %s' % (
93 type_, recipients_objs)
94 type_, recipients_objs)
94 )
95 )
95 notif = Notification.create(
96 notif = Notification.create(
96 created_by=created_by_obj, subject=subject,
97 created_by=created_by_obj, subject=subject,
97 body=body, recipients=recipients_objs, type_=type_
98 body=body, recipients=recipients_objs, type_=type_
98 )
99 )
99
100
100 if with_email is False:
101 if with_email is False:
101 return notif
102 return notif
102
103
103 #don't send email to person who created this comment
104 #don't send email to person who created this comment
104 rec_objs = set(recipients_objs).difference(set([created_by_obj]))
105 rec_objs = set(recipients_objs).difference(set([created_by_obj]))
105
106
106 # send email with notification to all other participants
107 # send email with notification to all other participants
107 for rec in rec_objs:
108 for rec in rec_objs:
108 email_subject = NotificationModel().make_description(notif, False)
109 email_subject = NotificationModel().make_description(notif, False)
109 type_ = type_
110 type_ = type_
110 email_body = body
111 email_body = body
111 ## this is passed into template
112 ## this is passed into template
112 kwargs = {'subject': subject, 'body': h.rst_w_mentions(body)}
113 kwargs = {'subject': subject, 'body': h.rst_w_mentions(body)}
113 kwargs.update(email_kwargs)
114 kwargs.update(email_kwargs)
114 email_body_html = EmailNotificationModel()\
115 email_body_html = EmailNotificationModel()\
115 .get_email_tmpl(type_, **kwargs)
116 .get_email_tmpl(type_, **kwargs)
116
117
117 run_task(tasks.send_email, rec.email, email_subject, email_body,
118 run_task(tasks.send_email, rec.email, email_subject, email_body,
118 email_body_html)
119 email_body_html)
119
120
120 return notif
121 return notif
121
122
122 def delete(self, user, notification):
123 def delete(self, user, notification):
123 # we don't want to remove actual notification just the assignment
124 # we don't want to remove actual notification just the assignment
124 try:
125 try:
125 notification = self.__get_notification(notification)
126 notification = self.__get_notification(notification)
126 user = self._get_user(user)
127 user = self._get_user(user)
127 if notification and user:
128 if notification and user:
128 obj = UserNotification.query()\
129 obj = UserNotification.query()\
129 .filter(UserNotification.user == user)\
130 .filter(UserNotification.user == user)\
130 .filter(UserNotification.notification
131 .filter(UserNotification.notification
131 == notification)\
132 == notification)\
132 .one()
133 .one()
133 self.sa.delete(obj)
134 self.sa.delete(obj)
134 return True
135 return True
135 except Exception:
136 except Exception:
136 log.error(traceback.format_exc())
137 log.error(traceback.format_exc())
137 raise
138 raise
138
139
139 def get_for_user(self, user, filter_=None):
140 def get_for_user(self, user, filter_=None):
140 """
141 """
141 Get mentions for given user, filter them if filter dict is given
142 Get mentions for given user, filter them if filter dict is given
142
143
143 :param user:
144 :param user:
144 :type user:
145 :type user:
145 :param filter:
146 :param filter:
146 """
147 """
147 user = self._get_user(user)
148 user = self._get_user(user)
148
149
149 q = UserNotification.query()\
150 q = UserNotification.query()\
150 .filter(UserNotification.user == user)\
151 .filter(UserNotification.user == user)\
151 .join((Notification, UserNotification.notification_id ==
152 .join((Notification, UserNotification.notification_id ==
152 Notification.notification_id))
153 Notification.notification_id))
153
154
154 if filter_:
155 if filter_:
155 q = q.filter(Notification.type_.in_(filter_))
156 q = q.filter(Notification.type_.in_(filter_))
156
157
157 return q.all()
158 return q.all()
158
159
159 def mark_read(self, user, notification):
160 def mark_read(self, user, notification):
160 try:
161 try:
161 notification = self.__get_notification(notification)
162 notification = self.__get_notification(notification)
162 user = self._get_user(user)
163 user = self._get_user(user)
163 if notification and user:
164 if notification and user:
164 obj = UserNotification.query()\
165 obj = UserNotification.query()\
165 .filter(UserNotification.user == user)\
166 .filter(UserNotification.user == user)\
166 .filter(UserNotification.notification
167 .filter(UserNotification.notification
167 == notification)\
168 == notification)\
168 .one()
169 .one()
169 obj.read = True
170 obj.read = True
170 self.sa.add(obj)
171 self.sa.add(obj)
171 return True
172 return True
172 except Exception:
173 except Exception:
173 log.error(traceback.format_exc())
174 log.error(traceback.format_exc())
174 raise
175 raise
175
176
176 def mark_all_read_for_user(self, user, filter_=None):
177 def mark_all_read_for_user(self, user, filter_=None):
177 user = self._get_user(user)
178 user = self._get_user(user)
178 q = UserNotification.query()\
179 q = UserNotification.query()\
179 .filter(UserNotification.user == user)\
180 .filter(UserNotification.user == user)\
180 .filter(UserNotification.read == False)\
181 .filter(UserNotification.read == False)\
181 .join((Notification, UserNotification.notification_id ==
182 .join((Notification, UserNotification.notification_id ==
182 Notification.notification_id))
183 Notification.notification_id))
183 if filter_:
184 if filter_:
184 q = q.filter(Notification.type_.in_(filter_))
185 q = q.filter(Notification.type_.in_(filter_))
185
186
186 # this is a little inefficient but sqlalchemy doesn't support
187 # this is a little inefficient but sqlalchemy doesn't support
187 # update on joined tables :(
188 # update on joined tables :(
188 for obj in q.all():
189 for obj in q.all():
189 obj.read = True
190 obj.read = True
190 self.sa.add(obj)
191 self.sa.add(obj)
191
192
192 def get_unread_cnt_for_user(self, user):
193 def get_unread_cnt_for_user(self, user):
193 user = self._get_user(user)
194 user = self._get_user(user)
194 return UserNotification.query()\
195 return UserNotification.query()\
195 .filter(UserNotification.read == False)\
196 .filter(UserNotification.read == False)\
196 .filter(UserNotification.user == user).count()
197 .filter(UserNotification.user == user).count()
197
198
198 def get_unread_for_user(self, user):
199 def get_unread_for_user(self, user):
199 user = self._get_user(user)
200 user = self._get_user(user)
200 return [x.notification for x in UserNotification.query()\
201 return [x.notification for x in UserNotification.query()\
201 .filter(UserNotification.read == False)\
202 .filter(UserNotification.read == False)\
202 .filter(UserNotification.user == user).all()]
203 .filter(UserNotification.user == user).all()]
203
204
204 def get_user_notification(self, user, notification):
205 def get_user_notification(self, user, notification):
205 user = self._get_user(user)
206 user = self._get_user(user)
206 notification = self.__get_notification(notification)
207 notification = self.__get_notification(notification)
207
208
208 return UserNotification.query()\
209 return UserNotification.query()\
209 .filter(UserNotification.notification == notification)\
210 .filter(UserNotification.notification == notification)\
210 .filter(UserNotification.user == user).scalar()
211 .filter(UserNotification.user == user).scalar()
211
212
212 def make_description(self, notification, show_age=True):
213 def make_description(self, notification, show_age=True):
213 """
214 """
214 Creates a human readable description based on properties
215 Creates a human readable description based on properties
215 of notification object
216 of notification object
216 """
217 """
217 #alias
218 #alias
218 _n = notification
219 _n = notification
219 _map = {
220 _map = {
220 _n.TYPE_CHANGESET_COMMENT: _('commented on commit at %(when)s'),
221 _n.TYPE_CHANGESET_COMMENT: _('commented on commit at %(when)s'),
221 _n.TYPE_MESSAGE: _('sent message at %(when)s'),
222 _n.TYPE_MESSAGE: _('sent message at %(when)s'),
222 _n.TYPE_MENTION: _('mentioned you at %(when)s'),
223 _n.TYPE_MENTION: _('mentioned you at %(when)s'),
223 _n.TYPE_REGISTRATION: _('registered in RhodeCode at %(when)s'),
224 _n.TYPE_REGISTRATION: _('registered in RhodeCode at %(when)s'),
224 _n.TYPE_PULL_REQUEST: _('opened new pull request at %(when)s'),
225 _n.TYPE_PULL_REQUEST: _('opened new pull request at %(when)s'),
225 _n.TYPE_PULL_REQUEST_COMMENT: _('commented on pull request at %(when)s')
226 _n.TYPE_PULL_REQUEST_COMMENT: _('commented on pull request at %(when)s')
226 }
227 }
227
228
228 # action == _map string
229 # action == _map string
229 tmpl = "%(user)s %(action)s "
230 tmpl = "%(user)s %(action)s "
230 if show_age:
231 if show_age:
231 when = h.age(notification.created_on)
232 when = h.age(notification.created_on)
232 else:
233 else:
233 when = h.fmt_date(notification.created_on)
234 when = h.fmt_date(notification.created_on)
234
235
235 data = dict(
236 data = dict(
236 user=notification.created_by_user.username,
237 user=notification.created_by_user.username,
237 action=_map[notification.type_] % {'when': when},
238 action=_map[notification.type_] % {'when': when},
238 )
239 )
239 return tmpl % data
240 return tmpl % data
240
241
241
242
242 class EmailNotificationModel(BaseModel):
243 class EmailNotificationModel(BaseModel):
243
244
244 TYPE_CHANGESET_COMMENT = Notification.TYPE_CHANGESET_COMMENT
245 TYPE_CHANGESET_COMMENT = Notification.TYPE_CHANGESET_COMMENT
245 TYPE_PASSWORD_RESET = 'password_link'
246 TYPE_PASSWORD_RESET = 'password_link'
246 TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
247 TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
247 TYPE_PULL_REQUEST = Notification.TYPE_PULL_REQUEST
248 TYPE_PULL_REQUEST = Notification.TYPE_PULL_REQUEST
248 TYPE_PULL_REQUEST_COMMENT = Notification.TYPE_PULL_REQUEST_COMMENT
249 TYPE_PULL_REQUEST_COMMENT = Notification.TYPE_PULL_REQUEST_COMMENT
249 TYPE_DEFAULT = 'default'
250 TYPE_DEFAULT = 'default'
250
251
251 def __init__(self):
252 def __init__(self):
252 self._template_root = rhodecode.CONFIG['pylons.paths']['templates'][0]
253 self._template_root = rhodecode.CONFIG['pylons.paths']['templates'][0]
253 self._tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
254 self._tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
254
255
255 self.email_types = {
256 self.email_types = {
256 self.TYPE_CHANGESET_COMMENT: 'email_templates/changeset_comment.html',
257 self.TYPE_CHANGESET_COMMENT: 'email_templates/changeset_comment.html',
257 self.TYPE_PASSWORD_RESET: 'email_templates/password_reset.html',
258 self.TYPE_PASSWORD_RESET: 'email_templates/password_reset.html',
258 self.TYPE_REGISTRATION: 'email_templates/registration.html',
259 self.TYPE_REGISTRATION: 'email_templates/registration.html',
259 self.TYPE_DEFAULT: 'email_templates/default.html',
260 self.TYPE_DEFAULT: 'email_templates/default.html',
260 self.TYPE_PULL_REQUEST: 'email_templates/pull_request.html',
261 self.TYPE_PULL_REQUEST: 'email_templates/pull_request.html',
261 self.TYPE_PULL_REQUEST_COMMENT: 'email_templates/pull_request_comment.html',
262 self.TYPE_PULL_REQUEST_COMMENT: 'email_templates/pull_request_comment.html',
262 }
263 }
263
264
264 def get_email_tmpl(self, type_, **kwargs):
265 def get_email_tmpl(self, type_, **kwargs):
265 """
266 """
266 return generated template for email based on given type
267 return generated template for email based on given type
267
268
268 :param type_:
269 :param type_:
269 """
270 """
270
271
271 base = self.email_types.get(type_, self.email_types[self.TYPE_DEFAULT])
272 base = self.email_types.get(type_, self.email_types[self.TYPE_DEFAULT])
272 email_template = self._tmpl_lookup.get_template(base)
273 email_template = self._tmpl_lookup.get_template(base)
273 # translator and helpers inject
274 # translator and helpers inject
274 _kwargs = {'_': _,
275 _kwargs = {'_': _,
275 'h': h}
276 'h': h,
277 'c': c}
276 _kwargs.update(kwargs)
278 _kwargs.update(kwargs)
277 log.debug('rendering tmpl %s with kwargs %s' % (base, _kwargs))
279 log.debug('rendering tmpl %s with kwargs %s' % (base, _kwargs))
278 return email_template.render(**_kwargs)
280 return email_template.render(**_kwargs)
General Comments 0
You need to be logged in to leave comments. Login now