##// END OF EJS Templates
Added filtering on inbox by comments
marcink -
r2503:d04243e9 beta
parent child Browse files
Show More
@@ -1,148 +1,150 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.notifications
3 rhodecode.controllers.admin.notifications
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 notifications controller for RhodeCode
6 notifications controller for RhodeCode
7
7
8 :created_on: Nov 23, 2010
8 :created_on: Nov 23, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-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 import request
29 from pylons import request
30 from pylons import tmpl_context as c, url
30 from pylons import tmpl_context as c, url
31 from pylons.controllers.util import redirect
31 from pylons.controllers.util import redirect
32
32
33 from webhelpers.paginate import Page
33 from webhelpers.paginate import Page
34
34
35 from rhodecode.lib.base import BaseController, render
35 from rhodecode.lib.base import BaseController, render
36 from rhodecode.model.db import Notification
36 from rhodecode.model.db import Notification
37
37
38 from rhodecode.model.notification import NotificationModel
38 from rhodecode.model.notification import NotificationModel
39 from rhodecode.lib.auth import LoginRequired, NotAnonymous
39 from rhodecode.lib.auth import LoginRequired, NotAnonymous
40 from rhodecode.lib import helpers as h
40 from rhodecode.lib import helpers as h
41 from rhodecode.model.meta import Session
41 from rhodecode.model.meta import Session
42
42
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46
46
47 class NotificationsController(BaseController):
47 class NotificationsController(BaseController):
48 """REST Controller styled on the Atom Publishing Protocol"""
48 """REST Controller styled on the Atom Publishing Protocol"""
49 # To properly map this controller, ensure your config/routing.py
49 # To properly map this controller, ensure your config/routing.py
50 # file has a resource setup:
50 # file has a resource setup:
51 # map.resource('notification', 'notifications', controller='_admin/notifications',
51 # map.resource('notification', 'notifications', controller='_admin/notifications',
52 # path_prefix='/_admin', name_prefix='_admin_')
52 # path_prefix='/_admin', name_prefix='_admin_')
53
53
54 @LoginRequired()
54 @LoginRequired()
55 @NotAnonymous()
55 @NotAnonymous()
56 def __before__(self):
56 def __before__(self):
57 super(NotificationsController, self).__before__()
57 super(NotificationsController, self).__before__()
58
58
59 def index(self, format='html'):
59 def index(self, format='html'):
60 """GET /_admin/notifications: All items in the collection"""
60 """GET /_admin/notifications: All items in the collection"""
61 # url('notifications')
61 # url('notifications')
62 c.user = self.rhodecode_user
62 c.user = self.rhodecode_user
63 notif = NotificationModel().get_for_user(self.rhodecode_user.user_id,
63 notif = NotificationModel().get_for_user(self.rhodecode_user.user_id,
64 filter_=request.GET)
64 filter_=request.GET.getall('type'))
65 p = int(request.params.get('page', 1))
65 p = int(request.params.get('page', 1))
66 c.notifications = Page(notif, page=p, items_per_page=10)
66 c.notifications = Page(notif, page=p, items_per_page=10)
67 c.pull_request_type = Notification.TYPE_PULL_REQUEST
67 c.pull_request_type = Notification.TYPE_PULL_REQUEST
68 c.comment_type = [Notification.TYPE_CHANGESET_COMMENT,
69 Notification.TYPE_PULL_REQUEST_COMMENT]
68 return render('admin/notifications/notifications.html')
70 return render('admin/notifications/notifications.html')
69
71
70 def mark_all_read(self):
72 def mark_all_read(self):
71 if request.environ.get('HTTP_X_PARTIAL_XHR'):
73 if request.environ.get('HTTP_X_PARTIAL_XHR'):
72 nm = NotificationModel()
74 nm = NotificationModel()
73 # mark all read
75 # mark all read
74 nm.mark_all_read_for_user(self.rhodecode_user.user_id,
76 nm.mark_all_read_for_user(self.rhodecode_user.user_id,
75 filter_=request.GET)
77 filter_=request.GET.getall('type'))
76 Session.commit()
78 Session.commit()
77 c.user = self.rhodecode_user
79 c.user = self.rhodecode_user
78 notif = nm.get_for_user(self.rhodecode_user.user_id,
80 notif = nm.get_for_user(self.rhodecode_user.user_id,
79 filter_=request.GET)
81 filter_=request.GET.getall('type'))
80 c.notifications = Page(notif, page=1, items_per_page=10)
82 c.notifications = Page(notif, page=1, items_per_page=10)
81 return render('admin/notifications/notifications_data.html')
83 return render('admin/notifications/notifications_data.html')
82
84
83 def create(self):
85 def create(self):
84 """POST /_admin/notifications: Create a new item"""
86 """POST /_admin/notifications: Create a new item"""
85 # url('notifications')
87 # url('notifications')
86
88
87 def new(self, format='html'):
89 def new(self, format='html'):
88 """GET /_admin/notifications/new: Form to create a new item"""
90 """GET /_admin/notifications/new: Form to create a new item"""
89 # url('new_notification')
91 # url('new_notification')
90
92
91 def update(self, notification_id):
93 def update(self, notification_id):
92 """PUT /_admin/notifications/id: Update an existing item"""
94 """PUT /_admin/notifications/id: Update an existing item"""
93 # Forms posted to this method should contain a hidden field:
95 # Forms posted to this method should contain a hidden field:
94 # <input type="hidden" name="_method" value="PUT" />
96 # <input type="hidden" name="_method" value="PUT" />
95 # Or using helpers:
97 # Or using helpers:
96 # h.form(url('notification', notification_id=ID),
98 # h.form(url('notification', notification_id=ID),
97 # method='put')
99 # method='put')
98 # url('notification', notification_id=ID)
100 # url('notification', notification_id=ID)
99
101
100 def delete(self, notification_id):
102 def delete(self, notification_id):
101 """DELETE /_admin/notifications/id: Delete an existing item"""
103 """DELETE /_admin/notifications/id: Delete an existing item"""
102 # Forms posted to this method should contain a hidden field:
104 # Forms posted to this method should contain a hidden field:
103 # <input type="hidden" name="_method" value="DELETE" />
105 # <input type="hidden" name="_method" value="DELETE" />
104 # Or using helpers:
106 # Or using helpers:
105 # h.form(url('notification', notification_id=ID),
107 # h.form(url('notification', notification_id=ID),
106 # method='delete')
108 # method='delete')
107 # url('notification', notification_id=ID)
109 # url('notification', notification_id=ID)
108
110
109 try:
111 try:
110 no = Notification.get(notification_id)
112 no = Notification.get(notification_id)
111 owner = lambda: (no.notifications_to_users.user.user_id
113 owner = lambda: (no.notifications_to_users.user.user_id
112 == c.rhodecode_user.user_id)
114 == c.rhodecode_user.user_id)
113 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
115 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
114 NotificationModel().delete(c.rhodecode_user.user_id, no)
116 NotificationModel().delete(c.rhodecode_user.user_id, no)
115 Session.commit()
117 Session.commit()
116 return 'ok'
118 return 'ok'
117 except Exception:
119 except Exception:
118 Session.rollback()
120 Session.rollback()
119 log.error(traceback.format_exc())
121 log.error(traceback.format_exc())
120 return 'fail'
122 return 'fail'
121
123
122 def show(self, notification_id, format='html'):
124 def show(self, notification_id, format='html'):
123 """GET /_admin/notifications/id: Show a specific item"""
125 """GET /_admin/notifications/id: Show a specific item"""
124 # url('notification', notification_id=ID)
126 # url('notification', notification_id=ID)
125 c.user = self.rhodecode_user
127 c.user = self.rhodecode_user
126 no = Notification.get(notification_id)
128 no = Notification.get(notification_id)
127
129
128 owner = lambda: (no.notifications_to_users.user.user_id
130 owner = lambda: (no.notifications_to_users.user.user_id
129 == c.user.user_id)
131 == c.user.user_id)
130 if no and (h.HasPermissionAny('hg.admin', 'repository.admin')() or owner):
132 if no and (h.HasPermissionAny('hg.admin', 'repository.admin')() or owner):
131 unotification = NotificationModel()\
133 unotification = NotificationModel()\
132 .get_user_notification(c.user.user_id, no)
134 .get_user_notification(c.user.user_id, no)
133
135
134 # if this association to user is not valid, we don't want to show
136 # if this association to user is not valid, we don't want to show
135 # this message
137 # this message
136 if unotification:
138 if unotification:
137 if unotification.read is False:
139 if unotification.read is False:
138 unotification.mark_as_read()
140 unotification.mark_as_read()
139 Session.commit()
141 Session.commit()
140 c.notification = no
142 c.notification = no
141
143
142 return render('admin/notifications/show_notification.html')
144 return render('admin/notifications/show_notification.html')
143
145
144 return redirect(url('notifications'))
146 return redirect(url('notifications'))
145
147
146 def edit(self, notification_id, format='html'):
148 def edit(self, notification_id, format='html'):
147 """GET /_admin/notifications/id/edit: Form to edit an existing item"""
149 """GET /_admin/notifications/id/edit: Form to edit an existing item"""
148 # url('edit_notification', notification_id=ID)
150 # url('edit_notification', notification_id=ID)
@@ -1,257 +1,255 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
31
30
32 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
33
32
34 import rhodecode
33 import rhodecode
35 from rhodecode.lib import helpers as h
34 from rhodecode.lib import helpers as h
36 from rhodecode.model import BaseModel
35 from rhodecode.model import BaseModel
37 from rhodecode.model.db import Notification, User, UserNotification
36 from rhodecode.model.db import Notification, User, UserNotification
38 from sqlalchemy.orm import joinedload
39
37
40 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
41
39
42
40
43 class NotificationModel(BaseModel):
41 class NotificationModel(BaseModel):
44
42
45 def __get_notification(self, notification):
43 def __get_notification(self, notification):
46 if isinstance(notification, Notification):
44 if isinstance(notification, Notification):
47 return notification
45 return notification
48 elif isinstance(notification, (int, long)):
46 elif isinstance(notification, (int, long)):
49 return Notification.get(notification)
47 return Notification.get(notification)
50 else:
48 else:
51 if notification:
49 if notification:
52 raise Exception('notification must be int, long or Instance'
50 raise Exception('notification must be int, long or Instance'
53 ' of Notification got %s' % type(notification))
51 ' of Notification got %s' % type(notification))
54
52
55 def create(self, created_by, subject, body, recipients=None,
53 def create(self, created_by, subject, body, recipients=None,
56 type_=Notification.TYPE_MESSAGE, with_email=True,
54 type_=Notification.TYPE_MESSAGE, with_email=True,
57 email_kwargs={}):
55 email_kwargs={}):
58 """
56 """
59
57
60 Creates notification of given type
58 Creates notification of given type
61
59
62 :param created_by: int, str or User instance. User who created this
60 :param created_by: int, str or User instance. User who created this
63 notification
61 notification
64 :param subject:
62 :param subject:
65 :param body:
63 :param body:
66 :param recipients: list of int, str or User objects, when None
64 :param recipients: list of int, str or User objects, when None
67 is given send to all admins
65 is given send to all admins
68 :param type_: type of notification
66 :param type_: type of notification
69 :param with_email: send email with this notification
67 :param with_email: send email with this notification
70 :param email_kwargs: additional dict to pass as args to email template
68 :param email_kwargs: additional dict to pass as args to email template
71 """
69 """
72 from rhodecode.lib.celerylib import tasks, run_task
70 from rhodecode.lib.celerylib import tasks, run_task
73
71
74 if recipients and not getattr(recipients, '__iter__', False):
72 if recipients and not getattr(recipients, '__iter__', False):
75 raise Exception('recipients must be a list of iterable')
73 raise Exception('recipients must be a list of iterable')
76
74
77 created_by_obj = self._get_user(created_by)
75 created_by_obj = self._get_user(created_by)
78
76
79 if recipients:
77 if recipients:
80 recipients_objs = []
78 recipients_objs = []
81 for u in recipients:
79 for u in recipients:
82 obj = self._get_user(u)
80 obj = self._get_user(u)
83 if obj:
81 if obj:
84 recipients_objs.append(obj)
82 recipients_objs.append(obj)
85 recipients_objs = set(recipients_objs)
83 recipients_objs = set(recipients_objs)
86 log.debug('sending notifications %s to %s' % (
84 log.debug('sending notifications %s to %s' % (
87 type_, recipients_objs)
85 type_, recipients_objs)
88 )
86 )
89 else:
87 else:
90 # empty recipients means to all admins
88 # empty recipients means to all admins
91 recipients_objs = User.query().filter(User.admin == True).all()
89 recipients_objs = User.query().filter(User.admin == True).all()
92 log.debug('sending notifications %s to admins: %s' % (
90 log.debug('sending notifications %s to admins: %s' % (
93 type_, recipients_objs)
91 type_, recipients_objs)
94 )
92 )
95 notif = Notification.create(
93 notif = Notification.create(
96 created_by=created_by_obj, subject=subject,
94 created_by=created_by_obj, subject=subject,
97 body=body, recipients=recipients_objs, type_=type_
95 body=body, recipients=recipients_objs, type_=type_
98 )
96 )
99
97
100 if with_email is False:
98 if with_email is False:
101 return notif
99 return notif
102
100
103 #don't send email to person who created this comment
101 #don't send email to person who created this comment
104 rec_objs = set(recipients_objs).difference(set([created_by_obj]))
102 rec_objs = set(recipients_objs).difference(set([created_by_obj]))
105
103
106 # send email with notification to all other participants
104 # send email with notification to all other participants
107 for rec in rec_objs:
105 for rec in rec_objs:
108 email_subject = NotificationModel().make_description(notif, False)
106 email_subject = NotificationModel().make_description(notif, False)
109 type_ = type_
107 type_ = type_
110 email_body = body
108 email_body = body
111 ## this is passed into template
109 ## this is passed into template
112 kwargs = {'subject': subject, 'body': h.rst_w_mentions(body)}
110 kwargs = {'subject': subject, 'body': h.rst_w_mentions(body)}
113 kwargs.update(email_kwargs)
111 kwargs.update(email_kwargs)
114 email_body_html = EmailNotificationModel()\
112 email_body_html = EmailNotificationModel()\
115 .get_email_tmpl(type_, **kwargs)
113 .get_email_tmpl(type_, **kwargs)
116
114
117 run_task(tasks.send_email, rec.email, email_subject, email_body,
115 run_task(tasks.send_email, rec.email, email_subject, email_body,
118 email_body_html)
116 email_body_html)
119
117
120 return notif
118 return notif
121
119
122 def delete(self, user, notification):
120 def delete(self, user, notification):
123 # we don't want to remove actual notification just the assignment
121 # we don't want to remove actual notification just the assignment
124 try:
122 try:
125 notification = self.__get_notification(notification)
123 notification = self.__get_notification(notification)
126 user = self._get_user(user)
124 user = self._get_user(user)
127 if notification and user:
125 if notification and user:
128 obj = UserNotification.query()\
126 obj = UserNotification.query()\
129 .filter(UserNotification.user == user)\
127 .filter(UserNotification.user == user)\
130 .filter(UserNotification.notification
128 .filter(UserNotification.notification
131 == notification)\
129 == notification)\
132 .one()
130 .one()
133 self.sa.delete(obj)
131 self.sa.delete(obj)
134 return True
132 return True
135 except Exception:
133 except Exception:
136 log.error(traceback.format_exc())
134 log.error(traceback.format_exc())
137 raise
135 raise
138
136
139 def get_for_user(self, user, filter_=None):
137 def get_for_user(self, user, filter_=None):
140 """
138 """
141 Get mentions for given user, filter them if filter dict is given
139 Get mentions for given user, filter them if filter dict is given
142
140
143 :param user:
141 :param user:
144 :type user:
142 :type user:
145 :param filter:
143 :param filter:
146 """
144 """
147 user = self._get_user(user)
145 user = self._get_user(user)
148
146
149 q = UserNotification.query()\
147 q = UserNotification.query()\
150 .filter(UserNotification.user == user)\
148 .filter(UserNotification.user == user)\
151 .join((Notification, UserNotification.notification_id ==
149 .join((Notification, UserNotification.notification_id ==
152 Notification.notification_id))
150 Notification.notification_id))
153
151
154 if filter_:
152 if filter_:
155 q = q.filter(Notification.type_ == filter_.get('type'))
153 q = q.filter(Notification.type_.in_(filter_))
156
154
157 return q.all()
155 return q.all()
158
156
159 def mark_all_read_for_user(self, user, filter_=None):
157 def mark_all_read_for_user(self, user, filter_=None):
160 user = self._get_user(user)
158 user = self._get_user(user)
161 q = UserNotification.query()\
159 q = UserNotification.query()\
162 .filter(UserNotification.user == user)\
160 .filter(UserNotification.user == user)\
163 .filter(UserNotification.read == False)\
161 .filter(UserNotification.read == False)\
164 .join((Notification, UserNotification.notification_id ==
162 .join((Notification, UserNotification.notification_id ==
165 Notification.notification_id))
163 Notification.notification_id))
166 if filter_:
164 if filter_:
167 q = q.filter(Notification.type_ == filter_.get('type'))
165 q = q.filter(Notification.type_.in_(filter_))
168
166
169 # this is a little inefficient but sqlalchemy doesn't support
167 # this is a little inefficient but sqlalchemy doesn't support
170 # update on joined tables :(
168 # update on joined tables :(
171 for obj in q.all():
169 for obj in q.all():
172 obj.read = True
170 obj.read = True
173 self.sa.add(obj)
171 self.sa.add(obj)
174
172
175 def get_unread_cnt_for_user(self, user):
173 def get_unread_cnt_for_user(self, user):
176 user = self._get_user(user)
174 user = self._get_user(user)
177 return UserNotification.query()\
175 return UserNotification.query()\
178 .filter(UserNotification.read == False)\
176 .filter(UserNotification.read == False)\
179 .filter(UserNotification.user == user).count()
177 .filter(UserNotification.user == user).count()
180
178
181 def get_unread_for_user(self, user):
179 def get_unread_for_user(self, user):
182 user = self._get_user(user)
180 user = self._get_user(user)
183 return [x.notification for x in UserNotification.query()\
181 return [x.notification for x in UserNotification.query()\
184 .filter(UserNotification.read == False)\
182 .filter(UserNotification.read == False)\
185 .filter(UserNotification.user == user).all()]
183 .filter(UserNotification.user == user).all()]
186
184
187 def get_user_notification(self, user, notification):
185 def get_user_notification(self, user, notification):
188 user = self._get_user(user)
186 user = self._get_user(user)
189 notification = self.__get_notification(notification)
187 notification = self.__get_notification(notification)
190
188
191 return UserNotification.query()\
189 return UserNotification.query()\
192 .filter(UserNotification.notification == notification)\
190 .filter(UserNotification.notification == notification)\
193 .filter(UserNotification.user == user).scalar()
191 .filter(UserNotification.user == user).scalar()
194
192
195 def make_description(self, notification, show_age=True):
193 def make_description(self, notification, show_age=True):
196 """
194 """
197 Creates a human readable description based on properties
195 Creates a human readable description based on properties
198 of notification object
196 of notification object
199 """
197 """
200 #alias
198 #alias
201 _n = notification
199 _n = notification
202 _map = {
200 _map = {
203 _n.TYPE_CHANGESET_COMMENT: _('commented on commit'),
201 _n.TYPE_CHANGESET_COMMENT: _('commented on commit'),
204 _n.TYPE_MESSAGE: _('sent message'),
202 _n.TYPE_MESSAGE: _('sent message'),
205 _n.TYPE_MENTION: _('mentioned you'),
203 _n.TYPE_MENTION: _('mentioned you'),
206 _n.TYPE_REGISTRATION: _('registered in RhodeCode'),
204 _n.TYPE_REGISTRATION: _('registered in RhodeCode'),
207 _n.TYPE_PULL_REQUEST: _('opened new pull request'),
205 _n.TYPE_PULL_REQUEST: _('opened new pull request'),
208 _n.TYPE_PULL_REQUEST_COMMENT: _('commented on pull request')
206 _n.TYPE_PULL_REQUEST_COMMENT: _('commented on pull request')
209 }
207 }
210
208
211 # action == _map string
209 # action == _map string
212 tmpl = "%(user)s %(action)s at %(when)s"
210 tmpl = "%(user)s %(action)s at %(when)s"
213 if show_age:
211 if show_age:
214 when = h.age(notification.created_on)
212 when = h.age(notification.created_on)
215 else:
213 else:
216 when = h.fmt_date(notification.created_on)
214 when = h.fmt_date(notification.created_on)
217
215
218 data = dict(
216 data = dict(
219 user=notification.created_by_user.username,
217 user=notification.created_by_user.username,
220 action=_map[notification.type_], when=when,
218 action=_map[notification.type_], when=when,
221 )
219 )
222 return tmpl % data
220 return tmpl % data
223
221
224
222
225 class EmailNotificationModel(BaseModel):
223 class EmailNotificationModel(BaseModel):
226
224
227 TYPE_CHANGESET_COMMENT = Notification.TYPE_CHANGESET_COMMENT
225 TYPE_CHANGESET_COMMENT = Notification.TYPE_CHANGESET_COMMENT
228 TYPE_PASSWORD_RESET = 'passoword_link'
226 TYPE_PASSWORD_RESET = 'passoword_link'
229 TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
227 TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
230 TYPE_PULL_REQUEST = Notification.TYPE_PULL_REQUEST
228 TYPE_PULL_REQUEST = Notification.TYPE_PULL_REQUEST
231 TYPE_DEFAULT = 'default'
229 TYPE_DEFAULT = 'default'
232
230
233 def __init__(self):
231 def __init__(self):
234 self._template_root = rhodecode.CONFIG['pylons.paths']['templates'][0]
232 self._template_root = rhodecode.CONFIG['pylons.paths']['templates'][0]
235 self._tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
233 self._tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
236
234
237 self.email_types = {
235 self.email_types = {
238 self.TYPE_CHANGESET_COMMENT: 'email_templates/changeset_comment.html',
236 self.TYPE_CHANGESET_COMMENT: 'email_templates/changeset_comment.html',
239 self.TYPE_PASSWORD_RESET: 'email_templates/password_reset.html',
237 self.TYPE_PASSWORD_RESET: 'email_templates/password_reset.html',
240 self.TYPE_REGISTRATION: 'email_templates/registration.html',
238 self.TYPE_REGISTRATION: 'email_templates/registration.html',
241 self.TYPE_DEFAULT: 'email_templates/default.html'
239 self.TYPE_DEFAULT: 'email_templates/default.html'
242 }
240 }
243
241
244 def get_email_tmpl(self, type_, **kwargs):
242 def get_email_tmpl(self, type_, **kwargs):
245 """
243 """
246 return generated template for email based on given type
244 return generated template for email based on given type
247
245
248 :param type_:
246 :param type_:
249 """
247 """
250
248
251 base = self.email_types.get(type_, self.email_types[self.TYPE_DEFAULT])
249 base = self.email_types.get(type_, self.email_types[self.TYPE_DEFAULT])
252 email_template = self._tmpl_lookup.get_template(base)
250 email_template = self._tmpl_lookup.get_template(base)
253 # translator inject
251 # translator inject
254 _kwargs = {'_': _}
252 _kwargs = {'_': _}
255 _kwargs.update(kwargs)
253 _kwargs.update(kwargs)
256 log.debug('rendering tmpl %s with kwargs %s' % (base, _kwargs))
254 log.debug('rendering tmpl %s with kwargs %s' % (base, _kwargs))
257 return email_template.render(**_kwargs)
255 return email_template.render(**_kwargs)
@@ -1,60 +1,58 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('My Notifications')} ${c.rhodecode_user.username} - ${c.rhodecode_name}
5 ${_('My Notifications')} ${c.rhodecode_user.username} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${_('My Notifications')}
9 ${_('My Notifications')}
10 </%def>
10 </%def>
11
11
12 <%def name="page_nav()">
12 <%def name="page_nav()">
13 ${self.menu('admin')}
13 ${self.menu('admin')}
14 </%def>
14 </%def>
15
15
16 <%def name="main()">
16 <%def name="main()">
17 <div class="box">
17 <div class="box">
18 <!-- box / title -->
18 <!-- box / title -->
19 <div class="title">
19 <div class="title">
20 ${self.breadcrumbs()}
20 ${self.breadcrumbs()}
21 ##<ul class="links">
21 ##<ul class="links">
22 ## <li>
22 ## <li>
23 ## <span style="text-transform: uppercase;"><a href="#">${_('Compose message')}</a></span>
23 ## <span style="text-transform: uppercase;"><a href="#">${_('Compose message')}</a></span>
24 ## </li>
24 ## </li>
25 ##</ul>
25 ##</ul>
26 </div>
26 </div>
27 %if c.notifications:
27
28 <div style="padding:14px 18px;text-align: right;float:left">
28 <div style="padding:14px 18px;text-align: right;float:left">
29 <span id='all' class="ui-btn"><a href="${h.url.current()}">${_('All')}</a></span>
29 <span id='all' class="ui-btn"><a href="${h.url.current()}">${_('All')}</a></span>
30 <span id='pull_request' class="ui-btn"><a href="${h.url.current(type=c.comment_type)}">${_('Comments')}</a></span>
30 <span id='pull_request' class="ui-btn"><a href="${h.url.current(type=c.pull_request_type)}">${_('Pull requests')}</a></span>
31 <span id='pull_request' class="ui-btn"><a href="${h.url.current(type=c.pull_request_type)}">${_('Pull requests')}</a></span>
31 </div>
32 </div>
33 %if c.notifications:
32 <div style="padding:14px 18px;text-align: right;float:right">
34 <div style="padding:14px 18px;text-align: right;float:right">
33 <span id='mark_all_read' class="ui-btn">${_('Mark all read')}</span>
35 <span id='mark_all_read' class="ui-btn">${_('Mark all read')}</span>
34 </div>
36 </div>
35 %endif
37 %endif
36 <div id='notification_data'>
38 <div id='notification_data'>
37 <%include file='notifications_data.html'/>
39 <%include file='notifications_data.html'/>
38 </div>
40 </div>
39 </div>
41 </div>
40 <script type="text/javascript">
42 <script type="text/javascript">
41 var url_del = "${url('notification', notification_id='__NOTIFICATION_ID__')}";
43 var url_del = "${url('notification', notification_id='__NOTIFICATION_ID__')}";
42 YUE.on(YUQ('.delete-notification'),'click',function(e){
44 YUE.on(YUQ('.delete-notification'),'click',function(e){
43 var notification_id = e.currentTarget.id;
45 var notification_id = e.currentTarget.id;
44 deleteNotification(url_del,notification_id)
46 deleteNotification(url_del,notification_id)
45 })
47 })
46 YUE.on('mark_all_read','click',function(e){
48 YUE.on('mark_all_read','click',function(e){
47 var url = "${h.url('notifications_mark_all_read', **request.GET)}";
49 var url = "${h.url('notifications_mark_all_read', **request.GET.mixed())}";
48 ypjax(url,'notification_data',function(){
50 ypjax(url,'notification_data',function(){
49 var notification_counter = YUD.get('notification_counter');
50 if(notification_counter){
51 notification_counter.innerHTML=0;
52 }
53 YUE.on(YUQ('.delete-notification'),'click',function(e){
51 YUE.on(YUQ('.delete-notification'),'click',function(e){
54 var notification_id = e.currentTarget.id;
52 var notification_id = e.currentTarget.id;
55 deleteNotification(url_del,notification_id)
53 deleteNotification(url_del,notification_id)
56 })
54 })
57 });
55 });
58 })
56 })
59 </script>
57 </script>
60 </%def>
58 </%def>
General Comments 0
You need to be logged in to leave comments. Login now