Show More
@@ -0,0 +1,84 b'' | |||||
|
1 | import logging | |||
|
2 | ||||
|
3 | from pylons import tmpl_context as c | |||
|
4 | ||||
|
5 | from rhodecode.lib.base import BaseController, render | |||
|
6 | from rhodecode.model.db import Notification | |||
|
7 | ||||
|
8 | from rhodecode.model.notification import NotificationModel | |||
|
9 | from rhodecode.lib.auth import LoginRequired | |||
|
10 | from rhodecode.lib import helpers as h | |||
|
11 | ||||
|
12 | log = logging.getLogger(__name__) | |||
|
13 | ||||
|
14 | class NotificationsController(BaseController): | |||
|
15 | """REST Controller styled on the Atom Publishing Protocol""" | |||
|
16 | # To properly map this controller, ensure your config/routing.py | |||
|
17 | # file has a resource setup: | |||
|
18 | # map.resource('notification', 'notifications', controller='_admin/notifications', | |||
|
19 | # path_prefix='/_admin', name_prefix='_admin_') | |||
|
20 | ||||
|
21 | @LoginRequired() | |||
|
22 | def __before__(self): | |||
|
23 | super(NotificationsController, self).__before__() | |||
|
24 | ||||
|
25 | ||||
|
26 | def index(self, format='html'): | |||
|
27 | """GET /_admin/notifications: All items in the collection""" | |||
|
28 | # url('notifications') | |||
|
29 | c.user = self.rhodecode_user | |||
|
30 | c.notifications = NotificationModel()\ | |||
|
31 | .get_for_user(self.rhodecode_user.user_id) | |||
|
32 | return render('admin/notifications/notifications.html') | |||
|
33 | ||||
|
34 | def create(self): | |||
|
35 | """POST /_admin/notifications: Create a new item""" | |||
|
36 | # url('notifications') | |||
|
37 | ||||
|
38 | def new(self, format='html'): | |||
|
39 | """GET /_admin/notifications/new: Form to create a new item""" | |||
|
40 | # url('new_notification') | |||
|
41 | ||||
|
42 | def update(self, notification_id): | |||
|
43 | """PUT /_admin/notifications/id: Update an existing item""" | |||
|
44 | # Forms posted to this method should contain a hidden field: | |||
|
45 | # <input type="hidden" name="_method" value="PUT" /> | |||
|
46 | # Or using helpers: | |||
|
47 | # h.form(url('notification', notification_id=ID), | |||
|
48 | # method='put') | |||
|
49 | # url('notification', notification_id=ID) | |||
|
50 | ||||
|
51 | def delete(self, notification_id): | |||
|
52 | """DELETE /_admin/notifications/id: Delete an existing item""" | |||
|
53 | # Forms posted to this method should contain a hidden field: | |||
|
54 | # <input type="hidden" name="_method" value="DELETE" /> | |||
|
55 | # Or using helpers: | |||
|
56 | # h.form(url('notification', notification_id=ID), | |||
|
57 | # method='delete') | |||
|
58 | # url('notification', notification_id=ID) | |||
|
59 | ||||
|
60 | no = Notification.get(notification_id) | |||
|
61 | owner = lambda: no.notifications_to_users.user.user_id == c.rhodecode_user.user_id | |||
|
62 | if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner: | |||
|
63 | NotificationModel().delete(notification_id) | |||
|
64 | return 'ok' | |||
|
65 | return 'fail' | |||
|
66 | ||||
|
67 | def show(self, notification_id, format='html'): | |||
|
68 | """GET /_admin/notifications/id: Show a specific item""" | |||
|
69 | # url('notification', notification_id=ID) | |||
|
70 | c.user = self.rhodecode_user | |||
|
71 | c.notification = Notification.get(notification_id) | |||
|
72 | ||||
|
73 | unotification = NotificationModel()\ | |||
|
74 | .get_user_notification(c.user.user_id, | |||
|
75 | c.notification) | |||
|
76 | ||||
|
77 | if unotification.read is False: | |||
|
78 | unotification.mark_as_read() | |||
|
79 | ||||
|
80 | return render('admin/notifications/show_notification.html') | |||
|
81 | ||||
|
82 | def edit(self, notification_id, format='html'): | |||
|
83 | """GET /_admin/notifications/id/edit: Form to edit an existing item""" | |||
|
84 | # url('edit_notification', notification_id=ID) |
@@ -0,0 +1,51 b'' | |||||
|
1 | ## -*- coding: utf-8 -*- | |||
|
2 | <%inherit file="/base/base.html"/> | |||
|
3 | ||||
|
4 | <%def name="title()"> | |||
|
5 | ${_('Show notification')} ${c.rhodecode_user.username} - ${c.rhodecode_name} | |||
|
6 | </%def> | |||
|
7 | ||||
|
8 | <%def name="breadcrumbs_links()"> | |||
|
9 | ${h.link_to(_('Notifications'),h.url('notifications'))} | |||
|
10 | » | |||
|
11 | ${_('Show notification')} | |||
|
12 | </%def> | |||
|
13 | ||||
|
14 | <%def name="page_nav()"> | |||
|
15 | ${self.menu('admin')} | |||
|
16 | </%def> | |||
|
17 | ||||
|
18 | <%def name="main()"> | |||
|
19 | <div class="box"> | |||
|
20 | <!-- box / title --> | |||
|
21 | <div class="title"> | |||
|
22 | ${self.breadcrumbs()} | |||
|
23 | <ul class="links"> | |||
|
24 | <li> | |||
|
25 | <span style="text-transform: uppercase;"><a href="#">${_('Compose message')}</a></span> | |||
|
26 | </li> | |||
|
27 | </ul> | |||
|
28 | </div> | |||
|
29 | <div class="table"> | |||
|
30 | <div class="notification-header"> | |||
|
31 | <div class="gravatar"> | |||
|
32 | <img alt="gravatar" src="${h.gravatar_url(h.email(c.notification.created_by_user.email),24)}"/> | |||
|
33 | </div> | |||
|
34 | <div class="desc"> | |||
|
35 | ${c.notification.description} | |||
|
36 | </div> | |||
|
37 | <div class="delete-notifications"> | |||
|
38 | <span id="${c.notification.notification_id}" class="delete_icon action"></span> | |||
|
39 | </div> | |||
|
40 | </div> | |||
|
41 | <div>${h.rst(c.notification.body)}</div> | |||
|
42 | </div> | |||
|
43 | </div> | |||
|
44 | <script type="text/javascript"> | |||
|
45 | var url = "${url('notification', notification_id='__NOTIFICATION_ID__')}"; | |||
|
46 | YUE.on(YUQ('.delete-notification'),'click',function(e){ | |||
|
47 | var notification_id = e.currentTarget.id; | |||
|
48 | deleteNotification(url,notification_id) | |||
|
49 | }) | |||
|
50 | </script> | |||
|
51 | </%def> |
@@ -0,0 +1,117 b'' | |||||
|
1 | from rhodecode.tests import * | |||
|
2 | from rhodecode.model.db import Notification, User, UserNotification | |||
|
3 | ||||
|
4 | from rhodecode.model.user import UserModel | |||
|
5 | from rhodecode.model.notification import NotificationModel | |||
|
6 | ||||
|
7 | class TestNotificationsController(TestController): | |||
|
8 | ||||
|
9 | def test_index(self): | |||
|
10 | self.log_user() | |||
|
11 | ||||
|
12 | ||||
|
13 | u1 = UserModel().create_or_update(username='u1', password='qweqwe', | |||
|
14 | email='u1@rhodecode.org', | |||
|
15 | name='u1', lastname='u1').user_id | |||
|
16 | u2 = UserModel().create_or_update(username='u2', password='qweqwe', | |||
|
17 | email='u2@rhodecode.org', | |||
|
18 | name='u2', lastname='u2').user_id | |||
|
19 | ||||
|
20 | response = self.app.get(url('notifications')) | |||
|
21 | self.assertTrue('''<div class="table">No notifications here yet</div>''' | |||
|
22 | in response.body) | |||
|
23 | ||||
|
24 | cur_user = self._get_logged_user() | |||
|
25 | ||||
|
26 | NotificationModel().create(created_by=u1, subject=u'test', | |||
|
27 | body=u'notification_1', | |||
|
28 | recipients=[cur_user]) | |||
|
29 | response = self.app.get(url('notifications')) | |||
|
30 | ||||
|
31 | self.assertTrue(u'notification_1' in response.body) | |||
|
32 | ||||
|
33 | User.delete(u1) | |||
|
34 | User.delete(u2) | |||
|
35 | ||||
|
36 | # def test_index_as_xml(self): | |||
|
37 | # response = self.app.get(url('formatted_notifications', format='xml')) | |||
|
38 | # | |||
|
39 | # def test_create(self): | |||
|
40 | # response = self.app.post(url('notifications')) | |||
|
41 | # | |||
|
42 | # def test_new(self): | |||
|
43 | # response = self.app.get(url('new_notification')) | |||
|
44 | # | |||
|
45 | # def test_new_as_xml(self): | |||
|
46 | # response = self.app.get(url('formatted_new_notification', format='xml')) | |||
|
47 | # | |||
|
48 | # def test_update(self): | |||
|
49 | # response = self.app.put(url('notification', notification_id=1)) | |||
|
50 | # | |||
|
51 | # def test_update_browser_fakeout(self): | |||
|
52 | # response = self.app.post(url('notification', notification_id=1), params=dict(_method='put')) | |||
|
53 | ||||
|
54 | def test_delete(self): | |||
|
55 | self.log_user() | |||
|
56 | cur_user = self._get_logged_user() | |||
|
57 | ||||
|
58 | u1 = UserModel().create_or_update(username='u1', password='qweqwe', | |||
|
59 | email='u1@rhodecode.org', | |||
|
60 | name='u1', lastname='u1') | |||
|
61 | u2 = UserModel().create_or_update(username='u2', password='qweqwe', | |||
|
62 | email='u2@rhodecode.org', | |||
|
63 | name='u2', lastname='u2') | |||
|
64 | ||||
|
65 | # make two notifications | |||
|
66 | notification = NotificationModel().create(created_by=cur_user, | |||
|
67 | subject=u'test', | |||
|
68 | body=u'hi there', | |||
|
69 | recipients=[cur_user, u1, u2]) | |||
|
70 | ||||
|
71 | u1 = User.get(u1.user_id) | |||
|
72 | u2 = User.get(u2.user_id) | |||
|
73 | ||||
|
74 | # check DB | |||
|
75 | self.assertEqual(u1.notifications, [notification]) | |||
|
76 | self.assertEqual(u2.notifications, [notification]) | |||
|
77 | cur_usr_id = cur_user.user_id | |||
|
78 | response = self.app.delete(url('notification', | |||
|
79 | notification_id=cur_usr_id)) | |||
|
80 | ||||
|
81 | cur_user = self._get_logged_user() | |||
|
82 | self.assertEqual(cur_user.notifications, []) | |||
|
83 | ||||
|
84 | User.delete(u1.user_id) | |||
|
85 | User.delete(u2.user_id) | |||
|
86 | ||||
|
87 | ||||
|
88 | # def test_delete_browser_fakeout(self): | |||
|
89 | # response = self.app.post(url('notification', notification_id=1), params=dict(_method='delete')) | |||
|
90 | ||||
|
91 | def test_show(self): | |||
|
92 | self.log_user() | |||
|
93 | cur_user = self._get_logged_user() | |||
|
94 | u1 = UserModel().create_or_update(username='u1', password='qweqwe', | |||
|
95 | email='u1@rhodecode.org', | |||
|
96 | name='u1', lastname='u1') | |||
|
97 | u2 = UserModel().create_or_update(username='u2', password='qweqwe', | |||
|
98 | email='u2@rhodecode.org', | |||
|
99 | name='u2', lastname='u2') | |||
|
100 | ||||
|
101 | notification = NotificationModel().create(created_by=cur_user, | |||
|
102 | subject='test', | |||
|
103 | body='hi there', | |||
|
104 | recipients=[cur_user, u1, u2]) | |||
|
105 | ||||
|
106 | response = self.app.get(url('notification', | |||
|
107 | notification_id=notification.notification_id)) | |||
|
108 | ||||
|
109 | # def test_show_as_xml(self): | |||
|
110 | # response = self.app.get(url('formatted_notification', notification_id=1, format='xml')) | |||
|
111 | # | |||
|
112 | # def test_edit(self): | |||
|
113 | # response = self.app.get(url('edit_notification', notification_id=1)) | |||
|
114 | # | |||
|
115 | # def test_edit_as_xml(self): | |||
|
116 | # response = self.app.get(url('formatted_edit_notification', notification_id=1, format='xml')) | |||
|
117 |
@@ -62,8 +62,8 b' def make_map(config):' | |||||
62 | rmap.connect('home', '/', controller='home', action='index') |
|
62 | rmap.connect('home', '/', controller='home', action='index') | |
63 | rmap.connect('repo_switcher', '/repos', controller='home', |
|
63 | rmap.connect('repo_switcher', '/repos', controller='home', | |
64 | action='repo_switcher') |
|
64 | action='repo_switcher') | |
65 |
rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*}', |
|
65 | rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*}', | |
66 |
controller='home',action='branch_tag_switcher') |
|
66 | controller='home', action='branch_tag_switcher') | |
67 | rmap.connect('bugtracker', |
|
67 | rmap.connect('bugtracker', | |
68 | "http://bitbucket.org/marcinkuzminski/rhodecode/issues", |
|
68 | "http://bitbucket.org/marcinkuzminski/rhodecode/issues", | |
69 | _static=True) |
|
69 | _static=True) | |
@@ -267,14 +267,41 b' def make_map(config):' | |||||
267 | action="show", conditions=dict(method=["GET"])) |
|
267 | action="show", conditions=dict(method=["GET"])) | |
268 | m.connect("admin_settings_my_account", "/my_account", |
|
268 | m.connect("admin_settings_my_account", "/my_account", | |
269 | action="my_account", conditions=dict(method=["GET"])) |
|
269 | action="my_account", conditions=dict(method=["GET"])) | |
270 | m.connect("admin_settings_notifications", "/notifications", |
|
|||
271 | action="notifications", conditions=dict(method=["GET"])) |
|
|||
272 | m.connect("admin_settings_my_account_update", "/my_account_update", |
|
270 | m.connect("admin_settings_my_account_update", "/my_account_update", | |
273 | action="my_account_update", conditions=dict(method=["PUT"])) |
|
271 | action="my_account_update", conditions=dict(method=["PUT"])) | |
274 | m.connect("admin_settings_create_repository", "/create_repository", |
|
272 | m.connect("admin_settings_create_repository", "/create_repository", | |
275 | action="create_repository", conditions=dict(method=["GET"])) |
|
273 | action="create_repository", conditions=dict(method=["GET"])) | |
276 |
|
274 | |||
277 |
|
275 | |||
|
276 | #NOTIFICATION REST ROUTES | |||
|
277 | with rmap.submapper(path_prefix=ADMIN_PREFIX, | |||
|
278 | controller='admin/notifications') as m: | |||
|
279 | m.connect("notifications", "/notifications", | |||
|
280 | action="create", conditions=dict(method=["POST"])) | |||
|
281 | m.connect("notifications", "/notifications", | |||
|
282 | action="index", conditions=dict(method=["GET"])) | |||
|
283 | m.connect("formatted_notifications", "/notifications.{format}", | |||
|
284 | action="index", conditions=dict(method=["GET"])) | |||
|
285 | m.connect("new_notification", "/notifications/new", | |||
|
286 | action="new", conditions=dict(method=["GET"])) | |||
|
287 | m.connect("formatted_new_notification", "/notifications/new.{format}", | |||
|
288 | action="new", conditions=dict(method=["GET"])) | |||
|
289 | m.connect("/notification/{notification_id}", | |||
|
290 | action="update", conditions=dict(method=["PUT"])) | |||
|
291 | m.connect("/notification/{notification_id}", | |||
|
292 | action="delete", conditions=dict(method=["DELETE"])) | |||
|
293 | m.connect("edit_notification", "/notification/{notification_id}/edit", | |||
|
294 | action="edit", conditions=dict(method=["GET"])) | |||
|
295 | m.connect("formatted_edit_notification", | |||
|
296 | "/notification/{notification_id}.{format}/edit", | |||
|
297 | action="edit", conditions=dict(method=["GET"])) | |||
|
298 | m.connect("notification", "/notification/{notification_id}", | |||
|
299 | action="show", conditions=dict(method=["GET"])) | |||
|
300 | m.connect("formatted_notification", "/notifications/{notification_id}.{format}", | |||
|
301 | action="show", conditions=dict(method=["GET"])) | |||
|
302 | ||||
|
303 | ||||
|
304 | ||||
278 | #ADMIN MAIN PAGES |
|
305 | #ADMIN MAIN PAGES | |
279 | with rmap.submapper(path_prefix=ADMIN_PREFIX, |
|
306 | with rmap.submapper(path_prefix=ADMIN_PREFIX, | |
280 | controller='admin/admin') as m: |
|
307 | controller='admin/admin') as m: | |
@@ -357,7 +384,7 b' def make_map(config):' | |||||
357 |
|
384 | |||
358 | rmap.connect('changeset_comment_delete', '/{repo_name:.*}/changeset/comment/{comment_id}/delete', |
|
385 | rmap.connect('changeset_comment_delete', '/{repo_name:.*}/changeset/comment/{comment_id}/delete', | |
359 | controller='changeset', action='delete_comment', |
|
386 | controller='changeset', action='delete_comment', | |
360 |
conditions |
|
387 | conditions=dict(function=check_repo, method=["DELETE"])) | |
361 |
|
388 | |||
362 | rmap.connect('raw_changeset_home', |
|
389 | rmap.connect('raw_changeset_home', | |
363 | '/{repo_name:.*}/raw-changeset/{revision}', |
|
390 | '/{repo_name:.*}/raw-changeset/{revision}', |
@@ -372,14 +372,6 b' class SettingsController(BaseController)' | |||||
372 |
|
372 | |||
373 | return redirect(url('my_account')) |
|
373 | return redirect(url('my_account')) | |
374 |
|
374 | |||
375 |
|
||||
376 | @NotAnonymous() |
|
|||
377 | def notifications(self): |
|
|||
378 | c.user = User.get(self.rhodecode_user.user_id) |
|
|||
379 | c.notifications = NotificationModel().get_for_user(c.user.user_id) |
|
|||
380 | return render('admin/users/notifications.html'), |
|
|||
381 |
|
||||
382 |
|
||||
383 | @NotAnonymous() |
|
375 | @NotAnonymous() | |
384 | @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository') |
|
376 | @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository') | |
385 | def create_repository(self): |
|
377 | def create_repository(self): |
@@ -32,8 +32,7 b' from pylons.controllers.util import redi' | |||||
32 | from pylons.decorators import jsonify |
|
32 | from pylons.decorators import jsonify | |
33 |
|
33 | |||
34 | import rhodecode.lib.helpers as h |
|
34 | import rhodecode.lib.helpers as h | |
35 |
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator |
|
35 | from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator | |
36 | NotAnonymous |
|
|||
37 | from rhodecode.lib.base import BaseRepoController, render |
|
36 | from rhodecode.lib.base import BaseRepoController, render | |
38 | from rhodecode.lib.utils import EmptyChangeset |
|
37 | from rhodecode.lib.utils import EmptyChangeset | |
39 | from rhodecode.lib.compat import OrderedDict |
|
38 | from rhodecode.lib.compat import OrderedDict | |
@@ -274,13 +273,12 b' class ChangesetController(BaseRepoContro' | |||||
274 | return render('changeset/raw_changeset.html') |
|
273 | return render('changeset/raw_changeset.html') | |
275 |
|
274 | |||
276 | def comment(self, repo_name, revision): |
|
275 | def comment(self, repo_name, revision): | |
277 | ccmodel = ChangesetCommentsModel() |
|
276 | ChangesetCommentsModel().create(text=request.POST.get('text'), | |
278 |
|
277 | repo_id=c.rhodecode_db_repo.repo_id, | ||
279 | ccmodel.create(text=request.POST.get('text'), |
|
278 | user_id=c.rhodecode_user.user_id, | |
280 | repo_id=c.rhodecode_db_repo.repo_id, |
|
279 | revision=revision, | |
281 | user_id=c.rhodecode_user.user_id, |
|
280 | f_path=request.POST.get('f_path'), | |
282 |
|
|
281 | line_no=request.POST.get('line')) | |
283 | line_no=request.POST.get('line')) |
|
|||
284 |
|
282 | |||
285 | return redirect(h.url('changeset_home', repo_name=repo_name, |
|
283 | return redirect(h.url('changeset_home', repo_name=repo_name, | |
286 | revision=revision)) |
|
284 | revision=revision)) | |
@@ -288,8 +286,8 b' class ChangesetController(BaseRepoContro' | |||||
288 | @jsonify |
|
286 | @jsonify | |
289 | def delete_comment(self, comment_id): |
|
287 | def delete_comment(self, comment_id): | |
290 | co = ChangesetComment.get(comment_id) |
|
288 | co = ChangesetComment.get(comment_id) | |
291 | if (h.HasPermissionAny('hg.admin', 'repository.admin')() or |
|
289 | owner = lambda : co.author.user_id == c.rhodecode_user.user_id | |
292 | co.author.user_id == c.rhodecode_user.user_id): |
|
290 | if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner: | |
293 | ccmodel = ChangesetCommentsModel() |
|
291 | ccmodel = ChangesetCommentsModel() | |
294 | ccmodel.delete(comment_id=comment_id) |
|
292 | ccmodel.delete(comment_id=comment_id) | |
295 | return True |
|
293 | return True |
@@ -23,13 +23,16 b'' | |||||
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 |
|
26 | import re | ||
27 | import logging |
|
27 | import logging | |
28 | import traceback |
|
28 | import traceback | |
29 |
|
29 | |||
|
30 | from pylons.i18n.translation import _ | |||
|
31 | from sqlalchemy.util.compat import defaultdict | |||
|
32 | ||||
|
33 | from rhodecode.lib import helpers as h | |||
30 | from rhodecode.model import BaseModel |
|
34 | from rhodecode.model import BaseModel | |
31 | from rhodecode.model.db import ChangesetComment, User, Notification |
|
35 | from rhodecode.model.db import ChangesetComment, User, Repository, Notification | |
32 | from sqlalchemy.util.compat import defaultdict |
|
|||
33 | from rhodecode.model.notification import NotificationModel |
|
36 | from rhodecode.model.notification import NotificationModel | |
34 |
|
37 | |||
35 | log = logging.getLogger(__name__) |
|
38 | log = logging.getLogger(__name__) | |
@@ -38,6 +41,15 b' log = logging.getLogger(__name__)' | |||||
38 | class ChangesetCommentsModel(BaseModel): |
|
41 | class ChangesetCommentsModel(BaseModel): | |
39 |
|
42 | |||
40 |
|
43 | |||
|
44 | def _extract_mentions(self, s): | |||
|
45 | usrs = [] | |||
|
46 | for username in re.findall(r'(?:^@|\s@)(\w+)', s): | |||
|
47 | user_obj = User.get_by_username(username, case_insensitive=True) | |||
|
48 | if user_obj: | |||
|
49 | usrs.append(user_obj) | |||
|
50 | ||||
|
51 | return usrs | |||
|
52 | ||||
41 | def create(self, text, repo_id, user_id, revision, f_path=None, |
|
53 | def create(self, text, repo_id, user_id, revision, f_path=None, | |
42 | line_no=None): |
|
54 | line_no=None): | |
43 | """ |
|
55 | """ | |
@@ -51,8 +63,10 b' class ChangesetCommentsModel(BaseModel):' | |||||
51 | :param line_no: |
|
63 | :param line_no: | |
52 | """ |
|
64 | """ | |
53 | if text: |
|
65 | if text: | |
|
66 | repo = Repository.get(repo_id) | |||
|
67 | desc = repo.scm_instance.get_changeset(revision).message | |||
54 | comment = ChangesetComment() |
|
68 | comment = ChangesetComment() | |
55 |
comment.repo |
|
69 | comment.repo = repo | |
56 | comment.user_id = user_id |
|
70 | comment.user_id = user_id | |
57 | comment.revision = revision |
|
71 | comment.revision = revision | |
58 | comment.text = text |
|
72 | comment.text = text | |
@@ -60,18 +74,26 b' class ChangesetCommentsModel(BaseModel):' | |||||
60 | comment.line_no = line_no |
|
74 | comment.line_no = line_no | |
61 |
|
75 | |||
62 | self.sa.add(comment) |
|
76 | self.sa.add(comment) | |
63 |
self.sa. |
|
77 | self.sa.flush() | |
64 |
|
78 | |||
65 | # make notification |
|
79 | # make notification | |
66 | usr = User.get(user_id) |
|
80 | line = '' | |
67 | subj = 'User %s commented on %s' % (usr.username, revision) |
|
81 | if line_no: | |
|
82 | line = _('on line %s') % line_no | |||
|
83 | subj = h.link_to('Re commit: %(commit_desc)s %(line)s' % \ | |||
|
84 | {'commit_desc':desc,'line':line}, | |||
|
85 | h.url('changeset_home', repo_name=repo.repo_name, | |||
|
86 | revision = revision, | |||
|
87 | anchor = 'comment-%s' % comment.comment_id | |||
|
88 | ) | |||
|
89 | ) | |||
68 | body = text |
|
90 | body = text | |
69 | recipients = ChangesetComment.get_users(revision=revision) |
|
91 | recipients = ChangesetComment.get_users(revision=revision) | |
|
92 | recipients += self._extract_mentions(body) | |||
70 | NotificationModel().create(created_by=user_id, subject=subj, |
|
93 | NotificationModel().create(created_by=user_id, subject=subj, | |
71 | body = body, recipients = recipients, |
|
94 | body = body, recipients = recipients, | |
72 | type_ = Notification.TYPE_CHANGESET_COMMENT) |
|
95 | type_ = Notification.TYPE_CHANGESET_COMMENT) | |
73 |
|
96 | |||
74 |
|
||||
75 | return comment |
|
97 | return comment | |
76 |
|
98 | |||
77 | def delete(self, comment_id): |
|
99 | def delete(self, comment_id): |
@@ -49,7 +49,6 b' from rhodecode.lib.caching_query import ' | |||||
49 | from rhodecode.model.meta import Base, Session |
|
49 | from rhodecode.model.meta import Base, Session | |
50 |
|
50 | |||
51 |
|
51 | |||
52 |
|
||||
53 | log = logging.getLogger(__name__) |
|
52 | log = logging.getLogger(__name__) | |
54 |
|
53 | |||
55 | #============================================================================== |
|
54 | #============================================================================== | |
@@ -286,7 +285,9 b' class User(Base, BaseModel):' | |||||
286 |
|
285 | |||
287 | group_member = relationship('UsersGroupMember', cascade='all') |
|
286 | group_member = relationship('UsersGroupMember', cascade='all') | |
288 |
|
287 | |||
289 |
notifications = relationship('Notification', |
|
288 | notifications = relationship('Notification', | |
|
289 | secondary='user_to_notification', | |||
|
290 | order_by=lambda :Notification.created_on.desc()) | |||
290 |
|
291 | |||
291 | @property |
|
292 | @property | |
292 | def full_contact(self): |
|
293 | def full_contact(self): | |
@@ -301,11 +302,9 b' class User(Base, BaseModel):' | |||||
301 | return self.admin |
|
302 | return self.admin | |
302 |
|
303 | |||
303 | def __repr__(self): |
|
304 | def __repr__(self): | |
304 | try: |
|
305 | return "<%s('id:%s:%s')>" % (self.__class__.__name__, | |
305 | return "<%s('id:%s:%s')>" % (self.__class__.__name__, |
|
306 | self.user_id, self.username) | |
306 | self.user_id, self.username) |
|
307 | ||
307 | except: |
|
|||
308 | return self.__class__.__name__ |
|
|||
309 |
|
308 | |||
310 | @classmethod |
|
309 | @classmethod | |
311 | def get_by_username(cls, username, case_insensitive=False, cache=False): |
|
310 | def get_by_username(cls, username, case_insensitive=False, cache=False): | |
@@ -336,6 +335,7 b' class User(Base, BaseModel):' | |||||
336 | Session.commit() |
|
335 | Session.commit() | |
337 | log.debug('updated user %s lastlogin', self.username) |
|
336 | log.debug('updated user %s lastlogin', self.username) | |
338 |
|
337 | |||
|
338 | ||||
339 | class UserLog(Base, BaseModel): |
|
339 | class UserLog(Base, BaseModel): | |
340 | __tablename__ = 'user_logs' |
|
340 | __tablename__ = 'user_logs' | |
341 | __table_args__ = {'extend_existing':True} |
|
341 | __table_args__ = {'extend_existing':True} | |
@@ -1131,9 +1131,9 b' class Notification(Base, BaseModel):' | |||||
1131 | __tablename__ = 'notifications' |
|
1131 | __tablename__ = 'notifications' | |
1132 | __table_args__ = ({'extend_existing':True}) |
|
1132 | __table_args__ = ({'extend_existing':True}) | |
1133 |
|
1133 | |||
1134 | TYPE_CHANGESET_COMMENT = 'cs_comment' |
|
1134 | TYPE_CHANGESET_COMMENT = u'cs_comment' | |
1135 | TYPE_MESSAGE = 'message' |
|
1135 | TYPE_MESSAGE = u'message' | |
1136 | TYPE_MENTION = 'mention' |
|
1136 | TYPE_MENTION = u'mention' | |
1137 |
|
1137 | |||
1138 | notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True) |
|
1138 | notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True) | |
1139 | subject = Column('subject', Unicode(512), nullable=True) |
|
1139 | subject = Column('subject', Unicode(512), nullable=True) | |
@@ -1142,9 +1142,10 b' class Notification(Base, BaseModel):' | |||||
1142 | created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now) |
|
1142 | created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now) | |
1143 | type_ = Column('type', Unicode(256)) |
|
1143 | type_ = Column('type', Unicode(256)) | |
1144 |
|
1144 | |||
1145 | create_by_user = relationship('User') |
|
1145 | created_by_user = relationship('User') | |
1146 |
|
|
1146 | notifications_to_users = relationship('UserNotification', | |
1147 |
primaryjoin |
|
1147 | primaryjoin='Notification.notification_id==UserNotification.notification_id', | |
|
1148 | lazy='joined', | |||
1148 | cascade = "all, delete, delete-orphan") |
|
1149 | cascade = "all, delete, delete-orphan") | |
1149 |
|
1150 | |||
1150 | @property |
|
1151 | @property | |
@@ -1158,16 +1159,20 b' class Notification(Base, BaseModel):' | |||||
1158 | type_ = Notification.TYPE_MESSAGE |
|
1159 | type_ = Notification.TYPE_MESSAGE | |
1159 |
|
1160 | |||
1160 | notification = cls() |
|
1161 | notification = cls() | |
1161 | notification.create_by_user = created_by |
|
1162 | notification.created_by_user = created_by | |
1162 | notification.subject = subject |
|
1163 | notification.subject = subject | |
1163 | notification.body = body |
|
1164 | notification.body = body | |
1164 | notification.type_ = type_ |
|
1165 | notification.type_ = type_ | |
1165 | Session.add(notification) |
|
1166 | Session.add(notification) | |
1166 | for u in recipients: |
|
1167 | for u in recipients: | |
1167 | u.notifications.append(notification) |
|
1168 | u.notifications.append(notification) | |
1168 | Session.commit() |
|
|||
1169 | return notification |
|
1169 | return notification | |
1170 |
|
1170 | |||
|
1171 | @property | |||
|
1172 | def description(self): | |||
|
1173 | from rhodecode.model.notification import NotificationModel | |||
|
1174 | return NotificationModel().make_description(self) | |||
|
1175 | ||||
1171 | class UserNotification(Base, BaseModel): |
|
1176 | class UserNotification(Base, BaseModel): | |
1172 | __tablename__ = 'user_to_notification' |
|
1177 | __tablename__ = 'user_to_notification' | |
1173 | __table_args__ = (UniqueConstraint('user_id', 'notification_id'), |
|
1178 | __table_args__ = (UniqueConstraint('user_id', 'notification_id'), | |
@@ -1179,9 +1184,12 b' class UserNotification(Base, BaseModel):' | |||||
1179 | sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None) |
|
1184 | sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None) | |
1180 |
|
1185 | |||
1181 | user = relationship('User', single_parent=True, lazy="joined") |
|
1186 | user = relationship('User', single_parent=True, lazy="joined") | |
1182 | notification = relationship('Notification',single_parent=True, |
|
1187 | notification = relationship('Notification', single_parent=True,) | |
1183 | cascade="all, delete, delete-orphan") |
|
|||
1184 |
|
1188 | |||
|
1189 | def mark_as_read(self): | |||
|
1190 | self.read = True | |||
|
1191 | Session.add(self) | |||
|
1192 | Session.commit() | |||
1185 |
|
1193 | |||
1186 | class DbMigrateVersion(Base, BaseModel): |
|
1194 | class DbMigrateVersion(Base, BaseModel): | |
1187 | __tablename__ = 'db_migrate_version' |
|
1195 | __tablename__ = 'db_migrate_version' |
@@ -15,7 +15,8 b' cache_manager = cache.CacheManager()' | |||||
15 | # |
|
15 | # | |
16 | Session = scoped_session( |
|
16 | Session = scoped_session( | |
17 | sessionmaker( |
|
17 | sessionmaker( | |
18 | query_cls=caching_query.query_callable(cache_manager) |
|
18 | query_cls = caching_query.query_callable(cache_manager), | |
|
19 | expire_on_commit = True, | |||
19 | ) |
|
20 | ) | |
20 | ) |
|
21 | ) | |
21 |
|
22 |
@@ -29,15 +29,38 b' import traceback' | |||||
29 |
|
29 | |||
30 | from pylons.i18n.translation import _ |
|
30 | from pylons.i18n.translation import _ | |
31 |
|
31 | |||
32 |
from rhodecode.lib import |
|
32 | from rhodecode.lib.helpers import age | |
33 | from rhodecode.lib.caching_query import FromCache |
|
|||
34 |
|
33 | |||
35 | from rhodecode.model import BaseModel |
|
34 | from rhodecode.model import BaseModel | |
36 | from rhodecode.model.db import Notification, User, UserNotification |
|
35 | from rhodecode.model.db import Notification, User, UserNotification | |
37 |
|
36 | |||
|
37 | log = logging.getLogger(__name__) | |||
38 |
|
38 | |||
39 | class NotificationModel(BaseModel): |
|
39 | class NotificationModel(BaseModel): | |
40 |
|
40 | |||
|
41 | ||||
|
42 | def __get_user(self, user): | |||
|
43 | if isinstance(user, User): | |||
|
44 | return user | |||
|
45 | elif isinstance(user, basestring): | |||
|
46 | return User.get_by_username(username=user) | |||
|
47 | elif isinstance(user, int): | |||
|
48 | return User.get(user) | |||
|
49 | else: | |||
|
50 | raise Exception('Unsupported user must be one of int,' | |||
|
51 | 'str or User object') | |||
|
52 | ||||
|
53 | def __get_notification(self, notification): | |||
|
54 | if isinstance(notification, Notification): | |||
|
55 | return notification | |||
|
56 | elif isinstance(notification, int): | |||
|
57 | return Notification.get(notification) | |||
|
58 | else: | |||
|
59 | if notification: | |||
|
60 | raise Exception('notification must be int or Instance' | |||
|
61 | ' of Notification got %s' % type(notification)) | |||
|
62 | ||||
|
63 | ||||
41 | def create(self, created_by, subject, body, recipients, |
|
64 | def create(self, created_by, subject, body, recipients, | |
42 | type_=Notification.TYPE_MESSAGE): |
|
65 | type_=Notification.TYPE_MESSAGE): | |
43 | """ |
|
66 | """ | |
@@ -55,37 +78,61 b' class NotificationModel(BaseModel):' | |||||
55 | if not getattr(recipients, '__iter__', False): |
|
78 | if not getattr(recipients, '__iter__', False): | |
56 | raise Exception('recipients must be a list of iterable') |
|
79 | raise Exception('recipients must be a list of iterable') | |
57 |
|
80 | |||
58 | created_by_obj = created_by |
|
81 | created_by_obj = self.__get_user(created_by) | |
59 | if not isinstance(created_by, User): |
|
|||
60 | created_by_obj = User.get(created_by) |
|
|||
61 |
|
||||
62 |
|
82 | |||
63 | recipients_objs = [] |
|
83 | recipients_objs = [] | |
64 | for u in recipients: |
|
84 | for u in recipients: | |
65 | if isinstance(u, User): |
|
85 | recipients_objs.append(self.__get_user(u)) | |
66 |
|
|
86 | recipients_objs = set(recipients_objs) | |
67 | elif isinstance(u, basestring): |
|
87 | return Notification.create(created_by=created_by_obj, subject=subject, | |
68 | recipients_objs.append(User.get_by_username(username=u)) |
|
88 | body=body, recipients=recipients_objs, | |
69 | elif isinstance(u, int): |
|
|||
70 | recipients_objs.append(User.get(u)) |
|
|||
71 | else: |
|
|||
72 | raise Exception('Unsupported recipient must be one of int,' |
|
|||
73 | 'str or User object') |
|
|||
74 |
|
||||
75 | Notification.create(created_by=created_by_obj, subject=subject, |
|
|||
76 | body = body, recipients = recipients_objs, |
|
|||
77 | type_=type_) |
|
89 | type_=type_) | |
78 |
|
90 | |||
|
91 | def delete(self, notification_id): | |||
|
92 | # we don't want to remove actuall notification just the assignment | |||
|
93 | try: | |||
|
94 | notification_id = int(notification_id) | |||
|
95 | no = self.__get_notification(notification_id) | |||
|
96 | if no: | |||
|
97 | UserNotification.delete(no.notifications_to_users.user_to_notification_id) | |||
|
98 | return True | |||
|
99 | except Exception: | |||
|
100 | log.error(traceback.format_exc()) | |||
|
101 | raise | |||
79 |
|
102 | |||
80 | def get_for_user(self, user_id): |
|
103 | def get_for_user(self, user_id): | |
81 | return User.get(user_id).notifications |
|
104 | return User.get(user_id).notifications | |
82 |
|
105 | |||
83 | def get_unread_cnt_for_user(self, user_id): |
|
106 | def get_unread_cnt_for_user(self, user_id): | |
84 | return UserNotification.query()\ |
|
107 | return UserNotification.query()\ | |
85 |
.filter(UserNotification. |
|
108 | .filter(UserNotification.read == False)\ | |
86 | .filter(UserNotification.user_id == user_id).count() |
|
109 | .filter(UserNotification.user_id == user_id).count() | |
87 |
|
110 | |||
88 | def get_unread_for_user(self, user_id): |
|
111 | def get_unread_for_user(self, user_id): | |
89 | return [x.notification for x in UserNotification.query()\ |
|
112 | return [x.notification for x in UserNotification.query()\ | |
90 |
.filter(UserNotification. |
|
113 | .filter(UserNotification.read == False)\ | |
91 | .filter(UserNotification.user_id == user_id).all()] |
|
114 | .filter(UserNotification.user_id == user_id).all()] | |
|
115 | ||||
|
116 | def get_user_notification(self, user, notification): | |||
|
117 | user = self.__get_user(user) | |||
|
118 | notification = self.__get_notification(notification) | |||
|
119 | ||||
|
120 | return UserNotification.query()\ | |||
|
121 | .filter(UserNotification.notification == notification)\ | |||
|
122 | .filter(UserNotification.user == user).scalar() | |||
|
123 | ||||
|
124 | def make_description(self, notification): | |||
|
125 | """ | |||
|
126 | Creates a human readable description based on properties | |||
|
127 | of notification object | |||
|
128 | """ | |||
|
129 | ||||
|
130 | _map = {notification.TYPE_CHANGESET_COMMENT:_('commented on commit'), | |||
|
131 | notification.TYPE_MESSAGE:_('sent message'), | |||
|
132 | notification.TYPE_MENTION:_('mentioned you')} | |||
|
133 | ||||
|
134 | tmpl = "%(user)s %(action)s %(when)s" | |||
|
135 | data = dict(user=notification.created_by_user.username, | |||
|
136 | action=_map[notification.type_], | |||
|
137 | when=age(notification.created_on)) | |||
|
138 | return tmpl % data |
@@ -2603,7 +2603,8 b' div.gravatar {' | |||||
2603 | border: 0px solid #D0D0D0; |
|
2603 | border: 0px solid #D0D0D0; | |
2604 | float: left; |
|
2604 | float: left; | |
2605 | margin-right: 0.7em; |
|
2605 | margin-right: 0.7em; | |
2606 |
padding: 2px 2px |
|
2606 | padding: 2px 2px 2px 2px; | |
|
2607 | line-height:0; | |||
2607 | -webkit-border-radius: 6px; |
|
2608 | -webkit-border-radius: 6px; | |
2608 | -khtml-border-radius: 6px; |
|
2609 | -khtml-border-radius: 6px; | |
2609 | -moz-border-radius: 6px; |
|
2610 | -moz-border-radius: 6px; | |
@@ -3481,4 +3482,29 b' form.comment-inline-form {' | |||||
3481 | } |
|
3482 | } | |
3482 | .notifications a:hover{ |
|
3483 | .notifications a:hover{ | |
3483 | text-decoration: none !important; |
|
3484 | text-decoration: none !important; | |
|
3485 | } | |||
|
3486 | .notification-header{ | |||
|
3487 | ||||
|
3488 | } | |||
|
3489 | .notification-header .desc{ | |||
|
3490 | font-size: 16px; | |||
|
3491 | height: 24px; | |||
|
3492 | padding-top: 6px; | |||
|
3493 | float: left | |||
|
3494 | } | |||
|
3495 | ||||
|
3496 | .notification-header .desc.unread{ | |||
|
3497 | font-weight: bold; | |||
|
3498 | font-size: 17px; | |||
|
3499 | } | |||
|
3500 | ||||
|
3501 | .notification-header .delete-notifications{ | |||
|
3502 | float: right; | |||
|
3503 | padding-top: 8px; | |||
|
3504 | cursor: pointer; | |||
|
3505 | } | |||
|
3506 | .notification-subject{ | |||
|
3507 | clear:both; | |||
|
3508 | border-bottom: 1px solid #eee; | |||
|
3509 | padding:5px 0px 5px 38px; | |||
3484 | } No newline at end of file |
|
3510 | } |
@@ -563,3 +563,19 b' var getSelectionLink = function(selecti' | |||||
563 | } |
|
563 | } | |
564 | } |
|
564 | } | |
565 | }; |
|
565 | }; | |
|
566 | ||||
|
567 | var deleteNotification = function(url, notification_id){ | |||
|
568 | var callback = { | |||
|
569 | success:function(o){ | |||
|
570 | var obj = YUD.get(String("notification_"+notification_id)); | |||
|
571 | obj.parentNode.removeChild(obj); | |||
|
572 | }, | |||
|
573 | failure:function(o){ | |||
|
574 | alert("error"); | |||
|
575 | }, | |||
|
576 | }; | |||
|
577 | var postData = '_method=delete'; | |||
|
578 | var sUrl = url.replace('__NOTIFICATION_ID__',notification_id); | |||
|
579 | var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, | |||
|
580 | callback, postData); | |||
|
581 | }; |
@@ -25,14 +25,36 b'' | |||||
25 | </ul> |
|
25 | </ul> | |
26 | </div> |
|
26 | </div> | |
27 | % if c.notifications: |
|
27 | % if c.notifications: | |
|
28 | <% | |||
|
29 | unread = lambda n:{False:'unread'}.get(n) | |||
|
30 | %> | |||
|
31 | <div class="table"> | |||
28 | %for notification in c.notifications: |
|
32 | %for notification in c.notifications: | |
29 | <div class="table"> |
|
33 | <div id="notification_${notification.notification_id}"> | |
30 | <h4>${notification.subject}</h4> |
|
34 | <div class="notification-header"> | |
31 | <div>${h.rst(notification.body)}</div> |
|
35 | <div class="gravatar"> | |
|
36 | <img alt="gravatar" src="${h.gravatar_url(h.email(notification.created_by_user.email),24)}"/> | |||
|
37 | </div> | |||
|
38 | <div class="desc"> | |||
|
39 | <a href="${url('notification', notification_id=notification.notification_id)}">${notification.description}</a> | |||
|
40 | </div> | |||
|
41 | <div class="delete-notifications"> | |||
|
42 | <span id="${notification.notification_id}" class="delete-notification delete_icon action"></span> | |||
|
43 | </div> | |||
32 | </div> |
|
44 | </div> | |
|
45 | <div class="notification-subject">${h.urlify_text(notification.subject)}</div> | |||
|
46 | </div> | |||
33 | %endfor |
|
47 | %endfor | |
|
48 | </div> | |||
34 | %else: |
|
49 | %else: | |
35 | <div class="table">${_('No notifications here yet')}</div> |
|
50 | <div class="table">${_('No notifications here yet')}</div> | |
36 | %endif |
|
51 | %endif | |
37 |
</div> |
|
52 | </div> | |
|
53 | <script type="text/javascript"> | |||
|
54 | var url = "${url('notification', notification_id='__NOTIFICATION_ID__')}"; | |||
|
55 | YUE.on(YUQ('.delete-notification'),'click',function(e){ | |||
|
56 | var notification_id = e.currentTarget.id; | |||
|
57 | deleteNotification(url,notification_id) | |||
|
58 | }) | |||
|
59 | </script> | |||
38 | </%def> |
|
60 | </%def> |
@@ -53,7 +53,7 b'' | |||||
53 | ${h.link_to(c.rhodecode_user.username,h.url('admin_settings_my_account'),title='%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname))} |
|
53 | ${h.link_to(c.rhodecode_user.username,h.url('admin_settings_my_account'),title='%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname))} | |
54 | </div> |
|
54 | </div> | |
55 | <div class="notifications"> |
|
55 | <div class="notifications"> | |
56 |
<a href="${h.url(' |
|
56 | <a href="${h.url('notifications')}">${c.unread_notifications}</a> | |
57 | </div> |
|
57 | </div> | |
58 | %endif |
|
58 | %endif | |
59 | </div> |
|
59 | </div> |
@@ -9,6 +9,7 b' setup-app`) and provides the base testin' | |||||
9 | """ |
|
9 | """ | |
10 | import os |
|
10 | import os | |
11 | import time |
|
11 | import time | |
|
12 | import logging | |||
12 | from os.path import join as jn |
|
13 | from os.path import join as jn | |
13 |
|
14 | |||
14 | from unittest import TestCase |
|
15 | from unittest import TestCase | |
@@ -20,7 +21,8 b' from routes.util import URLGenerator' | |||||
20 | from webtest import TestApp |
|
21 | from webtest import TestApp | |
21 |
|
22 | |||
22 | from rhodecode.model import meta |
|
23 | from rhodecode.model import meta | |
23 | import logging |
|
24 | from rhodecode.model.db import User | |
|
25 | ||||
24 | import pylons.test |
|
26 | import pylons.test | |
25 |
|
27 | |||
26 | os.environ['TZ'] = 'UTC' |
|
28 | os.environ['TZ'] = 'UTC' | |
@@ -68,10 +70,11 b' class TestController(TestCase):' | |||||
68 |
|
70 | |||
69 | def log_user(self, username=TEST_USER_ADMIN_LOGIN, |
|
71 | def log_user(self, username=TEST_USER_ADMIN_LOGIN, | |
70 | password=TEST_USER_ADMIN_PASS): |
|
72 | password=TEST_USER_ADMIN_PASS): | |
|
73 | self._logged_username = username | |||
71 | response = self.app.post(url(controller='login', action='index'), |
|
74 | response = self.app.post(url(controller='login', action='index'), | |
72 | {'username':username, |
|
75 | {'username':username, | |
73 | 'password':password}) |
|
76 | 'password':password}) | |
74 |
|
77 | |||
75 | if 'invalid user name' in response.body: |
|
78 | if 'invalid user name' in response.body: | |
76 | self.fail('could not login using %s %s' % (username, password)) |
|
79 | self.fail('could not login using %s %s' % (username, password)) | |
77 |
|
80 | |||
@@ -79,6 +82,10 b' class TestController(TestCase):' | |||||
79 | self.assertEqual(response.session['rhodecode_user'].username, username) |
|
82 | self.assertEqual(response.session['rhodecode_user'].username, username) | |
80 | return response.follow() |
|
83 | return response.follow() | |
81 |
|
84 | |||
|
85 | def _get_logged_user(self): | |||
|
86 | return User.get_by_username(self._logged_username) | |||
|
87 | ||||
|
88 | ||||
82 | def checkSessionFlash(self, response, msg): |
|
89 | def checkSessionFlash(self, response, msg): | |
83 | self.assertTrue('flash' in response.session) |
|
90 | self.assertTrue('flash' in response.session) | |
84 | self.assertTrue(msg in response.session['flash'][0][1]) |
|
91 | self.assertTrue(msg in response.session['flash'][0][1]) |
@@ -161,28 +161,35 b' class TestNotifications(unittest.TestCas' | |||||
161 |
|
161 | |||
162 |
|
162 | |||
163 | def setUp(self): |
|
163 | def setUp(self): | |
164 | self.u1 = UserModel().create_or_update(username='u1', password='qweqwe', |
|
164 | self.u1 = UserModel().create_or_update(username=u'u1', password=u'qweqwe', | |
165 | email='u1@rhodecode.org', |
|
165 | email=u'u1@rhodecode.org', | |
166 | name='u1', lastname='u1') |
|
166 | name=u'u1', lastname=u'u1') | |
167 | self.u2 = UserModel().create_or_update(username='u2', password='qweqwe', |
|
167 | self.u2 = UserModel().create_or_update(username=u'u2', password=u'qweqwe', | |
168 | email='u2@rhodecode.org', |
|
168 | email=u'u2@rhodecode.org', | |
169 | name='u2', lastname='u3') |
|
169 | name=u'u2', lastname=u'u3') | |
170 | self.u3 = UserModel().create_or_update(username='u3', password='qweqwe', |
|
170 | self.u3 = UserModel().create_or_update(username=u'u3', password=u'qweqwe', | |
171 | email='u3@rhodecode.org', |
|
171 | email=u'u3@rhodecode.org', | |
172 | name='u3', lastname='u3') |
|
172 | name=u'u3', lastname=u'u3') | |
173 |
|
173 | def tearDown(self): | ||
|
174 | User.delete(self.u1.user_id) | |||
|
175 | User.delete(self.u2.user_id) | |||
|
176 | User.delete(self.u3.user_id) | |||
174 |
|
177 | |||
175 |
|
178 | |||
176 | def test_create_notification(self): |
|
179 | def test_create_notification(self): | |
177 | usrs = [self.u1, self.u2] |
|
180 | usrs = [self.u1, self.u2] | |
178 | notification = Notification.create(created_by=self.u1, |
|
181 | notification = Notification.create(created_by=self.u1, | |
179 | subject='subj', body='hi there', |
|
182 | subject=u'subj', body=u'hi there', | |
180 | recipients=usrs) |
|
183 | recipients=usrs) | |
|
184 | Session.commit() | |||
181 |
|
185 | |||
182 | notifications = Session.query(Notification).all() |
|
186 | ||
|
187 | notifications = Notification.query().all() | |||
|
188 | self.assertEqual(len(notifications), 1) | |||
|
189 | ||||
183 | unotification = UserNotification.query()\ |
|
190 | unotification = UserNotification.query()\ | |
184 | .filter(UserNotification.notification == notification).all() |
|
191 | .filter(UserNotification.notification == notification).all() | |
185 | self.assertEqual(len(notifications), 1) |
|
192 | ||
186 | self.assertEqual(notifications[0].recipients, [self.u1, self.u2]) |
|
193 | self.assertEqual(notifications[0].recipients, [self.u1, self.u2]) | |
187 | self.assertEqual(notification.notification_id, |
|
194 | self.assertEqual(notification.notification_id, | |
188 | notifications[0].notification_id) |
|
195 | notifications[0].notification_id) | |
@@ -192,21 +199,23 b' class TestNotifications(unittest.TestCas' | |||||
192 |
|
199 | |||
193 | def test_user_notifications(self): |
|
200 | def test_user_notifications(self): | |
194 | notification1 = Notification.create(created_by=self.u1, |
|
201 | notification1 = Notification.create(created_by=self.u1, | |
195 | subject='subj', body='hi there', |
|
202 | subject=u'subj', body=u'hi there', | |
196 | recipients=[self.u3]) |
|
203 | recipients=[self.u3]) | |
197 | notification2 = Notification.create(created_by=self.u1, |
|
204 | notification2 = Notification.create(created_by=self.u1, | |
198 | subject='subj', body='hi there', |
|
205 | subject=u'subj', body=u'hi there', | |
199 | recipients=[self.u3]) |
|
206 | recipients=[self.u3]) | |
200 | self.assertEqual(self.u3.notifications, [notification1, notification2]) |
|
207 | self.assertEqual(self.u3.notifications, [notification1, notification2]) | |
201 |
|
208 | |||
202 | def test_delete_notifications(self): |
|
209 | def test_delete_notifications(self): | |
203 | notification = Notification.create(created_by=self.u1, |
|
210 | notification = Notification.create(created_by=self.u1, | |
204 | subject='title', body='hi there3', |
|
211 | subject=u'title', body=u'hi there3', | |
205 | recipients=[self.u3, self.u1, self.u2]) |
|
212 | recipients=[self.u3, self.u1, self.u2]) | |
|
213 | Session.commit() | |||
206 | notifications = Notification.query().all() |
|
214 | notifications = Notification.query().all() | |
207 | self.assertTrue(notification in notifications) |
|
215 | self.assertTrue(notification in notifications) | |
208 |
|
216 | |||
209 | Notification.delete(notification.notification_id) |
|
217 | Notification.delete(notification.notification_id) | |
|
218 | Session.commit() | |||
210 |
|
219 | |||
211 | notifications = Notification.query().all() |
|
220 | notifications = Notification.query().all() | |
212 | self.assertFalse(notification in notifications) |
|
221 | self.assertFalse(notification in notifications) | |
@@ -214,8 +223,3 b' class TestNotifications(unittest.TestCas' | |||||
214 | un = UserNotification.query().filter(UserNotification.notification |
|
223 | un = UserNotification.query().filter(UserNotification.notification | |
215 | == notification).all() |
|
224 | == notification).all() | |
216 | self.assertEqual(un, []) |
|
225 | self.assertEqual(un, []) | |
217 |
|
||||
218 | def tearDown(self): |
|
|||
219 | User.delete(self.u1.user_id) |
|
|||
220 | User.delete(self.u2.user_id) |
|
|||
221 | User.delete(self.u3.user_id) |
|
General Comments 0
You need to be logged in to leave comments.
Login now