##// END OF EJS Templates
notifications: skip fetching of settings in notifications....
marcink -
r1497:1047cf00 default
parent child Browse files
Show More
@@ -1,383 +1,374 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 """
23 23 Model for notifications
24 24 """
25 25
26 26
27 27 import logging
28 28 import traceback
29 29
30 30 from pylons.i18n.translation import _, ungettext
31 31 from sqlalchemy.sql.expression import false, true
32 32 from mako import exceptions
33 33
34 34 import rhodecode
35 35 from rhodecode.lib import helpers as h
36 36 from rhodecode.lib.utils import PartialRenderer
37 37 from rhodecode.model import BaseModel
38 38 from rhodecode.model.db import Notification, User, UserNotification
39 39 from rhodecode.model.meta import Session
40 40 from rhodecode.model.settings import SettingsModel
41 41 from rhodecode.translation import TranslationString
42 42
43 43 log = logging.getLogger(__name__)
44 44
45 45
46 46 class NotificationModel(BaseModel):
47 47
48 48 cls = Notification
49 49
50 50 def __get_notification(self, notification):
51 51 if isinstance(notification, Notification):
52 52 return notification
53 53 elif isinstance(notification, (int, long)):
54 54 return Notification.get(notification)
55 55 else:
56 56 if notification:
57 57 raise Exception('notification must be int, long or Instance'
58 58 ' of Notification got %s' % type(notification))
59 59
60 60 def create(
61 61 self, created_by, notification_subject, notification_body,
62 62 notification_type=Notification.TYPE_MESSAGE, recipients=None,
63 63 mention_recipients=None, with_email=True, email_kwargs=None):
64 64 """
65 65
66 66 Creates notification of given type
67 67
68 68 :param created_by: int, str or User instance. User who created this
69 69 notification
70 70 :param notification_subject: subject of notification itself
71 71 :param notification_body: body of notification text
72 72 :param notification_type: type of notification, based on that we
73 73 pick templates
74 74
75 75 :param recipients: list of int, str or User objects, when None
76 76 is given send to all admins
77 77 :param mention_recipients: list of int, str or User objects,
78 78 that were mentioned
79 79 :param with_email: send email with this notification
80 80 :param email_kwargs: dict with arguments to generate email
81 81 """
82 82
83 83 from rhodecode.lib.celerylib import tasks, run_task
84 84
85 85 if recipients and not getattr(recipients, '__iter__', False):
86 86 raise Exception('recipients must be an iterable object')
87 87
88 88 created_by_obj = self._get_user(created_by)
89 89 # default MAIN body if not given
90 90 email_kwargs = email_kwargs or {'body': notification_body}
91 91 mention_recipients = mention_recipients or set()
92 92
93 93 if not created_by_obj:
94 94 raise Exception('unknown user %s' % created_by)
95 95
96 96 if recipients is None:
97 97 # recipients is None means to all admins
98 98 recipients_objs = User.query().filter(User.admin == true()).all()
99 99 log.debug('sending notifications %s to admins: %s',
100 100 notification_type, recipients_objs)
101 101 else:
102 102 recipients_objs = []
103 103 for u in recipients:
104 104 obj = self._get_user(u)
105 105 if obj:
106 106 recipients_objs.append(obj)
107 107 else: # we didn't find this user, log the error and carry on
108 108 log.error('cannot notify unknown user %r', u)
109 109
110 110 recipients_objs = set(recipients_objs)
111 111 if not recipients_objs:
112 112 raise Exception('no valid recipients specified')
113 113
114 114 log.debug('sending notifications %s to %s',
115 115 notification_type, recipients_objs)
116 116
117 117 # add mentioned users into recipients
118 118 final_recipients = set(recipients_objs).union(mention_recipients)
119 119 notification = Notification.create(
120 120 created_by=created_by_obj, subject=notification_subject,
121 121 body=notification_body, recipients=final_recipients,
122 122 type_=notification_type
123 123 )
124 124
125 125 if not with_email: # skip sending email, and just create notification
126 126 return notification
127 127
128 128 # don't send email to person who created this comment
129 129 rec_objs = set(recipients_objs).difference(set([created_by_obj]))
130 130
131 131 # now notify all recipients in question
132 132
133 133 for recipient in rec_objs.union(mention_recipients):
134 134 # inject current recipient
135 135 email_kwargs['recipient'] = recipient
136 136 email_kwargs['mention'] = recipient in mention_recipients
137 137 (subject, headers, email_body,
138 138 email_body_plaintext) = EmailNotificationModel().render_email(
139 139 notification_type, **email_kwargs)
140 140
141 141 log.debug(
142 142 'Creating notification email task for user:`%s`', recipient)
143 143 task = run_task(
144 144 tasks.send_email, recipient.email, subject,
145 145 email_body_plaintext, email_body)
146 146 log.debug('Created email task: %s', task)
147 147
148 148 return notification
149 149
150 150 def delete(self, user, notification):
151 151 # we don't want to remove actual notification just the assignment
152 152 try:
153 153 notification = self.__get_notification(notification)
154 154 user = self._get_user(user)
155 155 if notification and user:
156 156 obj = UserNotification.query()\
157 157 .filter(UserNotification.user == user)\
158 158 .filter(UserNotification.notification == notification)\
159 159 .one()
160 160 Session().delete(obj)
161 161 return True
162 162 except Exception:
163 163 log.error(traceback.format_exc())
164 164 raise
165 165
166 166 def get_for_user(self, user, filter_=None):
167 167 """
168 168 Get mentions for given user, filter them if filter dict is given
169 169
170 170 :param user:
171 171 :param filter:
172 172 """
173 173 user = self._get_user(user)
174 174
175 175 q = UserNotification.query()\
176 176 .filter(UserNotification.user == user)\
177 177 .join((
178 178 Notification, UserNotification.notification_id ==
179 179 Notification.notification_id))
180 180
181 181 if filter_:
182 182 q = q.filter(Notification.type_.in_(filter_))
183 183
184 184 return q.all()
185 185
186 186 def mark_read(self, user, notification):
187 187 try:
188 188 notification = self.__get_notification(notification)
189 189 user = self._get_user(user)
190 190 if notification and user:
191 191 obj = UserNotification.query()\
192 192 .filter(UserNotification.user == user)\
193 193 .filter(UserNotification.notification == notification)\
194 194 .one()
195 195 obj.read = True
196 196 Session().add(obj)
197 197 return True
198 198 except Exception:
199 199 log.error(traceback.format_exc())
200 200 raise
201 201
202 202 def mark_all_read_for_user(self, user, filter_=None):
203 203 user = self._get_user(user)
204 204 q = UserNotification.query()\
205 205 .filter(UserNotification.user == user)\
206 206 .filter(UserNotification.read == false())\
207 207 .join((
208 208 Notification, UserNotification.notification_id ==
209 209 Notification.notification_id))
210 210 if filter_:
211 211 q = q.filter(Notification.type_.in_(filter_))
212 212
213 213 # this is a little inefficient but sqlalchemy doesn't support
214 214 # update on joined tables :(
215 215 for obj in q.all():
216 216 obj.read = True
217 217 Session().add(obj)
218 218
219 219 def get_unread_cnt_for_user(self, user):
220 220 user = self._get_user(user)
221 221 return UserNotification.query()\
222 222 .filter(UserNotification.read == false())\
223 223 .filter(UserNotification.user == user).count()
224 224
225 225 def get_unread_for_user(self, user):
226 226 user = self._get_user(user)
227 227 return [x.notification for x in UserNotification.query()
228 228 .filter(UserNotification.read == false())
229 229 .filter(UserNotification.user == user).all()]
230 230
231 231 def get_user_notification(self, user, notification):
232 232 user = self._get_user(user)
233 233 notification = self.__get_notification(notification)
234 234
235 235 return UserNotification.query()\
236 236 .filter(UserNotification.notification == notification)\
237 237 .filter(UserNotification.user == user).scalar()
238 238
239 239 def make_description(self, notification, show_age=True, translate=None):
240 240 """
241 241 Creates a human readable description based on properties
242 242 of notification object
243 243 """
244 244
245 245 _map = {
246 246 notification.TYPE_CHANGESET_COMMENT: [
247 247 _('%(user)s commented on commit %(date_or_age)s'),
248 248 _('%(user)s commented on commit at %(date_or_age)s'),
249 249 ],
250 250 notification.TYPE_MESSAGE: [
251 251 _('%(user)s sent message %(date_or_age)s'),
252 252 _('%(user)s sent message at %(date_or_age)s'),
253 253 ],
254 254 notification.TYPE_MENTION: [
255 255 _('%(user)s mentioned you %(date_or_age)s'),
256 256 _('%(user)s mentioned you at %(date_or_age)s'),
257 257 ],
258 258 notification.TYPE_REGISTRATION: [
259 259 _('%(user)s registered in RhodeCode %(date_or_age)s'),
260 260 _('%(user)s registered in RhodeCode at %(date_or_age)s'),
261 261 ],
262 262 notification.TYPE_PULL_REQUEST: [
263 263 _('%(user)s opened new pull request %(date_or_age)s'),
264 264 _('%(user)s opened new pull request at %(date_or_age)s'),
265 265 ],
266 266 notification.TYPE_PULL_REQUEST_COMMENT: [
267 267 _('%(user)s commented on pull request %(date_or_age)s'),
268 268 _('%(user)s commented on pull request at %(date_or_age)s'),
269 269 ],
270 270 }
271 271
272 272 templates = _map[notification.type_]
273 273
274 274 if show_age:
275 275 template = templates[0]
276 276 date_or_age = h.age(notification.created_on)
277 277 if translate:
278 278 date_or_age = translate(date_or_age)
279 279
280 280 if isinstance(date_or_age, TranslationString):
281 281 date_or_age = date_or_age.interpolate()
282 282
283 283 else:
284 284 template = templates[1]
285 285 date_or_age = h.format_date(notification.created_on)
286 286
287 287 return template % {
288 288 'user': notification.created_by_user.username,
289 289 'date_or_age': date_or_age,
290 290 }
291 291
292 292
293 293 class EmailNotificationModel(BaseModel):
294 294 TYPE_COMMIT_COMMENT = Notification.TYPE_CHANGESET_COMMENT
295 295 TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
296 296 TYPE_PULL_REQUEST = Notification.TYPE_PULL_REQUEST
297 297 TYPE_PULL_REQUEST_COMMENT = Notification.TYPE_PULL_REQUEST_COMMENT
298 298 TYPE_MAIN = Notification.TYPE_MESSAGE
299 299
300 300 TYPE_PASSWORD_RESET = 'password_reset'
301 301 TYPE_PASSWORD_RESET_CONFIRMATION = 'password_reset_confirmation'
302 302 TYPE_EMAIL_TEST = 'email_test'
303 303 TYPE_TEST = 'test'
304 304
305 305 email_types = {
306 306 TYPE_MAIN: 'email_templates/main.mako',
307 307 TYPE_TEST: 'email_templates/test.mako',
308 308 TYPE_EMAIL_TEST: 'email_templates/email_test.mako',
309 309 TYPE_REGISTRATION: 'email_templates/user_registration.mako',
310 310 TYPE_PASSWORD_RESET: 'email_templates/password_reset.mako',
311 311 TYPE_PASSWORD_RESET_CONFIRMATION: 'email_templates/password_reset_confirmation.mako',
312 312 TYPE_COMMIT_COMMENT: 'email_templates/commit_comment.mako',
313 313 TYPE_PULL_REQUEST: 'email_templates/pull_request_review.mako',
314 314 TYPE_PULL_REQUEST_COMMENT: 'email_templates/pull_request_comment.mako',
315 315 }
316 316
317 317 def __init__(self):
318 318 """
319 319 Example usage::
320 320
321 321 (subject, headers, email_body,
322 322 email_body_plaintext) = EmailNotificationModel().render_email(
323 323 EmailNotificationModel.TYPE_TEST, **email_kwargs)
324 324
325 325 """
326 326 super(EmailNotificationModel, self).__init__()
327 self.rhodecode_instance_name = None
327 self.rhodecode_instance_name = rhodecode.CONFIG.get('rhodecode_title')
328 328
329 329 def _update_kwargs_for_render(self, kwargs):
330 330 """
331 331 Inject params required for Mako rendering
332 332
333 333 :param kwargs:
334 :return:
335 334 """
336 rhodecode_name = self.rhodecode_instance_name
337 if not rhodecode_name:
338 try:
339 rc_config = SettingsModel().get_all_settings()
340 except Exception:
341 log.exception('failed to fetch settings')
342 rc_config = {}
343 rhodecode_name = rc_config.get('rhodecode_title', '')
344 kwargs['rhodecode_instance_name'] = rhodecode_name
335 kwargs['rhodecode_instance_name'] = self.rhodecode_instance_name
345 336
346 337 _kwargs = {
347 338 'instance_url': h.url('home', qualified=True),
348 339 }
349 340 _kwargs.update(kwargs)
350 341 return _kwargs
351 342
352 343 def get_renderer(self, type_):
353 344 template_name = self.email_types[type_]
354 345 return PartialRenderer(template_name)
355 346
356 347 def render_email(self, type_, **kwargs):
357 348 """
358 349 renders template for email, and returns a tuple of
359 350 (subject, email_headers, email_html_body, email_plaintext_body)
360 351 """
361 352 # translator and helpers inject
362 353 _kwargs = self._update_kwargs_for_render(kwargs)
363 354
364 355 email_template = self.get_renderer(type_)
365 356
366 357 subject = email_template.render('subject', **_kwargs)
367 358
368 359 try:
369 360 headers = email_template.render('headers', **_kwargs)
370 361 except AttributeError:
371 362 # it's not defined in template, ok we can skip it
372 363 headers = ''
373 364
374 365 try:
375 366 body_plaintext = email_template.render('body_plaintext', **_kwargs)
376 367 except AttributeError:
377 368 # it's not defined in template, ok we can skip it
378 369 body_plaintext = ''
379 370
380 371 # render WHOLE template
381 372 body = email_template.render(None, **_kwargs)
382 373
383 374 return subject, headers, body, body_plaintext
General Comments 0
You need to be logged in to leave comments. Login now