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