##// END OF EJS Templates
fixes #691: Notifications for pull requests: move link to pull request to the top...
marcink -
r3121:3274ba9f beta
parent child Browse files
Show More
@@ -1,277 +1,278 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.notification
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Model for notifications
7 7
8 8
9 9 :created_on: Nov 20, 2011
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26
27 27 import os
28 28 import logging
29 29 import traceback
30 30
31 31 from pylons.i18n.translation import _
32 32
33 33 import rhodecode
34 34 from rhodecode.lib import helpers as h
35 35 from rhodecode.model import BaseModel
36 36 from rhodecode.model.db import Notification, User, UserNotification
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 40
41 41 class NotificationModel(BaseModel):
42 42
43 43 cls = Notification
44 44
45 45 def __get_notification(self, notification):
46 46 if isinstance(notification, Notification):
47 47 return notification
48 48 elif isinstance(notification, (int, long)):
49 49 return Notification.get(notification)
50 50 else:
51 51 if notification:
52 52 raise Exception('notification must be int, long or Instance'
53 53 ' of Notification got %s' % type(notification))
54 54
55 55 def create(self, created_by, subject, body, recipients=None,
56 56 type_=Notification.TYPE_MESSAGE, with_email=True,
57 57 email_kwargs={}):
58 58 """
59 59
60 60 Creates notification of given type
61 61
62 62 :param created_by: int, str or User instance. User who created this
63 63 notification
64 64 :param subject:
65 65 :param body:
66 66 :param recipients: list of int, str or User objects, when None
67 67 is given send to all admins
68 68 :param type_: type of notification
69 69 :param with_email: send email with this notification
70 70 :param email_kwargs: additional dict to pass as args to email template
71 71 """
72 72 from rhodecode.lib.celerylib import tasks, run_task
73 73
74 74 if recipients and not getattr(recipients, '__iter__', False):
75 75 raise Exception('recipients must be a list of iterable')
76 76
77 77 created_by_obj = self._get_user(created_by)
78 78
79 79 if recipients:
80 80 recipients_objs = []
81 81 for u in recipients:
82 82 obj = self._get_user(u)
83 83 if obj:
84 84 recipients_objs.append(obj)
85 85 recipients_objs = set(recipients_objs)
86 86 log.debug('sending notifications %s to %s' % (
87 87 type_, recipients_objs)
88 88 )
89 89 else:
90 90 # empty recipients means to all admins
91 91 recipients_objs = User.query().filter(User.admin == True).all()
92 92 log.debug('sending notifications %s to admins: %s' % (
93 93 type_, recipients_objs)
94 94 )
95 95 notif = Notification.create(
96 96 created_by=created_by_obj, subject=subject,
97 97 body=body, recipients=recipients_objs, type_=type_
98 98 )
99 99
100 100 if with_email is False:
101 101 return notif
102 102
103 103 #don't send email to person who created this comment
104 104 rec_objs = set(recipients_objs).difference(set([created_by_obj]))
105 105
106 106 # send email with notification to all other participants
107 107 for rec in rec_objs:
108 108 email_subject = NotificationModel().make_description(notif, False)
109 109 type_ = type_
110 110 email_body = body
111 111 ## this is passed into template
112 112 kwargs = {'subject': subject, 'body': h.rst_w_mentions(body)}
113 113 kwargs.update(email_kwargs)
114 114 email_body_html = EmailNotificationModel()\
115 115 .get_email_tmpl(type_, **kwargs)
116 116
117 117 run_task(tasks.send_email, rec.email, email_subject, email_body,
118 118 email_body_html)
119 119
120 120 return notif
121 121
122 122 def delete(self, user, notification):
123 123 # we don't want to remove actual notification just the assignment
124 124 try:
125 125 notification = self.__get_notification(notification)
126 126 user = self._get_user(user)
127 127 if notification and user:
128 128 obj = UserNotification.query()\
129 129 .filter(UserNotification.user == user)\
130 130 .filter(UserNotification.notification
131 131 == notification)\
132 132 .one()
133 133 self.sa.delete(obj)
134 134 return True
135 135 except Exception:
136 136 log.error(traceback.format_exc())
137 137 raise
138 138
139 139 def get_for_user(self, user, filter_=None):
140 140 """
141 141 Get mentions for given user, filter them if filter dict is given
142 142
143 143 :param user:
144 144 :type user:
145 145 :param filter:
146 146 """
147 147 user = self._get_user(user)
148 148
149 149 q = UserNotification.query()\
150 150 .filter(UserNotification.user == user)\
151 151 .join((Notification, UserNotification.notification_id ==
152 152 Notification.notification_id))
153 153
154 154 if filter_:
155 155 q = q.filter(Notification.type_.in_(filter_))
156 156
157 157 return q.all()
158 158
159 159 def mark_read(self, user, notification):
160 160 try:
161 161 notification = self.__get_notification(notification)
162 162 user = self._get_user(user)
163 163 if notification and user:
164 164 obj = UserNotification.query()\
165 165 .filter(UserNotification.user == user)\
166 166 .filter(UserNotification.notification
167 167 == notification)\
168 168 .one()
169 169 obj.read = True
170 170 self.sa.add(obj)
171 171 return True
172 172 except Exception:
173 173 log.error(traceback.format_exc())
174 174 raise
175 175
176 176 def mark_all_read_for_user(self, user, filter_=None):
177 177 user = self._get_user(user)
178 178 q = UserNotification.query()\
179 179 .filter(UserNotification.user == user)\
180 180 .filter(UserNotification.read == False)\
181 181 .join((Notification, UserNotification.notification_id ==
182 182 Notification.notification_id))
183 183 if filter_:
184 184 q = q.filter(Notification.type_.in_(filter_))
185 185
186 186 # this is a little inefficient but sqlalchemy doesn't support
187 187 # update on joined tables :(
188 188 for obj in q.all():
189 189 obj.read = True
190 190 self.sa.add(obj)
191 191
192 192 def get_unread_cnt_for_user(self, user):
193 193 user = self._get_user(user)
194 194 return UserNotification.query()\
195 195 .filter(UserNotification.read == False)\
196 196 .filter(UserNotification.user == user).count()
197 197
198 198 def get_unread_for_user(self, user):
199 199 user = self._get_user(user)
200 200 return [x.notification for x in UserNotification.query()\
201 201 .filter(UserNotification.read == False)\
202 202 .filter(UserNotification.user == user).all()]
203 203
204 204 def get_user_notification(self, user, notification):
205 205 user = self._get_user(user)
206 206 notification = self.__get_notification(notification)
207 207
208 208 return UserNotification.query()\
209 209 .filter(UserNotification.notification == notification)\
210 210 .filter(UserNotification.user == user).scalar()
211 211
212 212 def make_description(self, notification, show_age=True):
213 213 """
214 214 Creates a human readable description based on properties
215 215 of notification object
216 216 """
217 217 #alias
218 218 _n = notification
219 219 _map = {
220 220 _n.TYPE_CHANGESET_COMMENT: _('commented on commit at %(when)s'),
221 221 _n.TYPE_MESSAGE: _('sent message at %(when)s'),
222 222 _n.TYPE_MENTION: _('mentioned you at %(when)s'),
223 223 _n.TYPE_REGISTRATION: _('registered in RhodeCode at %(when)s'),
224 224 _n.TYPE_PULL_REQUEST: _('opened new pull request at %(when)s'),
225 225 _n.TYPE_PULL_REQUEST_COMMENT: _('commented on pull request at %(when)s')
226 226 }
227 227
228 228 # action == _map string
229 229 tmpl = "%(user)s %(action)s "
230 230 if show_age:
231 231 when = h.age(notification.created_on)
232 232 else:
233 233 when = h.fmt_date(notification.created_on)
234 234
235 235 data = dict(
236 236 user=notification.created_by_user.username,
237 237 action=_map[notification.type_]
238 238 )
239 239 return (tmpl % data) % {'when': when}
240 240
241 241
242 242 class EmailNotificationModel(BaseModel):
243 243
244 244 TYPE_CHANGESET_COMMENT = Notification.TYPE_CHANGESET_COMMENT
245 245 TYPE_PASSWORD_RESET = 'passoword_link'
246 246 TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
247 247 TYPE_PULL_REQUEST = Notification.TYPE_PULL_REQUEST
248 248 TYPE_PULL_REQUEST_COMMENT = Notification.TYPE_PULL_REQUEST_COMMENT
249 249 TYPE_DEFAULT = 'default'
250 250
251 251 def __init__(self):
252 252 self._template_root = rhodecode.CONFIG['pylons.paths']['templates'][0]
253 253 self._tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
254 254
255 255 self.email_types = {
256 256 self.TYPE_CHANGESET_COMMENT: 'email_templates/changeset_comment.html',
257 257 self.TYPE_PASSWORD_RESET: 'email_templates/password_reset.html',
258 258 self.TYPE_REGISTRATION: 'email_templates/registration.html',
259 259 self.TYPE_DEFAULT: 'email_templates/default.html',
260 260 self.TYPE_PULL_REQUEST: 'email_templates/pull_request.html',
261 261 self.TYPE_PULL_REQUEST_COMMENT: 'email_templates/pull_request_comment.html',
262 262 }
263 263
264 264 def get_email_tmpl(self, type_, **kwargs):
265 265 """
266 266 return generated template for email based on given type
267 267
268 268 :param type_:
269 269 """
270 270
271 271 base = self.email_types.get(type_, self.email_types[self.TYPE_DEFAULT])
272 272 email_template = self._tmpl_lookup.get_template(base)
273 # translator inject
274 _kwargs = {'_': _}
273 # translator and helpers inject
274 _kwargs = {'_': _,
275 'h': h}
275 276 _kwargs.update(kwargs)
276 277 log.debug('rendering tmpl %s with kwargs %s' % (base, _kwargs))
277 278 return email_template.render(**_kwargs)
@@ -1,18 +1,19 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="main.html"/>
3 3
4 ${_('User %s opened pull request for repository %s and wants you to review changes.') % ('<b>%s</b>' % pr_user_created,pr_repo_url)}
4 ${_('User %s opened pull request for repository %s and wants you to review changes.') % (('<b>%s</b>' % pr_user_created),pr_repo_url) |n}
5 5 <div>${_('title')}: ${pr_title}</div>
6 6 <div>${_('description')}:</div>
7 <div>${_('View this pull request here')}: ${pr_url}</div>
7 8 <p>
8 9 ${body}
9 10 </p>
10 11
11 12 <div>${_('revisions for reviewing')}</div>
12 13 <ul>
13 14 %for r in pr_revisions:
14 15 <li>${r}</li>
15 16 %endfor
16 17 </ul>
17 18
18 ${_('View this pull request here')}: ${pr_url}
19
General Comments 0
You need to be logged in to leave comments. Login now