##// END OF EJS Templates
notifications: ported views to pyramid
marcink -
r1920:d6d649b2 default
parent child Browse files
Show More
@@ -0,0 +1,19 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
@@ -0,0 +1,198 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22
23 from pyramid.httpexceptions import (
24 HTTPFound, HTTPNotFound, HTTPInternalServerError)
25 from pyramid.view import view_config
26
27 from rhodecode.apps._base import BaseAppView
28 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
29
30 from rhodecode.lib import helpers as h
31 from rhodecode.lib.helpers import Page
32 from rhodecode.lib.utils2 import safe_int
33 from rhodecode.model.db import Notification
34 from rhodecode.model.notification import NotificationModel
35 from rhodecode.model.meta import Session
36
37
38 log = logging.getLogger(__name__)
39
40
41 class MyAccountNotificationsView(BaseAppView):
42
43 def load_default_context(self):
44 c = self._get_local_tmpl_context()
45 c.user = c.auth_user.get_instance()
46 self._register_global_c(c)
47 return c
48
49 def _has_permissions(self, notification):
50 def is_owner():
51 user_id = self._rhodecode_db_user.user_id
52 for user_notification in notification.notifications_to_users:
53 if user_notification.user.user_id == user_id:
54 return True
55 return False
56 return h.HasPermissionAny('hg.admin')() or is_owner()
57
58 @LoginRequired()
59 @NotAnonymous()
60 @view_config(
61 route_name='notifications_show_all', request_method='GET',
62 renderer='rhodecode:templates/admin/notifications/notifications_show_all.mako')
63 def notifications_show_all(self):
64 c = self.load_default_context()
65
66 c.unread_count = NotificationModel().get_unread_cnt_for_user(
67 self._rhodecode_db_user.user_id)
68
69 _current_filter = self.request.GET.getall('type') or ['unread']
70
71 notifications = NotificationModel().get_for_user(
72 self._rhodecode_db_user.user_id,
73 filter_=_current_filter)
74
75 p = safe_int(self.request.GET.get('page', 1), 1)
76
77 def url_generator(**kw):
78 _query = self.request.GET.mixed()
79 _query.update(kw)
80 return self.request.current_route_path(_query=_query)
81
82 c.notifications = Page(notifications, page=p, items_per_page=10,
83 url=url_generator)
84
85 c.unread_type = 'unread'
86 c.all_type = 'all'
87 c.pull_request_type = Notification.TYPE_PULL_REQUEST
88 c.comment_type = [Notification.TYPE_CHANGESET_COMMENT,
89 Notification.TYPE_PULL_REQUEST_COMMENT]
90
91 c.current_filter = 'unread' # default filter
92
93 if _current_filter == [c.pull_request_type]:
94 c.current_filter = 'pull_request'
95 elif _current_filter == c.comment_type:
96 c.current_filter = 'comment'
97 elif _current_filter == [c.unread_type]:
98 c.current_filter = 'unread'
99 elif _current_filter == [c.all_type]:
100 c.current_filter = 'all'
101 return self._get_template_context(c)
102
103 @LoginRequired()
104 @NotAnonymous()
105 @CSRFRequired()
106 @view_config(
107 route_name='notifications_mark_all_read', request_method='POST',
108 renderer='rhodecode:templates/admin/notifications/notifications_show_all.mako')
109 def notifications_mark_all_read(self):
110 NotificationModel().mark_all_read_for_user(
111 self._rhodecode_db_user.user_id,
112 filter_=self.request.GET.getall('type'))
113 Session().commit()
114 raise HTTPFound(h.route_path('notifications_show_all'))
115
116 @LoginRequired()
117 @NotAnonymous()
118 @view_config(
119 route_name='notifications_show', request_method='GET',
120 renderer='rhodecode:templates/admin/notifications/notifications_show.mako')
121 def notifications_show(self):
122 c = self.load_default_context()
123 notification_id = self.request.matchdict['notification_id']
124 notification = Notification.get_or_404(notification_id)
125
126 if not self._has_permissions(notification):
127 log.debug('User %s does not have permission to access notification',
128 self._rhodecode_user)
129 raise HTTPNotFound()
130
131 u_notification = NotificationModel().get_user_notification(
132 self._rhodecode_db_user.user_id, notification)
133 if not u_notification:
134 log.debug('User %s notification does not exist',
135 self._rhodecode_user)
136 raise HTTPNotFound()
137
138 # when opening this notification, mark it as read for this use
139 if not u_notification.read:
140 u_notification.mark_as_read()
141 Session().commit()
142
143 c.notification = notification
144
145 return self._get_template_context(c)
146
147 @LoginRequired()
148 @NotAnonymous()
149 @CSRFRequired()
150 @view_config(
151 route_name='notifications_update', request_method='POST',
152 renderer='json_ext')
153 def notification_update(self):
154 notification_id = self.request.matchdict['notification_id']
155 notification = Notification.get_or_404(notification_id)
156
157 if not self._has_permissions(notification):
158 log.debug('User %s does not have permission to access notification',
159 self._rhodecode_user)
160 raise HTTPNotFound()
161
162 try:
163 # updates notification read flag
164 NotificationModel().mark_read(
165 self._rhodecode_user.user_id, notification)
166 Session().commit()
167 return 'ok'
168 except Exception:
169 Session().rollback()
170 log.exception("Exception updating a notification item")
171
172 raise HTTPInternalServerError()
173
174 @LoginRequired()
175 @NotAnonymous()
176 @CSRFRequired()
177 @view_config(
178 route_name='notifications_delete', request_method='POST',
179 renderer='json_ext')
180 def notification_delete(self):
181 notification_id = self.request.matchdict['notification_id']
182 notification = Notification.get_or_404(notification_id)
183 if not self._has_permissions(notification):
184 log.debug('User %s does not have permission to access notification',
185 self._rhodecode_user)
186 raise HTTPNotFound()
187
188 try:
189 # deletes only notification2user
190 NotificationModel().delete(
191 self._rhodecode_user.user_id, notification)
192 Session().commit()
193 return 'ok'
194 except Exception:
195 Session().rollback()
196 log.exception("Exception deleting a notification item")
197
198 raise HTTPInternalServerError()
@@ -95,6 +95,28 b' def includeme(config):'
95 name='my_account_pullrequests_data',
95 name='my_account_pullrequests_data',
96 pattern=ADMIN_PREFIX + '/my_account/pull_requests/data')
96 pattern=ADMIN_PREFIX + '/my_account/pull_requests/data')
97
97
98 # notifications
99 config.add_route(
100 name='notifications_show_all',
101 pattern=ADMIN_PREFIX + '/notifications')
102
103 # notifications
104 config.add_route(
105 name='notifications_mark_all_read',
106 pattern=ADMIN_PREFIX + '/notifications/mark_all_read')
107
108 config.add_route(
109 name='notifications_show',
110 pattern=ADMIN_PREFIX + '/notifications/{notification_id}')
111
112 config.add_route(
113 name='notifications_update',
114 pattern=ADMIN_PREFIX + '/notifications/{notification_id}/update')
115
116 config.add_route(
117 name='notifications_delete',
118 pattern=ADMIN_PREFIX + '/notifications/{notification_id}/delete')
119
98 # channelstream test
120 # channelstream test
99 config.add_route(
121 config.add_route(
100 name='my_account_notifications_test_channelstream',
122 name='my_account_notifications_test_channelstream',
@@ -20,7 +20,10 b''
20
20
21 import pytest
21 import pytest
22
22
23 from rhodecode.tests import *
23 from rhodecode.apps._base import ADMIN_PREFIX
24 from rhodecode.tests import (
25 TestController, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
26 TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS)
24 from rhodecode.tests.fixture import Fixture
27 from rhodecode.tests.fixture import Fixture
25
28
26 from rhodecode.model.db import Notification, User
29 from rhodecode.model.db import Notification, User
@@ -31,12 +34,25 b' from rhodecode.model.meta import Session'
31 fixture = Fixture()
34 fixture = Fixture()
32
35
33
36
34 class TestNotificationsController(TestController):
37 def route_path(name, params=None, **kwargs):
35 destroy_users = set()
38 import urllib
39 from rhodecode.apps._base import ADMIN_PREFIX
36
40
37 @classmethod
41 base_url = {
38 def teardown_class(cls):
42 'notifications_show_all': ADMIN_PREFIX + '/notifications',
39 fixture.destroy_users(cls.destroy_users)
43 'notifications_mark_all_read': ADMIN_PREFIX + '/notifications/mark_all_read',
44 'notifications_show': ADMIN_PREFIX + '/notifications/{notification_id}',
45 'notifications_update': ADMIN_PREFIX + '/notifications/{notification_id}/update',
46 'notifications_delete': ADMIN_PREFIX + '/notifications/{notification_id}/delete',
47
48 }[name].format(**kwargs)
49
50 if params:
51 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
52 return base_url
53
54
55 class TestNotificationsController(TestController):
40
56
41 def teardown_method(self, method):
57 def teardown_method(self, method):
42 for n in Notification.query().all():
58 for n in Notification.query().all():
@@ -44,43 +60,61 b' class TestNotificationsController(TestCo'
44 Session().delete(inst)
60 Session().delete(inst)
45 Session().commit()
61 Session().commit()
46
62
47 def test_index(self):
63 def test_show_all(self, user_util):
48 u1 = UserModel().create_or_update(
64 user = user_util.create_user(password='qweqwe')
49 username='u1', password='qweqwe', email='u1@rhodecode.org',
65 user_id = user.user_id
50 firstname='u1', lastname='u1')
66 self.log_user(user.username, 'qweqwe')
51 u1 = u1.user_id
52 self.destroy_users.add('u1')
53
67
54 self.log_user('u1', 'qweqwe')
68 response = self.app.get(
55
69 route_path('notifications_show_all', params={'type': 'all'}))
56 response = self.app.get(url('notifications'))
57 response.mustcontain(
70 response.mustcontain(
58 '<div class="table">No notifications here yet</div>')
71 '<div class="table">No notifications here yet</div>')
59
72
60 cur_user = self._get_logged_user()
73 notification = NotificationModel().create(
61 notif = NotificationModel().create(
74 created_by=user_id, notification_subject=u'test_notification_1',
62 created_by=u1, notification_subject=u'test_notification_1',
75 notification_body=u'notification_1', recipients=[user_id])
63 notification_body=u'notification_1', recipients=[cur_user])
64 Session().commit()
76 Session().commit()
65 response = self.app.get(url('notifications'))
77 notification_id = notification.notification_id
66 response.mustcontain('id="notification_%s"' % notif.notification_id)
78
79 response = self.app.get(route_path('notifications_show_all',
80 params={'type': 'all'}))
81 response.mustcontain('id="notification_%s"' % notification_id)
82
83 def test_show_unread(self, user_util):
84 user = user_util.create_user(password='qweqwe')
85 user_id = user.user_id
86 self.log_user(user.username, 'qweqwe')
87
88 response = self.app.get(route_path('notifications_show_all'))
89 response.mustcontain(
90 '<div class="table">No notifications here yet</div>')
91
92 notification = NotificationModel().create(
93 created_by=user_id, notification_subject=u'test_notification_1',
94 notification_body=u'notification_1', recipients=[user_id])
95
96 # mark the USER notification as unread
97 user_notification = NotificationModel().get_user_notification(
98 user_id, notification)
99 user_notification.read = False
100
101 Session().commit()
102 notification_id = notification.notification_id
103
104 response = self.app.get(route_path('notifications_show_all'))
105 response.mustcontain('id="notification_%s"' % notification_id)
106 response.mustcontain('<div class="desc unread')
67
107
68 @pytest.mark.parametrize('user,password', [
108 @pytest.mark.parametrize('user,password', [
69 (TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS),
109 (TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS),
70 (TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS),
110 (TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS),
71 ])
111 ])
72 def test_delete(self, user, password):
112 def test_delete(self, user, password, user_util):
73 self.log_user(user, password)
113 self.log_user(user, password)
74 cur_user = self._get_logged_user()
114 cur_user = self._get_logged_user()
75
115
76 u1 = UserModel().create_or_update(
116 u1 = user_util.create_user()
77 username='u1', password='qweqwe',
117 u2 = user_util.create_user()
78 email='u1@rhodecode.org', firstname='u1', lastname='u1')
79 u2 = UserModel().create_or_update(
80 username='u2', password='qweqwe', email='u2@rhodecode.org',
81 firstname='u2', lastname='u2')
82 self.destroy_users.add('u1')
83 self.destroy_users.add('u2')
84
118
85 # make notifications
119 # make notifications
86 notification = NotificationModel().create(
120 notification = NotificationModel().create(
@@ -98,9 +132,10 b' class TestNotificationsController(TestCo'
98 cur_usr_id = cur_user.user_id
132 cur_usr_id = cur_user.user_id
99
133
100 response = self.app.post(
134 response = self.app.post(
101 url('notification', notification_id=notification.notification_id),
135 route_path('notifications_delete',
102 params={'_method': 'delete', 'csrf_token': self.csrf_token})
136 notification_id=notification.notification_id),
103 assert response.body == 'ok'
137 params={'csrf_token': self.csrf_token})
138 assert response.json == 'ok'
104
139
105 cur_user = User.get(cur_usr_id)
140 cur_user = User.get(cur_usr_id)
106 assert cur_user.notifications == []
141 assert cur_user.notifications == []
@@ -109,17 +144,11 b' class TestNotificationsController(TestCo'
109 (TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS),
144 (TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS),
110 (TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS),
145 (TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS),
111 ])
146 ])
112 def test_show(self, user, password):
147 def test_show(self, user, password, user_util):
113 self.log_user(user, password)
148 self.log_user(user, password)
114 cur_user = self._get_logged_user()
149 cur_user = self._get_logged_user()
115 u1 = UserModel().create_or_update(username='u1', password='qweqwe',
150 u1 = user_util.create_user()
116 email='u1@rhodecode.org',
151 u2 = user_util.create_user()
117 firstname='u1', lastname='u1')
118 u2 = UserModel().create_or_update(username='u2', password='qweqwe',
119 email='u2@rhodecode.org',
120 firstname='u2', lastname='u2')
121 self.destroy_users.add('u1')
122 self.destroy_users.add('u2')
123
152
124 subject = u'test'
153 subject = u'test'
125 notif_body = u'hi there'
154 notif_body = u'hi there'
@@ -127,8 +156,9 b' class TestNotificationsController(TestCo'
127 created_by=cur_user, notification_subject=subject,
156 created_by=cur_user, notification_subject=subject,
128 notification_body=notif_body, recipients=[cur_user, u1, u2])
157 notification_body=notif_body, recipients=[cur_user, u1, u2])
129
158
130 response = self.app.get(url(
159 response = self.app.get(
131 'notification', notification_id=notification.notification_id))
160 route_path('notifications_show',
161 notification_id=notification.notification_id))
132
162
133 response.mustcontain(subject)
163 response.mustcontain(subject)
134 response.mustcontain(notif_body)
164 response.mustcontain(notif_body)
@@ -137,18 +167,11 b' class TestNotificationsController(TestCo'
137 (TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS),
167 (TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS),
138 (TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS),
168 (TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS),
139 ])
169 ])
140 def test_update(self, user, password):
170 def test_update(self, user, password, user_util):
141 self.log_user(user, password)
171 self.log_user(user, password)
142 cur_user = self._get_logged_user()
172 cur_user = self._get_logged_user()
143
173 u1 = user_util.create_user()
144 u1 = UserModel().create_or_update(username='u1', password='qweqwe',
174 u2 = user_util.create_user()
145 email='u1@rhodecode.org',
146 firstname='u1', lastname='u1')
147 u2 = UserModel().create_or_update(username='u2', password='qweqwe',
148 email='u2@rhodecode.org',
149 firstname='u2', lastname='u2')
150 self.destroy_users.add('u1')
151 self.destroy_users.add('u2')
152
175
153 # make notifications
176 # make notifications
154 recipients = [cur_user, u1, u2]
177 recipients = [cur_user, u1, u2]
@@ -164,9 +187,10 b' class TestNotificationsController(TestCo'
164 assert u_obj.notifications[0].read == read
187 assert u_obj.notifications[0].read == read
165
188
166 response = self.app.post(
189 response = self.app.post(
167 url('notification', notification_id=notification.notification_id),
190 route_path('notifications_update',
168 params={'_method': 'put', 'csrf_token': self.csrf_token})
191 notification_id=notification.notification_id),
169 assert response.body == 'ok'
192 params={'csrf_token': self.csrf_token})
193 assert response.json == 'ok'
170
194
171 cur_user = self._get_logged_user()
195 cur_user = self._get_logged_user()
172 assert True == cur_user.notifications[0].read
196 assert True is cur_user.notifications[0].read
1 NO CONTENT: file renamed from rhodecode/apps/my_account/views.py to rhodecode/apps/my_account/views/my_account.py
NO CONTENT: file renamed from rhodecode/apps/my_account/views.py to rhodecode/apps/my_account/views/my_account.py
@@ -458,20 +458,6 b' def make_map(config):'
458 m.connect('my_account_password', '/my_account/password',
458 m.connect('my_account_password', '/my_account/password',
459 action='my_account_password', conditions={'method': ['GET']})
459 action='my_account_password', conditions={'method': ['GET']})
460
460
461 # NOTIFICATION REST ROUTES
462 with rmap.submapper(path_prefix=ADMIN_PREFIX,
463 controller='admin/notifications') as m:
464 m.connect('notifications', '/notifications',
465 action='index', conditions={'method': ['GET']})
466 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
467 action='mark_all_read', conditions={'method': ['POST']})
468 m.connect('/notifications/{notification_id}',
469 action='update', conditions={'method': ['PUT']})
470 m.connect('/notifications/{notification_id}',
471 action='delete', conditions={'method': ['DELETE']})
472 m.connect('notification', '/notifications/{notification_id}',
473 action='show', conditions={'method': ['GET']})
474
475 # USER JOURNAL
461 # USER JOURNAL
476 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
462 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
477 controller='journal', action='index')
463 controller='journal', action='index')
@@ -166,9 +166,6 b' class NotificationModel(BaseModel):'
166 def get_for_user(self, user, filter_=None):
166 def get_for_user(self, user, filter_=None):
167 """
167 """
168 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
169
170 :param user:
171 :param filter:
172 """
169 """
173 user = self._get_user(user)
170 user = self._get_user(user)
174
171
@@ -177,11 +174,14 b' class NotificationModel(BaseModel):'
177 .join((
174 .join((
178 Notification, UserNotification.notification_id ==
175 Notification, UserNotification.notification_id ==
179 Notification.notification_id))
176 Notification.notification_id))
180
177 if filter_ == ['all']:
181 if filter_:
178 q = q # no filter
179 elif filter_ == ['unread']:
180 q = q.filter(UserNotification.read == false())
181 elif filter_:
182 q = q.filter(Notification.type_.in_(filter_))
182 q = q.filter(Notification.type_.in_(filter_))
183
183
184 return q.all()
184 return q
185
185
186 def mark_read(self, user, notification):
186 def mark_read(self, user, notification):
187 try:
187 try:
@@ -207,7 +207,9 b' class NotificationModel(BaseModel):'
207 .join((
207 .join((
208 Notification, UserNotification.notification_id ==
208 Notification, UserNotification.notification_id ==
209 Notification.notification_id))
209 Notification.notification_id))
210 if filter_:
210 if filter_ == ['unread']:
211 q = q.filter(UserNotification.read == false())
212 elif filter_:
211 q = q.filter(Notification.type_.in_(filter_))
213 q = q.filter(Notification.type_.in_(filter_))
212
214
213 # this is a little inefficient but sqlalchemy doesn't support
215 # this is a little inefficient but sqlalchemy doesn't support
@@ -160,6 +160,11 b' function registerRCRoutes() {'
160 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
160 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
161 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
161 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
162 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
162 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
163 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
164 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
165 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
166 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
167 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
163 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
168 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
164 pyroutes.register('gists_show', '/_admin/gists', []);
169 pyroutes.register('gists_show', '/_admin/gists', []);
165 pyroutes.register('gists_new', '/_admin/gists/new', []);
170 pyroutes.register('gists_new', '/_admin/gists/new', []);
@@ -30,35 +30,37 b' var _run_callbacks = function(callbacks)'
30 }
30 }
31 };
31 };
32
32
33 var deleteNotification = function(url, notification_id,callbacks){
33 var deleteNotification = function(notification_id, callbacks){
34 var callback = function(o){
34 var callback = function(o){
35 var obj = $("#notification_"+notification_id);
35 var obj = $("#notification_"+notification_id);
36 obj.remove();
36 obj.remove();
37 _run_callbacks(callbacks);
37 _run_callbacks(callbacks);
38 };
38 };
39 var postData = {'_method': 'delete', 'csrf_token': CSRF_TOKEN};
39 var postData = {'csrf_token': CSRF_TOKEN};
40 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
40 var sUrl = pyroutes.url('notifications_delete', {'notification_id': notification_id});
41 var request = $.post(sUrl, postData)
41 var request =
42 .done(callback)
42 $.post(sUrl, postData)
43 .fail(function(data, textStatus, errorThrown){
43 .done(callback)
44 alert("Error while deleting notification.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(this)[0].url));
44 .fail(function(data, textStatus, errorThrown){
45 });
45 alert("Error while deleting notification.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(this)[0].url));
46 });
46 };
47 };
47
48
48 var readNotification = function(url, notification_id,callbacks){
49 var readNotification = function(notification_id, callbacks){
49 var callback = function(o){
50 var callback = function(o){
50 var obj = $("#notification_"+notification_id);
51 var obj = $("#notification_"+notification_id);
51 obj.removeClass('unread');
52 obj.removeClass('unread');
52 var r_button = $('.read-notification',obj)[0];
53 var r_button = $('.read-notification', obj)[0];
53 r_button.remove();
54 r_button.remove();
54
55
55 _run_callbacks(callbacks);
56 _run_callbacks(callbacks);
56 };
57 };
57 var postData = {'_method': 'put', 'csrf_token': CSRF_TOKEN};
58 var postData = {'csrf_token': CSRF_TOKEN};
58 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
59 var sUrl = pyroutes.url('notifications_update', {'notification_id': notification_id});
59 var request = $.post(sUrl, postData)
60 var request =
60 .done(callback)
61 $.post(sUrl, postData)
61 .fail(function(data, textStatus, errorThrown){
62 .done(callback)
62 alert("Error while saving notification.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(this)[0].url));
63 .fail(function(data, textStatus, errorThrown){
63 });
64 alert("Error while saving notification.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(this)[0].url));
65 });
64 };
66 };
@@ -1,40 +1,46 b''
1 <%namespace name="base" file="/base/base.mako"/>
1 <%namespace name="base" file="/base/base.mako"/>
2 %if c.notifications:
3 <%
4 unread = lambda n:{False:'unread'}.get(n)
5 %>
6
2
3 <div class="panel panel-default">
4 <div class="panel-heading">
5 <h3 class="panel-title">${_('My notifications')}</h3>
6 </div>
7
8 <div class="panel-body">
9 %if c.notifications:
7
10
8 <div class="notification-list notification-table">
11 <div class="notification-list notification-table">
9 %for notification in c.notifications:
12 %for notification in c.notifications:
10 <div id="notification_${notification.notification.notification_id}" class="container ${unread(notification.read)}">
13 <div id="notification_${notification.notification.notification_id}" class="container ${'unread' if not notification.read else '' }">
11 <div class="notification-header">
14 <div class="notification-header">
12 <div class="desc ${unread(notification.read)}">
15 <div class="desc ${'unread' if not notification.read else '' }">
13 <a href="${h.url('notification', notification_id=notification.notification.notification_id)}">
16 <a href="${h.route_path('notifications_show', notification_id=notification.notification.notification_id)}">
14 ${base.gravatar(notification.notification.created_by_user.email, 16)}
17 ${base.gravatar(notification.notification.created_by_user.email, 16)}
15 ${notification.notification.description}
18 ${notification.notification.description}
16 </a>
19 </a>
17 </div>
20 </div>
18 <div class="delete-notifications">
21 <div class="delete-notifications">
19 <span id="${notification.notification.notification_id}" class="delete-notification"><i class="icon-delete" ></i></span>
22 <span onclick="deleteNotification(${notification.notification.notification_id})" class="delete-notification tooltip" title="${_('Delete')}"><i class="icon-delete"></i></span>
20 </div>
23 </div>
21 <div class="read-notifications">
24 <div class="read-notifications">
22 %if not notification.read:
25 %if not notification.read:
23 <span id="${notification.notification.notification_id}" class="read-notification"><i class="icon-ok" ></i></span>
26 <span onclick="readNotification(${notification.notification.notification_id})" class="read-notification tooltip" title="${_('Mark as read')}"><i class="icon-ok"></i></span>
24 %endif
27 %endif
25 </div>
28 </div>
26 </div>
29 </div>
27 <div class="notification-subject"></div>
30 <div class="notification-subject"></div>
28 </div>
31 </div>
29 %endfor
32 %endfor
30 </div>
33 </div>
31
34
32 <div class="notification-paginator">
35 <div class="notification-paginator">
33 <div class="pagination-wh pagination-left">
36 <div class="pagination-wh pagination-left">
34 ${c.notifications.pager('$link_previous ~2~ $link_next')}
37 ${c.notifications.pager('$link_previous ~2~ $link_next')}
38 </div>
39 </div>
40
41 %else:
42 <div class="table">${_('No notifications here yet')}</div>
43 %endif
44
35 </div>
45 </div>
36 </div>
46 </div>
37
38 %else:
39 <div class="table">${_('No notifications here yet')}</div>
40 %endif
@@ -9,7 +9,7 b''
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Notifications'), h.url('notifications'))}
12 ${h.link_to(_('My Notifications'), h.route_path('notifications_show_all'))}
13 &raquo;
13 &raquo;
14 ${_('Show notification')}
14 ${_('Show notification')}
15 </%def>
15 </%def>
@@ -32,7 +32,7 b''
32 ${c.notification.description}
32 ${c.notification.description}
33 </div>
33 </div>
34 <div class="delete-notifications">
34 <div class="delete-notifications">
35 <span id="${c.notification.notification_id}" class="delete-notification action"><i class="icon-delete" ></i></span>
35 <span class="delete-notification tooltip" title="${_('Delete')}" onclick="deleteNotification(${c.notification.notification_id}, [function(){window.location=pyroutes.url('notifications_show_all')}])" class="delete-notification action"><i class="icon-delete" ></i></span>
36 </div>
36 </div>
37 </div>
37 </div>
38 <div class="notification-body">
38 <div class="notification-body">
@@ -46,12 +46,5 b''
46 </div>
46 </div>
47 </div>
47 </div>
48 </div>
48 </div>
49 <script type="text/javascript">
49
50 var url = "${h.url('notification', notification_id='__NOTIFICATION_ID__')}";
51 var main = "${h.url('notifications')}";
52 $('.delete-notification').on('click',function(e){
53 var notification_id = e.currentTarget.id;
54 deleteNotification(url,notification_id,[function(){window.location=main}])
55 })
56 </script>
57 </%def>
50 </%def>
@@ -13,66 +13,56 b''
13 </%def>
13 </%def>
14
14
15 <%def name="menu_bar_nav()">
15 <%def name="menu_bar_nav()">
16 ${self.menu_items(active='admin')}
16 ${self.menu_items(active='my_account')}
17 </%def>
17 </%def>
18
18
19 <%def name="main()">
19 <%def name="main()">
20 <div class="box">
20 <div class="box">
21 <!-- box / title -->
21 <div class="title">
22 <div class="title">
22 ${self.breadcrumbs()}
23 ${self.breadcrumbs()}
24 ##<ul class="links">
25 ## <li>
26 ## <span ><a href="#">${_('Compose message')}</a></span>
27 ## </li>
28 ##</ul>
29
30 <div class="notifications_buttons">
23 <div class="notifications_buttons">
31 <span id='all' class="action-link first ${'active' if c.current_filter=='all' else ''}"><a href="${h.url.current()}">${_('All')}</a></span>
32 <span id='comment' class="action-link ${'active' if c.current_filter=='comment' else ''}"><a href="${h.url.current(type=c.comment_type)}">${_('Comments')}</a></span>
33 <span id='pull_request' class="action-link last ${'active' if c.current_filter=='pull_request' else ''}"><a href="${h.url.current(type=c.pull_request_type)}">${_('Pull Requests')}</a></span>
34
35 %if c.notifications:
24 %if c.notifications:
36
25 <button id='mark_all_read' class="btn btn-default" type="submit">
37 <span id='mark_all_read' class="btn btn-default">${_('Mark all as read')}</span>
26 ${_('Mark all as read')}
38
27 </button>
28 %else:
29 <button class="btn btn-default" type="submit" disabled="disabled">
30 ${_('Mark all as read')}
31 </button>
39 %endif
32 %endif
40 </div>
33 </div>
34 </div>
35
36 <div class="sidebar-col-wrapper scw-small">
37 ##main
38 <div class="sidebar">
39 <ul class="nav nav-pills nav-stacked">
40 <li id='unread' class="${'active' if c.current_filter=='unread' else ''}"><a href="${h.route_path('notifications_show_all', _query=dict(type=c.unread_type))}">${_('Unread')} (${c.unread_count})</a></li>
41 <li id='all' class="${'active' if c.current_filter=='all' else ''}"><a href="${h.route_path('notifications_show_all', _query=dict(type=c.all_type))}">${_('All')}</a></li>
42 <li id='comment' class="${'active' if c.current_filter=='comment' else ''}"><a href="${h.route_path('notifications_show_all', _query=dict(type=c.comment_type))}">${_('Comments')}</a></li>
43 <li id='pull_request' class="${'active' if c.current_filter=='pull_request' else ''}"><a href="${h.route_path('notifications_show_all', _query=dict(type=c.pull_request_type))}">${_('Pull Requests')}</a></li>
44 </ul>
41 </div>
45 </div>
42 <div id='notification_data' class='main-content-full'>
46
43 <%include file='notifications_data.mako'/>
47 <div class="main-content-full-width">
48 <%include file='notifications_data.mako'/>
49 </div>
44 </div>
50 </div>
45 </div>
51 </div>
52
46 <script type="text/javascript">
53 <script type="text/javascript">
47 var url_action = "${h.url('notification', notification_id='__NOTIFICATION_ID__')}";
48 var run = function(){
49 $('#notification_data').on('click','.delete-notification',function(e){
50 var notification_id = e.currentTarget.id;
51 deleteNotification(url_action,notification_id)
52 });
53 $('#notification_data').on('click','.read-notification',function(e){
54 var notification_id = e.currentTarget.id;
55 readNotification(url_action,notification_id)
56 })
57 };
58 run();
59 $('#mark_all_read').on('click',function(e){
60 //set notifications as read
61 var url = "${h.url('notifications_mark_all_read', **request.GET.mixed())}";
62 $.post(url, {'csrf_token': CSRF_TOKEN}).
63 done(function(data){
64 // hide notifications counter
65 $('#quick_login_link > .menu_link_notifications').hide();
66 $('#notification_data').html(data);
67 })
68 .fail(function(data, textStatus, errorThrown){
69 alert("Error while saving notifications.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(this)[0].url));
70 });
71 });
72
54
73 var current_filter = $("${c.current_filter}");
55 $('#mark_all_read').on('click',function(e){
74 if (current_filter.length){
56 //set notifications as read
75 current_filter.addClass('active');
57 var url = "${h.route_path('notifications_mark_all_read', _query=request.GET.mixed())}";
76 }
58 $.post(url, {'csrf_token': CSRF_TOKEN}).
59 done(function(data){
60 window.location = "${request.current_route_path(_query=request.GET.mixed())}";
61 })
62 .fail(function(data, textStatus, errorThrown){
63 alert("Error while saving notifications.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(this)[0].url));
64 });
65 });
66
77 </script>
67 </script>
78 </%def>
68 </%def>
@@ -359,11 +359,7 b''
359 </div>
359 </div>
360 %if c.rhodecode_user.username != h.DEFAULT_USER:
360 %if c.rhodecode_user.username != h.DEFAULT_USER:
361 <div class="pill_container">
361 <div class="pill_container">
362 % if c.unread_notifications == 0:
362 <a class="menu_link_notifications ${'empty' if c.unread_notifications == 0 else ''}" href="${h.route_path('notifications_show_all')}">${c.unread_notifications}</a>
363 <a class="menu_link_notifications empty" href="${h.url('notifications')}">${c.unread_notifications}</a>
364 % else:
365 <a class="menu_link_notifications" href="${h.url('notifications')}">${c.unread_notifications}</a>
366 % endif
367 </div>
363 </div>
368 % endif
364 % endif
369 </li>
365 </li>
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now